summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xAndroid.bp47
-rw-r--r--StubLibraries.bp6
-rw-r--r--TEST_MAPPING11
-rw-r--r--apct-tests/perftests/autofill/Android.bp9
-rw-r--r--apct-tests/perftests/blobstore/Android.bp11
-rw-r--r--apct-tests/perftests/core/Android.bp9
-rw-r--r--apct-tests/perftests/core/apps/overlay/Android.bp11
-rw-r--r--apct-tests/perftests/core/apps/reources_manager/Android.bp11
-rw-r--r--apct-tests/perftests/core/jni/Android.bp9
-rw-r--r--apct-tests/perftests/core/src/android/app/OWNERS2
-rw-r--r--apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java5
-rw-r--r--apct-tests/perftests/multiuser/Android.bp9
-rw-r--r--apct-tests/perftests/multiuser/apps/dummyapp/Android.bp9
-rw-r--r--apct-tests/perftests/packagemanager/Android.bp9
-rw-r--r--apct-tests/perftests/packagemanager/apps/query-all/Android.bp9
-rw-r--r--apct-tests/perftests/textclassifier/Android.bp9
-rw-r--r--apct-tests/perftests/utils/Android.bp9
-rw-r--r--apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfRunPreconditionBase.java2
-rw-r--r--apct-tests/perftests/windowmanager/Android.bp9
-rw-r--r--apex/Android.bp6
-rw-r--r--apex/appsearch/framework/api/current.txt8
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java42
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java43
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java226
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java56
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/SearchResults.java8
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java1
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java118
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java50
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java39
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java298
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResult.java125
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java42
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java40
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java28
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java4
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java439
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java14
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResponseToProtoConverter.java (renamed from apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResultToProtoConverter.java)24
-rw-r--r--apex/appsearch/synced_jetpack_changeid.txt2
-rw-r--r--apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java14
-rw-r--r--apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java139
-rw-r--r--apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java12
-rw-r--r--apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java45
-rw-r--r--apex/blobstore/framework/Android.bp9
-rw-r--r--apex/blobstore/service/Android.bp9
-rw-r--r--apex/jobscheduler/framework/Android.bp10
-rw-r--r--apex/jobscheduler/framework/java/android/app/AlarmManager.java47
-rw-r--r--apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl1
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobInfo.java21
-rw-r--r--apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java2
-rw-r--r--apex/jobscheduler/service/Android.bp9
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java357
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java11
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java644
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java66
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java289
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java114
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java150
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java49
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java2
-rw-r--r--apex/media/Android.bp13
-rw-r--r--apex/media/aidl/Android.bp9
-rw-r--r--apex/media/framework/Android.bp9
-rw-r--r--apex/media/framework/api/current.txt10
-rw-r--r--apex/media/framework/java/android/media/ApplicationMediaCapabilities.java58
-rw-r--r--apex/media/framework/java/android/media/MediaCommunicationManager.java22
-rw-r--r--apex/media/framework/java/android/media/MediaTranscodeManager.java32
-rw-r--r--api/Android.bp77
-rw-r--r--boot/Android.bp11
-rw-r--r--cmds/am/Android.bp17
-rw-r--r--cmds/app_process/Android.bp17
-rw-r--r--cmds/appops/Android.bp17
-rw-r--r--cmds/appwidget/Android.bp17
-rw-r--r--cmds/backup/Android.bp17
-rw-r--r--cmds/bmgr/Android.bp17
-rw-r--r--cmds/bootanimation/Android.bp9
-rw-r--r--cmds/bootanimation/BootAnimation.cpp22
-rw-r--r--cmds/bu/Android.bp17
-rw-r--r--cmds/content/Android.bp17
-rw-r--r--cmds/device_config/Android.bp11
-rw-r--r--cmds/dpm/Android.bp17
-rw-r--r--cmds/hid/Android.bp17
-rw-r--r--cmds/hid/jni/Android.bp9
-rw-r--r--cmds/idmap2/Android.bp122
-rw-r--r--cmds/idmap2/AndroidTest.xml26
-rw-r--r--cmds/idmap2/idmap2/CommandUtils.cpp17
-rw-r--r--cmds/idmap2/idmap2/Create.cpp25
-rw-r--r--cmds/idmap2/idmap2/CreateMultiple.cpp17
-rw-r--r--cmds/idmap2/idmap2/Lookup.cpp14
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.cpp274
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.h53
-rw-r--r--cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInfo.aidl28
-rw-r--r--cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternal.aidl30
-rw-r--r--cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl26
-rw-r--r--cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl (renamed from cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl)10
-rw-r--r--cmds/idmap2/idmap2d/aidl/services/android/os/OverlayablePolicy.aidl (renamed from cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl)0
-rw-r--r--cmds/idmap2/include/idmap2/FabricatedOverlay.h105
-rw-r--r--cmds/idmap2/include/idmap2/FileUtils.h3
-rw-r--r--cmds/idmap2/include/idmap2/Idmap.h35
-rw-r--r--cmds/idmap2/include/idmap2/PrettyPrintVisitor.h5
-rw-r--r--cmds/idmap2/include/idmap2/RawPrintVisitor.h5
-rw-r--r--cmds/idmap2/include/idmap2/ResourceContainer.h106
-rw-r--r--cmds/idmap2/include/idmap2/ResourceMapping.h114
-rw-r--r--cmds/idmap2/include/idmap2/ResourceUtils.h33
-rw-r--r--cmds/idmap2/include/idmap2/XmlParser.h17
-rw-r--r--cmds/idmap2/include/idmap2/ZipFile.h60
-rw-r--r--cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp4
-rw-r--r--cmds/idmap2/libidmap2/FabricatedOverlay.cpp302
-rw-r--r--cmds/idmap2/libidmap2/FileUtils.cpp15
-rw-r--r--cmds/idmap2/libidmap2/Idmap.cpp103
-rw-r--r--cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp32
-rw-r--r--cmds/idmap2/libidmap2/RawPrintVisitor.cpp46
-rw-r--r--cmds/idmap2/libidmap2/ResourceContainer.cpp448
-rw-r--r--cmds/idmap2/libidmap2/ResourceMapping.cpp401
-rw-r--r--cmds/idmap2/libidmap2/ResourceUtils.cpp78
-rw-r--r--cmds/idmap2/libidmap2/XmlParser.cpp18
-rw-r--r--cmds/idmap2/libidmap2/ZipFile.cpp70
-rw-r--r--cmds/idmap2/libidmap2/proto/fabricated_v1.proto56
-rw-r--r--cmds/idmap2/tests/BinaryStreamVisitorTests.cpp2
-rw-r--r--cmds/idmap2/tests/FabricatedOverlayTests.cpp138
-rw-r--r--cmds/idmap2/tests/IdmapTests.cpp176
-rw-r--r--cmds/idmap2/tests/PrettyPrintVisitorTests.cpp26
-rw-r--r--cmds/idmap2/tests/RawPrintVisitorTests.cpp36
-rw-r--r--cmds/idmap2/tests/ResourceMappingTests.cpp95
-rw-r--r--cmds/idmap2/tests/ResourceUtilsTests.cpp33
-rw-r--r--cmds/idmap2/tests/TestHelpers.h55
-rw-r--r--cmds/idmap2/tests/XmlParserTests.cpp22
-rw-r--r--cmds/idmap2/tests/ZipFileTests.cpp66
-rw-r--r--cmds/ime/Android.bp17
-rw-r--r--cmds/incident/Android.bp9
-rw-r--r--cmds/incident_helper/Android.bp9
-rw-r--r--cmds/incidentd/Android.bp9
-rw-r--r--cmds/input/Android.bp17
-rw-r--r--cmds/interrupter/Android.bp9
-rw-r--r--cmds/locksettings/Android.bp9
-rw-r--r--cmds/pm/Android.bp17
-rw-r--r--cmds/requestsync/Android.bp17
-rw-r--r--cmds/screencap/Android.bp9
-rw-r--r--cmds/settings/Android.bp11
-rw-r--r--cmds/sm/Android.bp17
-rw-r--r--cmds/svc/Android.bp17
-rw-r--r--cmds/svc/src/com/android/commands/svc/UsbCommand.java25
-rw-r--r--cmds/telecom/Android.bp17
-rw-r--r--cmds/uiautomator/Android.bp9
-rw-r--r--cmds/uiautomator/cmds/uiautomator/Android.bp9
-rw-r--r--cmds/uiautomator/instrumentation/Android.bp9
-rw-r--r--cmds/uiautomator/library/Android.bp9
-rw-r--r--cmds/vr/Android.bp17
-rw-r--r--cmds/wm/Android.bp17
-rw-r--r--config/Android.bp17
-rw-r--r--config/hiddenapi-unsupported.txt1
-rw-r--r--config/preloaded-classes3
-rw-r--r--config/preloaded-classes-denylist1
-rw-r--r--core/api/Android.bp8
-rw-r--r--core/api/current.txt630
-rw-r--r--core/api/module-lib-current.txt16
-rw-r--r--core/api/system-current.txt323
-rw-r--r--core/api/test-current.txt72
-rw-r--r--core/api/test-lint-baseline.txt2
-rw-r--r--core/java/Android.bp16
-rw-r--r--core/java/android/accessibilityservice/AccessibilityGestureEvent.java8
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java4
-rw-r--r--core/java/android/accounts/OWNERS1
-rw-r--r--core/java/android/app/Activity.java43
-rw-r--r--core/java/android/app/ActivityClient.java19
-rw-r--r--core/java/android/app/ActivityManager.java78
-rw-r--r--core/java/android/app/ActivityManagerInternal.java6
-rw-r--r--core/java/android/app/ActivityOptions.java19
-rw-r--r--core/java/android/app/ActivityTaskManager.java18
-rw-r--r--core/java/android/app/ActivityThread.java149
-rw-r--r--core/java/android/app/AnrController.java29
-rw-r--r--core/java/android/app/AppOpsManager.java31
-rw-r--r--core/java/android/app/ApplicationExitInfo.java58
-rw-r--r--core/java/android/app/ApplicationPackageManager.java2
-rw-r--r--core/java/android/app/ClientTransactionHandler.java11
-rw-r--r--core/java/android/app/ContextImpl.java40
-rw-r--r--core/java/android/app/EnterTransitionCoordinator.java6
-rw-r--r--core/java/android/app/ExitTransitionCoordinator.java2
-rw-r--r--core/java/android/app/GameManager.java6
-rw-r--r--core/java/android/app/IActivityClientController.aidl9
-rw-r--r--core/java/android/app/IActivityManager.aidl2
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl7
-rw-r--r--core/java/android/app/ILocalWallpaperColorConsumer.aidl27
-rw-r--r--core/java/android/app/IParcelFileDescriptorRetriever.aidl31
-rw-r--r--core/java/android/app/IRequestFinishCallback.aidl27
-rw-r--r--core/java/android/app/ITaskStackListener.aidl6
-rw-r--r--core/java/android/app/IUiModeManager.aidl10
-rw-r--r--core/java/android/app/IWallpaperManager.aidl14
-rw-r--r--core/java/android/app/LoadedApk.java29
-rw-r--r--core/java/android/app/Notification.java644
-rw-r--r--core/java/android/app/NotificationChannel.java42
-rw-r--r--core/java/android/app/OWNERS2
-rw-r--r--core/java/android/app/RESOURCES_OWNERS2
-rw-r--r--core/java/android/app/ResourcesManager.java75
-rw-r--r--core/java/android/app/StatusBarManager.java18
-rw-r--r--core/java/android/app/SystemServiceRegistry.java30
-rw-r--r--core/java/android/app/TaskInfo.java15
-rw-r--r--core/java/android/app/TaskStackListener.java4
-rw-r--r--core/java/android/app/UiModeManager.java44
-rw-r--r--core/java/android/app/WallpaperManager.java85
-rw-r--r--core/java/android/app/admin/DeviceAdminReceiver.java87
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java316
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerInternal.java11
-rw-r--r--core/java/android/app/admin/DevicePolicySafetyChecker.java11
-rw-r--r--core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java31
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl11
-rw-r--r--core/java/android/app/admin/ManagedProfileProvisioningParams.java32
-rw-r--r--core/java/android/app/admin/UnsafeStateException.java29
-rw-r--r--core/java/android/app/backup/BackupAgent.java14
-rw-r--r--core/java/android/app/backup/FullBackup.java56
-rw-r--r--core/java/android/app/compat/CompatChanges.java29
-rw-r--r--core/java/android/app/compat/OWNERS1
-rw-r--r--core/java/android/app/compat/PackageOverride.java211
-rw-r--r--core/java/android/app/people/IConversationListener.aidl33
-rw-r--r--core/java/android/app/people/IPeopleManager.aidl6
-rw-r--r--core/java/android/app/people/PeopleManager.java164
-rw-r--r--core/java/android/app/servertransaction/ResumeActivityItem.java2
-rw-r--r--core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java105
-rw-r--r--core/java/android/app/smartspace/SmartspaceAction.java2
-rw-r--r--core/java/android/app/smartspace/SmartspaceTargetEvent.java9
-rw-r--r--core/java/android/app/time/ExternalTimeSuggestion.aidl (renamed from core/java/android/app/timedetector/ExternalTimeSuggestion.aidl)2
-rw-r--r--core/java/android/app/time/ExternalTimeSuggestion.java (renamed from core/java/android/app/timedetector/ExternalTimeSuggestion.java)2
-rw-r--r--core/java/android/app/timedetector/ITimeDetectorService.aidl2
-rw-r--r--core/java/android/app/timedetector/TimeDetector.java1
-rw-r--r--core/java/android/app/timedetector/TimeDetectorImpl.java1
-rw-r--r--core/java/android/app/usage/IUsageStatsManager.aidl2
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java4
-rw-r--r--core/java/android/appwidget/AppWidgetHost.java12
-rw-r--r--core/java/android/appwidget/AppWidgetHostView.java123
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java6
-rw-r--r--core/java/android/appwidget/AppWidgetProviderInfo.java78
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java57
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java28
-rw-r--r--core/java/android/bluetooth/BluetoothUuid.java5
-rw-r--r--core/java/android/companion/Association.java51
-rw-r--r--core/java/android/companion/ICompanionDeviceManager.aidl2
-rw-r--r--core/java/android/content/ClipData.java19
-rw-r--r--core/java/android/content/ClipDescription.java23
-rw-r--r--core/java/android/content/ContentResolver.java4
-rw-r--r--core/java/android/content/Context.java128
-rw-r--r--core/java/android/content/ContextWrapper.java20
-rw-r--r--core/java/android/content/Intent.java38
-rw-r--r--core/java/android/content/om/CriticalOverlayInfo.java65
-rw-r--r--core/java/android/content/om/FabricatedOverlay.java126
-rw-r--r--core/java/android/content/om/IOverlayManager.aidl14
-rw-r--r--core/java/android/content/om/OverlayIdentifier.aidl19
-rw-r--r--core/java/android/content/om/OverlayIdentifier.java208
-rw-r--r--core/java/android/content/om/OverlayInfo.java116
-rw-r--r--core/java/android/content/om/OverlayManager.java19
-rw-r--r--core/java/android/content/om/OverlayManagerTransaction.java88
-rw-r--r--core/java/android/content/pm/AppSearchShortcutInfo.java2
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java23
-rw-r--r--core/java/android/content/pm/DataLoaderManager.java5
-rw-r--r--core/java/android/content/pm/IDataLoaderManager.aidl2
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/LauncherApps.java9
-rw-r--r--core/java/android/content/pm/PackageInstaller.java9
-rw-r--r--core/java/android/content/pm/PackageManager.java56
-rw-r--r--core/java/android/content/pm/PackageParser.java58
-rw-r--r--core/java/android/content/pm/PackageUserState.java84
-rw-r--r--core/java/android/content/pm/PermissionInfo.java30
-rw-r--r--core/java/android/content/pm/ShortcutInfo.java47
-rw-r--r--core/java/android/content/pm/ShortcutServiceInternal.java7
-rw-r--r--core/java/android/content/pm/dex/DexMetadataHelper.java102
-rw-r--r--core/java/android/content/pm/overlay/OverlayPaths.java192
-rw-r--r--core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java8
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageUtils.java24
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedPermission.java29
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java32
-rw-r--r--core/java/android/content/pm/permission/OWNERS1
-rw-r--r--core/java/android/content/pm/verify/domain/DomainVerificationManager.java10
-rw-r--r--core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java28
-rw-r--r--core/java/android/content/res/CompatibilityInfo.java13
-rw-r--r--core/java/android/content/res/ResourcesKey.java20
-rw-r--r--core/java/android/graphics/fonts/FontFamilyUpdateRequest.java264
-rw-r--r--core/java/android/graphics/fonts/FontFileUpdateRequest.java65
-rw-r--r--core/java/android/graphics/fonts/FontManager.java135
-rw-r--r--core/java/android/graphics/fonts/FontUpdateRequest.java81
-rw-r--r--core/java/android/hardware/OWNERS3
-rw-r--r--core/java/android/hardware/SystemSensorManager.java50
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java6
-rw-r--r--core/java/android/hardware/biometrics/BiometricTestSession.java82
-rw-r--r--core/java/android/hardware/biometrics/IAuthService.aidl3
-rw-r--r--core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl3
-rw-r--r--core/java/android/hardware/biometrics/IBiometricService.aidl3
-rw-r--r--core/java/android/hardware/biometrics/ITestSession.aidl2
-rw-r--r--core/java/android/hardware/biometrics/ITestSessionCallback.aidl (renamed from tools/hiddenapi/Android.bp)21
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java23
-rw-r--r--core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java7
-rw-r--r--core/java/android/hardware/camera2/params/StreamConfigurationMap.java15
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateManager.java6
-rw-r--r--core/java/android/hardware/display/DisplayManager.java4
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java6
-rw-r--r--core/java/android/hardware/display/IDisplayManager.aidl2
-rw-r--r--core/java/android/hardware/face/FaceAuthenticationFrame.aidl21
-rw-r--r--core/java/android/hardware/face/FaceDataFrame.java16
-rw-r--r--core/java/android/hardware/face/FaceEnrollFrame.aidl21
-rw-r--r--core/java/android/hardware/face/FaceEnrollStage.java6
-rw-r--r--core/java/android/hardware/face/FaceManager.java287
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl8
-rw-r--r--core/java/android/hardware/face/IFaceServiceReceiver.aidl4
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java78
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl6
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl3
-rw-r--r--core/java/android/hardware/input/InputDeviceVibrator.java98
-rw-r--r--core/java/android/hardware/input/InputDeviceVibratorManager.java14
-rw-r--r--core/java/android/hardware/input/InputManager.java27
-rw-r--r--core/java/android/hardware/location/ContextHubClient.java14
-rw-r--r--core/java/android/hardware/location/ContextHubClientCallback.java9
-rw-r--r--core/java/android/hardware/location/ContextHubManager.java12
-rw-r--r--core/java/android/hardware/location/ContextHubTransaction.java8
-rw-r--r--core/java/android/hardware/location/IContextHubService.aidl3
-rw-r--r--core/java/android/hardware/location/OWNERS3
-rw-r--r--core/java/android/hardware/usb/IUsbManager.aidl6
-rw-r--r--core/java/android/hardware/usb/OWNERS1
-rw-r--r--core/java/android/hardware/usb/UsbManager.java92
-rw-r--r--core/java/android/net/ConnectivityMetricsEvent.aidl (renamed from packages/Connectivity/framework/src/android/net/ConnectivityMetricsEvent.aidl)0
-rw-r--r--core/java/android/net/IIpSecService.aidl3
-rw-r--r--core/java/android/net/IOnSetOemNetworkPreferenceListener.aidl23
-rw-r--r--core/java/android/net/IVpnManager.aidl62
-rw-r--r--core/java/android/net/InterfaceConfiguration.aidl (renamed from packages/Connectivity/framework/src/android/net/InterfaceConfiguration.aidl)0
-rw-r--r--core/java/android/net/IpSecManager.java36
-rw-r--r--core/java/android/net/LocalSocketImpl.java12
-rw-r--r--core/java/android/net/NetworkIdentity.java8
-rw-r--r--core/java/android/net/NetworkPolicyManager.java11
-rw-r--r--core/java/android/net/NetworkState.java29
-rw-r--r--core/java/android/net/OemNetworkPreferences.java24
-rw-r--r--core/java/android/net/UidRange.aidl (renamed from packages/Connectivity/framework/src/android/net/UidRange.aidl)0
-rw-r--r--core/java/android/net/UidRange.java9
-rw-r--r--core/java/android/net/VpnManager.java406
-rw-r--r--core/java/android/net/VpnService.java (renamed from packages/Connectivity/framework/src/android/net/VpnService.java)17
-rw-r--r--core/java/android/net/vcn/IVcnManagementService.aidl4
-rw-r--r--core/java/android/net/vcn/IVcnStatusCallback.aidl27
-rw-r--r--core/java/android/net/vcn/VcnConfig.java19
-rw-r--r--core/java/android/net/vcn/VcnControlPlaneConfig.java112
-rw-r--r--core/java/android/net/vcn/VcnControlPlaneIkeConfig.java150
-rw-r--r--core/java/android/net/vcn/VcnGatewayConnectionConfig.java107
-rw-r--r--core/java/android/net/vcn/VcnManager.java251
-rw-r--r--core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java125
-rw-r--r--core/java/android/os/BatteryConsumer.java27
-rw-r--r--core/java/android/os/BatteryStats.java54
-rw-r--r--core/java/android/os/BatteryUsageStats.java122
-rw-r--r--core/java/android/os/BatteryUsageStatsQuery.java41
-rw-r--r--core/java/android/os/CombinedVibrationEffect.java51
-rw-r--r--core/java/android/os/Debug.java38
-rw-r--r--core/java/android/os/Environment.java33
-rw-r--r--core/java/android/os/INetworkManagementService.aidl35
-rw-r--r--core/java/android/os/ISystemConfig.aidl5
-rw-r--r--core/java/android/os/IUserManager.aidl1
-rw-r--r--core/java/android/os/IVibratorService.aidl37
-rw-r--r--core/java/android/os/OWNERS5
-rw-r--r--core/java/android/os/PowerComponents.java77
-rw-r--r--core/java/android/os/PowerManager.java14
-rw-r--r--core/java/android/os/Process.java21
-rw-r--r--core/java/android/os/SystemBatteryConsumer.java8
-rw-r--r--core/java/android/os/SystemConfigManager.java18
-rw-r--r--core/java/android/os/SystemVibrator.java426
-rw-r--r--core/java/android/os/SystemVibratorManager.java360
-rw-r--r--core/java/android/os/Trace.java6
-rw-r--r--core/java/android/os/UidBatteryConsumer.java49
-rw-r--r--core/java/android/os/UserBatteryConsumer.java115
-rw-r--r--core/java/android/os/UserHandle.java13
-rw-r--r--core/java/android/os/UserManager.java13
-rw-r--r--core/java/android/os/Vibrator.java9
-rw-r--r--core/java/android/os/VibratorManager.java110
-rw-r--r--core/java/android/os/incremental/IncrementalFileStorages.java10
-rw-r--r--core/java/android/os/incremental/IncrementalManager.java8
-rw-r--r--core/java/android/permission/AdminPermissionControlParams.aidl19
-rw-r--r--core/java/android/permission/AdminPermissionControlParams.java132
-rw-r--r--core/java/android/permission/IPermissionController.aidl5
-rw-r--r--core/java/android/permission/OWNERS1
-rw-r--r--core/java/android/permission/PermissionControllerManager.java35
-rw-r--r--core/java/android/permission/PermissionControllerService.java37
-rw-r--r--core/java/android/permission/PermissionUsageHelper.java9
-rw-r--r--core/java/android/permissionpresenterservice/OWNERS1
-rw-r--r--core/java/android/print/OWNERS1
-rw-r--r--core/java/android/print/pdf/OWNERS1
-rw-r--r--core/java/android/printservice/OWNERS1
-rw-r--r--core/java/android/printservice/recommendation/OWNERS1
-rw-r--r--core/java/android/provider/DeviceConfig.java7
-rw-r--r--core/java/android/provider/FontsContract.java3
-rw-r--r--core/java/android/provider/Settings.java1660
-rw-r--r--core/java/android/provider/SimPhonebookContract.java191
-rw-r--r--core/java/android/provider/Telephony.java3
-rw-r--r--core/java/android/rotationresolver/RotationResolverInternal.java4
-rw-r--r--core/java/android/security/keymaster/KeymasterDefs.java14
-rw-r--r--core/java/android/service/displayhash/DisplayHasherService.java168
-rw-r--r--core/java/android/service/displayhash/IDisplayHasherService.aidl (renamed from core/java/android/service/screenshot/IScreenshotHasherService.aidl)34
-rw-r--r--core/java/android/service/displayhash/OWNERS (renamed from core/java/android/service/screenshot/OWNERS)0
-rw-r--r--core/java/android/service/notification/INotificationListener.aidl4
-rw-r--r--core/java/android/service/notification/NotificationAssistantService.java39
-rw-r--r--core/java/android/service/notification/NotificationListenerFilter.java23
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java46
-rw-r--r--core/java/android/service/rotationresolver/RotationResolutionRequest.java3
-rw-r--r--core/java/android/service/rotationresolver/RotationResolverService.java7
-rw-r--r--core/java/android/service/screenshot/ScreenshotHash.java256
-rw-r--r--core/java/android/service/screenshot/ScreenshotHasherService.java160
-rw-r--r--core/java/android/service/storage/ExternalStorageService.java48
-rw-r--r--core/java/android/service/storage/IExternalStorageService.aidl1
-rw-r--r--core/java/android/service/wallpaper/Android.bp9
-rw-r--r--core/java/android/service/wallpaper/EngineWindowPage.java96
-rw-r--r--core/java/android/service/wallpaper/IWallpaperConnection.aidl2
-rw-r--r--core/java/android/service/wallpaper/IWallpaperEngine.aidl5
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java488
-rw-r--r--core/java/android/speech/IRecognitionService.aidl5
-rw-r--r--core/java/android/speech/IRecognitionServiceManager.aidl10
-rw-r--r--core/java/android/speech/IRecognitionServiceManagerCallback.aidl2
-rw-r--r--core/java/android/speech/RecognitionService.java18
-rw-r--r--core/java/android/speech/SpeechRecognizer.java243
-rw-r--r--core/java/android/speech/tts/ITextToSpeechManager.aidl29
-rw-r--r--core/java/android/speech/tts/ITextToSpeechSession.aidl33
-rw-r--r--core/java/android/speech/tts/ITextToSpeechSessionCallback.aidl32
-rw-r--r--core/java/android/speech/tts/TextToSpeech.java170
-rw-r--r--core/java/android/telephony/PhoneStateListener.java8
-rw-r--r--core/java/android/text/DynamicLayout.java6
-rw-r--r--core/java/android/text/FontConfig.java114
-rw-r--r--core/java/android/text/StaticLayout.java3
-rw-r--r--core/java/android/tracing/ITracingServiceProxy.aidl32
-rw-r--r--core/java/android/tracing/OWNERS2
-rw-r--r--core/java/android/util/FeatureFlagUtils.java2
-rw-r--r--core/java/android/uwb/OWNERS1
-rw-r--r--core/java/android/uwb/RangingManager.java5
-rw-r--r--core/java/android/uwb/RangingSession.java6
-rw-r--r--core/java/android/uwb/SessionHandle.java7
-rw-r--r--core/java/android/uwb/TEST_MAPPING5
-rw-r--r--core/java/android/uwb/UwbManager.java14
-rw-r--r--core/java/android/view/DisplayEventReceiver.java14
-rw-r--r--core/java/android/view/FrameMetrics.java40
-rw-r--r--core/java/android/view/IWindowManager.aidl25
-rw-r--r--core/java/android/view/IWindowSession.aidl25
-rw-r--r--core/java/android/view/InsetsSource.java6
-rw-r--r--core/java/android/view/MotionEvent.java35
-rw-r--r--core/java/android/view/Surface.java4
-rw-r--r--core/java/android/view/SurfaceControl.java81
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java2
-rw-r--r--core/java/android/view/SurfaceSession.java15
-rw-r--r--core/java/android/view/SurfaceView.java89
-rw-r--r--core/java/android/view/SyncRtSurfaceTransactionApplier.java9
-rw-r--r--core/java/android/view/View.java90
-rw-r--r--core/java/android/view/ViewRootImpl.java68
-rw-r--r--core/java/android/view/Window.java24
-rw-r--r--core/java/android/view/WindowlessWindowManager.java52
-rw-r--r--core/java/android/view/autofill/AutofillManager.java33
-rw-r--r--core/java/android/view/displayhash/DisplayHash.aidl19
-rw-r--r--core/java/android/view/displayhash/DisplayHash.java226
-rw-r--r--core/java/android/view/displayhash/DisplayHashManager.java97
-rw-r--r--core/java/android/view/displayhash/DisplayHashResultCallback.java98
-rw-r--r--core/java/android/view/displayhash/OWNERS1
-rw-r--r--core/java/android/view/displayhash/VerifiedDisplayHash.aidl19
-rw-r--r--core/java/android/view/displayhash/VerifiedDisplayHash.java242
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestionsRequest.java32
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java27
-rw-r--r--core/java/android/webkit/WebViewFactory.java17
-rwxr-xr-xcore/java/android/widget/AbsListView.java6
-rw-r--r--core/java/android/widget/AdapterViewAnimator.java6
-rw-r--r--core/java/android/widget/EdgeEffect.java230
-rw-r--r--core/java/android/widget/HorizontalScrollView.java45
-rw-r--r--core/java/android/widget/ImageView.java1
-rw-r--r--core/java/android/widget/ImeAwareEditText.java2
-rw-r--r--core/java/android/widget/ProgressBar.java4
-rw-r--r--core/java/android/widget/RemoteViews.java905
-rw-r--r--core/java/android/widget/RemoteViewsAdapter.java20
-rw-r--r--core/java/android/widget/ScrollView.java44
-rw-r--r--core/java/android/widget/SpellChecker.java298
-rw-r--r--core/java/android/widget/TextView.java14
-rw-r--r--core/java/android/widget/ToastPresenter.java44
-rw-r--r--core/java/android/window/ITaskOrganizer.aidl5
-rw-r--r--core/java/android/window/SplashScreen.java188
-rw-r--r--core/java/android/window/SplashScreenView.aidl (renamed from core/java/android/service/screenshot/ScreenshotHash.aidl)5
-rw-r--r--core/java/android/window/SplashScreenView.java510
-rw-r--r--core/java/android/window/StartingWindowInfo.java11
-rw-r--r--core/java/android/window/TaskOrganizer.java11
-rw-r--r--core/java/com/android/internal/compat/CompatibilityOverrideConfig.aidl19
-rw-r--r--core/java/com/android/internal/compat/CompatibilityOverrideConfig.java75
-rw-r--r--core/java/com/android/internal/compat/IPlatformCompat.aidl15
-rw-r--r--core/java/com/android/internal/content/FileSystemProvider.java21
-rw-r--r--core/java/com/android/internal/display/BrightnessSynchronizer.java (renamed from core/java/com/android/internal/BrightnessSynchronizer.java)2
-rw-r--r--core/java/com/android/internal/display/OWNERS3
-rw-r--r--core/java/com/android/internal/graphics/fonts/IFontManager.aidl6
-rw-r--r--core/java/com/android/internal/graphics/palette/OWNERS3
-rw-r--r--core/java/com/android/internal/infra/ServiceConnector.java39
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java14
-rw-r--r--core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java28
-rw-r--r--core/java/com/android/internal/os/BatterySipper.java27
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHelper.java1
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java328
-rw-r--r--core/java/com/android/internal/os/BatteryUsageStatsProvider.java51
-rw-r--r--core/java/com/android/internal/os/BluetoothPowerCalculator.java5
-rw-r--r--core/java/com/android/internal/os/CPU_OWNERS3
-rw-r--r--core/java/com/android/internal/os/CpuPowerCalculator.java209
-rw-r--r--core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java85
-rw-r--r--core/java/com/android/internal/os/DischargedPowerCalculator.java52
-rw-r--r--core/java/com/android/internal/os/GnssPowerCalculator.java3
-rw-r--r--core/java/com/android/internal/os/IdlePowerCalculator.java3
-rw-r--r--core/java/com/android/internal/os/KernelCpuBpfTracking.java26
-rw-r--r--core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java6
-rw-r--r--core/java/com/android/internal/os/KernelCpuUidTimeReader.java13
-rw-r--r--core/java/com/android/internal/os/MemoryPowerCalculator.java3
-rw-r--r--core/java/com/android/internal/os/MobileRadioPowerCalculator.java3
-rw-r--r--core/java/com/android/internal/os/OWNERS1
-rw-r--r--core/java/com/android/internal/os/PhonePowerCalculator.java3
-rw-r--r--core/java/com/android/internal/os/PowerCalculator.java43
-rw-r--r--core/java/com/android/internal/os/ScreenPowerCalculator.java173
-rw-r--r--core/java/com/android/internal/os/SystemServerCpuThreadReader.java20
-rw-r--r--core/java/com/android/internal/os/SystemServicePowerCalculator.java140
-rw-r--r--core/java/com/android/internal/os/UserPowerCalculator.java32
-rw-r--r--core/java/com/android/internal/os/WakelockPowerCalculator.java120
-rw-r--r--core/java/com/android/internal/os/Zygote.java7
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java16
-rw-r--r--core/java/com/android/internal/os/ZygoteServer.java18
-rw-r--r--core/java/com/android/internal/policy/DecorView.java55
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java14
-rw-r--r--core/java/com/android/internal/power/MeasuredEnergyArray.java66
-rw-r--r--core/java/com/android/internal/power/MeasuredEnergyStats.java25
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl2
-rw-r--r--core/java/com/android/internal/util/CollectionUtils.java7
-rw-r--r--core/java/com/android/internal/util/LatencyTracker.java2
-rw-r--r--core/java/com/android/internal/util/PerfettoTrigger.java15
-rw-r--r--core/java/com/android/internal/widget/CallLayout.java137
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java137
-rw-r--r--core/java/com/android/internal/widget/EmphasizedNotificationButton.java63
-rw-r--r--core/java/com/android/internal/widget/NotificationActionListLayout.java47
-rw-r--r--core/java/com/android/internal/widget/PeopleHelper.java179
-rw-r--r--core/java/com/android/internal/widget/PointerLocationView.java25
-rw-r--r--core/java/com/android/server/BootReceiver.java5
-rw-r--r--core/java/com/android/server/SystemConfig.java2
-rw-r--r--core/jni/Android.bp30
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/OWNERS3
-rw-r--r--core/jni/android_content_res_ApkAssets.cpp4
-rw-r--r--core/jni/android_os_Debug.cpp98
-rw-r--r--core/jni/android_os_Trace.cpp2
-rw-r--r--core/jni/android_os_incremental_IncrementalManager.cpp16
-rw-r--r--core/jni/android_util_Process.cpp5
-rw-r--r--core/jni/android_view_DisplayEventReceiver.cpp36
-rw-r--r--core/jni/android_view_InputEventSender.cpp8
-rw-r--r--core/jni/android_view_SurfaceControl.cpp207
-rw-r--r--core/jni/android_view_SurfaceSession.cpp7
-rw-r--r--core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp36
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp32
-rw-r--r--core/proto/android/content/package_item_info.proto1
-rw-r--r--core/proto/android/net/networkrequest.proto3
-rw-r--r--core/proto/android/server/vibrator/vibratormanagerservice.proto (renamed from core/proto/android/server/vibratorservice.proto)64
-rw-r--r--core/res/Android.bp30
-rw-r--r--core/res/AndroidManifest.xml98
-rw-r--r--core/res/res/color/btn_leanback_color.xml23
-rw-r--r--core/res/res/color/btn_leanback_text_color.xml19
-rw-r--r--core/res/res/drawable/btn_borderless_material.xml3
-rw-r--r--core/res/res/drawable/btn_colored_material.xml3
-rw-r--r--core/res/res/drawable/btn_default_material.xml3
-rw-r--r--core/res/res/drawable/btn_leanback.xml20
-rw-r--r--core/res/res/drawable/btn_notification_emphasized.xml2
-rw-r--r--core/res/res/drawable/btn_toggle_material.xml3
-rw-r--r--core/res/res/drawable/ic_call_answer.xml34
-rw-r--r--core/res/res/drawable/ic_call_decline.xml36
-rw-r--r--core/res/res/layout/alert_dialog_button_bar_leanback.xml59
-rw-r--r--core/res/res/layout/alert_dialog_leanback.xml128
-rw-r--r--core/res/res/layout/notification_material_action_emphasized.xml1
-rw-r--r--core/res/res/layout/notification_template_conversation_header.xml179
-rw-r--r--core/res/res/layout/notification_template_conversation_icon_container.xml97
-rw-r--r--core/res/res/layout/notification_template_header.xml1
-rw-r--r--core/res/res/layout/notification_template_material_base.xml2
-rw-r--r--core/res/res/layout/notification_template_material_big_picture.xml1
-rw-r--r--core/res/res/layout/notification_template_material_call.xml92
-rw-r--r--core/res/res/layout/notification_template_material_conversation.xml228
-rw-r--r--core/res/res/layout/notification_template_part_chronometer.xml2
-rw-r--r--core/res/res/layout/notification_template_right_icon.xml1
-rw-r--r--core/res/res/layout/notification_top_line_views.xml24
-rw-r--r--core/res/res/layout/splash_screen_view.xml34
-rw-r--r--core/res/res/values-af/strings.xml12
-rw-r--r--core/res/res/values-am/strings.xml12
-rw-r--r--core/res/res/values-ar/strings.xml15
-rw-r--r--core/res/res/values-as/strings.xml15
-rw-r--r--core/res/res/values-az/strings.xml20
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml12
-rw-r--r--core/res/res/values-be/strings.xml15
-rw-r--r--core/res/res/values-bg/strings.xml12
-rw-r--r--core/res/res/values-bn/strings.xml15
-rw-r--r--core/res/res/values-bs/strings.xml12
-rw-r--r--core/res/res/values-ca/strings.xml20
-rw-r--r--core/res/res/values-cs/strings.xml12
-rw-r--r--core/res/res/values-da/strings.xml14
-rw-r--r--core/res/res/values-de/strings.xml15
-rw-r--r--core/res/res/values-el/strings.xml12
-rw-r--r--core/res/res/values-en-rAU/strings.xml10
-rw-r--r--core/res/res/values-en-rCA/strings.xml10
-rw-r--r--core/res/res/values-en-rGB/strings.xml10
-rw-r--r--core/res/res/values-en-rIN/strings.xml10
-rw-r--r--core/res/res/values-en-rXC/strings.xml10
-rw-r--r--core/res/res/values-es-rUS/strings.xml10
-rw-r--r--core/res/res/values-es/strings.xml12
-rw-r--r--core/res/res/values-et/strings.xml12
-rw-r--r--core/res/res/values-eu/strings.xml27
-rw-r--r--core/res/res/values-fa/strings.xml14
-rw-r--r--core/res/res/values-fi/strings.xml12
-rw-r--r--core/res/res/values-fr-rCA/strings.xml13
-rw-r--r--core/res/res/values-fr/strings.xml15
-rw-r--r--core/res/res/values-gl/strings.xml12
-rw-r--r--core/res/res/values-gu/strings.xml15
-rw-r--r--core/res/res/values-hi/strings.xml14
-rw-r--r--core/res/res/values-hr/strings.xml12
-rw-r--r--core/res/res/values-hu/strings.xml12
-rw-r--r--core/res/res/values-hy/strings.xml14
-rw-r--r--core/res/res/values-in/strings.xml12
-rw-r--r--core/res/res/values-is/strings.xml12
-rw-r--r--core/res/res/values-it/strings.xml15
-rw-r--r--core/res/res/values-iw/strings.xml12
-rw-r--r--core/res/res/values-ja/strings.xml18
-rw-r--r--core/res/res/values-ka/strings.xml10
-rw-r--r--core/res/res/values-kk/strings.xml15
-rw-r--r--core/res/res/values-km/strings.xml14
-rw-r--r--core/res/res/values-kn/strings.xml12
-rw-r--r--core/res/res/values-ko/strings.xml12
-rw-r--r--core/res/res/values-ky/strings.xml24
-rw-r--r--core/res/res/values-lo/strings.xml13
-rw-r--r--core/res/res/values-lt/strings.xml12
-rw-r--r--core/res/res/values-lv/strings.xml12
-rw-r--r--core/res/res/values-mcc310-mnc950-si/strings.xml26
-rw-r--r--core/res/res/values-mk/strings.xml12
-rw-r--r--core/res/res/values-ml/strings.xml15
-rw-r--r--core/res/res/values-mn/strings.xml20
-rw-r--r--core/res/res/values-mr/strings.xml15
-rw-r--r--core/res/res/values-ms/strings.xml20
-rw-r--r--core/res/res/values-my/strings.xml12
-rw-r--r--core/res/res/values-nb/strings.xml12
-rw-r--r--core/res/res/values-ne/strings.xml15
-rw-r--r--core/res/res/values-nl/strings.xml28
-rw-r--r--core/res/res/values-or/strings.xml15
-rw-r--r--core/res/res/values-pa/strings.xml15
-rw-r--r--core/res/res/values-pl/strings.xml12
-rw-r--r--core/res/res/values-pt-rBR/strings.xml10
-rw-r--r--core/res/res/values-pt-rPT/strings.xml12
-rw-r--r--core/res/res/values-pt/strings.xml10
-rw-r--r--core/res/res/values-ro/strings.xml12
-rw-r--r--core/res/res/values-ru/strings.xml12
-rw-r--r--core/res/res/values-si/strings.xml10
-rw-r--r--core/res/res/values-sk/strings.xml12
-rw-r--r--core/res/res/values-sl/strings.xml15
-rw-r--r--core/res/res/values-sq/strings.xml10
-rw-r--r--core/res/res/values-sr/strings.xml12
-rw-r--r--core/res/res/values-sv/strings.xml12
-rw-r--r--core/res/res/values-sw/strings.xml10
-rw-r--r--core/res/res/values-ta/strings.xml12
-rw-r--r--core/res/res/values-te/strings.xml15
-rw-r--r--core/res/res/values-television/styles_device_defaults.xml (renamed from packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml)7
-rw-r--r--core/res/res/values-th/strings.xml12
-rw-r--r--core/res/res/values-tl/strings.xml10
-rw-r--r--core/res/res/values-tr/strings.xml10
-rw-r--r--core/res/res/values-uk/strings.xml12
-rw-r--r--core/res/res/values-ur/strings.xml15
-rw-r--r--core/res/res/values-uz/strings.xml12
-rw-r--r--core/res/res/values-vi/strings.xml12
-rw-r--r--core/res/res/values-zh-rCN/strings.xml12
-rw-r--r--core/res/res/values-zh-rHK/strings.xml14
-rw-r--r--core/res/res/values-zh-rTW/strings.xml12
-rw-r--r--core/res/res/values-zu/strings.xml12
-rw-r--r--core/res/res/values/attrs.xml72
-rw-r--r--core/res/res/values/attrs_manifest.xml14
-rw-r--r--core/res/res/values/colors.xml138
-rw-r--r--core/res/res/values/colors_device_defaults.xml36
-rw-r--r--core/res/res/values/colors_leanback.xml5
-rw-r--r--core/res/res/values/config.xml34
-rw-r--r--core/res/res/values/dimens.xml31
-rw-r--r--core/res/res/values/dimens_leanback.xml6
-rw-r--r--core/res/res/values/ids.xml5
-rw-r--r--core/res/res/values/public.xml95
-rw-r--r--core/res/res/values/strings.xml25
-rw-r--r--core/res/res/values/styles_leanback.xml23
-rw-r--r--core/res/res/values/symbols.xml33
-rw-r--r--core/res/res/values/themes.xml3
-rw-r--r--core/res/res/values/themes_device_defaults.xml89
-rw-r--r--core/res/res/values/themes_leanback.xml12
-rw-r--r--core/sysprop/Android.bp9
-rw-r--r--core/tests/BroadcastRadioTests/Android.bp9
-rw-r--r--core/tests/ConnectivityManagerTest/Android.bp9
-rw-r--r--core/tests/PackageInstallerSessions/Android.bp9
-rw-r--r--core/tests/PlatformCompatFramework/Android.bp9
-rw-r--r--core/tests/bandwidthtests/Android.bp9
-rw-r--r--core/tests/benchmarks/Android.bp9
-rw-r--r--core/tests/bluetoothtests/Android.bp9
-rw-r--r--core/tests/bugreports/Android.bp9
-rw-r--r--core/tests/coretests/Android.bp9
-rw-r--r--core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp9
-rw-r--r--core/tests/coretests/BinderProxyCountingTestApp/Android.bp9
-rw-r--r--core/tests/coretests/BinderProxyCountingTestService/Android.bp9
-rw-r--r--core/tests/coretests/BstatsTestApp/Android.bp9
-rw-r--r--core/tests/coretests/DisabledTestApp/Android.bp9
-rw-r--r--core/tests/coretests/EnabledTestApp/Android.bp9
-rw-r--r--core/tests/coretests/aidl/Android.bp9
-rw-r--r--core/tests/coretests/apks/Android.bp9
-rw-r--r--core/tests/coretests/apks/install/Android.bp9
-rw-r--r--core/tests/coretests/apks/install_bad_dex/Android.bp9
-rw-r--r--core/tests/coretests/apks/install_complete_package_info/Android.bp10
-rw-r--r--core/tests/coretests/apks/install_decl_perm/Android.bp9
-rw-r--r--core/tests/coretests/apks/install_jni_lib/Android.bp9
-rw-r--r--core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp9
-rw-r--r--core/tests/coretests/apks/install_loc_auto/Android.bp9
-rw-r--r--core/tests/coretests/apks/install_loc_internal/Android.bp9
-rw-r--r--core/tests/coretests/apks/install_loc_sdcard/Android.bp9
-rw-r--r--core/tests/coretests/apks/install_loc_unspecified/Android.bp9
-rw-r--r--core/tests/coretests/apks/install_use_perm_good/Android.bp9
-rw-r--r--core/tests/coretests/apks/install_uses_feature/Android.bp9
-rw-r--r--core/tests/coretests/apks/install_verifier_bad/Android.bp9
-rw-r--r--core/tests/coretests/apks/install_verifier_good/Android.bp9
-rw-r--r--core/tests/coretests/apks/keyset/Android.bp9
-rw-r--r--core/tests/coretests/apks/locales/Android.bp9
-rw-r--r--core/tests/coretests/apks/overlay_config/Android.bp9
-rw-r--r--core/tests/coretests/apks/version/Android.bp9
-rw-r--r--core/tests/coretests/apks/version_nosys/Android.bp9
-rw-r--r--core/tests/coretests/certs/Android.bp11
-rw-r--r--core/tests/coretests/jni/Android.bp9
-rw-r--r--core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java12
-rw-r--r--core/tests/coretests/src/android/app/people/PeopleManagerTest.java120
-rw-r--r--core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java (renamed from core/tests/coretests/src/android/app/timedetector/ExternalTimeSuggestionTest.java)2
-rw-r--r--core/tests/coretests/src/android/content/pm/PermissionInfoTest.java61
-rw-r--r--core/tests/coretests/src/android/content/pm/SigningDetailsTest.java130
-rw-r--r--core/tests/coretests/src/android/content/res/ResourcesManagerTest.java38
-rw-r--r--core/tests/coretests/src/android/graphics/FontListParserTest.java245
-rw-r--r--core/tests/coretests/src/android/os/BrightnessLimit.java3
-rw-r--r--core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java34
-rw-r--r--core/tests/coretests/src/android/os/OWNERS3
-rw-r--r--core/tests/coretests/src/android/provider/OWNERS1
-rw-r--r--core/tests/coretests/src/android/provider/SimPhonebookContractTest.java51
-rw-r--r--core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java42
-rw-r--r--core/tests/coretests/src/android/view/InsetsSourceTest.java7
-rw-r--r--core/tests/coretests/src/android/view/MotionEventTest.java20
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java50
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java6
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java119
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java5
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java64
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java4
-rw-r--r--core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java152
-rw-r--r--core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java76
-rw-r--r--core/tests/coretests/src/com/android/internal/os/DischargedPowerCalculatorTest.java66
-rw-r--r--core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java49
-rw-r--r--core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/os/OWNERS3
-rw-r--r--core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java112
-rw-r--r--core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java67
-rw-r--r--core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java145
-rw-r--r--core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java76
-rw-r--r--core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java53
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/OWNERS3
-rw-r--r--core/tests/featureflagtests/Android.bp9
-rw-r--r--core/tests/hdmitests/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/AutoLocTestApp/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v1/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v2/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/ExternalLocAllPermsTestApp/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/ExternalLocTestApp/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v1/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v2/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/ExternalSharedPerms/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/InternalLocTestApp/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/NoLocTestApp/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/NoLocVersionedTestApp_v1/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/NoLocVersionedTestApp_v2/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/SharedUid/32/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/SharedUid/64/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/SharedUid/dual/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/SharedUid/java_only/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/SimpleTestApp/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/VersatileTestApp_Auto/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/VersatileTestApp_External/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/VersatileTestApp_Internal/Android.bp9
-rw-r--r--core/tests/hosttests/test-apps/VersatileTestApp_None/Android.bp9
-rw-r--r--core/tests/mockingcoretests/Android.bp9
-rw-r--r--core/tests/notificationtests/Android.bp9
-rw-r--r--core/tests/overlaytests/device/Android.bp9
-rw-r--r--core/tests/overlaytests/device/res/values/config.xml1
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/FabricatedOverlaysTest.java277
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java31
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java10
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java32
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java8
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java6
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java8
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp9
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml1
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp9
-rw-r--r--core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.bp9
-rw-r--r--core/tests/overlaytests/host/Android.bp9
-rw-r--r--core/tests/overlaytests/remount/Android.bp9
-rw-r--r--core/tests/overlaytests/remount/test-apps/Overlay/Android.bp11
-rw-r--r--core/tests/overlaytests/remount/test-apps/SharedLibrary/Android.bp9
-rw-r--r--core/tests/overlaytests/remount/test-apps/SharedLibraryOverlay/Android.bp9
-rw-r--r--core/tests/overlaytests/remount/test-apps/Target/Android.bp9
-rw-r--r--core/tests/overlaytests/remount/test-apps/certs/Android.bp11
-rw-r--r--core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp11
-rw-r--r--core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp11
-rw-r--r--core/tests/packagemanagertests/Android.bp9
-rw-r--r--core/tests/privacytests/Android.bp9
-rw-r--r--core/tests/screenshothelpertests/Android.bp10
-rw-r--r--core/tests/systemproperties/Android.bp9
-rw-r--r--core/tests/utillib/Android.bp9
-rw-r--r--core/tests/utiltests/Android.bp9
-rw-r--r--core/tests/utiltests/jni/Android.bp9
-rw-r--r--core/tests/uwbtests/Android.bp9
-rw-r--r--core/tests/uwbtests/src/android/uwb/AngleMeasurementTest.java85
-rw-r--r--core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java89
-rw-r--r--core/tests/uwbtests/src/android/uwb/DistanceMeasurementTest.java87
-rw-r--r--core/tests/uwbtests/src/android/uwb/RangingMeasurementTest.java95
-rw-r--r--core/tests/uwbtests/src/android/uwb/RangingReportTest.java90
-rw-r--r--core/tests/uwbtests/src/android/uwb/RangingSessionTest.java378
-rw-r--r--core/tests/uwbtests/src/android/uwb/SessionHandleTest.java52
-rw-r--r--core/tests/uwbtests/src/android/uwb/UwbAddressTest.java79
-rw-r--r--core/tests/uwbtests/src/android/uwb/UwbManagerTest.java49
-rw-r--r--core/xsd/Android.bp9
-rw-r--r--core/xsd/vts/Android.bp9
-rw-r--r--data/etc/Android.bp9
-rw-r--r--data/etc/OWNERS1
-rw-r--r--data/etc/car/Android.bp9
-rw-r--r--data/etc/car/com.android.car.developeroptions.xml3
-rw-r--r--data/etc/car/com.android.car.provision.xml1
-rw-r--r--data/etc/car/com.android.car.xml1
-rw-r--r--data/etc/car/com.google.android.car.kitchensink.xml4
-rw-r--r--data/etc/com.android.emergency.xml3
-rw-r--r--data/etc/com.android.provision.xml2
-rw-r--r--data/etc/com.android.settings.xml1
-rw-r--r--data/etc/privapp-permissions-platform.xml13
-rw-r--r--data/etc/services.core.protolog.json72
-rw-r--r--data/fonts/Android.bp17
-rw-r--r--data/keyboards/Android.mk3
-rw-r--r--drm/jni/Android.bp9
-rw-r--r--errorprone/Android.bp9
-rw-r--r--graphics/java/android/graphics/FontListParser.java107
-rw-r--r--graphics/java/android/graphics/HardwareRenderer.java31
-rw-r--r--graphics/java/android/graphics/Point.java22
-rw-r--r--graphics/java/android/graphics/RenderEffect.java23
-rw-r--r--graphics/java/android/graphics/Typeface.java32
-rw-r--r--graphics/java/android/graphics/drawable/RippleAnimationSession.java284
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java323
-rw-r--r--graphics/java/android/graphics/drawable/RippleShader.java89
-rw-r--r--graphics/java/android/graphics/fonts/Font.java271
-rw-r--r--graphics/java/android/graphics/fonts/FontFamily.java20
-rw-r--r--graphics/java/android/graphics/fonts/FontVariationAxis.java12
-rw-r--r--graphics/java/android/graphics/fonts/NativeFont.java205
-rw-r--r--graphics/java/android/graphics/fonts/NativeFontBufferHelper.java62
-rw-r--r--graphics/java/android/graphics/fonts/SystemFonts.java44
-rw-r--r--graphics/java/android/graphics/pdf/OWNERS1
-rw-r--r--graphics/java/android/graphics/text/PositionedGlyphs.java6
-rw-r--r--graphics/proto/Android.bp9
-rw-r--r--keystore/Android.bp31
-rw-r--r--keystore/java/android/security/AndroidKeyStoreMaintenance.java105
-rw-r--r--keystore/java/android/security/Authorization.java22
-rw-r--r--keystore/java/android/security/KeyStore.java2
-rw-r--r--keystore/java/android/security/KeyStore2.java1
-rw-r--r--keystore/java/android/security/KeyStoreSecurityLevel.java2
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java70
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreProvider.java12
-rw-r--r--keystore/java/android/security/keystore/ArrayUtils.java8
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java134
-rw-r--r--keystore/java/android/security/keystore/KeyProperties.java21
-rw-r--r--keystore/java/android/security/keystore/KeyProtection.java3
-rw-r--r--keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java8
-rw-r--r--keystore/java/android/security/keystore/Utils.java4
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java3
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKey.java10
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java342
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java3
-rw-r--r--keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java22
-rw-r--r--keystore/tests/Android.bp9
-rw-r--r--libs/WindowManager/Jetpack/Android.bp9
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java2
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java2
-rw-r--r--libs/WindowManager/Shell/Android.bp33
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_expand.xml6
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml4
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_pause_white.xml4
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_play_arrow_white.xml4
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml4
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_skip_next_white.xml6
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_skip_previous_white.xml6
-rw-r--r--libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml4
-rw-r--r--libs/WindowManager/Shell/res/layout/pip_menu.xml18
-rw-r--r--libs/WindowManager/Shell/res/layout/pip_menu_action.xml14
-rw-r--r--libs/WindowManager/Shell/res/layout/size_compat_ui.xml30
-rw-r--r--libs/WindowManager/Shell/res/raw/wm_shell_protolog.json286
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml3
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java100
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java44
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java235
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java32
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java70
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java93
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java94
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java124
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.java48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java65
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java138
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java175
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java235
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java103
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java189
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java187
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java82
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java57
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java45
-rw-r--r--libs/WindowManager/Shell/tests/OWNERS1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/AndroidTest.xml2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt116
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt32
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt22
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt36
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt42
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt41
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt47
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt96
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt128
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt82
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt123
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt77
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt148
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt205
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt119
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt81
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt80
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt138
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt115
-rw-r--r--libs/WindowManager/Shell/tests/unittest/Android.bp9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java22
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java53
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java191
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java109
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java21
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java73
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java19
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java26
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java29
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java62
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java115
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java103
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java206
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java3
-rw-r--r--libs/androidfw/Android.bp17
-rwxr-xr-xlibs/androidfw/ApkAssets.cpp10
-rw-r--r--libs/androidfw/AssetManager2.cpp80
-rw-r--r--libs/androidfw/AssetsProvider.cpp12
-rw-r--r--libs/androidfw/Idmap.cpp27
-rw-r--r--libs/androidfw/ResourceTypes.cpp11
-rw-r--r--libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp9
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h2
-rw-r--r--libs/androidfw/include/androidfw/AssetsProvider.h2
-rw-r--r--libs/androidfw/include/androidfw/Idmap.h7
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h15
-rw-r--r--libs/androidfw/tests/data/overlay/overlay.idmapbin640 -> 636 bytes
-rw-r--r--libs/hostgraphics/Android.bp9
-rw-r--r--libs/hwui/Android.bp39
-rw-r--r--libs/hwui/Animator.cpp8
-rw-r--r--libs/hwui/DeviceInfo.h3
-rw-r--r--libs/hwui/FrameInfo.cpp3
-rw-r--r--libs/hwui/FrameInfo.h5
-rw-r--r--libs/hwui/FrameMetricsReporter.h30
-rw-r--r--libs/hwui/JankTracker.cpp15
-rw-r--r--libs/hwui/JankTracker.h7
-rw-r--r--libs/hwui/Layer.cpp2
-rw-r--r--libs/hwui/ProfileDataContainer.cpp7
-rw-r--r--libs/hwui/ProfileDataContainer.h11
-rw-r--r--libs/hwui/Properties.cpp5
-rw-r--r--libs/hwui/Properties.h4
-rw-r--r--libs/hwui/SkiaCanvas.cpp24
-rw-r--r--libs/hwui/SkiaCanvas.h24
-rw-r--r--libs/hwui/apex/jni_runtime.cpp2
-rw-r--r--libs/hwui/hwui/BlurDrawLooper.cpp45
-rw-r--r--libs/hwui/hwui/BlurDrawLooper.h53
-rw-r--r--libs/hwui/hwui/MinikinSkia.cpp9
-rw-r--r--libs/hwui/hwui/MinikinSkia.h4
-rw-r--r--libs/hwui/hwui/Paint.h8
-rw-r--r--libs/hwui/hwui/Typeface.cpp6
-rw-r--r--libs/hwui/jni/FontFamily.cpp19
-rw-r--r--libs/hwui/jni/Paint.cpp6
-rw-r--r--libs/hwui/jni/RenderEffect.cpp21
-rw-r--r--libs/hwui/jni/Shader.cpp10
-rw-r--r--libs/hwui/jni/Typeface.cpp57
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp13
-rw-r--r--libs/hwui/jni/fonts/Font.cpp154
-rw-r--r--libs/hwui/jni/fonts/Font.h2
-rw-r--r--libs/hwui/jni/fonts/FontFamily.cpp18
-rw-r--r--libs/hwui/jni/fonts/NativeFont.cpp125
-rw-r--r--libs/hwui/jni/text/TextShaper.cpp10
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.cpp2
-rw-r--r--libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp4
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp24
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp8
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp168
-rw-r--r--libs/hwui/renderthread/CanvasContext.h27
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp20
-rw-r--r--libs/hwui/renderthread/RenderProxy.h19
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp35
-rw-r--r--libs/hwui/renderthread/RenderThread.h30
-rw-r--r--libs/hwui/tests/common/TestContext.cpp8
-rw-r--r--libs/hwui/tests/common/TestContext.h6
-rw-r--r--libs/hwui/tests/common/TestScene.h1
-rw-r--r--libs/hwui/tests/macrobench/TestSceneRunner.cpp5
-rw-r--r--libs/hwui/tests/macrobench/main.cpp13
-rw-r--r--libs/hwui/tests/unit/SkiaBehaviorTests.cpp13
-rw-r--r--libs/hwui/tests/unit/SkiaCanvasTests.cpp3
-rw-r--r--libs/hwui/tests/unit/TypefaceTests.cpp2
-rw-r--r--libs/hwui/utils/MathUtils.h6
-rw-r--r--libs/hwui/utils/PaintUtils.h1
-rw-r--r--libs/incident/Android.bp14
-rw-r--r--libs/input/Android.bp9
-rw-r--r--libs/input/tests/Android.bp9
-rw-r--r--libs/protoutil/Android.bp9
-rw-r--r--libs/services/Android.bp9
-rw-r--r--libs/storage/Android.bp17
-rw-r--r--libs/tracingproxy/Android.bp53
-rw-r--r--libs/usb/Android.bp10
-rw-r--r--libs/usb/tests/AccessoryChat/Android.bp9
-rw-r--r--libs/usb/tests/AccessoryChat/accessorychat/Android.bp9
-rw-r--r--libs/usb/tests/accessorytest/Android.bp10
-rw-r--r--location/java/android/location/ILocationManager.aidl4
-rw-r--r--location/java/android/location/Location.java7
-rw-r--r--location/java/android/location/LocationManager.java83
-rw-r--r--location/java/android/location/provider/IProviderRequestListener.aidl26
-rw-r--r--location/java/android/location/provider/ProviderRequest.java11
-rw-r--r--location/lib/Android.bp9
-rw-r--r--lowpan/tests/Android.bp9
-rw-r--r--media/Android.bp9
-rw-r--r--media/OWNERS4
-rw-r--r--media/TEST_MAPPING2
-rw-r--r--media/java/Android.bp9
-rw-r--r--media/java/android/media/AudioDeviceInfo.java2
-rw-r--r--media/java/android/media/AudioManager.java20
-rw-r--r--media/java/android/media/AudioPlaybackConfiguration.java58
-rw-r--r--media/java/android/media/AudioTrack.java8
-rw-r--r--media/java/android/media/CamcorderProfile.java13
-rw-r--r--media/java/android/media/DrmInitData.java7
-rw-r--r--media/java/android/media/HwAudioSource.java2
-rwxr-xr-xmedia/java/android/media/IAudioService.aidl2
-rw-r--r--media/java/android/media/IRingtonePlayer.aidl3
-rw-r--r--media/java/android/media/ImageWriter.java20
-rw-r--r--media/java/android/media/MediaCodec.java61
-rw-r--r--media/java/android/media/MediaDrm.java562
-rw-r--r--media/java/android/media/MediaFormat.java17
-rw-r--r--media/java/android/media/MediaPlayer.java38
-rw-r--r--media/java/android/media/MediaRouter.java7
-rw-r--r--media/java/android/media/PlayerBase.java32
-rw-r--r--media/java/android/media/Ringtone.java44
-rw-r--r--media/java/android/media/SoundPool.java3
-rw-r--r--media/java/android/media/metrics/Event.java44
-rw-r--r--media/java/android/media/metrics/MediaMetricsManager.java3
-rw-r--r--media/java/android/media/metrics/NetworkEvent.java86
-rw-r--r--media/java/android/media/metrics/PlaybackComponent.java3
-rw-r--r--media/java/android/media/metrics/PlaybackErrorEvent.java74
-rw-r--r--media/java/android/media/metrics/PlaybackMetrics.java101
-rw-r--r--media/java/android/media/metrics/PlaybackSession.java13
-rw-r--r--media/java/android/media/metrics/PlaybackStateEvent.java128
-rw-r--r--media/java/android/media/metrics/TrackChangeEvent.java229
-rw-r--r--media/java/android/media/session/ISession.aidl2
-rw-r--r--media/java/android/media/session/ISessionManager.aidl6
-rw-r--r--media/java/android/media/session/MediaSession.java2
-rw-r--r--media/java/android/media/session/MediaSessionManager.java55
-rw-r--r--media/java/android/media/tv/TvInputManager.java10
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/Android.bp9
-rw-r--r--media/jni/Android.bp22
-rw-r--r--media/jni/android_media_MediaDrm.cpp215
-rw-r--r--media/jni/android_media_MediaDrm.h40
-rw-r--r--media/jni/android_media_MediaPlayer.cpp2
-rw-r--r--media/jni/audioeffect/Android.bp9
-rw-r--r--media/jni/soundpool/Android.bp19
-rw-r--r--media/jni/soundpool/tests/Android.bp11
-rw-r--r--media/jni/tuner/ClientHelper.h2
-rw-r--r--media/jni/tuner/DemuxClient.cpp5
-rw-r--r--media/jni/tuner/DemuxClient.h5
-rw-r--r--media/jni/tuner/DescramblerClient.cpp59
-rw-r--r--media/jni/tuner/DescramblerClient.h13
-rw-r--r--media/jni/tuner/DvrClient.cpp5
-rw-r--r--media/jni/tuner/FilterClient.cpp26
-rw-r--r--media/jni/tuner/FrontendClient.cpp205
-rw-r--r--media/jni/tuner/FrontendClient.h3
-rw-r--r--media/jni/tuner/LnbClient.cpp5
-rw-r--r--media/jni/tuner/LnbClient.h2
-rw-r--r--media/jni/tuner/TimeFilterClient.cpp5
-rw-r--r--media/jni/tuner/TunerClient.cpp151
-rw-r--r--media/jni/tuner/TunerClient.h6
-rw-r--r--media/lib/remotedisplay/Android.bp9
-rw-r--r--media/lib/signer/Android.bp9
-rw-r--r--media/lib/tvremote/Android.bp9
-rw-r--r--media/lib/tvremote/tests/Android.bp9
-rw-r--r--media/mca/filterfw/Android.bp9
-rw-r--r--media/mca/filterfw/native/Android.bp9
-rw-r--r--media/mca/filterpacks/Android.bp9
-rw-r--r--media/mca/samples/CameraEffectsRecordingSample/Android.bp10
-rw-r--r--media/mca/tests/Android.bp9
-rw-r--r--media/native/midi/Android.bp9
-rw-r--r--media/packages/BluetoothMidiService/Android.bp9
-rw-r--r--media/packages/BluetoothMidiService/tests/unit/Android.bp9
-rw-r--r--media/tests/AudioPolicyTest/Android.bp9
-rw-r--r--media/tests/CameraBrowser/Android.bp9
-rw-r--r--media/tests/EffectsTest/Android.bp9
-rw-r--r--media/tests/MediaDump/Android.bp9
-rw-r--r--media/tests/MediaFrameworkTest/Android.bp9
-rw-r--r--media/tests/MediaRouter/Android.bp11
-rw-r--r--media/tests/MtpTests/Android.bp9
-rw-r--r--media/tests/ScoAudioTest/Android.bp9
-rw-r--r--media/tests/SoundPoolTest/Android.bp9
-rw-r--r--media/tests/TunerTest/Android.bp9
-rw-r--r--media/tests/audiotests/Android.bp9
-rw-r--r--media/tests/players/Android.bp9
-rw-r--r--mime/Android.bp9
-rw-r--r--native/android/Android.bp11
-rw-r--r--native/android/libandroid.map.txt11
-rw-r--r--native/android/surface_control.cpp58
-rw-r--r--native/graphics/jni/Android.bp9
-rw-r--r--native/webview/loader/Android.bp9
-rw-r--r--native/webview/plat_support/Android.bp32
-rw-r--r--nfc-extras/Android.bp9
-rw-r--r--nfc-extras/tests/Android.bp9
-rw-r--r--obex/Android.bp30
-rw-r--r--packages/AppPredictionLib/Android.bp9
-rw-r--r--packages/BackupEncryption/Android.bp9
-rw-r--r--packages/BackupEncryption/test/robolectric-integration/Android.bp9
-rw-r--r--packages/BackupEncryption/test/robolectric/Android.bp9
-rw-r--r--packages/BackupEncryption/test/unittest/Android.bp11
-rw-r--r--packages/BackupRestoreConfirmation/Android.bp9
-rw-r--r--packages/CarrierDefaultApp/Android.bp9
-rw-r--r--packages/CarrierDefaultApp/tests/unit/Android.bp9
-rw-r--r--packages/CompanionDeviceManager/Android.bp19
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java2
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java5
-rw-r--r--packages/Connectivity/framework/Android.bp37
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/CaptivePortalData.aidl (renamed from packages/Connectivity/framework/src/android/net/CaptivePortalData.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/ConnectivityDiagnosticsManager.aidl (renamed from packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/DhcpInfo.aidl (renamed from packages/Connectivity/framework/src/android/net/DhcpInfo.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/IpConfiguration.aidl (renamed from packages/Connectivity/framework/src/android/net/IpConfiguration.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/IpPrefix.aidl (renamed from packages/Connectivity/framework/src/android/net/IpPrefix.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/KeepalivePacketData.aidl (renamed from packages/Connectivity/framework/src/android/net/KeepalivePacketData.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/LinkAddress.aidl (renamed from packages/Connectivity/framework/src/android/net/LinkAddress.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/LinkProperties.aidl (renamed from packages/Connectivity/framework/src/android/net/LinkProperties.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/MacAddress.aidl (renamed from packages/Connectivity/framework/src/android/net/MacAddress.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/Network.aidl (renamed from packages/Connectivity/framework/src/android/net/Network.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/NetworkAgentConfig.aidl (renamed from packages/Connectivity/framework/src/android/net/NetworkAgentConfig.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/NetworkCapabilities.aidl (renamed from packages/Connectivity/framework/src/android/net/NetworkCapabilities.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/NetworkInfo.aidl (renamed from packages/Connectivity/framework/src/android/net/NetworkInfo.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/NetworkRequest.aidl (renamed from packages/Connectivity/framework/src/android/net/NetworkRequest.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/ProxyInfo.aidl (renamed from packages/Connectivity/framework/src/android/net/ProxyInfo.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/RouteInfo.aidl (renamed from packages/Connectivity/framework/src/android/net/RouteInfo.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/StaticIpConfiguration.aidl (renamed from packages/Connectivity/framework/src/android/net/StaticIpConfiguration.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/TestNetworkInterface.aidl (renamed from packages/Connectivity/framework/src/android/net/TestNetworkInterface.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/apf/ApfCapabilities.aidl (renamed from packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.aidl)0
-rw-r--r--packages/Connectivity/framework/src/android/net/CaptivePortalData.java88
-rw-r--r--packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java11
-rw-r--r--packages/Connectivity/framework/src/android/net/ConnectivityManager.java346
-rw-r--r--packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl55
-rw-r--r--packages/Connectivity/framework/src/android/net/INetworkActivityListener.aidl (renamed from core/java/android/os/INetworkActivityListener.aidl)2
-rw-r--r--packages/Connectivity/framework/src/android/net/NetworkCapabilities.java16
-rw-r--r--packages/Connectivity/framework/src/android/net/NetworkRequest.java22
-rw-r--r--packages/Connectivity/framework/src/android/net/Proxy.java60
-rw-r--r--packages/Connectivity/framework/src/android/net/ProxyInfo.java4
-rw-r--r--packages/Connectivity/framework/src/android/net/TestNetworkManager.java1
-rw-r--r--packages/Connectivity/framework/src/android/net/VpnManager.java164
-rw-r--r--packages/Connectivity/framework/src/android/net/VpnTransportInfo.java89
-rw-r--r--packages/Connectivity/service/Android.bp10
-rw-r--r--packages/Connectivity/service/jni/onload.cpp6
-rw-r--r--packages/CtsShim/Android.bp9
-rw-r--r--packages/CtsShim/build/Android.bp9
-rw-r--r--packages/CtsShim/build/jni/Android.bp9
-rw-r--r--packages/DynamicSystemInstallationService/Android.bp19
-rw-r--r--packages/DynamicSystemInstallationService/tests/Android.bp11
-rw-r--r--packages/EasterEgg/Android.bp9
-rw-r--r--packages/EncryptedLocalTransport/Android.bp9
-rw-r--r--packages/ExtShared/Android.bp17
-rw-r--r--packages/ExternalStorageProvider/Android.bp9
-rw-r--r--packages/ExternalStorageProvider/tests/Android.bp9
-rw-r--r--packages/FakeOemFeatures/Android.bp19
-rw-r--r--packages/FusedLocation/Android.bp19
-rw-r--r--packages/InputDevices/Android.bp19
-rw-r--r--packages/LocalTransport/Android.bp9
-rw-r--r--packages/PackageInstaller/Android.bp19
-rw-r--r--packages/PackageInstaller/OWNERS1
-rw-r--r--packages/PackageInstaller/res/values-eu/strings.xml6
-rw-r--r--packages/PrintRecommendationService/Android.bp19
-rw-r--r--packages/PrintSpooler/Android.bp19
-rw-r--r--packages/PrintSpooler/jni/Android.bp11
-rw-r--r--packages/PrintSpooler/res/values-fa/strings.xml4
-rw-r--r--packages/PrintSpooler/tests/outofprocess/Android.bp11
-rw-r--r--packages/SettingsLib/ActionBarShadow/Android.bp9
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/Android.bp9
-rw-r--r--packages/SettingsLib/AdaptiveIcon/Android.bp9
-rw-r--r--packages/SettingsLib/Android.bp9
-rw-r--r--packages/SettingsLib/AppPreference/Android.bp9
-rw-r--r--packages/SettingsLib/BarChartPreference/Android.bp9
-rw-r--r--packages/SettingsLib/DisplayDensityUtils/Android.bp9
-rw-r--r--packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java56
-rw-r--r--packages/SettingsLib/EntityHeaderWidgets/Android.bp9
-rw-r--r--packages/SettingsLib/HelpUtils/Android.bp9
-rw-r--r--packages/SettingsLib/LayoutPreference/Android.bp9
-rw-r--r--packages/SettingsLib/ProgressBar/Android.bp11
-rw-r--r--packages/SettingsLib/RadioButtonPreference/Android.bp9
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/Android.bp11
-rw-r--r--packages/SettingsLib/SchedulesProvider/Android.bp9
-rw-r--r--packages/SettingsLib/SearchProvider/Android.bp9
-rw-r--r--packages/SettingsLib/SearchWidget/Android.bp9
-rw-r--r--packages/SettingsLib/SettingsSpinner/Android.bp9
-rw-r--r--packages/SettingsLib/SettingsTheme/Android.bp9
-rw-r--r--packages/SettingsLib/Tile/Android.bp9
-rw-r--r--packages/SettingsLib/Utils/Android.bp9
-rw-r--r--packages/SettingsLib/res/drawable/ic_mobile_call_strength_1.xml35
-rw-r--r--packages/SettingsLib/res/drawable/ic_mobile_call_strength_2.xml34
-rw-r--r--packages/SettingsLib/res/drawable/ic_mobile_call_strength_3.xml33
-rw-r--r--packages/SettingsLib/res/drawable/ic_wifi_call_strength_1.xml35
-rw-r--r--packages/SettingsLib/res/drawable/ic_wifi_call_strength_2.xml34
-rw-r--r--packages/SettingsLib/res/drawable/ic_wifi_call_strength_3.xml33
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-az/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-en-rCA/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-en-rXC/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-is/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ky/arrays.xml16
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml11
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-mn/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ms/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-my/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-si/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-sr/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-tl/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml5
-rw-r--r--packages/SettingsLib/res/values/strings.xml5
-rw-r--r--packages/SettingsLib/search/Android.bp9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java22
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java13
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java16
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java42
-rw-r--r--packages/SettingsLib/tests/integ/Android.bp9
-rw-r--r--packages/SettingsLib/tests/robotests/Android.bp9
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java28
-rw-r--r--packages/SettingsProvider/Android.bp19
-rw-r--r--packages/SettingsProvider/res/values/defaults.xml2
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java5
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java20
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java73
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java2
-rw-r--r--packages/SharedStorageBackup/Android.bp9
-rw-r--r--packages/Shell/Android.bp9
-rw-r--r--packages/Shell/AndroidManifest.xml15
-rw-r--r--packages/Shell/OWNERS1
-rw-r--r--packages/Shell/res/values-ky/strings.xml2
-rw-r--r--packages/Shell/tests/Android.bp9
-rw-r--r--packages/SimAppDialog/Android.bp9
-rw-r--r--packages/SoundPicker/Android.bp9
-rw-r--r--packages/StatementService/Android.bp9
-rw-r--r--packages/SystemUI/Android.bp22
-rw-r--r--packages/SystemUI/AndroidManifest.xml8
-rw-r--r--packages/SystemUI/README.md4
-rw-r--r--packages/SystemUI/plugin/Android.bp9
-rw-r--r--packages/SystemUI/plugin/ExamplePlugin/Android.bp9
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java5
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java15
-rw-r--r--packages/SystemUI/plugin_core/Android.bp9
-rw-r--r--packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml (renamed from packages/SystemUI/res-keyguard/drawable/ic_backspace_black_24dp.xml)4
-rw-r--r--packages/SystemUI/res-keyguard/drawable/num_pad_key_background.xml25
-rw-r--r--packages/SystemUI/res-keyguard/drawable/ripple_drawable_pin.xml20
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml4
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml4
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml5
-rw-r--r--packages/SystemUI/res-keyguard/values/config.xml1
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml5
-rw-r--r--packages/SystemUI/res/color/kg_user_avatar_frame.xml (renamed from packages/SystemUI/res/layout/keyguard_user_switcher_inner.xml)18
-rw-r--r--packages/SystemUI/res/drawable/circle_green_10dp.xml22
-rw-r--r--packages/SystemUI/res/drawable/end_guest_button_background.xml25
-rw-r--r--packages/SystemUI/res/drawable/kg_bg_avatar.xml28
-rw-r--r--packages/SystemUI/res/drawable/people_space_content_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/people_space_new_story_outline.xml (renamed from packages/SystemUI/res/drawable/people_space_round_tile_view_card.xml)5
-rw-r--r--packages/SystemUI/res/drawable/privacy_chip_bg.xml4
-rw-r--r--packages/SystemUI/res/drawable/toast_background.xml21
-rwxr-xr-xpackages/SystemUI/res/layout/keyguard_bottom_area.xml9
-rw-r--r--packages/SystemUI/res/layout/keyguard_qs_user_switch.xml32
-rw-r--r--packages/SystemUI/res/layout/keyguard_status_bar.xml17
-rw-r--r--packages/SystemUI/res/layout/keyguard_user_switcher.xml54
-rw-r--r--packages/SystemUI/res/layout/keyguard_user_switcher_item.xml41
-rw-r--r--packages/SystemUI/res/layout/media_carousel.xml2
-rw-r--r--packages/SystemUI/res/layout/media_view.xml40
-rw-r--r--packages/SystemUI/res/layout/navigation_layout.xml3
-rw-r--r--packages/SystemUI/res/layout/navigation_layout_vertical.xml3
-rw-r--r--packages/SystemUI/res/layout/ongoing_privacy_chip.xml19
-rw-r--r--packages/SystemUI/res/layout/people_space_large_avatar_tile.xml70
-rw-r--r--packages/SystemUI/res/layout/people_space_notification_content_tile.xml154
-rw-r--r--packages/SystemUI/res/layout/people_space_small_avatar_tile.xml271
-rw-r--r--packages/SystemUI/res/layout/pip_menu_activity.xml93
-rw-r--r--packages/SystemUI/res/layout/punctuation_layout.xml100
-rw-r--r--packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml12
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml25
-rw-r--r--packages/SystemUI/res/layout/text_toast.xml51
-rw-r--r--packages/SystemUI/res/values-af/strings.xml16
-rw-r--r--packages/SystemUI/res/values-am/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml22
-rw-r--r--packages/SystemUI/res/values-as/strings.xml16
-rw-r--r--packages/SystemUI/res/values-az/strings.xml16
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml16
-rw-r--r--packages/SystemUI/res/values-be/strings.xml16
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml16
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml19
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml16
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml16
-rw-r--r--packages/SystemUI/res/values-da/strings.xml16
-rw-r--r--packages/SystemUI/res/values-de/strings.xml32
-rw-r--r--packages/SystemUI/res/values-el/strings.xml16
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml16
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml16
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml16
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml16
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml16
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml20
-rw-r--r--packages/SystemUI/res/values-es/strings.xml18
-rw-r--r--packages/SystemUI/res/values-et/strings.xml16
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml16
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml28
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml18
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml16
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml16
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml16
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml19
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml26
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml16
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml16
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml16
-rw-r--r--packages/SystemUI/res/values-in/strings.xml16
-rw-r--r--packages/SystemUI/res/values-is/strings.xml16
-rw-r--r--packages/SystemUI/res/values-it/strings.xml16
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml34
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml16
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml16
-rw-r--r--packages/SystemUI/res/values-km/strings.xml16
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml28
-rw-r--r--packages/SystemUI/res/values-land/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml16
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml16
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml16
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml32
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml18
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml32
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml16
-rw-r--r--packages/SystemUI/res/values-my/strings.xml16
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml19
-rw-r--r--packages/SystemUI/res/values-night/colors.xml2
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml34
-rw-r--r--packages/SystemUI/res/values-or/strings.xml16
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml32
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml21
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml16
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml18
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml16
-rw-r--r--packages/SystemUI/res/values-si/strings.xml16
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml16
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml18
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml16
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml16
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml16
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml16
-rw-r--r--packages/SystemUI/res/values-sw600dp/styles.xml7
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml16
-rw-r--r--packages/SystemUI/res/values-te/strings.xml34
-rw-r--r--packages/SystemUI/res/values-th/strings.xml16
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml16
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml16
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml16
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml19
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml16
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml16
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml18
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml16
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml16
-rw-r--r--packages/SystemUI/res/values/attrs.xml18
-rw-r--r--packages/SystemUI/res/values/colors.xml15
-rw-r--r--packages/SystemUI/res/values/config.xml8
-rw-r--r--packages/SystemUI/res/values/dimens.xml52
-rw-r--r--packages/SystemUI/res/values/flags.xml8
-rw-r--r--packages/SystemUI/res/values/strings.xml49
-rw-r--r--packages/SystemUI/res/values/styles.xml7
-rw-r--r--packages/SystemUI/res/xml/media_collapsed.xml8
-rw-r--r--packages/SystemUI/res/xml/media_expanded.xml8
-rw-r--r--packages/SystemUI/res/xml/people_space_widget_info.xml6
-rw-r--r--packages/SystemUI/shared/Android.bp9
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java21
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java167
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java9
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/PeopleProviderUtils.java76
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java7
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java6
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java12
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java62
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java31
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java15
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java20
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java223
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java9
-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/KeyguardStatusView.java28
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java100
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java137
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java29
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadButton.java52
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadKey.java34
-rw-r--r--packages/SystemUI/src/com/android/keyguard/TextAnimator.kt23
-rw-r--r--packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt67
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java40
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherComponent.java40
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherModule.java24
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherScope.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/BatteryMeterView.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/ImageWallpaper.java88
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt5
-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.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeUi.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java172
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java323
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java177
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java157
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java343
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSDetail.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt158
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/CropView.java143
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java131
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java470
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java163
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java135
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java150
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java113
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java317
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java89
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java130
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java79
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyState.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyViewHolder.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java345
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java100
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java414
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java639
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java168
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherView.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java312
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt)108
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java72
-rwxr-xr-xpackages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java144
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java180
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java136
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt105
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/ToastUI.java150
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/TunerService.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/Utils.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java74
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java344
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt126
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt111
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java304
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationTest.java110
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java63
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTest.java156
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTestable.java40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java398
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt295
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java196
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java45
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java77
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java321
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java115
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java101
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java2
-rw-r--r--packages/SystemUI/tools/lint/baseline.xml11
-rw-r--r--packages/VpnDialogs/Android.bp9
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java21
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java38
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java16
-rw-r--r--packages/WAPPushManager/Android.bp19
-rw-r--r--packages/WAPPushManager/tests/Android.bp11
-rw-r--r--packages/WallpaperBackup/Android.bp9
-rw-r--r--packages/WallpaperCropper/Android.bp9
-rw-r--r--packages/overlays/Android.mk6
-rw-r--r--packages/overlays/tests/Android.bp10
-rw-r--r--packages/services/CameraExtensionsProxy/Android.bp1
-rw-r--r--packages/services/PacProcessor/Android.bp9
-rw-r--r--packages/services/Proxy/Android.bp9
-rw-r--r--proto/Android.bp9
-rw-r--r--rs/jni/Android.mk3
-rw-r--r--samples/demo/haptic-assessment/Android.bp11
-rw-r--r--sax/tests/saxtests/Android.bp9
-rw-r--r--services/Android.bp67
-rw-r--r--services/accessibility/Android.bp9
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java29
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java9
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java8
-rw-r--r--services/api/non-updatable-current.txt55
-rw-r--r--services/api/non-updatable-lint-baseline.txt9
-rw-r--r--services/api/non-updatable-removed.txt1
-rw-r--r--services/appprediction/Android.bp9
-rw-r--r--services/appwidget/Android.bp9
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java16
-rw-r--r--services/autofill/Android.bp9
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java17
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/FillUi.java19
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/SaveUi.java24
-rw-r--r--services/backup/Android.bp9
-rw-r--r--services/backup/backuplib/Android.bp9
-rw-r--r--services/companion/Android.bp9
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java498
-rw-r--r--services/contentcapture/Android.bp9
-rw-r--r--services/contentsuggestions/Android.bp9
-rw-r--r--services/core/Android.bp20
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java27
-rw-r--r--services/core/java/android/os/BatteryStatsInternal.java4
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java1880
-rw-r--r--services/core/java/com/android/server/EntropyMixer.java51
-rw-r--r--services/core/java/com/android/server/IpSecService.java54
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java174
-rw-r--r--services/core/java/com/android/server/OWNERS4
-rw-r--r--services/core/java/com/android/server/PackageWatchdog.java38
-rw-r--r--services/core/java/com/android/server/PersistentDataBlockService.java16
-rw-r--r--services/core/java/com/android/server/SensorPrivacyService.java16
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java16
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java37
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java217
-rw-r--r--services/core/java/com/android/server/VibratorService.java1237
-rw-r--r--services/core/java/com/android/server/VpnManagerService.java918
-rw-r--r--services/core/java/com/android/server/accounts/OWNERS1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java118
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java8
-rw-r--r--services/core/java/com/android/server/am/AppExitInfoTracker.java10
-rw-r--r--services/core/java/com/android/server/am/AppProfiler.java64
-rw-r--r--services/core/java/com/android/server/am/BatteryExternalStatsWorker.java291
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java47
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java50
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java16
-rw-r--r--services/core/java/com/android/server/am/MeasuredEnergySnapshot.java272
-rw-r--r--services/core/java/com/android/server/am/ProcessErrorStateRecord.java10
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java5
-rw-r--r--services/core/java/com/android/server/am/ProcessProfileRecord.java7
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java3
-rw-r--r--services/core/java/com/android/server/am/ProcessStateRecord.java8
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java1
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java63
-rw-r--r--services/core/java/com/android/server/app/GameManagerSettings.java (renamed from services/core/java/com/android/server/app/Settings.java)5
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java11
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java4
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java645
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java31
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java8
-rw-r--r--services/core/java/com/android/server/biometrics/AuthSession.java11
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java26
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java32
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/EnrollClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/RemovalClient.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java23
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java43
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java32
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java31
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java38
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java56
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java89
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java117
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java41
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java35
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java15
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java26
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java22
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java29
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java40
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java56
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java91
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java119
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java27
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java53
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java4
-rw-r--r--services/core/java/com/android/server/compat/CompatChange.java223
-rw-r--r--services/core/java/com/android/server/compat/CompatConfig.java91
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java34
-rw-r--r--services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java34
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java39
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkNotificationManager.java27
-rw-r--r--services/core/java/com/android/server/connectivity/PermissionMonitor.java35
-rw-r--r--services/core/java/com/android/server/connectivity/QosCallbackTracker.java10
-rw-r--r--services/core/java/com/android/server/connectivity/TcpKeepaliveController.java11
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java55
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceState.java18
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerService.java9
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateProvider.java9
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java2
-rw-r--r--services/core/java/com/android/server/display/BrightnessMappingStrategy.java2
-rw-r--r--services/core/java/com/android/server/display/DeviceStateToLayoutMap.java121
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceInfo.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceRepository.java12
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java13
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerShellCommand.java26
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerState.java2
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java253
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java9
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java145
-rw-r--r--services/core/java/com/android/server/display/RampAnimator.java2
-rw-r--r--services/core/java/com/android/server/display/layout/Layout.java154
-rw-r--r--services/core/java/com/android/server/graphics/fonts/FontManagerService.java23
-rw-r--r--services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java18
-rw-r--r--services/core/java/com/android/server/graphics/fonts/TEST_MAPPING7
-rw-r--r--services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java98
-rw-r--r--services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java15
-rw-r--r--services/core/java/com/android/server/hdmi/DeviceSelectAction.java7
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecConfig.java8
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java265
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java12
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java1
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java8
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java5
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java23
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java41
-rw-r--r--services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java33
-rw-r--r--services/core/java/com/android/server/infra/AbstractMasterSystemService.java14
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java195
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java18
-rw-r--r--services/core/java/com/android/server/lights/LightsService.java2
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java134
-rw-r--r--services/core/java/com/android/server/location/contexthub/AuthStateDenialTimer.java105
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java434
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java84
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java100
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java27
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubShellCommand.java71
-rw-r--r--services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java82
-rw-r--r--services/core/java/com/android/server/location/contexthub/NanoAppStateManager.java6
-rw-r--r--services/core/java/com/android/server/location/eventlog/LocalEventLog.java130
-rw-r--r--services/core/java/com/android/server/location/eventlog/LocationEventLog.java (renamed from services/core/java/com/android/server/location/injector/LocationEventLog.java)276
-rw-r--r--services/core/java/com/android/server/location/injector/DeviceIdleHelper.java76
-rw-r--r--services/core/java/com/android/server/location/injector/DeviceStationaryHelper.java36
-rw-r--r--services/core/java/com/android/server/location/injector/Injector.java9
-rw-r--r--services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java2
-rw-r--r--services/core/java/com/android/server/location/injector/SystemDeviceIdleHelper.java100
-rw-r--r--services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java63
-rw-r--r--services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java1
-rw-r--r--services/core/java/com/android/server/location/injector/SystemScreenInteractiveHelper.java2
-rw-r--r--services/core/java/com/android/server/location/provider/AbstractLocationProvider.java6
-rw-r--r--services/core/java/com/android/server/location/provider/DelegateLocationProvider.java119
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java79
-rw-r--r--services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java6
-rw-r--r--services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java294
-rw-r--r--services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java2
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java47
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java34
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowData.java42
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowManager.java83
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java2
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java7
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java22
-rw-r--r--services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java21
-rw-r--r--services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java14
-rw-r--r--services/core/java/com/android/server/media/MediaButtonReceiverHolder.java129
-rw-r--r--services/core/java/com/android/server/media/MediaKeyDispatcher.java9
-rw-r--r--services/core/java/com/android/server/media/MediaSessionPolicyProvider.java (renamed from services/core/java/com/android/server/media/SessionPolicyProvider.java)4
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java223
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecordImpl.java2
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java57
-rw-r--r--services/core/java/com/android/server/media/MediaSessionStack.java2
-rw-r--r--services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java19
-rw-r--r--services/core/java/com/android/server/net/LockdownVpnTracker.java192
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java18
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsService.java149
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java6
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java20
-rw-r--r--services/core/java/com/android/server/notification/NotificationDelegate.java9
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java227
-rw-r--r--services/core/java/com/android/server/om/DumpState.java13
-rw-r--r--services/core/java/com/android/server/om/IdmapDaemon.java51
-rw-r--r--services/core/java/com/android/server/om/IdmapManager.java92
-rw-r--r--services/core/java/com/android/server/om/OverlayActorEnforcer.java44
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java573
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerServiceImpl.java843
-rwxr-xr-xservices/core/java/com/android/server/om/OverlayManagerSettings.java284
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerShellCommand.java125
-rw-r--r--services/core/java/com/android/server/om/PackageManagerHelper.java50
-rw-r--r--services/core/java/com/android/server/os/NativeTombstoneManager.java325
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java27
-rw-r--r--services/core/java/com/android/server/pm/BackgroundDexOptService.java2
-rw-r--r--services/core/java/com/android/server/pm/DataLoaderManagerService.java36
-rw-r--r--services/core/java/com/android/server/pm/DefaultAppProvider.java9
-rw-r--r--services/core/java/com/android/server/pm/InstantAppRegistry.java5
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java53
-rw-r--r--services/core/java/com/android/server/pm/PackageAbiHelperImpl.java16
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java32
-rwxr-xr-xservices/core/java/com/android/server/pm/PackageInstallerSession.java17
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java344
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java21
-rw-r--r--services/core/java/com/android/server/pm/Settings.java78
-rw-r--r--services/core/java/com/android/server/pm/SettingsXml.java5
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java8
-rw-r--r--services/core/java/com/android/server/pm/ShortcutParser.java10
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java67
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java343
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java8
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtManagerService.java7
-rw-r--r--services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java35
-rw-r--r--services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java3
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java4
-rw-r--r--services/core/java/com/android/server/pm/permission/OWNERS2
-rw-r--r--services/core/java/com/android/server/pm/permission/Permission.java9
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java249
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java35
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java24
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java89
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java136
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java10
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java499
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java7
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java4
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java48
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java5
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java95
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java3
-rw-r--r--services/core/java/com/android/server/policy/DeviceStateProviderImpl.java4
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java38
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java17
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java11
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java39
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsLogger.java111
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsService.java67
-rw-r--r--services/core/java/com/android/server/powerstats/ProtoStreamUtils.java84
-rw-r--r--services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java89
-rw-r--r--services/core/java/com/android/server/role/OWNERS1
-rw-r--r--services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java57
-rw-r--r--services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java20
-rw-r--r--services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java9
-rw-r--r--services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java (renamed from services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java)4
-rw-r--r--services/core/java/com/android/server/security/FileIntegrityService.java11
-rw-r--r--services/core/java/com/android/server/security/VerityUtils.java7
-rw-r--r--services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java343
-rw-r--r--services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java11
-rw-r--r--services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java204
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java115
-rw-r--r--services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java32
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java12
-rw-r--r--services/core/java/com/android/server/storage/StorageSessionController.java23
-rw-r--r--services/core/java/com/android/server/storage/StorageUserConnection.java89
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorService.java2
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java2
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java2
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java6
-rw-r--r--services/core/java/com/android/server/tracing/OWNERS2
-rw-r--r--services/core/java/com/android/server/tracing/TracingServiceProxy.java99
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java4
-rw-r--r--services/core/java/com/android/server/utils/Watchable.java56
-rw-r--r--services/core/java/com/android/server/vcn/Android.bp11
-rw-r--r--services/core/java/com/android/server/vcn/Vcn.java89
-rw-r--r--services/core/java/com/android/server/vcn/VcnGatewayConnection.java795
-rw-r--r--services/core/java/com/android/server/vcn/VcnNetworkProvider.java17
-rw-r--r--services/core/java/com/android/server/vibrator/InputDeviceDelegate.java42
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java75
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationScaler.java5
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java20
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationThread.java157
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorController.java12
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java (renamed from services/core/java/com/android/server/VibratorManagerService.java)387
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java158
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java85
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java1
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java327
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java17
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java22
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java168
-rwxr-xr-xservices/core/java/com/android/server/wm/ActivityTaskSupervisor.java2
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java22
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java20
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java26
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java71
-rw-r--r--services/core/java/com/android/server/wm/DisplayHashController.java (renamed from services/core/java/com/android/server/wm/ScreenshotHashController.java)111
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java1
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java19
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowSettings.java37
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java14
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java6
-rw-r--r--services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java143
-rw-r--r--services/core/java/com/android/server/wm/PackageConfigPersister.java380
-rw-r--r--services/core/java/com/android/server/wm/PointerEventDispatcher.java19
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java3
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java3
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java16
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java29
-rw-r--r--services/core/java/com/android/server/wm/Session.java20
-rw-r--r--services/core/java/com/android/server/wm/StartingSurfaceController.java5
-rw-r--r--services/core/java/com/android/server/wm/Task.java147
-rw-r--r--services/core/java/com/android/server/wm/TaskChangeNotificationController.java17
-rwxr-xr-xservices/core/java/com/android/server/wm/TaskDisplayArea.java6
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java40
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java26
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java101
-rw-r--r--services/core/java/com/android/server/wm/WindowOrientationListener.java122
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java16
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java11
-rw-r--r--services/core/jni/Android.bp23
-rw-r--r--services/core/jni/OWNERS4
-rw-r--r--services/core/jni/com_android_server_VibratorManagerService.cpp103
-rw-r--r--services/core/jni/com_android_server_am_CachedAppOptimizer.cpp9
-rw-r--r--services/core/jni/com_android_server_connectivity_Vpn.cpp (renamed from packages/Connectivity/service/jni/com_android_server_connectivity_Vpn.cpp)0
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp16
-rw-r--r--services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp20
-rw-r--r--services/core/jni/com_android_server_vibrator_VibratorController.cpp9
-rw-r--r--services/core/jni/com_android_server_vibrator_VibratorManagerService.cpp183
-rw-r--r--services/core/jni/com_android_server_vibrator_VibratorManagerService.h (renamed from services/core/jni/com_android_server_VibratorManagerService.h)6
-rw-r--r--services/core/jni/onload.cpp18
-rw-r--r--services/core/xsd/Android.bp9
-rw-r--r--services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd14
-rw-r--r--services/core/xsd/platform-compat/overrides/schema/current.txt19
-rw-r--r--services/core/xsd/vts/Android.bp9
-rw-r--r--services/coverage/Android.bp9
-rw-r--r--services/devicepolicy/Android.bp9
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java25
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java474
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java21
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java33
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java60
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java55
-rw-r--r--services/incremental/Android.bp9
-rw-r--r--services/incremental/IncrementalService.cpp103
-rw-r--r--services/incremental/IncrementalService.h10
-rw-r--r--services/incremental/ServiceWrappers.cpp3
-rw-r--r--services/incremental/ServiceWrappers.h2
-rw-r--r--services/incremental/test/IncrementalServiceTest.cpp118
-rw-r--r--services/java/com/android/server/SystemConfigService.java20
-rw-r--r--services/java/com/android/server/SystemServer.java69
-rw-r--r--services/midi/Android.bp9
-rw-r--r--services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java166
-rw-r--r--services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java2
-rw-r--r--services/net/Android.bp9
-rw-r--r--services/people/Android.bp9
-rw-r--r--services/people/java/com/android/server/people/PeopleService.java159
-rw-r--r--services/people/java/com/android/server/people/data/DataManager.java68
-rw-r--r--services/print/Android.bp9
-rw-r--r--services/profcollect/Android.bp9
-rw-r--r--services/restrictions/Android.bp9
-rw-r--r--services/robotests/Android.bp9
-rw-r--r--services/robotests/backup/Android.bp9
-rw-r--r--services/startop/Android.bp11
-rw-r--r--services/systemcaptions/Android.bp9
-rw-r--r--services/tests/PackageManagerComponentOverrideTests/Android.bp9
-rw-r--r--services/tests/PackageManagerServiceTests/host/Android.bp9
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp9
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt667
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt18
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt275
-rw-r--r--services/tests/inprocesstests/AndroidTest.xml4
-rw-r--r--services/tests/mockingservicestests/Android.bp18
-rw-r--r--services/tests/mockingservicestests/jni/Android.bp33
-rw-r--r--services/tests/mockingservicestests/jni/onload.cpp45
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java480
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java55
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java193
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java188
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java92
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/FakeDeviceIdleHelper.java38
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/FakeDeviceStationaryHelper.java52
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java29
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java27
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java186
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/OWNERS2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java725
-rw-r--r--services/tests/rescueparty/Android.bp9
-rw-r--r--services/tests/servicestests/Android.bp11
-rw-r--r--services/tests/servicestests/aidl/Android.bp9
-rw-r--r--services/tests/servicestests/apks/Android.bp9
-rw-r--r--services/tests/servicestests/apks/install-split-base/Android.bp9
-rw-r--r--services/tests/servicestests/apks/install-split-feature-a/Android.bp9
-rw-r--r--services/tests/servicestests/apks/install_intent_filters/Android.bp10
-rw-r--r--services/tests/servicestests/apks/install_uses_sdk/Android.bp9
-rw-r--r--services/tests/servicestests/res/layout/widget_preview.xml21
-rw-r--r--services/tests/servicestests/res/values/values.xml (renamed from services/tests/servicestests/res/values/strings.xml)8
-rw-r--r--services/tests/servicestests/res/xml/dummy_appwidget_info.xml14
-rw-r--r--services/tests/servicestests/src/com/android/server/EntropyMixerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/VibratorServiceTest.java774
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java107
-rw-r--r--services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java297
-rw-r--r--services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java140
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java166
-rw-r--r--services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java196
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java163
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java72
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java72
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java214
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java73
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java266
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java200
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java40
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java280
-rw-r--r--services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java106
-rw-r--r--services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java659
-rw-r--r--services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java245
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java47
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt57
-rw-r--r--services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java103
-rw-r--r--services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java125
-rw-r--r--services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java159
-rw-r--r--services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java385
-rw-r--r--services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java192
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java105
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java36
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/StagingManagerTest.java135
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java204
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt1
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java90
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java363
-rw-r--r--services/tests/servicestests/src/com/android/server/rotationresolver/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java58
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java155
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java (renamed from services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java)182
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java151
-rw-r--r--services/tests/servicestests/test-apps/ConnTestApp/Android.bp9
-rw-r--r--services/tests/servicestests/test-apps/ConnTestApp/OWNERS1
-rw-r--r--services/tests/servicestests/test-apps/JobTestApp/Android.bp9
-rw-r--r--services/tests/servicestests/test-apps/PackageParserApp/Android.bp9
-rw-r--r--services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp9
-rw-r--r--services/tests/servicestests/test-apps/SuspendTestApp/Android.bp9
-rw-r--r--services/tests/shortcutmanagerutils/Android.bp13
-rw-r--r--services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java66
-rw-r--r--services/tests/uiservicestests/Android.bp9
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java5
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java27
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java124
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java4
-rw-r--r--services/tests/wmtests/Android.bp9
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java21
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java41
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java31
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java85
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java100
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java53
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java64
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java25
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java43
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java5
-rw-r--r--services/texttospeech/Android.bp13
-rw-r--r--services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java184
-rw-r--r--services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerService.java77
-rw-r--r--services/usage/Android.bp9
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java10
-rw-r--r--services/usb/Android.bp10
-rw-r--r--services/usb/OWNERS1
-rw-r--r--services/usb/java/com/android/server/usb/UsbPortManager.java69
-rw-r--r--services/usb/java/com/android/server/usb/UsbService.java32
-rw-r--r--services/voiceinteraction/Android.bp9
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java92
-rw-r--r--services/wifi/Android.bp9
-rw-r--r--startop/apps/test/Android.bp9
-rw-r--r--startop/iorap/Android.bp9
-rw-r--r--startop/iorap/functional_tests/Android.bp10
-rw-r--r--startop/iorap/stress/Android.bp9
-rw-r--r--startop/iorap/tests/Android.bp9
-rw-r--r--startop/view_compiler/Android.bp9
-rw-r--r--startop/view_compiler/dex_builder_test/Android.bp9
-rw-r--r--telecomm/java/android/telecom/CallScreeningService.aidl22
-rw-r--r--telecomm/java/android/telecom/CallScreeningService.java273
-rw-r--r--telecomm/java/android/telecom/Connection.java11
-rwxr-xr-xtelecomm/java/android/telecom/ConnectionService.java27
-rw-r--r--telecomm/java/android/telecom/RemoteConnection.java12
-rw-r--r--telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl15
-rw-r--r--telecomm/java/com/android/internal/telecom/IConnectionService.aidl4
-rw-r--r--telephony/common/com/android/internal/telephony/TelephonyPermissions.java61
-rw-r--r--telephony/java/android/telephony/Annotation.java2
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java64
-rw-r--r--telephony/java/android/telephony/RadioInterfaceCapabilities.java53
-rw-r--r--telephony/java/android/telephony/SignalThresholdInfo.java24
-rw-r--r--telephony/java/android/telephony/TelephonyDisplayInfo.java19
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java146
-rw-r--r--telephony/java/android/telephony/UiccAccessRule.java49
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java43
-rw-r--r--telephony/java/android/telephony/data/EpsQos.java4
-rw-r--r--telephony/java/android/telephony/data/Qos.java18
-rw-r--r--telephony/java/android/telephony/data/QosBearerFilter.java (renamed from telephony/java/android/telephony/data/QosFilter.java)62
-rw-r--r--telephony/java/android/telephony/data/QosBearerSession.java137
-rw-r--r--telephony/java/android/telephony/data/QosSession.java125
-rw-r--r--telephony/java/android/telephony/ims/ImsRcsManager.java276
-rw-r--r--telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl19
-rw-r--r--telephony/java/android/telephony/ims/ImsRegistrationAttributes.java252
-rw-r--r--telephony/java/android/telephony/ims/ProvisioningManager.java99
-rw-r--r--telephony/java/android/telephony/ims/RcsUceAdapter.java11
-rw-r--r--telephony/java/android/telephony/ims/RegistrationManager.java123
-rw-r--r--telephony/java/android/telephony/ims/SipDelegateManager.java8
-rw-r--r--telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java19
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl2
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl5
-rw-r--r--telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl3
-rw-r--r--telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java30
-rw-r--r--telephony/java/android/telephony/ims/feature/RcsFeature.java33
-rw-r--r--telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java61
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java85
-rw-r--r--telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java25
-rw-r--r--telephony/java/com/android/internal/telephony/DctConstants.java1
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl14
-rw-r--r--telephony/java/com/android/internal/telephony/uicc/IccUtils.java45
-rw-r--r--test-base/Android.bp10
-rw-r--r--test-base/hiddenapi/Android.bp9
-rw-r--r--test-legacy/Android.mk3
-rw-r--r--test-mock/Android.bp9
-rw-r--r--test-runner/Android.bp10
-rw-r--r--test-runner/tests/Android.bp9
-rw-r--r--tests/AccessibilityEventsLogger/Android.bp9
-rw-r--r--tests/AccessoryDisplay/common/Android.bp9
-rw-r--r--tests/AccessoryDisplay/sink/Android.bp9
-rw-r--r--tests/AccessoryDisplay/source/Android.bp9
-rw-r--r--tests/ActivityManagerPerfTests/stub-app/Android.bp10
-rw-r--r--tests/ActivityManagerPerfTests/test-app/Android.bp9
-rw-r--r--tests/ActivityManagerPerfTests/tests/Android.bp9
-rw-r--r--tests/ActivityManagerPerfTests/utils/Android.bp9
-rw-r--r--tests/ActivityTests/Android.bp9
-rw-r--r--tests/ActivityViewTest/Android.bp9
-rw-r--r--tests/AmSlam/Android.bp9
-rw-r--r--tests/ApkVerityTest/Android.bp14
-rw-r--r--tests/ApkVerityTest/ApkVerityTestApp/Android.bp9
-rw-r--r--tests/ApkVerityTest/OWNERS3
-rw-r--r--tests/ApkVerityTest/block_device_writer/Android.bp15
-rw-r--r--tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java100
-rw-r--r--tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java41
-rw-r--r--tests/ApkVerityTest/testdata/Android.bp12
-rw-r--r--tests/AppLaunch/Android.bp9
-rw-r--r--tests/AppLaunchWear/Android.bp9
-rw-r--r--tests/AppResourcesLoaders/Android.bp9
-rw-r--r--tests/AppResourcesLoaders/Overlay/Android.bp9
-rw-r--r--tests/Assist/Android.bp9
-rw-r--r--tests/BackgroundDexOptServiceIntegrationTests/Android.bp9
-rw-r--r--tests/BandwidthTests/Android.bp9
-rw-r--r--tests/BatteryWaster/Android.bp9
-rw-r--r--tests/BiDiTests/Android.bp9
-rw-r--r--tests/BlobStoreTestUtils/Android.bp11
-rw-r--r--tests/BootImageProfileTest/Android.bp9
-rw-r--r--tests/BrowserPowerTest/Android.bp9
-rw-r--r--tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp9
-rw-r--r--tests/CameraPrewarmTest/Android.bp9
-rw-r--r--tests/Codegen/Android.bp9
-rw-r--r--tests/Compatibility/Android.bp9
-rw-r--r--tests/CoreTests/android/Android.bp9
-rw-r--r--tests/DataIdleTest/Android.bp9
-rw-r--r--tests/DozeTest/Android.bp9
-rw-r--r--tests/DpiTest/Android.bp9
-rw-r--r--tests/DynamicCodeLoggerIntegrationTests/Android.mk9
-rw-r--r--tests/FeatureSplit/base/Android.bp9
-rw-r--r--tests/FeatureSplit/feature1/Android.bp9
-rw-r--r--tests/FeatureSplit/feature2/Android.bp9
-rw-r--r--tests/FixVibrateSetting/Android.bp9
-rw-r--r--tests/FlickerTests/Android.bp9
-rw-r--r--tests/FlickerTests/AndroidTest.xml1
-rw-r--r--tests/FlickerTests/OWNERS1
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt323
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt60
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt57
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt36
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt51
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt68
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt41
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt64
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt80
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt56
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt84
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt63
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt75
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt64
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt79
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt100
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/Android.bp9
-rw-r--r--tests/FrameworkPerf/Android.bp9
-rw-r--r--tests/GamePerformance/Android.bp9
-rw-r--r--tests/GridLayoutTest/Android.bp9
-rw-r--r--tests/HierarchyViewerTest/Android.bp9
-rw-r--r--tests/HugeBackup/Android.bp9
-rw-r--r--tests/HwAccelerationTest/Android.bp9
-rw-r--r--tests/HwAccelerationTest/AndroidManifest.xml9
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java107
-rw-r--r--tests/Internal/Android.bp9
-rw-r--r--tests/JankBench/Android.bp9
-rw-r--r--tests/JobSchedulerPerfTests/Android.bp9
-rw-r--r--tests/JobSchedulerTestApp/Android.bp9
-rw-r--r--tests/LargeAssetTest/Android.bp9
-rw-r--r--tests/LegacyAssistant/Android.bp9
-rw-r--r--tests/LocalizationTest/Android.bp9
-rw-r--r--tests/LocationTracker/Android.bp9
-rw-r--r--tests/LotsOfApps/Android.bp9
-rw-r--r--tests/LowStorageTest/Android.bp9
-rw-r--r--tests/ManagedProfileLifecycleStressTest/Android.bp9
-rw-r--r--tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp9
-rw-r--r--tests/MemoryUsage/Android.bp9
-rw-r--r--tests/MirrorSurfaceTest/Android.bp9
-rw-r--r--tests/NativeProcessesMemoryTest/Android.bp9
-rw-r--r--tests/NetworkSecurityConfigTest/Android.bp9
-rw-r--r--tests/NullHomeTest/Android.bp9
-rw-r--r--tests/OdmApps/Android.bp9
-rw-r--r--tests/OdmApps/app/Android.bp9
-rw-r--r--tests/OdmApps/priv-app/Android.bp9
-rw-r--r--tests/OneMedia/Android.bp9
-rw-r--r--tests/PackageWatchdog/Android.bp9
-rw-r--r--tests/PackageWatchdog/TEST_MAPPING7
-rw-r--r--tests/PlatformCompatGating/Android.bp9
-rw-r--r--tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt4
-rw-r--r--tests/PlatformCompatGating/test-rules/Android.bp11
-rw-r--r--tests/ProtoInputStreamTests/Android.bp9
-rw-r--r--tests/RemoteDisplayProvider/Android.bp9
-rw-r--r--tests/RenderThreadTest/Android.bp9
-rw-r--r--tests/RollbackTest/Android.bp11
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java57
-rw-r--r--tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java43
-rw-r--r--tests/SerialChat/Android.bp9
-rw-r--r--tests/ServiceCrashTest/Android.bp9
-rw-r--r--tests/SharedLibrary/client/Android.bp9
-rw-r--r--tests/SharedLibrary/lib/Android.bp9
-rw-r--r--tests/ShowWhenLockedApp/Android.bp9
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt4
-rw-r--r--tests/SmokeTest/Android.bp9
-rw-r--r--tests/SmokeTest/tests/Android.bp9
-rw-r--r--tests/SmokeTestApps/Android.bp9
-rw-r--r--tests/SoundTriggerTestApp/Android.bp9
-rw-r--r--tests/Split/Android.bp9
-rw-r--r--tests/StagedInstallTest/Android.bp10
-rw-r--r--tests/StagedInstallTest/StagedInstallInternalTest.xml2
-rw-r--r--tests/StagedInstallTest/TEST_MAPPING10
-rw-r--r--tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java2
-rw-r--r--tests/StatusBar/Android.bp9
-rw-r--r--tests/SurfaceComposition/Android.bp9
-rw-r--r--tests/SurfaceControlViewHostTest/Android.bp9
-rw-r--r--tests/SystemMemoryTest/device/Android.bp9
-rw-r--r--tests/SystemMemoryTest/host/Android.bp9
-rw-r--r--tests/SystemUIDemoModeController/Android.bp9
-rw-r--r--tests/TaskOrganizerTest/Android.bp9
-rw-r--r--tests/TelephonyCommonTests/Android.bp9
-rw-r--r--tests/TouchLatency/Android.bp9
-rw-r--r--tests/TransformTest/Android.bp9
-rw-r--r--tests/TransitionTests/Android.bp11
-rw-r--r--tests/TtsTests/Android.bp9
-rw-r--r--tests/UiBench/Android.bp9
-rw-r--r--tests/UpdatableSystemFontTest/Android.bp8
-rw-r--r--tests/UpdatableSystemFontTest/AndroidTest.xml1
-rw-r--r--tests/UpdatableSystemFontTest/TEST_MAPPING2
-rw-r--r--tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java77
-rw-r--r--tests/UsageReportingTest/Android.bp9
-rw-r--r--tests/UsageStatsPerfTests/Android.bp9
-rw-r--r--tests/UsageStatsTest/Android.bp9
-rw-r--r--tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.bp9
-rw-r--r--tests/UsbHostExternalManagmentTest/AoapTestHost/Android.bp9
-rw-r--r--tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.bp9
-rw-r--r--tests/UsbManagerTests/Android.bp9
-rw-r--r--tests/UsbManagerTests/lib/Android.bp9
-rw-r--r--tests/UsbTests/Android.bp9
-rw-r--r--tests/UsesFeature2Test/Android.bp9
-rw-r--r--tests/VectorDrawableTest/Android.bp9
-rw-r--r--tests/VoiceEnrollment/Android.bp9
-rw-r--r--tests/VoiceInteraction/Android.bp9
-rw-r--r--tests/WallpaperTest/Android.bp9
-rw-r--r--tests/WindowAnimationJank/Android.bp9
-rw-r--r--tests/WindowInsetsTests/Android.bp10
-rw-r--r--tests/appwidgets/AppWidgetHostTest/Android.bp9
-rw-r--r--tests/appwidgets/AppWidgetProviderTest/Android.bp9
-rw-r--r--tests/backup/Android.mk3
-rw-r--r--tests/benchmarks/Android.bp9
-rw-r--r--tests/libs-permissions/Android.bp9
-rw-r--r--tests/net/Android.bp9
-rw-r--r--tests/net/AndroidManifest.xml1
-rw-r--r--tests/net/common/Android.bp9
-rw-r--r--tests/net/common/java/android/net/CaptivePortalDataTest.kt53
-rw-r--r--tests/net/common/java/android/net/OemNetworkPreferencesTest.java6
-rw-r--r--tests/net/deflake/Android.bp9
-rw-r--r--tests/net/integration/Android.bp9
-rw-r--r--tests/net/java/android/net/ConnectivityManagerTest.java21
-rw-r--r--tests/net/java/android/net/NetworkTemplateTest.kt6
-rw-r--r--tests/net/java/android/net/VpnManagerTest.java22
-rw-r--r--tests/net/java/android/net/VpnTransportInfoTest.java49
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java1561
-rw-r--r--tests/net/java/com/android/server/IpSecServiceParameterizedTest.java152
-rw-r--r--tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java13
-rw-r--r--tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java64
-rw-r--r--tests/net/java/com/android/server/connectivity/VpnTest.java28
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsServiceTest.java16
-rw-r--r--tests/net/jni/Android.bp9
-rw-r--r--tests/net/smoketest/Android.bp11
-rw-r--r--tests/notification/Android.bp9
-rw-r--r--tests/permission/Android.bp9
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java (renamed from tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java)32
-rw-r--r--tests/privapp-permissions/Android.bp9
-rw-r--r--tests/testables/Android.bp9
-rw-r--r--tests/testables/tests/Android.bp9
-rw-r--r--tests/utils/StubIME/Android.bp9
-rw-r--r--tests/utils/hostutils/Android.bp9
-rw-r--r--tests/utils/testutils/Android.bp9
-rw-r--r--tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java38
-rw-r--r--tests/vcn/Android.bp10
-rw-r--r--tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java118
-rw-r--r--tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java38
-rw-r--r--tests/vcn/java/android/net/vcn/VcnManagerTest.java89
-rw-r--r--tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java61
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java273
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java168
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java9
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java17
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java27
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java99
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java22
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java131
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnTest.java17
-rw-r--r--tools/aapt/Android.bp17
-rw-r--r--tools/aapt2/Android.bp9
-rw-r--r--tools/aapt2/integration-tests/AutoVersionTest/Android.bp9
-rw-r--r--tools/aapt2/integration-tests/BasicTest/Android.bp9
-rw-r--r--tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk5
-rw-r--r--tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk5
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk3
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk3
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/Android.bp9
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp9
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp9
-rw-r--r--tools/aapt2/integration-tests/SymlinkTest/Android.bp9
-rw-r--r--tools/bit/Android.bp9
-rw-r--r--tools/codegen/Android.bp9
-rw-r--r--tools/dump-coverage/Android.bp9
-rwxr-xr-xtools/hiddenapi/generate_hiddenapi_lists.py383
-rwxr-xr-xtools/hiddenapi/generate_hiddenapi_lists_test.py104
-rwxr-xr-xtools/hiddenapi/merge_csv.py69
-rw-r--r--tools/incident_report/Android.bp9
-rw-r--r--tools/incident_section_gen/Android.bp9
-rw-r--r--tools/lock_agent/Android.bp9
-rw-r--r--tools/locked_region_code_injection/Android.bp9
-rw-r--r--tools/obbtool/Android.bp9
-rw-r--r--tools/powermodel/Android.bp10
-rw-r--r--tools/preload-check/Android.bp9
-rw-r--r--tools/preload-check/device/Android.bp9
-rw-r--r--tools/preload/Android.bp10
-rw-r--r--tools/preload/loadclass/Android.bp9
-rw-r--r--tools/processors/staledataclass/Android.bp9
-rw-r--r--tools/processors/view_inspector/Android.bp9
-rw-r--r--tools/protologtool/Android.bp9
-rw-r--r--tools/sdkparcelables/Android.bp9
-rw-r--r--tools/split-select/Android.bp9
-rw-r--r--tools/streaming_proto/Android.bp9
-rw-r--r--tools/validatekeymaps/Android.bp9
-rw-r--r--wifi/java/Android.bp9
-rw-r--r--wifi/tests/Android.bp9
2701 files changed, 87143 insertions, 29653 deletions
diff --git a/Android.bp b/Android.bp
index 9655daf649c7..e4c5c37ab576 100755
--- a/Android.bp
+++ b/Android.bp
@@ -60,6 +60,44 @@ java_defaults {
//
// READ ME: ########################################################
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ "SPDX-license-identifier-BSD",
+ "SPDX-license-identifier-CC-BY",
+ "SPDX-license-identifier-CPL-1.0",
+ "SPDX-license-identifier-GPL",
+ "SPDX-license-identifier-GPL-2.0",
+ "SPDX-license-identifier-MIT",
+ "SPDX-license-identifier-Unicode-DFS",
+ "SPDX-license-identifier-W3C",
+ "legacy_unencumbered",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
filegroup {
name: "framework-core-sources",
srcs: [
@@ -369,6 +407,8 @@ filegroup {
":framework_native_aidl",
":gatekeeper_aidl",
":gsiservice_aidl",
+ ":idmap2_aidl",
+ ":idmap2_core_aidl",
":incidentcompanion_aidl",
":inputconstants_aidl",
":installd_aidl",
@@ -401,10 +441,12 @@ filegroup {
":framework-mediaprovider-sources",
":framework-permission-sources",
":framework-permission-s-sources",
+ ":framework-scheduling-sources",
":framework-sdkextensions-sources",
":framework-statsd-sources",
":framework-tethering-srcs",
":framework-wifi-updatable-sources",
+ ":ike-srcs",
":updatable-media-srcs",
],
visibility: ["//visibility:private"],
@@ -413,12 +455,14 @@ filegroup {
java_library {
name: "framework-updatable-stubs-module_libs_api",
static_libs: [
+ "android.net.ipsec.ike.stubs.module_lib",
"framework-appsearch.stubs.module_lib",
"framework-graphics.stubs.module_lib",
"framework-media.stubs.module_lib",
"framework-mediaprovider.stubs.module_lib",
"framework-permission.stubs.module_lib",
"framework-permission-s.stubs.module_lib",
+ "framework-scheduling.stubs.module_lib",
"framework-sdkextensions.stubs.module_lib",
"framework-statsd.stubs.module_lib",
"framework-tethering.stubs.module_lib",
@@ -432,12 +476,14 @@ java_library {
name: "framework-all",
installable: false,
static_libs: [
+ "android.net.ipsec.ike.impl",
"framework-minus-apex",
"framework-appsearch.impl",
"framework-graphics.impl",
"framework-mediaprovider.impl",
"framework-permission.impl",
"framework-permission-s.impl",
+ "framework-scheduling.impl",
"framework-sdkextensions.impl",
"framework-statsd.impl",
"framework-tethering.impl",
@@ -536,6 +582,7 @@ java_library {
"android.hardware.vibrator-V1.3-java",
"android.security.apc-java",
"android.security.authorization-java",
+ "android.security.usermanager-java",
"android.system.keystore2-V1-java",
"android.system.suspend.control.internal-java",
"cameraprotosnano",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 86364af20812..4bd524f229ca 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -306,6 +306,7 @@ java_library_static {
name: "android_stubs_current",
srcs: [ ":api-stubs-docs-non-updatable" ],
static_libs: [
+ "android.net.ipsec.ike.stubs",
"art.module.public.api.stubs",
"conscrypt.module.public.api.stubs",
"framework-appsearch.stubs",
@@ -314,6 +315,7 @@ java_library_static {
"framework-mediaprovider.stubs",
"framework-permission.stubs",
"framework-permission-s.stubs",
+ "framework-scheduling.stubs",
"framework-sdkextensions.stubs",
"framework-statsd.stubs",
"framework-tethering.stubs",
@@ -328,6 +330,7 @@ java_library_static {
name: "android_system_stubs_current",
srcs: [ ":system-api-stubs-docs-non-updatable" ],
static_libs: [
+ "android.net.ipsec.ike.stubs.system",
"art.module.public.api.stubs",
"conscrypt.module.public.api.stubs",
"framework-appsearch.stubs.system",
@@ -336,6 +339,7 @@ java_library_static {
"framework-mediaprovider.stubs.system",
"framework-permission.stubs.system",
"framework-permission-s.stubs.system",
+ "framework-scheduling.stubs.system",
"framework-sdkextensions.stubs.system",
"framework-statsd.stubs.system",
"framework-tethering.stubs.system",
@@ -366,6 +370,7 @@ java_library_static {
static_libs: [
// Modules do not have test APIs, but we want to include their SystemApis, like we include
// the SystemApi of framework-non-updatable-sources.
+ "android.net.ipsec.ike.stubs.system",
"art.module.public.api.stubs",
"conscrypt.module.public.api.stubs",
"framework-appsearch.stubs.system",
@@ -374,6 +379,7 @@ java_library_static {
"framework-mediaprovider.stubs.system",
"framework-permission.stubs.system",
"framework-permission-s.stubs.system",
+ "framework-scheduling.stubs.system",
"framework-sdkextensions.stubs.system",
"framework-statsd.stubs.system",
"framework-tethering.stubs.system",
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 2b12da291acb..6c265bc1a338 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -51,6 +51,17 @@
"exclude-annotation": "org.junit.Ignore"
}
]
+ },
+ {
+ "name": "FrameworksInProcessTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
}
],
"postsubmit-managedprofile-stress": [
diff --git a/apct-tests/perftests/autofill/Android.bp b/apct-tests/perftests/autofill/Android.bp
index 79176ff993c4..84145beb5b5f 100644
--- a/apct-tests/perftests/autofill/Android.bp
+++ b/apct-tests/perftests/autofill/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AutofillPerfTests",
srcs: ["src/**/*.java"],
diff --git a/apct-tests/perftests/blobstore/Android.bp b/apct-tests/perftests/blobstore/Android.bp
index be700a2b021c..25c4250729c4 100644
--- a/apct-tests/perftests/blobstore/Android.bp
+++ b/apct-tests/perftests/blobstore/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "BlobStorePerfTests",
srcs: ["src/**/*.java"],
@@ -26,4 +35,4 @@ android_test {
platform_apis: true,
test_suites: ["device-tests"],
certificate: "platform",
-} \ No newline at end of file
+}
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp
index c5419637dec8..2182f0bc2501 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "CorePerfTests",
diff --git a/apct-tests/perftests/core/apps/overlay/Android.bp b/apct-tests/perftests/core/apps/overlay/Android.bp
index 7bee30ee9cb4..646530788907 100644
--- a/apct-tests/perftests/core/apps/overlay/Android.bp
+++ b/apct-tests/perftests/core/apps/overlay/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "Overlay0",
aaptflags: [
@@ -185,4 +194,4 @@ java_library {
":LargeOverlay8",
":LargeOverlay9",
],
-} \ No newline at end of file
+}
diff --git a/apct-tests/perftests/core/apps/reources_manager/Android.bp b/apct-tests/perftests/core/apps/reources_manager/Android.bp
index 451613236140..85dd0c4be48d 100644
--- a/apct-tests/perftests/core/apps/reources_manager/Android.bp
+++ b/apct-tests/perftests/core/apps/reources_manager/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "LargeResourcesCompressed",
static_libs: [ "androidx.appcompat_appcompat" ],
@@ -31,4 +40,4 @@ java_library {
":LargeResourcesCompressed",
":LargeResourcesUncompressed",
],
-} \ No newline at end of file
+}
diff --git a/apct-tests/perftests/core/jni/Android.bp b/apct-tests/perftests/core/jni/Android.bp
index d160d48538c3..1e4405dea2d2 100644
--- a/apct-tests/perftests/core/jni/Android.bp
+++ b/apct-tests/perftests/core/jni/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_shared {
name: "libperftestscore_jni",
sdk_version: "21",
diff --git a/apct-tests/perftests/core/src/android/app/OWNERS b/apct-tests/perftests/core/src/android/app/OWNERS
new file mode 100644
index 000000000000..4f168ceb3c55
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/app/OWNERS
@@ -0,0 +1,2 @@
+per-file Overlay* = file:/core/java/android/app/RESOURCES_OWNERS
+per-file Resources* = file:/core/java/android/app/RESOURCES_OWNERS
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
index d3938f4c0926..afd8e2948c41 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
@@ -77,7 +77,7 @@ public class ResourcesManagerPerfTest {
}
private void getResourcesForPath(String path) {
- ResourcesManager.getInstance().getResources(null, path, null, null, null,
+ ResourcesManager.getInstance().getResources(null, path, null, null, null, null,
Display.DEFAULT_DISPLAY, null, sContext.getResources().getCompatibilityInfo(),
null, null);
}
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
index f4c0a172710b..45c723bea9db 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
@@ -95,8 +95,9 @@ public class ResourcesThemePerfTest {
? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
Resources destResources = resourcesManager.getResources(null, ai.sourceDir,
- ai.splitSourceDirs, ai.resourceDirs, ai.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
- c, mContext.getResources().getCompatibilityInfo(), null, null);
+ ai.splitSourceDirs, ai.resourceDirs, ai.overlayPaths, ai.sharedLibraryFiles,
+ Display.DEFAULT_DISPLAY, c, mContext.getResources().getCompatibilityInfo(),
+ null, null);
Assert.assertNotEquals(destResources.getAssets(), mContext.getAssets());
Resources.Theme destTheme = destResources.newTheme();
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
index fc45d4adcc15..917753e28829 100644
--- a/apct-tests/perftests/multiuser/Android.bp
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "MultiUserPerfTests",
srcs: ["src/**/*.java"],
diff --git a/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp b/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp
index 08c54a60a290..892c14006f87 100644
--- a/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp
+++ b/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "MultiUserPerfDummyApp",
diff --git a/apct-tests/perftests/packagemanager/Android.bp b/apct-tests/perftests/packagemanager/Android.bp
index c15b6418ce4d..0e76488d9642 100644
--- a/apct-tests/perftests/packagemanager/Android.bp
+++ b/apct-tests/perftests/packagemanager/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "PackageManagerPerfTests",
diff --git a/apct-tests/perftests/packagemanager/apps/query-all/Android.bp b/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
index 3cb1589bc376..b2339d5965a0 100644
--- a/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
+++ b/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "QueriesAll0",
aaptflags: [
diff --git a/apct-tests/perftests/textclassifier/Android.bp b/apct-tests/perftests/textclassifier/Android.bp
index c40e0252cb7e..1011267bcdc3 100644
--- a/apct-tests/perftests/textclassifier/Android.bp
+++ b/apct-tests/perftests/textclassifier/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "TextClassifierPerfTests",
srcs: ["src/**/*.java"],
diff --git a/apct-tests/perftests/utils/Android.bp b/apct-tests/perftests/utils/Android.bp
index be85816629c1..6c46a9b58f06 100644
--- a/apct-tests/perftests/utils/Android.bp
+++ b/apct-tests/perftests/utils/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "apct-perftests-utils",
static_libs: [
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfRunPreconditionBase.java b/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfRunPreconditionBase.java
index 8d2ac0276592..330a19e03c39 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfRunPreconditionBase.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfRunPreconditionBase.java
@@ -89,7 +89,7 @@ public class WindowPerfRunPreconditionBase extends RunListener {
navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
break;
}
- executeShellCommand("cmd overlay enable-exclusive " + navOverlay);
+ executeShellCommand("cmd overlay enable-exclusive --category " + navOverlay);
});
/** It only executes once before all tests. */
diff --git a/apct-tests/perftests/windowmanager/Android.bp b/apct-tests/perftests/windowmanager/Android.bp
index dbfe6ab3210f..365824eb6d00 100644
--- a/apct-tests/perftests/windowmanager/Android.bp
+++ b/apct-tests/perftests/windowmanager/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "WmPerfTests",
srcs: ["src/**/*.java"],
diff --git a/apex/Android.bp b/apex/Android.bp
index 8310ba769f55..f3a936233ae8 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -14,4 +14,10 @@
package {
default_visibility: [":__subpackages__"],
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index a3b80139ced8..905000ac7c3d 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -208,8 +208,8 @@ package android.app.appsearch {
ctor public GetByUriRequest.Builder();
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.lang.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 addUri(@NonNull java.lang.String...);
- method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@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);
}
@@ -243,8 +243,8 @@ package android.app.appsearch {
public static final class RemoveByUriRequest.Builder {
ctor public RemoveByUriRequest.Builder();
- method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.lang.String...);
- method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.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);
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
index 97cfe36fca80..cd75b1456ba8 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
@@ -37,24 +37,36 @@ import java.util.Map;
public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelable {
@NonNull private final Map<KeyType, ValueType> mSuccesses;
@NonNull private final Map<KeyType, AppSearchResult<ValueType>> mFailures;
+ @NonNull private final Map<KeyType, AppSearchResult<ValueType>> mAll;
AppSearchBatchResult(
@NonNull Map<KeyType, ValueType> successes,
- @NonNull Map<KeyType, AppSearchResult<ValueType>> failures) {
+ @NonNull Map<KeyType, AppSearchResult<ValueType>> failures,
+ @NonNull Map<KeyType, AppSearchResult<ValueType>> all) {
mSuccesses = successes;
mFailures = failures;
+ mAll = all;
}
private AppSearchBatchResult(@NonNull Parcel in) {
- mSuccesses = Collections.unmodifiableMap(in.readHashMap(/*loader=*/ null));
- mFailures = Collections.unmodifiableMap(in.readHashMap(/*loader=*/ null));
+ mAll = Collections.unmodifiableMap(in.readHashMap(/*loader=*/ null));
+ Map<KeyType, ValueType> successes = new ArrayMap<>();
+ Map<KeyType, AppSearchResult<ValueType>> failures = new ArrayMap<>();
+ for (Map.Entry<KeyType, AppSearchResult<ValueType>> entry : mAll.entrySet()) {
+ if (entry.getValue().isSuccess()) {
+ successes.put(entry.getKey(), entry.getValue().getResultValue());
+ } else {
+ failures.put(entry.getKey(), entry.getValue());
+ }
+ }
+ mSuccesses = Collections.unmodifiableMap(successes);
+ mFailures = Collections.unmodifiableMap(failures);
}
/** @hide */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeMap(mSuccesses);
- dest.writeMap(mFailures);
+ dest.writeMap(mAll);
}
/** Returns {@code true} if this {@link AppSearchBatchResult} has no failures. */
@@ -63,8 +75,8 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl
}
/**
- * Returns a {@link Map} of all successful keys mapped to the successful
- * {@link AppSearchResult}s they produced.
+ * Returns a {@link Map} of all successful keys mapped to the successful {@link
+ * AppSearchResult}s they produced.
*
* <p>The values of the {@link Map} will not be {@code null}.
*/
@@ -85,7 +97,19 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl
}
/**
+ * Returns a {@link Map} of all keys mapped to the {@link AppSearchResult}s they produced.
+ *
+ * <p>The values of the {@link Map} will not be {@code null}.
+ * @hide
+ */
+ @NonNull
+ public Map<KeyType, AppSearchResult<ValueType>> getAll() {
+ return mAll;
+ }
+
+ /**
* Asserts that this {@link AppSearchBatchResult} has no failures.
+ *
* @hide
*/
public void checkSuccess() {
@@ -133,6 +157,7 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl
public static final class Builder<KeyType, ValueType> {
private final Map<KeyType, ValueType> mSuccesses = new ArrayMap<>();
private final Map<KeyType, AppSearchResult<ValueType>> mFailures = new ArrayMap<>();
+ private final Map<KeyType, AppSearchResult<ValueType>> mAll = new ArrayMap<>();
private boolean mBuilt = false;
/**
@@ -181,6 +206,7 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl
mFailures.put(key, result);
mSuccesses.remove(key);
}
+ mAll.put(key, result);
return this;
}
@@ -189,7 +215,7 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl
public AppSearchBatchResult<KeyType, ValueType> build() {
Preconditions.checkState(!mBuilt, "Builder has already been used");
mBuilt = true;
- return new AppSearchBatchResult<>(mSuccesses, mFailures);
+ return new AppSearchBatchResult<>(mSuccesses, mFailures, mAll);
}
}
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
index 76225e40c56e..440f63341151 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.app.appsearch.exceptions.AppSearchException;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -35,19 +36,21 @@ import java.util.Objects;
*/
public final class AppSearchResult<ValueType> implements Parcelable {
/**
- * Result codes from {@link AppSearchManager} methods.
+ * Result codes from {@link AppSearchSession} methods.
+ *
* @hide
*/
- @IntDef(value = {
- RESULT_OK,
- RESULT_UNKNOWN_ERROR,
- RESULT_INTERNAL_ERROR,
- RESULT_INVALID_ARGUMENT,
- RESULT_IO_ERROR,
- RESULT_OUT_OF_SPACE,
- RESULT_NOT_FOUND,
- RESULT_INVALID_SCHEMA,
- })
+ @IntDef(
+ value = {
+ RESULT_OK,
+ RESULT_UNKNOWN_ERROR,
+ RESULT_INTERNAL_ERROR,
+ RESULT_INVALID_ARGUMENT,
+ RESULT_IO_ERROR,
+ RESULT_OUT_OF_SPACE,
+ RESULT_NOT_FOUND,
+ RESULT_INVALID_SCHEMA,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface ResultCode {}
@@ -60,21 +63,21 @@ public final class AppSearchResult<ValueType> implements Parcelable {
/**
* An internal error occurred within AppSearch, which the caller cannot address.
*
- * This error may be considered similar to {@link IllegalStateException}
+ * <p>This error may be considered similar to {@link IllegalStateException}
*/
public static final int RESULT_INTERNAL_ERROR = 2;
/**
* The caller supplied invalid arguments to the call.
*
- * This error may be considered similar to {@link IllegalArgumentException}.
+ * <p>This error may be considered similar to {@link IllegalArgumentException}.
*/
public static final int RESULT_INVALID_ARGUMENT = 3;
/**
* An issue occurred reading or writing to storage. The call might succeed if repeated.
*
- * This error may be considered similar to {@link java.io.IOException}.
+ * <p>This error may be considered similar to {@link java.io.IOException}.
*/
public static final int RESULT_IO_ERROR = 4;
@@ -127,7 +130,7 @@ public final class AppSearchResult<ValueType> implements Parcelable {
/**
* Returns the result value associated with this result, if it was successful.
*
- * <p>See the documentation of the particular {@link AppSearchManager} call producing this
+ * <p>See the documentation of the particular {@link AppSearchSession} call producing this
* {@link AppSearchResult} for what is placed in the result value by that call.
*
* @throws IllegalStateException if this {@link AppSearchResult} is not successful.
@@ -145,8 +148,8 @@ public final class AppSearchResult<ValueType> implements Parcelable {
*
* <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}. The error
* message may be {@code null} even if {@link #isSuccess} is {@code false}. See the
- * documentation of the particular {@link AppSearchManager} call producing this
- * {@link AppSearchResult} for what is returned by {@link #getErrorMessage}.
+ * documentation of the particular {@link AppSearchSession} call producing this {@link
+ * AppSearchResult} for what is returned by {@link #getErrorMessage}.
*/
@Nullable
public String getErrorMessage() {
@@ -205,6 +208,7 @@ public final class AppSearchResult<ValueType> implements Parcelable {
/**
* Creates a new successful {@link AppSearchResult}.
+ *
* @hide
*/
@NonNull
@@ -215,6 +219,7 @@ public final class AppSearchResult<ValueType> implements Parcelable {
/**
* Creates a new failed {@link AppSearchResult}.
+ *
* @hide
*/
@NonNull
@@ -227,6 +232,8 @@ public final class AppSearchResult<ValueType> implements Parcelable {
@NonNull
public static <ValueType> AppSearchResult<ValueType> throwableToFailedResult(
@NonNull Throwable t) {
+ Log.d("AppSearchResult", "Converting throwable to failed result.", t);
+
if (t instanceof AppSearchException) {
return ((AppSearchException) t).toAppSearchResult();
}
@@ -241,6 +248,6 @@ public final class AppSearchResult<ValueType> implements Parcelable {
} else {
resultCode = AppSearchResult.RESULT_UNKNOWN_ERROR;
}
- return AppSearchResult.newFailedResult(resultCode, t.toString());
+ return AppSearchResult.newFailedResult(resultCode, t.getMessage());
}
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 8723515e1fdb..73ca0ccab46d 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -108,40 +108,53 @@ public final class AppSearchSession implements Closeable {
* 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 AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} or
- * {@link 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 AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a
- * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} property.
+ * <li>Addition of new types
+ * <li>Addition of new {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} or
+ * {@link 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
+ * AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link
+ * 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 Document} type, changing the schema type of
- * {@code Document}s of that property
- * <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an
- * {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a
- * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property).
- * <li>Adding a
- * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property.
+ * <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 Document} type, changing the schema type of {@code Document}s
+ * of that property
+ * <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an {@link
+ * AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link
+ * AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property).
+ * <li>Adding a {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property.
* </ul>
- * <p>Supplying a schema with such changes will, by default, 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, you can set the
- * {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. In this case,
- * instead of returning an {@link AppSearchResult} with the
- * {@link AppSearchResult#RESULT_INVALID_SCHEMA} error code, all documents which are not
- * compatible with the new schema will be deleted and the incompatible schema will be applied.
+ * <p>Supplying a schema with such changes will, by default, result in this call completing its
+ * future with an {@link android.app.appsearch.exceptions.AppSearchException} with a code of
+ * {@link AppSearchResult#RESULT_INVALID_SCHEMA} and a 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, you can either:
+ *
+ * <ul>
+ * <li>Set the {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. In
+ * this case, instead of completing its future with an {@link
+ * android.app.appsearch.exceptions.AppSearchException} with the {@link
+ * AppSearchResult#RESULT_INVALID_SCHEMA} error code, all documents which are not
+ * compatible with the new schema will be deleted and the incompatible schema will be
+ * applied. Incompatible types and deleted types will be set into {@link
+ * SetSchemaResponse#getIncompatibleTypes()} and {@link
+ * SetSchemaResponse#getDeletedTypes()}, respectively.
+ * <li>Add a {@link android.app.appsearch.AppSearchSchema.Migrator} for each incompatible type
+ * and make no deletion. The migrator will migrate documents from it's old schema version
+ * to the new version. Migrated types will be set into both {@link
+ * SetSchemaResponse#getIncompatibleTypes()} and {@link
+ * SetSchemaResponse#getMigratedTypes()}. See the migration section below.
+ * </ul>
*
* <p>It is a no-op to set the same schema as has been previously set; this is handled
* efficiently.
@@ -149,16 +162,32 @@ public final class AppSearchSession implements Closeable {
* <p>By default, documents are visible on platform surfaces. To opt out, call {@code
* SetSchemaRequest.Builder#setPlatformSurfaceable} with {@code surfaceable} as false. Any
* visibility settings apply only to the schemas that are included in the {@code request}.
- * Visibility settings for a schema type do not apply or persist across
- * {@link SetSchemaRequest}s.
+ * Visibility settings for a schema type do not apply or persist across {@link
+ * SetSchemaRequest}s.
+ *
+ * <p>Migration: make non-backwards-compatible changes will delete all stored documents in old
+ * schema. You can save your documents by setting {@link
+ * android.app.appsearch.AppSearchSchema.Migrator} via the {@link
+ * SetSchemaRequest.Builder#setMigrator} for each type you want to save.
+ *
+ * <p>{@link android.app.appsearch.AppSearchSchema.Migrator#onDowngrade} or {@link
+ * android.app.appsearch.AppSearchSchema.Migrator#onUpgrade} will be triggered if the version
+ * number of the schema stored in AppSearch is different with the version in the request.
+ *
+ * <p>If any error or Exception occurred in the {@link
+ * android.app.appsearch.AppSearchSchema.Migrator#onDowngrade}, {@link
+ * android.app.appsearch.AppSearchSchema.Migrator#onUpgrade} or {@link
+ * android.app.appsearch.AppSearchMigrationHelper.Transformer#transform}, the migration will be
+ * terminated, the setSchema request will be rejected unless the schema changes are
+ * backwards-compatible, and stored documents won't have any observable changes.
*
- * @param request The schema update request.
+ * @param request The schema update request.
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive errors resulting from setting the schema. If the
* operation succeeds, the callback will be invoked with {@code null}.
+ * @see android.app.appsearch.AppSearchSchema.Migrator
+ * @see android.app.appsearch.AppSearchMigrationHelper.Transformer
*/
- // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are
- // exposed.
public void setSchema(
@NonNull SetSchemaRequest request,
@NonNull @CallbackExecutor Executor executor,
@@ -195,11 +224,9 @@ public final class AppSearchSession implements Closeable {
executor.execute(() -> {
if (result.isSuccess()) {
callback.accept(
- // TODO(b/151178558) implement Migration in platform.
+ // TODO(b/177266929) implement Migration in platform.
AppSearchResult.newSuccessfulResult(
- new SetSchemaResponse.Builder().setResultCode(
- result.getResultCode())
- .build()));
+ new SetSchemaResponse.Builder().build()));
} else {
callback.accept(result);
}
@@ -258,7 +285,7 @@ public final class AppSearchSession implements Closeable {
* <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
+ * @param request {@link PutDocumentsRequest} containing documents to be indexed
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive pending result of performing this operation. The keys
* of the returned {@link AppSearchBatchResult} are the URIs of the input
@@ -299,9 +326,10 @@ public final class AppSearchSession implements Closeable {
}
/**
- * Retrieves {@link GenericDocument}s by URI.
+ * Gets {@link GenericDocument} objects by URIs and namespace from the {@link AppSearchSession}
+ * database.
*
- * @param request {@link GetByUriRequest} containing URIs to be retrieved.
+ * @param request a request containing URIs and namespace to get documents for.
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive the pending result of performing this operation. The keys
* of the returned {@link AppSearchBatchResult} are the input URIs. The values
@@ -379,48 +407,65 @@ public final class AppSearchSession implements Closeable {
}
/**
- * Searches a document based on a given query string.
+ * Retrieves documents from the open {@link AppSearchSession} that match a given query string
+ * and type of search provided.
+ *
+ * <p>Query strings can be empty, contain one term with no operators, or contain multiple terms
+ * and operators.
+ *
+ * <p>For query strings that are empty, all documents that match the {@link SearchSpec} will be
+ * returned.
+ *
+ * <p>For query strings with a single term and no operators, documents that match the provided
+ * query string and {@link SearchSpec} will be returned.
+ *
+ * <p>The following operators are supported:
*
- * <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.
+ * <li>AND (implicit)
+ * <p>AND is an operator that matches documents that contain <i>all</i> provided terms.
+ * <p><b>NOTE:</b> A space between terms is treated as an "AND" operator. Explicitly
+ * including "AND" in a query string will treat "AND" as a term, returning documents that
+ * also contain "AND".
+ * <p>Example: "apple AND banana" matches documents that contain the terms "apple", "and",
+ * "banana".
+ * <p>Example: "apple banana" matches documents that contain both "apple" and "banana".
+ * <p>Example: "apple banana cherry" matches documents that contain "apple", "banana", and
+ * "cherry".
+ * <li>OR
+ * <p>OR is an operator that matches documents that contain <i>any</i> provided term.
+ * <p>Example: "apple OR banana" matches documents that contain either "apple" or
+ * "banana".
+ * <p>Example: "apple OR banana OR cherry" matches documents that contain any of "apple",
+ * "banana", or "cherry".
+ * <li>Exclusion (-)
+ * <p>Exclusion (-) is an operator that matches documents that <i>do not</i> contain the
+ * provided term.
+ * <p>Example: "-apple" matches documents that do not contain "apple".
+ * <li>Grouped Terms
+ * <p>For queries that require multiple operators and terms, terms can be grouped into
+ * subqueries. Subqueries are contained within an open "(" and close ")" parenthesis.
+ * <p>Example: "(donut OR bagel) (coffee OR tea)" matches documents that contain either
+ * "donut" or "bagel" and either "coffee" or "tea".
+ * <li>Property Restricts
+ * <p>For queries that require a term to match a specific {@link AppSearchSchema} property
+ * of a document, a ":" must be included between the property name and the term.
+ * <p>Example: "subject:important" matches documents that contain the term "important" in
+ * the "subject" property.
* </ul>
*
- * <p> This method is lightweight. The heavy work will be done in
- * {@link SearchResults#getNextPage}.
+ * <p>Additional search specifications, such as filtering by {@link AppSearchSchema} type or
+ * adding projection, can be set by calling the corresponding {@link SearchSpec.Builder} setter.
*
- * @param queryExpression Query String to search.
- * @param searchSpec Spec for setting filters, raw query etc.
+ * <p>This method is lightweight. The heavy work will be done in {@link
+ * SearchResults#getNextPage}.
+ *
+ * @param queryExpression query string to search.
+ * @param searchSpec spec for setting document filters, adding projection, setting term match
+ * type, etc.
* @param executor Executor on which to invoke the callback of the following request
* {@link SearchResults#getNextPage}.
- * @return The search result of performing this operation.
+ * @return a {@link SearchResults} object for retrieved matched documents.
*/
@NonNull
public SearchResults search(
@@ -442,8 +487,8 @@ public final class AppSearchSession implements Closeable {
*
* <p>For each call to {@link #reportUsage}, AppSearch updates usage count and usage recency
* metrics for that particular document. These metrics are used for ordering {@link #search}
- * results by the {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} and
- * {@link SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} ranking strategies.
+ * results by the {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} and {@link
+ * SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} ranking strategies.
*
* <p>Reporting usage of a document is optional.
*
@@ -481,9 +526,17 @@ public final class AppSearchSession implements Closeable {
}
/**
- * Removes {@link GenericDocument}s from the index by URI.
+ * Removes {@link GenericDocument} objects by URIs and namespace from the {@link
+ * AppSearchSession} database.
+ *
+ * <p>Removed documents will no longer be surfaced by {@link #search} or {@link #getByUri}
+ * calls.
+ *
+ * <p><b>NOTE:</b>By default, documents are removed via a soft delete operation. Once the
+ * document crosses the count threshold or byte usage threshold, the documents will be removed
+ * from disk.
*
- * @param request Request containing URIs to be removed.
+ * @param request {@link RemoveByUriRequest} with URIs and namespace to remove from the index.
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive the pending result of performing this operation. The keys
* of the returned {@link AppSearchBatchResult} are the input URIs. The values
@@ -522,19 +575,18 @@ public final class AppSearchSession implements Closeable {
/**
* Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they
- * match the {@code queryExpression} in given namespaces and schemaTypes which is set via
- * {@link SearchSpec.Builder#addFilterNamespaces} and
- * {@link SearchSpec.Builder#addFilterSchemas}.
+ * match the {@code queryExpression} in given namespaces and schemaTypes which is set via {@link
+ * SearchSpec.Builder#addFilterNamespaces} and {@link SearchSpec.Builder#addFilterSchemas}.
*
- * <p> An empty {@code queryExpression} matches all documents.
+ * <p>An empty {@code queryExpression} matches all documents.
*
- * <p> An empty set of namespaces or schemaTypes matches all namespaces or schemaTypes in
- * the current database.
+ * <p>An empty set of namespaces or schemaTypes matches all namespaces or schemaTypes in the
+ * current database.
*
* @param queryExpression Query String to search.
- * @param searchSpec Spec containing schemaTypes, namespaces and query expression indicates
- * how document will be removed. All specific about how to scoring,
- * ordering, snippeting and resulting will be ignored.
+ * @param searchSpec Spec containing schemaTypes, namespaces and query expression indicates how
+ * document will be removed. All specific about how to scoring, ordering, snippeting and
+ * resulting will be ignored.
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive errors resulting from removing the documents. If
* the operation succeeds, the callback will be invoked with
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
index 86518347d770..09bca4fb3b9d 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@ import java.util.function.Consumer;
/**
* This class provides global access to the centralized AppSearch index maintained by the system.
*
- * <p>Apps can retrieve indexed documents through the query API.
+ * <p>Apps can retrieve indexed documents through the {@link #search} API.
*/
public class GlobalSearchSession implements Closeable {
@@ -90,48 +90,26 @@ public class GlobalSearchSession implements Closeable {
}
/**
- * Searches across all documents in the storage based on a given query string.
+ * Retrieves documents from all AppSearch databases that the querying application has access to.
*
- * <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>
+ * <p>Applications can be granted access to documents by specifying {@link
+ * SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage} when building a schema.
*
- * <p> This method is lightweight. The heavy work will be done in
- * {@link SearchResults#getNextPage}.
+ * <p>Document access can also be granted to system UIs by specifying {@link
+ * SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi} when building a schema.
*
- * @param queryExpression Query String to search.
- * @param searchSpec Spec for setting filters, raw query etc.
+ * <p>See {@link AppSearchSession#search} for a detailed explanation on
+ * forming a query string.
+ *
+ * <p>This method is lightweight. The heavy work will be done in {@link
+ * SearchResults#getNextPage}.
+ *
+ * @param queryExpression query string to search.
+ * @param searchSpec spec for setting document filters, adding projection, setting term match
+ * type, etc.
* @param executor Executor on which to invoke the callback of the following request
* {@link SearchResults#getNextPage}.
- * @return The search result of performing this operation.
+ * @return a {@link SearchResults} object for retrieved matched documents.
*/
@NonNull
public SearchResults search(
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
index 704509bce2a1..a63e01555f6c 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
@@ -35,8 +35,8 @@ import java.util.function.Consumer;
/**
* SearchResults are a returned object from a query API.
*
- * <p>Each {@link SearchResult} contains a document and may contain other fields like snippets
- * based on request.
+ * <p>Each {@link SearchResult} contains a document and may contain other fields like snippets based
+ * on request.
*
* <p>Should close this object after finish fetching results.
*
@@ -89,8 +89,8 @@ public class SearchResults implements Closeable {
/**
* Gets a whole page of {@link SearchResult}s.
*
- * <p>Re-call this method to get next page of {@link SearchResult}, until it returns an
- * empty list.
+ * <p>Re-call this method to get next page of {@link SearchResult}, until it returns an empty
+ * list.
*
* <p>The page size is set by {@link SearchSpec.Builder#setResultCountPerPage}.
*
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 e94b3b269041..8bf438d43cd1 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -90,6 +90,7 @@ public final class AppSearchSchema {
* <p>This method creates a new list when called.
*/
@NonNull
+ @SuppressWarnings("MixedMutabilityReturnType")
public List<PropertyConfig> getProperties() {
ArrayList<Bundle> propertyBundles =
mBundle.getParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD);
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 4c11514cf403..138eb23148af 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -36,7 +36,8 @@ import java.util.Set;
/**
* Represents a document unit.
*
- * <p>Documents are constructed via {@link GenericDocument.Builder}.
+ * <p>Documents contain structured data conforming to their {@link AppSearchSchema} type. Each
+ * document is uniquely identified by a URI and namespace.
*
* @see AppSearchSession#put
* @see AppSearchSession#getByUri
@@ -48,16 +49,10 @@ public class GenericDocument {
/** The default empty namespace. */
public static final String DEFAULT_NAMESPACE = "";
- /**
- * The maximum number of elements in a repeatable field. Will reject the request if exceed this
- * limit.
- */
+ /** The maximum number of elements in a repeatable field. */
private static final int MAX_REPEATED_PROPERTY_LENGTH = 100;
- /**
- * The maximum {@link String#length} of a {@link String} field. Will reject the request if
- * {@link String}s longer than this.
- */
+ /** The maximum {@link String#length} of a {@link String} field. */
private static final int MAX_STRING_LENGTH = 20_000;
/** The maximum number of indexed properties a document can have. */
@@ -149,7 +144,7 @@ public class GenericDocument {
return mBundle.getString(NAMESPACE_FIELD, DEFAULT_NAMESPACE);
}
- /** Returns the schema type of the {@link GenericDocument}. */
+ /** Returns the {@link AppSearchSchema} type of the {@link GenericDocument}. */
@NonNull
public String getSchemaType() {
return mSchemaType;
@@ -165,14 +160,14 @@ public class GenericDocument {
}
/**
- * Returns the TTL (Time To Live) of the {@link GenericDocument}, in milliseconds.
+ * Returns the TTL (time-to-live) of the {@link GenericDocument}, in milliseconds.
*
* <p>The TTL is measured against {@link #getCreationTimestampMillis}. At the timestamp of
* {@code creationTimestampMillis + ttlMillis}, measured in the {@link System#currentTimeMillis}
* time base, the document will be auto-deleted.
*
* <p>The default value is 0, which means the document is permanent and won't be auto-deleted
- * until the app is uninstalled.
+ * until the app is uninstalled or {@link AppSearchSession#remove} is called.
*/
public long getTtlMillis() {
return mBundle.getLong(TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS);
@@ -182,12 +177,12 @@ public class GenericDocument {
* Returns the score of the {@link GenericDocument}.
*
* <p>The score is a query-independent measure of the document's quality, relative to other
- * {@link GenericDocument}s of the same type.
+ * {@link GenericDocument} objects of the same {@link AppSearchSchema} type.
*
* <p>Results may be sorted by score using {@link SearchSpec.Builder#setRankingStrategy}.
* Documents with higher scores are considered better than documents with lower scores.
*
- * <p>Any nonnegative integer can be used a score.
+ * <p>Any non-negative integer can be used a score.
*/
public int getScore() {
return mBundle.getInt(SCORE_FIELD, DEFAULT_SCORE);
@@ -355,7 +350,7 @@ public class GenericDocument {
}
/**
- * Retrieves a repeated {@link String} property by key.
+ * Retrieves a repeated {@code long[]} property by key.
*
* @param key The key to look for.
* @return The {@code long[]} associated with the given key, or {@code null} if no value is set
@@ -580,14 +575,17 @@ public class GenericDocument {
private boolean mBuilt = false;
/**
- * Create a new {@link GenericDocument.Builder}.
+ * Creates a new {@link GenericDocument.Builder}.
+ *
+ * <p>Once {@link #build} is called, the instance can no longer be used.
*
- * @param uri The uri of {@link GenericDocument}.
- * @param schemaType The schema type of the {@link GenericDocument}. The passed-in {@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}.
+ * @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 uri, @NonNull String schemaType) {
@@ -606,15 +604,18 @@ public class GenericDocument {
}
/**
- * Sets the app-defined namespace this Document resides in. No special values are reserved
+ * Sets the app-defined namespace this document resides in. No special values are reserved
* or understood by the infrastructure.
*
* <p>URIs are unique within a namespace.
*
* <p>The number of namespaces per app should be kept small for efficiency reasons.
+ *
+ * @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setNamespace(@NonNull String namespace) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace);
return mBuilderTypeInstance;
}
@@ -623,14 +624,15 @@ public class GenericDocument {
* Sets the score of the {@link GenericDocument}.
*
* <p>The score is a query-independent measure of the document's quality, relative to other
- * {@link GenericDocument}s of the same type.
+ * {@link GenericDocument} objects of the same {@link AppSearchSchema} type.
*
* <p>Results may be sorted by score using {@link SearchSpec.Builder#setRankingStrategy}.
* Documents with higher scores are considered better than documents with lower scores.
*
- * <p>Any nonnegative integer can be used a score.
+ * <p>Any non-negative integer can be used a score. By default, scores are set to 0.
*
- * @throws IllegalArgumentException If the provided value is negative.
+ * @param score any non-negative {@code int} representing the document's score.
+ * @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setScore(@IntRange(from = 0, to = Integer.MAX_VALUE) int score) {
@@ -645,8 +647,11 @@ public class GenericDocument {
/**
* Sets the creation timestamp of the {@link GenericDocument}, in milliseconds.
*
- * <p>Should be set using a value obtained from the {@link System#currentTimeMillis} time
- * base.
+ * <p>This should be set using a value obtained from the {@link System#currentTimeMillis}
+ * time base.
+ *
+ * @param creationTimestampMillis a creation timestamp in milliseconds.
+ * @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setCreationTimestampMillis(long creationTimestampMillis) {
@@ -657,17 +662,17 @@ public class GenericDocument {
}
/**
- * Sets the TTL (Time To Live) of the {@link GenericDocument}, in milliseconds.
+ * Sets the TTL (time-to-live) of the {@link GenericDocument}, in milliseconds.
*
* <p>The TTL is measured against {@link #getCreationTimestampMillis}. At the timestamp of
* {@code creationTimestampMillis + ttlMillis}, measured in the {@link
* System#currentTimeMillis} time base, the document will be auto-deleted.
*
* <p>The default value is 0, which means the document is permanent and won't be
- * auto-deleted until the app is uninstalled.
+ * auto-deleted until the app is uninstalled or {@link AppSearchSession#remove} is called.
*
- * @param ttlMillis A non-negative duration in milliseconds.
- * @throws IllegalArgumentException If the provided value is negative.
+ * @param ttlMillis a non-negative duration in milliseconds.
+ * @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setTtlMillis(long ttlMillis) {
@@ -682,8 +687,11 @@ public class GenericDocument {
/**
* Sets one or multiple {@code String} values for a property, replacing its previous values.
*
- * @param key The key associated with the {@code values}.
- * @param values The {@code String} values of the property.
+ * @param key the key associated with the {@code values}.
+ * @param values the {@code String} values of the property.
+ * @throws IllegalArgumentException if no values are provided, if provided values exceed
+ * maximum repeated property length, or if a passed in {@code String} is {@code null}.
+ * @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setPropertyString(@NonNull String key, @NonNull String... values) {
@@ -698,8 +706,11 @@ public class GenericDocument {
* Sets one or multiple {@code boolean} values for a property, replacing its previous
* values.
*
- * @param key The key associated with the {@code values}.
- * @param values The {@code boolean} values of the property.
+ * @param key the key associated with the {@code values}.
+ * @param values the {@code boolean} values of the property.
+ * @throws IllegalArgumentException if no values are provided or if values exceed maximum
+ * repeated property length.
+ * @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setPropertyBoolean(@NonNull String key, @NonNull boolean... values) {
@@ -713,8 +724,11 @@ public class GenericDocument {
/**
* Sets one or multiple {@code long} values for a property, replacing its previous values.
*
- * @param key The key associated with the {@code values}.
- * @param values The {@code long} values of the property.
+ * @param key the key associated with the {@code values}.
+ * @param values the {@code long} values of the property.
+ * @throws IllegalArgumentException if no values are provided or if values exceed maximum
+ * repeated property length.
+ * @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setPropertyLong(@NonNull String key, @NonNull long... values) {
@@ -728,8 +742,11 @@ public class GenericDocument {
/**
* Sets one or multiple {@code double} values for a property, replacing its previous values.
*
- * @param key The key associated with the {@code values}.
- * @param values The {@code double} values of the property.
+ * @param key the key associated with the {@code values}.
+ * @param values the {@code double} values of the property.
+ * @throws IllegalArgumentException if no values are provided or if values exceed maximum
+ * repeated property length.
+ * @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setPropertyDouble(@NonNull String key, @NonNull double... values) {
@@ -743,8 +760,11 @@ public class GenericDocument {
/**
* Sets one or multiple {@code byte[]} for a property, replacing its previous values.
*
- * @param key The key associated with the {@code values}.
- * @param values The {@code byte[]} of the property.
+ * @param key the key associated with the {@code values}.
+ * @param values the {@code byte[]} of the property.
+ * @throws IllegalArgumentException if no values are provided, if provided values exceed
+ * maximum repeated property length, or if a passed in {@code byte[]} is {@code null}.
+ * @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setPropertyBytes(@NonNull String key, @NonNull byte[]... values) {
@@ -759,8 +779,12 @@ public class GenericDocument {
* Sets one or multiple {@link GenericDocument} values for a property, replacing its
* previous values.
*
- * @param key The key associated with the {@code values}.
- * @param values The {@link GenericDocument} values of the property.
+ * @param key the key associated with the {@code values}.
+ * @param values the {@link GenericDocument} values of the property.
+ * @throws IllegalArgumentException if no values are provided, if provided values exceed if
+ * provided values exceed maximum repeated property length, or if a passed in {@link
+ * GenericDocument} is {@code null}.
+ * @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setPropertyDocument(
@@ -853,7 +877,11 @@ public class GenericDocument {
}
}
- /** Builds the {@link GenericDocument} object. */
+ /**
+ * Builds the {@link GenericDocument} object.
+ *
+ * @throws IllegalStateException if the builder has already been used.
+ */
@NonNull
public GenericDocument build() {
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 0fcf0613dd38..c927e3412cbc 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -31,7 +31,8 @@ import java.util.Map;
import java.util.Set;
/**
- * Encapsulates a request to retrieve documents by namespace and URI.
+ * Encapsulates a request to retrieve documents by namespace and URIs from the {@link
+ * AppSearchSession} database.
*
* @see AppSearchSession#getByUri
*/
@@ -56,13 +57,13 @@ public final class GetByUriRequest {
mTypePropertyPathsMap = Preconditions.checkNotNull(typePropertyPathsMap);
}
- /** Returns the namespace to get documents from. */
+ /** Returns the namespace attached to the request. */
@NonNull
public String getNamespace() {
return mNamespace;
}
- /** Returns the URIs to get from the namespace. */
+ /** Returns the set of URIs attached to the request. */
@NonNull
public Set<String> getUris() {
return Collections.unmodifiableSet(mUris);
@@ -100,7 +101,11 @@ public final class GetByUriRequest {
return mTypePropertyPathsMap;
}
- /** Builder for {@link GetByUriRequest} objects. */
+ /**
+ * Builder for {@link GetByUriRequest} objects.
+ *
+ * <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 final Set<String> mUris = new ArraySet<>();
@@ -108,9 +113,12 @@ public final class GetByUriRequest {
private boolean mBuilt = false;
/**
- * Sets which namespace these documents will be retrieved from.
+ * 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 set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}.
+ * @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public Builder setNamespace(@NonNull String namespace) {
@@ -120,16 +128,24 @@ public final class GetByUriRequest {
return this;
}
- /** Adds one or more URIs to the request. */
+ /**
+ * Adds one or more URIs to the request.
+ *
+ * @throws IllegalStateException if the builder has already been used.
+ */
@NonNull
- public Builder addUri(@NonNull String... uris) {
+ public Builder addUris(@NonNull String... uris) {
Preconditions.checkNotNull(uris);
- return addUri(Arrays.asList(uris));
+ return addUris(Arrays.asList(uris));
}
- /** Adds one or more URIs to the request. */
+ /**
+ * Adds a collection of URIs to the request.
+ *
+ * @throws IllegalStateException if the builder has already been used.
+ */
@NonNull
- public Builder addUri(@NonNull Collection<String> uris) {
+ public Builder addUris(@NonNull Collection<String> uris) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(uris);
mUris.addAll(uris);
@@ -149,7 +165,8 @@ public final class GetByUriRequest {
* GetByUriRequest#PROJECTION_SCHEMA_TYPE_WILDCARD}, then those property paths will apply to
* all results, excepting any types that have their own, specific property paths set.
*
- * <p>{@see SearchSpec.Builder#addProjection(String, String...)}
+ * @throws IllegalStateException if the builder has already been used.
+ * <p>{@see SearchSpec.Builder#addProjection(String, String...)}
*/
@NonNull
public Builder addProjection(@NonNull String schemaType, @NonNull String... propertyPaths) {
@@ -170,7 +187,8 @@ public final class GetByUriRequest {
* GetByUriRequest#PROJECTION_SCHEMA_TYPE_WILDCARD}, then those property paths will apply to
* all results, excepting any types that have their own, specific property paths set.
*
- * <p>{@see SearchSpec.Builder#addProjection(String, String...)}
+ * @throws IllegalStateException if the builder has already been used.
+ * <p>{@see SearchSpec.Builder#addProjection(String, String...)}
*/
@NonNull
public Builder addProjection(
@@ -187,7 +205,11 @@ public final class GetByUriRequest {
return this;
}
- /** Builds a new {@link GetByUriRequest}. */
+ /**
+ * Builds a new {@link GetByUriRequest}.
+ *
+ * @throws IllegalStateException if the builder has already been used.
+ */
@NonNull
public GetByUriRequest build() {
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 2104198d371c..39b53b604abb 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
@@ -27,7 +27,8 @@ import java.util.Collections;
import java.util.Set;
/**
- * Encapsulates a request to remove documents by namespace and URI.
+ * Encapsulates a request to remove documents by namespace and URIs from the {@link
+ * AppSearchSession} database.
*
* @see AppSearchSession#remove
*/
@@ -46,22 +47,28 @@ public final class RemoveByUriRequest {
return mNamespace;
}
- /** Returns the URIs of documents to remove from the namespace. */
+ /** Returns the set of URIs attached to the request. */
@NonNull
public Set<String> getUris() {
return Collections.unmodifiableSet(mUris);
}
- /** Builder for {@link RemoveByUriRequest} objects. */
+ /**
+ * Builder for {@link RemoveByUriRequest} objects.
+ *
+ * <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 final Set<String> mUris = new ArraySet<>();
private boolean mBuilt = false;
/**
- * Sets which namespace these documents will be removed from.
+ * Sets the namespace to remove documents for.
*
* <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}.
+ *
+ * @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public Builder setNamespace(@NonNull String namespace) {
@@ -71,23 +78,35 @@ public final class RemoveByUriRequest {
return this;
}
- /** Adds one or more URIs to the request. */
+ /**
+ * Adds one or more URIs to the request.
+ *
+ * @throws IllegalStateException if the builder has already been used.
+ */
@NonNull
- public Builder addUri(@NonNull String... uris) {
+ public Builder addUris(@NonNull String... uris) {
Preconditions.checkNotNull(uris);
- return addUri(Arrays.asList(uris));
+ return addUris(Arrays.asList(uris));
}
- /** Adds one or more URIs to the request. */
+ /**
+ * Adds a collection of URIs to the request.
+ *
+ * @throws IllegalStateException if the builder has already been used.
+ */
@NonNull
- public Builder addUri(@NonNull Collection<String> uris) {
+ public Builder addUris(@NonNull Collection<String> uris) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(uris);
mUris.addAll(uris);
return this;
}
- /** Builds a new {@link RemoveByUriRequest}. */
+ /**
+ * Builds a new {@link RemoveByUriRequest}.
+ *
+ * @throws IllegalStateException if the builder has already been used.
+ */
@NonNull
public RemoveByUriRequest build() {
Preconditions.checkState(!mBuilt, "Builder has already been used");
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 0328d5453ab8..bc99d4f67d86 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -16,10 +16,9 @@
package android.app.appsearch;
-import static android.app.appsearch.AppSearchResult.RESULT_OK;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Bundle;
import android.util.ArraySet;
import com.android.internal.util.Preconditions;
@@ -32,23 +31,58 @@ import java.util.Set;
/** The response class of {@link AppSearchSession#setSchema} */
public class SetSchemaResponse {
+
+ private static final String DELETED_TYPES_FIELD = "deletedTypes";
+ private static final String INCOMPATIBLE_TYPES_FIELD = "incompatibleTypes";
+ private static final String MIGRATED_TYPES_FIELD = "migratedTypes";
+
+ private final Bundle mBundle;
+ /**
+ * The migrationFailures won't be saved in the bundle. Since:
+ *
+ * <ul>
+ * <li>{@link MigrationFailure} is generated in {@link AppSearchSession} which will be the SDK
+ * side in platform. We don't need to pass it from service side via binder.
+ * <li>Translate multiple {@link MigrationFailure}s to bundles in {@link Builder} and then
+ * back in constructor will be a huge waste.
+ * </ul>
+ */
private final List<MigrationFailure> mMigrationFailures;
- private final Set<String> mDeletedTypes;
- private final Set<String> mMigratedTypes;
- private final Set<String> mIncompatibleTypes;
- private final @AppSearchResult.ResultCode int mResultCode;
-
- SetSchemaResponse(
- @NonNull List<MigrationFailure> migrationFailures,
- @NonNull Set<String> deletedTypes,
- @NonNull Set<String> migratedTypes,
- @NonNull Set<String> incompatibleTypes,
- @AppSearchResult.ResultCode int resultCode) {
+
+ /** Cache of the inflated deleted schema types. Comes from inflating mBundles at first use. */
+ @Nullable private Set<String> mDeletedTypes;
+
+ /** Cache of the inflated migrated schema types. Comes from inflating mBundles at first use. */
+ @Nullable private Set<String> mMigratedTypes;
+
+ /**
+ * Cache of the inflated incompatible schema types. Comes from inflating mBundles at first use.
+ */
+ @Nullable private Set<String> mIncompatibleTypes;
+
+ SetSchemaResponse(@NonNull Bundle bundle, @NonNull List<MigrationFailure> migrationFailures) {
+ mBundle = Preconditions.checkNotNull(bundle);
mMigrationFailures = Preconditions.checkNotNull(migrationFailures);
- mDeletedTypes = Preconditions.checkNotNull(deletedTypes);
- mMigratedTypes = Preconditions.checkNotNull(migratedTypes);
- mIncompatibleTypes = Preconditions.checkNotNull(incompatibleTypes);
- mResultCode = resultCode;
+ }
+
+ SetSchemaResponse(@NonNull Bundle bundle) {
+ this(bundle, /*migrationFailures=*/ Collections.emptyList());
+ }
+
+ /**
+ * Returns the {@link Bundle} populated by this builder.
+ *
+ * @hide
+ */
+ @NonNull
+ public Bundle getBundle() {
+ return mBundle;
+ }
+
+ /** TODO(b/177266929): Remove this deprecated method */
+ //@Deprecated
+ public boolean isSuccess() {
+ return true;
}
/**
@@ -72,6 +106,12 @@ public class SetSchemaResponse {
*/
@NonNull
public Set<String> getDeletedTypes() {
+ if (mDeletedTypes == null) {
+ mDeletedTypes =
+ new ArraySet<>(
+ Preconditions.checkNotNull(
+ mBundle.getStringArrayList(DELETED_TYPES_FIELD)));
+ }
return Collections.unmodifiableSet(mDeletedTypes);
}
@@ -81,6 +121,12 @@ public class SetSchemaResponse {
*/
@NonNull
public Set<String> getMigratedTypes() {
+ if (mMigratedTypes == null) {
+ mMigratedTypes =
+ new ArraySet<>(
+ Preconditions.checkNotNull(
+ mBundle.getStringArrayList(MIGRATED_TYPES_FIELD)));
+ }
return Collections.unmodifiableSet(mMigratedTypes);
}
@@ -96,22 +142,28 @@ public class SetSchemaResponse {
*/
@NonNull
public Set<String> getIncompatibleTypes() {
+ if (mIncompatibleTypes == null) {
+ mIncompatibleTypes =
+ new ArraySet<>(
+ Preconditions.checkNotNull(
+ mBundle.getStringArrayList(INCOMPATIBLE_TYPES_FIELD)));
+ }
return Collections.unmodifiableSet(mIncompatibleTypes);
}
- /** Returns {@code true} if all {@link AppSearchSchema}s are successful set to the system. */
- public boolean isSuccess() {
- return mResultCode == RESULT_OK;
- }
-
- @Override
+ /**
+ * Translates the {@link SetSchemaResponse}'s bundle to {@link Builder}.
+ *
+ * @hide
+ */
@NonNull
- public String toString() {
- return "{\n Does setSchema success? : "
- + isSuccess()
- + "\n failures: "
- + mMigrationFailures
- + "\n}";
+ // TODO(b/179302942) change to Builder(mBundle) powered by mBundle.deepCopy
+ public Builder toBuilder() {
+ return new Builder()
+ .addDeletedTypes(getDeletedTypes())
+ .addIncompatibleTypes(getIncompatibleTypes())
+ .addMigratedTypes(getMigratedTypes())
+ .addMigrationFailures(mMigrationFailures);
}
/**
@@ -120,76 +172,74 @@ public class SetSchemaResponse {
* @hide
*/
public static class Builder {
- private final List<MigrationFailure> mMigrationFailures = new ArrayList<>();
- private final Set<String> mDeletedTypes = new ArraySet<>();
- private final Set<String> mMigratedTypes = new ArraySet<>();
- private final Set<String> mIncompatibleTypes = new ArraySet<>();
- private @AppSearchResult.ResultCode int mResultCode = RESULT_OK;
+ private final ArrayList<MigrationFailure> mMigrationFailures = new ArrayList<>();
+ private final ArrayList<String> mDeletedTypes = new ArrayList<>();
+ private final ArrayList<String> mMigratedTypes = new ArrayList<>();
+ private final ArrayList<String> mIncompatibleTypes = new ArrayList<>();
private boolean mBuilt = false;
- /** Adds a {@link MigrationFailure}. */
+ /** Adds {@link MigrationFailure}s to the list of migration failures. */
@NonNull
- public Builder setFailure(
- @NonNull String schemaType,
- @NonNull String namespace,
- @NonNull String uri,
- @NonNull AppSearchResult<Void> failureResult) {
+ public Builder addMigrationFailures(
+ @NonNull Collection<MigrationFailure> migrationFailures) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(schemaType);
- Preconditions.checkNotNull(namespace);
- Preconditions.checkNotNull(uri);
- Preconditions.checkNotNull(failureResult);
- Preconditions.checkState(!failureResult.isSuccess());
- mMigrationFailures.add(new MigrationFailure(schemaType, namespace, uri, failureResult));
+ mMigrationFailures.addAll(Preconditions.checkNotNull(migrationFailures));
return this;
}
- /** Adds a {@link MigrationFailure}. */
+ /** Adds a {@link MigrationFailure} to the list of migration failures. */
@NonNull
- public Builder setFailure(
- @NonNull String schemaType,
- @NonNull String namespace,
- @NonNull String uri,
- @AppSearchResult.ResultCode int resultCode,
- @Nullable String errorMessage) {
- mMigrationFailures.add(
- new MigrationFailure(
- schemaType,
- namespace,
- uri,
- AppSearchResult.newFailedResult(resultCode, errorMessage)));
+ public Builder addMigrationFailure(@NonNull MigrationFailure migrationFailure) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mMigrationFailures.add(Preconditions.checkNotNull(migrationFailure));
return this;
}
/** Adds deletedTypes to the list of deleted schema types. */
@NonNull
- public Builder addDeletedType(@NonNull Collection<String> deletedTypes) {
+ public Builder addDeletedTypes(@NonNull Collection<String> deletedTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
mDeletedTypes.addAll(Preconditions.checkNotNull(deletedTypes));
return this;
}
+ /** Adds one deletedType to the list of deleted schema types. */
+ @NonNull
+ public Builder addDeletedType(@NonNull String deletedType) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mDeletedTypes.add(Preconditions.checkNotNull(deletedType));
+ return this;
+ }
+
/** Adds incompatibleTypes to the list of incompatible schema types. */
@NonNull
- public Builder addIncompatibleType(@NonNull Collection<String> incompatibleTypes) {
+ public Builder addIncompatibleTypes(@NonNull Collection<String> incompatibleTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
mIncompatibleTypes.addAll(Preconditions.checkNotNull(incompatibleTypes));
return this;
}
+ /** Adds one incompatibleType to the list of incompatible schema types. */
+ @NonNull
+ public Builder addIncompatibleType(@NonNull String incompatibleType) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mIncompatibleTypes.add(Preconditions.checkNotNull(incompatibleType));
+ return this;
+ }
+
/** Adds migratedTypes to the list of migrated schema types. */
@NonNull
- public Builder addMigratedType(@NonNull Collection<String> migratedTypes) {
+ public Builder addMigratedTypes(@NonNull Collection<String> migratedTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
mMigratedTypes.addAll(Preconditions.checkNotNull(migratedTypes));
return this;
}
- /** Sets the {@link AppSearchResult.ResultCode} of the response. */
+ /** Adds one migratedType to the list of migrated schema types. */
@NonNull
- public Builder setResultCode(@AppSearchResult.ResultCode int resultCode) {
+ public Builder addMigratedType(@NonNull String migratedType) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mResultCode = resultCode;
+ mMigratedTypes.add(Preconditions.checkNotNull(migratedType));
return this;
}
@@ -197,13 +247,15 @@ public class SetSchemaResponse {
@NonNull
public SetSchemaResponse build() {
Preconditions.checkState(!mBuilt, "Builder has already been used");
+ Bundle bundle = new Bundle();
+ bundle.putStringArrayList(INCOMPATIBLE_TYPES_FIELD, mIncompatibleTypes);
+ bundle.putStringArrayList(DELETED_TYPES_FIELD, mDeletedTypes);
+ bundle.putStringArrayList(MIGRATED_TYPES_FIELD, mMigratedTypes);
mBuilt = true;
- return new SetSchemaResponse(
- mMigrationFailures,
- mDeletedTypes,
- mMigratedTypes,
- mIncompatibleTypes,
- mResultCode);
+ // Avoid converting the potential thousands of MigrationFailures to Pracelable and
+ // back just for put in bundle. In platform, we should set MigrationFailures in
+ // AppSearchSession after we pass SetSchemaResponse via binder.
+ return new SetSchemaResponse(bundle, mMigrationFailures);
}
}
@@ -212,38 +264,44 @@ public class SetSchemaResponse {
* {@link AppSearchSession#setSchema}.
*/
public static class MigrationFailure {
- private final String mSchemaType;
- private final String mNamespace;
- private final String mUri;
- AppSearchResult<Void> mFailureResult;
-
- MigrationFailure(
- @NonNull String schemaType,
- @NonNull String namespace,
- @NonNull String uri,
- @NonNull AppSearchResult<Void> result) {
- mSchemaType = schemaType;
- mNamespace = namespace;
- mUri = uri;
- mFailureResult = result;
+ private static final String SCHEMA_TYPE_FIELD = "schemaType";
+ private static final String NAMESPACE_FIELD = "namespace";
+ private static final String URI_FIELD = "uri";
+ private static final String ERROR_MESSAGE_FIELD = "errorMessage";
+ private static final String RESULT_CODE_FIELD = "resultCode";
+
+ private final Bundle mBundle;
+
+ MigrationFailure(@NonNull Bundle bundle) {
+ mBundle = bundle;
+ }
+
+ /**
+ * Returns the Bundle of the {@link MigrationFailure}.
+ *
+ * @hide
+ */
+ @NonNull
+ public Bundle getBundle() {
+ return mBundle;
}
/** Returns the schema type of the {@link GenericDocument} that fails to be migrated. */
@NonNull
public String getSchemaType() {
- return mSchemaType;
+ return mBundle.getString(SCHEMA_TYPE_FIELD, /*defaultValue=*/ "");
}
/** Returns the namespace of the {@link GenericDocument} that fails to be migrated. */
@NonNull
public String getNamespace() {
- return mNamespace;
+ return mBundle.getString(NAMESPACE_FIELD, /*defaultValue=*/ "");
}
/** Returns the uri of the {@link GenericDocument} that fails to be migrated. */
@NonNull
public String getUri() {
- return mUri;
+ return mBundle.getString(URI_FIELD, /*defaultValue=*/ "");
}
/**
@@ -252,7 +310,69 @@ public class SetSchemaResponse {
*/
@NonNull
public AppSearchResult<Void> getAppSearchResult() {
- return mFailureResult;
+ return AppSearchResult.newFailedResult(
+ mBundle.getInt(RESULT_CODE_FIELD),
+ mBundle.getString(ERROR_MESSAGE_FIELD, /*defaultValue=*/ ""));
+ }
+
+ /**
+ * Builder for {@link MigrationFailure} objects.
+ *
+ * @hide
+ */
+ public static class Builder {
+ private String mSchemaType;
+ private String mNamespace;
+ private String mUri;
+ private final Bundle mBundle = new Bundle();
+ private AppSearchResult<Void> mFailureResult;
+ private boolean mBuilt = false;
+
+ /** Sets the schema type for the {@link MigrationFailure}. */
+ @NonNull
+ public Builder setSchemaType(@NonNull String schemaType) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mSchemaType = Preconditions.checkNotNull(schemaType);
+ return this;
+ }
+
+ /** Sets the namespace for the {@link MigrationFailure}. */
+ @NonNull
+ public Builder setNamespace(@NonNull String namespace) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mNamespace = Preconditions.checkNotNull(namespace);
+ return this;
+ }
+
+ /** Sets the uri for the {@link MigrationFailure}. */
+ @NonNull
+ public Builder setUri(@NonNull String uri) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mUri = Preconditions.checkNotNull(uri);
+ return this;
+ }
+
+ /** Sets the failure {@link AppSearchResult} for the {@link MigrationFailure}. */
+ @NonNull
+ public Builder setAppSearchResult(@NonNull AppSearchResult<Void> appSearchResult) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ Preconditions.checkState(!appSearchResult.isSuccess(), "Input a success result");
+ mFailureResult = Preconditions.checkNotNull(appSearchResult);
+ return this;
+ }
+
+ /** Builds a {@link MigrationFailure} object. */
+ @NonNull
+ public MigrationFailure build() {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBundle.putString(SCHEMA_TYPE_FIELD, mSchemaType);
+ mBundle.putString(NAMESPACE_FIELD, mNamespace);
+ mBundle.putString(URI_FIELD, mUri);
+ mBundle.putString(ERROR_MESSAGE_FIELD, mFailureResult.getErrorMessage());
+ mBundle.putInt(RESULT_CODE_FIELD, mFailureResult.getResultCode());
+ mBuilt = true;
+ return new MigrationFailure(mBundle);
+ }
}
}
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResult.java
deleted file mode 100644
index f04ace684839..000000000000
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResult.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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.
- */
-
-package android.app.appsearch;
-
-import android.annotation.NonNull;
-import android.os.Bundle;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * This class represents the results of setSchema().
- *
- * @hide
- */
-public class SetSchemaResult {
-
- public static final String DELETED_SCHEMA_TYPES_FIELD = "deletedSchemaTypes";
- public static final String INCOMPATIBLE_SCHEMA_TYPES_FIELD = "incompatibleSchemaTypes";
- public static final String RESULT_CODE_FIELD = "resultCode";
- private final List<String> mDeletedSchemaTypes;
- private final List<String> mIncompatibleSchemaTypes;
- private final Bundle mBundle;
-
- SetSchemaResult(@NonNull Bundle bundle) {
- mBundle = Preconditions.checkNotNull(bundle);
- mDeletedSchemaTypes =
- Preconditions.checkNotNull(mBundle.getStringArrayList(DELETED_SCHEMA_TYPES_FIELD));
- mIncompatibleSchemaTypes =
- Preconditions.checkNotNull(
- mBundle.getStringArrayList(INCOMPATIBLE_SCHEMA_TYPES_FIELD));
- }
-
- /** Returns the {@link Bundle} of this class. */
- @NonNull
- public Bundle getBundle() {
- return mBundle;
- }
-
- /** returns all deleted schema types in this setSchema call. */
- @NonNull
- public List<String> getDeletedSchemaTypes() {
- return Collections.unmodifiableList(mDeletedSchemaTypes);
- }
-
- /** returns all incompatible schema types in this setSchema call. */
- @NonNull
- public List<String> getIncompatibleSchemaTypes() {
- return Collections.unmodifiableList(mIncompatibleSchemaTypes);
- }
-
- /**
- * returns the {@link android.app.appsearch.AppSearchResult.ResultCode} of the {@link
- * AppSearchSession#setSchema} call.
- */
- public int getResultCode() {
- return mBundle.getInt(RESULT_CODE_FIELD);
- }
-
- /** Builder for {@link SetSchemaResult} objects. */
- public static final class Builder {
- private final ArrayList<String> mDeletedSchemaTypes = new ArrayList<>();
- private final ArrayList<String> mIncompatibleSchemaTypes = new ArrayList<>();
- @AppSearchResult.ResultCode private int mResultCode;
- private boolean mBuilt = false;
-
- /** Adds a deletedSchemaTypes to the {@link SetSchemaResult}. */
- @NonNull
- public Builder addDeletedSchemaType(@NonNull String deletedSchemaType) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
- mDeletedSchemaTypes.add(Preconditions.checkNotNull(deletedSchemaType));
- return this;
- }
-
- /** Adds a incompatible SchemaTypes to the {@link SetSchemaResult}. */
- @NonNull
- public Builder addIncompatibleSchemaType(@NonNull String incompatibleSchemaTypes) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
- mIncompatibleSchemaTypes.add(Preconditions.checkNotNull(incompatibleSchemaTypes));
- return this;
- }
-
- /**
- * Sets the {@link android.app.appsearch.AppSearchResult.ResultCode} of the {@link
- * AppSearchSession#setSchema} call to the {@link SetSchemaResult}
- */
- @NonNull
- public Builder setResultCode(@AppSearchResult.ResultCode int resultCode) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
- mResultCode = resultCode;
- return this;
- }
-
- /** Builds a {@link SetSchemaResult}. */
- @NonNull
- public SetSchemaResult build() {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
- Bundle bundle = new Bundle();
- bundle.putStringArrayList(
- SetSchemaResult.DELETED_SCHEMA_TYPES_FIELD, mDeletedSchemaTypes);
- bundle.putStringArrayList(
- SetSchemaResult.INCOMPATIBLE_SCHEMA_TYPES_FIELD, mIncompatibleSchemaTypes);
- bundle.putInt(RESULT_CODE_FIELD, mResultCode);
- mBuilt = true;
- return new SetSchemaResult(bundle);
- }
- }
-}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java
index 1b4d28401ea0..14dd472c9b9c 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java
@@ -147,35 +147,41 @@ public final class BundleUtil {
if (bundle == null) {
return 0;
}
- int[] hashCodes = new int[bundle.size()];
- int i = 0;
+ int[] hashCodes = new int[bundle.size() + 1];
+ int hashCodeIdx = 0;
// Bundle inherit its hashCode() from Object.java, which only relative to their memory
// address. Bundle doesn't have an order, so we should iterate all keys and combine
// their value's hashcode into an array. And use the hashcode of the array to be
// the hashcode of the bundle.
- for (String key : bundle.keySet()) {
- Object value = bundle.get(key);
+ // Because bundle.keySet() doesn't guarantee any particular order, we need to sort the keys
+ // in case the iteration order varies from run to run.
+ String[] keys = bundle.keySet().toArray(new String[0]);
+ Arrays.sort(keys);
+ // Hash the keys so we can detect key-only differences
+ hashCodes[hashCodeIdx++] = Arrays.hashCode(keys);
+ for (int keyIdx = 0; keyIdx < keys.length; keyIdx++) {
+ Object value = bundle.get(keys[keyIdx]);
if (value instanceof Bundle) {
- hashCodes[i++] = deepHashCode((Bundle) value);
+ hashCodes[hashCodeIdx++] = deepHashCode((Bundle) value);
} else if (value instanceof int[]) {
- hashCodes[i++] = Arrays.hashCode((int[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((int[]) value);
} else if (value instanceof byte[]) {
- hashCodes[i++] = Arrays.hashCode((byte[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((byte[]) value);
} else if (value instanceof char[]) {
- hashCodes[i++] = Arrays.hashCode((char[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((char[]) value);
} else if (value instanceof long[]) {
- hashCodes[i++] = Arrays.hashCode((long[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((long[]) value);
} else if (value instanceof float[]) {
- hashCodes[i++] = Arrays.hashCode((float[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((float[]) value);
} else if (value instanceof short[]) {
- hashCodes[i++] = Arrays.hashCode((short[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((short[]) value);
} else if (value instanceof double[]) {
- hashCodes[i++] = Arrays.hashCode((double[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((double[]) value);
} else if (value instanceof boolean[]) {
- hashCodes[i++] = Arrays.hashCode((boolean[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((boolean[]) value);
} else if (value instanceof String[]) {
// Optimization to avoid Object[] handler creating an inner array for common cases
- hashCodes[i++] = Arrays.hashCode((String[]) value);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode((String[]) value);
} else if (value instanceof Object[]) {
Object[] array = (Object[]) value;
int[] innerHashCodes = new int[array.length];
@@ -186,7 +192,7 @@ public final class BundleUtil {
innerHashCodes[j] = array[j].hashCode();
}
}
- hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes);
} else if (value instanceof ArrayList) {
ArrayList<?> list = (ArrayList<?>) value;
int[] innerHashCodes = new int[list.size()];
@@ -198,7 +204,7 @@ public final class BundleUtil {
innerHashCodes[j] = item.hashCode();
}
}
- hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes);
} else if (value instanceof SparseArray) {
SparseArray<?> array = (SparseArray<?>) value;
int[] innerHashCodes = new int[array.size() * 2];
@@ -211,9 +217,9 @@ public final class BundleUtil {
innerHashCodes[j * 2 + 1] = item.hashCode();
}
}
- hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+ hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes);
} else {
- hashCodes[i++] = value.hashCode();
+ hashCodes[hashCodeIdx++] = value.hashCode();
}
}
return Arrays.hashCode(hashCodes);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 3bbc945c8b87..a45fa39bd58d 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -38,8 +38,10 @@ import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -49,6 +51,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
/** TODO(b/142567528): add comments when implement this class */
public class AppSearchManagerService extends SystemService {
@@ -56,6 +59,12 @@ public class AppSearchManagerService extends SystemService {
private PackageManagerInternal mPackageManagerInternal;
private ImplInstanceManager mImplInstanceManager;
+ // Cache of unlocked user ids so we don't have to query UserManager service each time. The
+ // "locked" suffix refers to the fact that access to the field should be locked; unrelated to
+ // the unlocked status of user ids.
+ @GuardedBy("mUnlockedUserIdsLocked")
+ private final Set<Integer> mUnlockedUserIdsLocked = new ArraySet<>();
+
public AppSearchManagerService(Context context) {
super(context);
}
@@ -67,6 +76,13 @@ public class AppSearchManagerService extends SystemService {
mImplInstanceManager = ImplInstanceManager.getInstance(getContext());
}
+ @Override
+ public void onUserUnlocked(@NonNull TargetUser user) {
+ synchronized (mUnlockedUserIdsLocked) {
+ mUnlockedUserIdsLocked.add(user.getUserIdentifier());
+ }
+ }
+
private class Stub extends IAppSearchManager.Stub {
@Override
public void setSchema(
@@ -86,6 +102,7 @@ public class AppSearchManagerService extends SystemService {
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
+ verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
List<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size());
for (int i = 0; i < schemaBundles.size(); i++) {
@@ -97,7 +114,7 @@ public class AppSearchManagerService extends SystemService {
schemasPackageAccessibleBundles.entrySet()) {
List<PackageIdentifier> packageIdentifiers =
new ArrayList<>(entry.getValue().size());
- for (int i = 0; i < packageIdentifiers.size(); i++) {
+ for (int i = 0; i < entry.getValue().size(); i++) {
packageIdentifiers.add(new PackageIdentifier(entry.getValue().get(i)));
}
schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
@@ -133,6 +150,7 @@ public class AppSearchManagerService extends SystemService {
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
+ verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
@@ -165,6 +183,7 @@ public class AppSearchManagerService extends SystemService {
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
+ verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
AppSearchBatchResult.Builder<String, Void> resultBuilder =
new AppSearchBatchResult.Builder<>();
@@ -207,6 +226,7 @@ public class AppSearchManagerService extends SystemService {
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
+ verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
new AppSearchBatchResult.Builder<>();
@@ -253,6 +273,7 @@ public class AppSearchManagerService extends SystemService {
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
+ verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
@@ -287,6 +308,7 @@ public class AppSearchManagerService extends SystemService {
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
+ verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
@@ -318,6 +340,7 @@ public class AppSearchManagerService extends SystemService {
// TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally
// opened it
try {
+ verifyUserUnlocked(callingUserId);
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
SearchResultPage searchResultPage = impl.getNextPage(nextPageToken);
@@ -337,6 +360,7 @@ public class AppSearchManagerService extends SystemService {
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
+ verifyUserUnlocked(callingUserId);
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
impl.invalidateNextPageToken(nextPageToken);
@@ -364,6 +388,7 @@ public class AppSearchManagerService extends SystemService {
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
+ verifyUserUnlocked(callingUserId);
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
impl.reportUsage(packageName, databaseName, namespace, uri, usageTimeMillis);
@@ -392,6 +417,7 @@ public class AppSearchManagerService extends SystemService {
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
+ verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
AppSearchBatchResult.Builder<String, Void> resultBuilder =
new AppSearchBatchResult.Builder<>();
@@ -431,6 +457,7 @@ public class AppSearchManagerService extends SystemService {
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
+ verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
@@ -453,6 +480,7 @@ public class AppSearchManagerService extends SystemService {
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
+ verifyUserUnlocked(callingUserId);
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
impl.persistToDisk();
@@ -470,6 +498,7 @@ public class AppSearchManagerService extends SystemService {
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
+ verifyUserUnlocked(callingUserId);
mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
} catch (Throwable t) {
@@ -479,6 +508,15 @@ public class AppSearchManagerService extends SystemService {
}
}
+ private void verifyUserUnlocked(int callingUserId) {
+ synchronized (mUnlockedUserIdsLocked) {
+ if (!mUnlockedUserIdsLocked.contains(callingUserId)) {
+ throw new IllegalStateException(
+ "User " + callingUserId + " is locked or not running.");
+ }
+ }
+ }
+
private void verifyCallingPackage(int callingUid, @NonNull String callingPackage) {
Preconditions.checkNotNull(callingPackage);
if (mPackageManagerInternal.getPackageUid(
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index 97b1a8cd6d50..82319d4f353b 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -29,6 +29,7 @@ import android.os.storage.StorageManager;
import android.util.SparseArray;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.appsearch.external.localstorage.AppSearchImpl;
import java.io.File;
@@ -43,7 +44,9 @@ public final class ImplInstanceManager {
private static ImplInstanceManager sImplInstanceManager;
- private final SparseArray<AppSearchImpl> mInstances = new SparseArray<>();
+ @GuardedBy("mInstancesLocked")
+ private final SparseArray<AppSearchImpl> mInstancesLocked = new SparseArray<>();
+
private final String mGlobalQuerierPackage;
private ImplInstanceManager(@NonNull String globalQuerierPackage) {
@@ -73,27 +76,24 @@ public final class ImplInstanceManager {
/**
* Gets an instance of AppSearchImpl for the given user.
*
- * <p>If no AppSearchImpl instance exists for this user, Icing will be initialized and one will
- * be created.
+ * <p>If no AppSearchImpl instance exists for the unlocked user, Icing will be initialized and
+ * one will be created.
*
* @param context The context
* @param userId The multi-user userId of the device user calling AppSearch
* @return An initialized {@link AppSearchImpl} for this user
*/
@NonNull
- public AppSearchImpl getAppSearchImpl(@NonNull Context context, @UserIdInt int userId)
- throws AppSearchException {
- AppSearchImpl instance = mInstances.get(userId);
- if (instance == null) {
- synchronized (ImplInstanceManager.class) {
- instance = mInstances.get(userId);
- if (instance == null) {
- instance = createImpl(context, userId);
- mInstances.put(userId, instance);
- }
+ public AppSearchImpl getAppSearchImpl(
+ @NonNull Context context, @UserIdInt int userId) throws AppSearchException {
+ synchronized (mInstancesLocked) {
+ AppSearchImpl instance = mInstancesLocked.get(userId);
+ if (instance == null) {
+ instance = createImpl(context, userId);
+ mInstancesLocked.put(userId, instance);
}
+ return instance;
}
- return instance;
}
private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
index 64dc972d301c..babcd25e3e26 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
@@ -332,10 +332,8 @@ public class VisibilityStore {
for (Map.Entry<String, List<PackageIdentifier>> entry :
schemasPackageAccessible.entrySet()) {
for (int i = 0; i < entry.getValue().size(); i++) {
- // TODO(b/169883602): remove the "placeholder" uri once upstream changes to relax
- // nested document uri rules gets synced down.
GenericDocument packageAccessibleDocument =
- new GenericDocument.Builder(/*uri=*/ "placeholder", PACKAGE_ACCESSIBLE_TYPE)
+ new GenericDocument.Builder(/*uri=*/"", PACKAGE_ACCESSIBLE_TYPE)
.setNamespace(NAMESPACE)
.setPropertyString(
PACKAGE_NAME_PROPERTY,
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index 8bff7207a553..6c2e30e98877 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -25,7 +25,7 @@ import android.app.appsearch.GetByUriRequest;
import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.SearchResultPage;
import android.app.appsearch.SearchSpec;
-import android.app.appsearch.SetSchemaResult;
+import android.app.appsearch.SetSchemaResponse;
import android.app.appsearch.exceptions.AppSearchException;
import android.content.Context;
import android.os.Bundle;
@@ -41,7 +41,7 @@ import com.android.server.appsearch.external.localstorage.converter.ResultCodeTo
import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.SearchResultToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.SearchSpecToProtoConverter;
-import com.android.server.appsearch.external.localstorage.converter.SetSchemaResultToProtoConverter;
+import com.android.server.appsearch.external.localstorage.converter.SetSchemaResponseToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.TypePropertyPathToProtoConverter;
import com.google.android.icing.IcingSearchEngine;
@@ -73,6 +73,7 @@ import com.google.android.icing.proto.StatusProto;
import com.google.android.icing.proto.TypePropertyMask;
import com.google.android.icing.proto.UsageReport;
+import java.io.Closeable;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
@@ -119,7 +120,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
* @hide
*/
@WorkerThread
-public final class AppSearchImpl {
+public final class AppSearchImpl implements Closeable {
private static final String TAG = "AppSearchImpl";
@VisibleForTesting static final char DATABASE_DELIMITER = '/';
@@ -151,12 +152,16 @@ public final class AppSearchImpl {
private final Map<String, Set<String>> mNamespaceMapLocked = new HashMap<>();
/**
- * The counter to check when to call {@link #checkForOptimizeLocked(boolean)}. The interval is
- * {@link #CHECK_OPTIMIZE_INTERVAL}.
+ * The counter to check when to call {@link #checkForOptimize}. The interval is {@link
+ * #CHECK_OPTIMIZE_INTERVAL}.
*/
@GuardedBy("mReadWriteLock")
private int mOptimizeIntervalCountLocked = 0;
+ /** Whether this instance has been closed, and therefore unusable. */
+ @GuardedBy("mReadWriteLock")
+ private boolean mClosedLocked = false;
+
/**
* Creates and initializes an instance of {@link AppSearchImpl} which writes data to the given
* folder.
@@ -208,7 +213,7 @@ public final class AppSearchImpl {
} catch (AppSearchException e) {
Log.w(TAG, "Error initializing, resetting IcingSearchEngine.", e);
// Some error. Reset and see if it fixes it.
- reset();
+ resetLocked();
return;
}
@@ -222,11 +227,6 @@ public final class AppSearchImpl {
for (String prefixedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
addToMap(mNamespaceMapLocked, getPrefix(prefixedNamespace), prefixedNamespace);
}
-
- // TODO(b/155939114): It's possible to optimize after init, which would reduce the time
- // to when we're able to serve queries. Consider moving this optimize call out.
- checkForOptimizeLocked(/* force= */ true);
-
} finally {
mReadWriteLock.writeLock().unlock();
}
@@ -240,12 +240,45 @@ public final class AppSearchImpl {
void initializeVisibilityStore() throws AppSearchException {
mReadWriteLock.writeLock().lock();
try {
+ throwIfClosedLocked();
+
mVisibilityStoreLocked.initialize();
} finally {
mReadWriteLock.writeLock().unlock();
}
}
+ @GuardedBy("mReadWriteLock")
+ private void throwIfClosedLocked() {
+ if (mClosedLocked) {
+ throw new IllegalStateException("Trying to use a closed AppSearchImpl instance.");
+ }
+ }
+
+ /**
+ * Persists data to disk and closes the instance.
+ *
+ * <p>This instance is no longer usable after it's been closed. Call {@link #create} to create a
+ * new, usable instance.
+ */
+ @Override
+ public void close() {
+ mReadWriteLock.writeLock().lock();
+ try {
+ if (mClosedLocked) {
+ return;
+ }
+
+ persistToDisk();
+ mIcingSearchEngineLocked.close();
+ mClosedLocked = true;
+ } catch (AppSearchException e) {
+ Log.w(TAG, "Error when closing AppSearchImpl.", e);
+ } finally {
+ mReadWriteLock.writeLock().unlock();
+ }
+ }
+
/**
* Updates the AppSearch schema for this app.
*
@@ -259,10 +292,14 @@ public final class AppSearchImpl {
* @param schemasPackageAccessible Schema types that are visible to the specified packages.
* @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents
* which do not comply with the new schema will be deleted.
- * @throws AppSearchException on IcingSearchEngine error.
+ * @throws AppSearchException On IcingSearchEngine error. If the status code is
+ * FAILED_PRECONDITION for the incompatible change, the exception will be converted to the
+ * SetSchemaResponse.
+ * @return The response contains deleted schema types and incompatible schema types of this
+ * call.
*/
@NonNull
- public SetSchemaResult setSchema(
+ public SetSchemaResponse setSchema(
@NonNull String packageName,
@NonNull String databaseName,
@NonNull List<AppSearchSchema> schemas,
@@ -272,6 +309,8 @@ public final class AppSearchImpl {
throws AppSearchException {
mReadWriteLock.writeLock().lock();
try {
+ throwIfClosedLocked();
+
SchemaProto.Builder existingSchemaBuilder = getSchemaProtoLocked().toBuilder();
SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder();
@@ -297,9 +336,16 @@ public final class AppSearchImpl {
try {
checkSuccess(setSchemaResultProto.getStatus());
} catch (AppSearchException e) {
- if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
- || setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0) {
- return SetSchemaResultToProtoConverter.toSetSchemaResult(
+ // Swallow the exception for the incompatible change case. We will propagate
+ // those deleted schemas and incompatible types to the SetSchemaResponse.
+ boolean isFailedPrecondition =
+ setSchemaResultProto.getStatus().getCode()
+ == StatusProto.Code.FAILED_PRECONDITION;
+ boolean isIncompatible =
+ setSchemaResultProto.getDeletedSchemaTypesCount() > 0
+ || setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0;
+ if (isFailedPrecondition && isIncompatible) {
+ return SetSchemaResponseToProtoConverter.toSetSchemaResponse(
setSchemaResultProto, prefix);
} else {
throw e;
@@ -317,7 +363,7 @@ public final class AppSearchImpl {
}
Map<String, List<PackageIdentifier>> prefixedSchemasPackageAccessible =
- new ArrayMap<>(schemasNotPlatformSurfaceable.size());
+ new ArrayMap<>(schemasPackageAccessible.size());
for (Map.Entry<String, List<PackageIdentifier>> entry :
schemasPackageAccessible.entrySet()) {
prefixedSchemasPackageAccessible.put(prefix + entry.getKey(), entry.getValue());
@@ -328,16 +374,8 @@ public final class AppSearchImpl {
prefixedSchemasNotPlatformSurfaceable,
prefixedSchemasPackageAccessible);
- // Determine whether to schedule an immediate optimize.
- if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
- || (setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0
- && forceOverride)) {
- // Any existing schemas which is not in 'schemas' will be deleted, and all
- // documents of these types were also deleted. And so well if we force override
- // incompatible schemas.
- checkForOptimizeLocked(/* force= */ true);
- }
- return SetSchemaResultToProtoConverter.toSetSchemaResult(setSchemaResultProto, prefix);
+ return SetSchemaResponseToProtoConverter.toSetSchemaResponse(
+ setSchemaResultProto, prefix);
} finally {
mReadWriteLock.writeLock().unlock();
}
@@ -355,44 +393,47 @@ public final class AppSearchImpl {
@NonNull
public List<AppSearchSchema> getSchema(
@NonNull String packageName, @NonNull String databaseName) throws AppSearchException {
- SchemaProto fullSchema;
mReadWriteLock.readLock().lock();
try {
- fullSchema = getSchemaProtoLocked();
- } finally {
- mReadWriteLock.readLock().unlock();
- }
+ throwIfClosedLocked();
- String prefix = createPrefix(packageName, databaseName);
- List<AppSearchSchema> result = new ArrayList<>();
- for (int i = 0; i < fullSchema.getTypesCount(); i++) {
- String typePrefix = getPrefix(fullSchema.getTypes(i).getSchemaType());
- if (!prefix.equals(typePrefix)) {
- continue;
- }
- // Rewrite SchemaProto.types.schema_type
- SchemaTypeConfigProto.Builder typeConfigBuilder = fullSchema.getTypes(i).toBuilder();
- String newSchemaType = typeConfigBuilder.getSchemaType().substring(prefix.length());
- typeConfigBuilder.setSchemaType(newSchemaType);
+ SchemaProto fullSchema = getSchemaProtoLocked();
- // Rewrite SchemaProto.types.properties.schema_type
- for (int propertyIdx = 0;
- propertyIdx < typeConfigBuilder.getPropertiesCount();
- propertyIdx++) {
- PropertyConfigProto.Builder propertyConfigBuilder =
- typeConfigBuilder.getProperties(propertyIdx).toBuilder();
- if (!propertyConfigBuilder.getSchemaType().isEmpty()) {
- String newPropertySchemaType =
- propertyConfigBuilder.getSchemaType().substring(prefix.length());
- propertyConfigBuilder.setSchemaType(newPropertySchemaType);
- typeConfigBuilder.setProperties(propertyIdx, propertyConfigBuilder);
+ String prefix = createPrefix(packageName, databaseName);
+ List<AppSearchSchema> result = new ArrayList<>();
+ for (int i = 0; i < fullSchema.getTypesCount(); i++) {
+ String typePrefix = getPrefix(fullSchema.getTypes(i).getSchemaType());
+ if (!prefix.equals(typePrefix)) {
+ continue;
+ }
+ // Rewrite SchemaProto.types.schema_type
+ SchemaTypeConfigProto.Builder typeConfigBuilder =
+ fullSchema.getTypes(i).toBuilder();
+ String newSchemaType = typeConfigBuilder.getSchemaType().substring(prefix.length());
+ typeConfigBuilder.setSchemaType(newSchemaType);
+
+ // Rewrite SchemaProto.types.properties.schema_type
+ for (int propertyIdx = 0;
+ propertyIdx < typeConfigBuilder.getPropertiesCount();
+ propertyIdx++) {
+ PropertyConfigProto.Builder propertyConfigBuilder =
+ typeConfigBuilder.getProperties(propertyIdx).toBuilder();
+ if (!propertyConfigBuilder.getSchemaType().isEmpty()) {
+ String newPropertySchemaType =
+ propertyConfigBuilder.getSchemaType().substring(prefix.length());
+ propertyConfigBuilder.setSchemaType(newPropertySchemaType);
+ typeConfigBuilder.setProperties(propertyIdx, propertyConfigBuilder);
+ }
}
- }
- AppSearchSchema schema = SchemaToProtoConverter.toAppSearchSchema(typeConfigBuilder);
- result.add(schema);
+ AppSearchSchema schema =
+ SchemaToProtoConverter.toAppSearchSchema(typeConfigBuilder);
+ result.add(schema);
+ }
+ return result;
+ } finally {
+ mReadWriteLock.readLock().unlock();
}
- return result;
}
/**
@@ -410,23 +451,22 @@ public final class AppSearchImpl {
@NonNull String databaseName,
@NonNull GenericDocument document)
throws AppSearchException {
- DocumentProto.Builder documentBuilder =
- GenericDocumentToProtoConverter.toDocumentProto(document).toBuilder();
- String prefix = createPrefix(packageName, databaseName);
- addPrefixToDocument(documentBuilder, prefix);
-
- PutResultProto putResultProto;
mReadWriteLock.writeLock().lock();
try {
- putResultProto = mIcingSearchEngineLocked.put(documentBuilder.build());
+ throwIfClosedLocked();
+
+ DocumentProto.Builder documentBuilder =
+ GenericDocumentToProtoConverter.toDocumentProto(document).toBuilder();
+ String prefix = createPrefix(packageName, databaseName);
+ addPrefixToDocument(documentBuilder, prefix);
+
+ PutResultProto putResultProto = mIcingSearchEngineLocked.put(documentBuilder.build());
addToMap(mNamespaceMapLocked, prefix, documentBuilder.getNamespace());
- // The existing documents with same URI will be deleted, so there maybe some resources
- // could be released after optimize().
- checkForOptimizeLocked(/* force= */ false);
+
+ checkSuccess(putResultProto.getStatus());
} finally {
mReadWriteLock.writeLock().unlock();
}
- checkSuccess(putResultProto.getStatus());
}
/**
@@ -451,40 +491,42 @@ public final class AppSearchImpl {
@NonNull String uri,
@NonNull Map<String, List<String>> typePropertyPaths)
throws AppSearchException {
- GetResultProto getResultProto;
- List<TypePropertyMask> nonPrefixedPropertyMasks =
- TypePropertyPathToProtoConverter.toTypePropertyMaskList(typePropertyPaths);
- List<TypePropertyMask> prefixedPropertyMasks =
- new ArrayList<>(nonPrefixedPropertyMasks.size());
- for (int i = 0; i < nonPrefixedPropertyMasks.size(); ++i) {
- TypePropertyMask typePropertyMask = nonPrefixedPropertyMasks.get(i);
- String nonPrefixedType = typePropertyMask.getSchemaType();
- String prefixedType =
- nonPrefixedType.equals(GetByUriRequest.PROJECTION_SCHEMA_TYPE_WILDCARD)
- ? nonPrefixedType
- : createPrefix(packageName, databaseName) + nonPrefixedType;
- prefixedPropertyMasks.add(
- typePropertyMask.toBuilder().setSchemaType(prefixedType).build());
- }
- GetResultSpecProto getResultSpec =
- GetResultSpecProto.newBuilder()
- .addAllTypePropertyMasks(prefixedPropertyMasks)
- .build();
mReadWriteLock.readLock().lock();
try {
- getResultProto =
+ throwIfClosedLocked();
+
+ List<TypePropertyMask> nonPrefixedPropertyMasks =
+ TypePropertyPathToProtoConverter.toTypePropertyMaskList(typePropertyPaths);
+ List<TypePropertyMask> prefixedPropertyMasks =
+ new ArrayList<>(nonPrefixedPropertyMasks.size());
+ for (int i = 0; i < nonPrefixedPropertyMasks.size(); ++i) {
+ TypePropertyMask typePropertyMask = nonPrefixedPropertyMasks.get(i);
+ String nonPrefixedType = typePropertyMask.getSchemaType();
+ String prefixedType =
+ nonPrefixedType.equals(GetByUriRequest.PROJECTION_SCHEMA_TYPE_WILDCARD)
+ ? nonPrefixedType
+ : createPrefix(packageName, databaseName) + nonPrefixedType;
+ prefixedPropertyMasks.add(
+ typePropertyMask.toBuilder().setSchemaType(prefixedType).build());
+ }
+ GetResultSpecProto getResultSpec =
+ GetResultSpecProto.newBuilder()
+ .addAllTypePropertyMasks(prefixedPropertyMasks)
+ .build();
+
+ GetResultProto getResultProto =
mIcingSearchEngineLocked.get(
createPrefix(packageName, databaseName) + namespace,
uri,
getResultSpec);
+ checkSuccess(getResultProto.getStatus());
+
+ DocumentProto.Builder documentBuilder = getResultProto.getDocument().toBuilder();
+ removePrefixesFromDocument(documentBuilder);
+ return GenericDocumentToProtoConverter.toGenericDocument(documentBuilder.build());
} finally {
mReadWriteLock.readLock().unlock();
}
- checkSuccess(getResultProto.getStatus());
-
- DocumentProto.Builder documentBuilder = getResultProto.getDocument().toBuilder();
- removePrefixesFromDocument(documentBuilder);
- return GenericDocumentToProtoConverter.toGenericDocument(documentBuilder.build());
}
/**
@@ -507,17 +549,19 @@ public final class AppSearchImpl {
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec)
throws AppSearchException {
- List<String> filterPackageNames = searchSpec.getFilterPackageNames();
- if (!filterPackageNames.isEmpty() && !filterPackageNames.contains(packageName)) {
- // Client wanted to query over some packages that weren't its own. This isn't
- // allowed through local query so we can return early with no results.
- return new SearchResultPage(Bundle.EMPTY);
- }
-
mReadWriteLock.readLock().lock();
try {
+ throwIfClosedLocked();
+
+ List<String> filterPackageNames = searchSpec.getFilterPackageNames();
+ if (!filterPackageNames.isEmpty() && !filterPackageNames.contains(packageName)) {
+ // Client wanted to query over some packages that weren't its own. This isn't
+ // allowed through local query so we can return early with no results.
+ return new SearchResultPage(Bundle.EMPTY);
+ }
+
String prefix = createPrefix(packageName, databaseName);
- Set<String> allowedPrefixedSchemas = getAllowedPrefixSchemas(prefix, searchSpec);
+ Set<String> allowedPrefixedSchemas = getAllowedPrefixSchemasLocked(prefix, searchSpec);
return doQueryLocked(
Collections.singleton(createPrefix(packageName, databaseName)),
@@ -552,6 +596,8 @@ public final class AppSearchImpl {
throws AppSearchException {
mReadWriteLock.readLock().lock();
try {
+ throwIfClosedLocked();
+
Set<String> packageFilters = new ArraySet<>(searchSpec.getFilterPackageNames());
Set<String> prefixFilters = new ArraySet<>();
Set<String> allPrefixes = mNamespaceMapLocked.keySet();
@@ -654,6 +700,8 @@ public final class AppSearchImpl {
public SearchResultPage getNextPage(long nextPageToken) throws AppSearchException {
mReadWriteLock.readLock().lock();
try {
+ throwIfClosedLocked();
+
SearchResultProto searchResultProto =
mIcingSearchEngineLocked.getNextPage(nextPageToken);
checkSuccess(searchResultProto.getStatus());
@@ -674,6 +722,8 @@ public final class AppSearchImpl {
public void invalidateNextPageToken(long nextPageToken) {
mReadWriteLock.readLock().lock();
try {
+ throwIfClosedLocked();
+
mIcingSearchEngineLocked.invalidateNextPageToken(nextPageToken);
} finally {
mReadWriteLock.readLock().unlock();
@@ -688,16 +738,19 @@ public final class AppSearchImpl {
@NonNull String uri,
long usageTimestampMillis)
throws AppSearchException {
- String prefixedNamespace = createPrefix(packageName, databaseName) + namespace;
- UsageReport report =
- UsageReport.newBuilder()
- .setDocumentNamespace(prefixedNamespace)
- .setDocumentUri(uri)
- .setUsageTimestampMs(usageTimestampMillis)
- .setUsageType(UsageReport.UsageType.USAGE_TYPE1)
- .build();
mReadWriteLock.writeLock().lock();
try {
+ throwIfClosedLocked();
+
+ String prefixedNamespace = createPrefix(packageName, databaseName) + namespace;
+ UsageReport report =
+ UsageReport.newBuilder()
+ .setDocumentNamespace(prefixedNamespace)
+ .setDocumentUri(uri)
+ .setUsageTimestampMs(usageTimestampMillis)
+ .setUsageType(UsageReport.UsageType.USAGE_TYPE1)
+ .build();
+
ReportUsageResultProto result = mIcingSearchEngineLocked.reportUsage(report);
checkSuccess(result.getStatus());
} finally {
@@ -722,16 +775,18 @@ public final class AppSearchImpl {
@NonNull String namespace,
@NonNull String uri)
throws AppSearchException {
- String prefixedNamespace = createPrefix(packageName, databaseName) + namespace;
- DeleteResultProto deleteResultProto;
mReadWriteLock.writeLock().lock();
try {
- deleteResultProto = mIcingSearchEngineLocked.delete(prefixedNamespace, uri);
- checkForOptimizeLocked(/* force= */ false);
+ throwIfClosedLocked();
+
+ String prefixedNamespace = createPrefix(packageName, databaseName) + namespace;
+ DeleteResultProto deleteResultProto =
+ mIcingSearchEngineLocked.delete(prefixedNamespace, uri);
+
+ checkSuccess(deleteResultProto.getStatus());
} finally {
mReadWriteLock.writeLock().unlock();
}
- checkSuccess(deleteResultProto.getStatus());
}
/**
@@ -751,22 +806,25 @@ public final class AppSearchImpl {
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec)
throws AppSearchException {
- List<String> filterPackageNames = searchSpec.getFilterPackageNames();
- if (!filterPackageNames.isEmpty() && !filterPackageNames.contains(packageName)) {
- // We're only removing documents within the parameter `packageName`. If we're not
- // restricting our remove-query to this package name, then there's nothing for us to
- // remove.
- return;
- }
-
- SearchSpecProto searchSpecProto = SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
- SearchSpecProto.Builder searchSpecBuilder =
- searchSpecProto.toBuilder().setQuery(queryExpression);
- DeleteByQueryResultProto deleteResultProto;
mReadWriteLock.writeLock().lock();
try {
+ throwIfClosedLocked();
+
+ List<String> filterPackageNames = searchSpec.getFilterPackageNames();
+ if (!filterPackageNames.isEmpty() && !filterPackageNames.contains(packageName)) {
+ // We're only removing documents within the parameter `packageName`. If we're not
+ // restricting our remove-query to this package name, then there's nothing for us to
+ // remove.
+ return;
+ }
+
+ SearchSpecProto searchSpecProto =
+ SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
+ SearchSpecProto.Builder searchSpecBuilder =
+ searchSpecProto.toBuilder().setQuery(queryExpression);
+
String prefix = createPrefix(packageName, databaseName);
- Set<String> allowedPrefixedSchemas = getAllowedPrefixSchemas(prefix, searchSpec);
+ Set<String> allowedPrefixedSchemas = getAllowedPrefixSchemasLocked(prefix, searchSpec);
// rewriteSearchSpecForPrefixesLocked will return false if there is nothing to search
// over given their search filters, so we can return early and skip sending request
@@ -775,15 +833,16 @@ public final class AppSearchImpl {
searchSpecBuilder, Collections.singleton(prefix), allowedPrefixedSchemas)) {
return;
}
- deleteResultProto = mIcingSearchEngineLocked.deleteByQuery(searchSpecBuilder.build());
- checkForOptimizeLocked(/* force= */ true);
+ DeleteByQueryResultProto deleteResultProto =
+ mIcingSearchEngineLocked.deleteByQuery(searchSpecBuilder.build());
+
+ // It seems that the caller wants to get success if the data matching the query is
+ // not in the DB because it was not there or was successfully deleted.
+ checkCodeOneOf(
+ deleteResultProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND);
} finally {
mReadWriteLock.writeLock().unlock();
}
- // It seems that the caller wants to get success if the data matching the query is not in
- // the DB because it was not there or was successfully deleted.
- checkCodeOneOf(
- deleteResultProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND);
}
/**
@@ -799,9 +858,16 @@ public final class AppSearchImpl {
* @throws AppSearchException on any error that AppSearch persist data to disk.
*/
public void persistToDisk() throws AppSearchException {
- PersistToDiskResultProto persistToDiskResultProto =
- mIcingSearchEngineLocked.persistToDisk();
- checkSuccess(persistToDiskResultProto.getStatus());
+ mReadWriteLock.writeLock().lock();
+ try {
+ throwIfClosedLocked();
+
+ PersistToDiskResultProto persistToDiskResultProto =
+ mIcingSearchEngineLocked.persistToDisk();
+ checkSuccess(persistToDiskResultProto.getStatus());
+ } finally {
+ mReadWriteLock.writeLock().unlock();
+ }
}
/**
@@ -814,21 +880,16 @@ public final class AppSearchImpl {
*
* @throws AppSearchException on IcingSearchEngine error.
*/
- private void reset() throws AppSearchException {
- ResetResultProto resetResultProto;
- mReadWriteLock.writeLock().lock();
- try {
- resetResultProto = mIcingSearchEngineLocked.reset();
- mOptimizeIntervalCountLocked = 0;
- mSchemaMapLocked.clear();
- mNamespaceMapLocked.clear();
-
- // Must be called after everything else since VisibilityStore may repopulate
- // IcingSearchEngine with an initial schema.
- mVisibilityStoreLocked.handleReset();
- } finally {
- mReadWriteLock.writeLock().unlock();
- }
+ @GuardedBy("mReadWriteLock")
+ private void resetLocked() throws AppSearchException {
+ ResetResultProto resetResultProto = mIcingSearchEngineLocked.reset();
+ mOptimizeIntervalCountLocked = 0;
+ mSchemaMapLocked.clear();
+ mNamespaceMapLocked.clear();
+
+ // Must be called after everything else since VisibilityStore may repopulate
+ // IcingSearchEngine with an initial schema.
+ mVisibilityStoreLocked.handleReset();
checkSuccess(resetResultProto.getStatus());
}
@@ -1079,7 +1140,8 @@ public final class AppSearchImpl {
* <p>This only checks intersection of schema filters on the search spec with those that the
* prefix owns itself. This does not check global query permissions.
*/
- private Set<String> getAllowedPrefixSchemas(
+ @GuardedBy("mReadWriteLock")
+ private Set<String> getAllowedPrefixSchemasLocked(
@NonNull String prefix, @NonNull SearchSpec searchSpec) {
Set<String> allowedPrefixedSchemas = new ArraySet<>();
@@ -1295,35 +1357,72 @@ public final class AppSearchImpl {
/**
* Checks whether {@link IcingSearchEngine#optimize()} should be called to release resources.
*
- * <p>This method should be only called in mutate methods and get the WRITE lock to keep thread
- * safety.
+ * <p>This method should be only called after a mutation to local storage backend which deletes
+ * a mass of data and could release lots resources after {@link IcingSearchEngine#optimize()}.
+ *
+ * <p>This method will trigger {@link IcingSearchEngine#getOptimizeInfo()} to check resources
+ * that could be released for every {@link #CHECK_OPTIMIZE_INTERVAL} mutations.
*
* <p>{@link IcingSearchEngine#optimize()} should be called only if {@link
* GetOptimizeInfoResultProto} shows there is enough resources could be released.
*
- * <p>{@link IcingSearchEngine#getOptimizeInfo()} should be called once per {@link
- * #CHECK_OPTIMIZE_INTERVAL} of remove executions.
+ * @param mutationSize The number of how many mutations have been executed for current request.
+ * An inside counter will accumulates it. Once the counter reaches {@link
+ * #CHECK_OPTIMIZE_INTERVAL}, {@link IcingSearchEngine#getOptimizeInfo()} will be triggered
+ * and the counter will be reset.
+ */
+ public void checkForOptimize(int mutationSize) throws AppSearchException {
+ mReadWriteLock.writeLock().lock();
+ try {
+ mOptimizeIntervalCountLocked += mutationSize;
+ if (mOptimizeIntervalCountLocked >= CHECK_OPTIMIZE_INTERVAL) {
+ checkForOptimize();
+ }
+ } finally {
+ mReadWriteLock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Checks whether {@link IcingSearchEngine#optimize()} should be called to release resources.
+ *
+ * <p>This method will directly trigger {@link IcingSearchEngine#getOptimizeInfo()} to check
+ * resources that could be released.
*
- * @param force whether we should directly call {@link IcingSearchEngine#getOptimizeInfo()}.
+ * <p>{@link IcingSearchEngine#optimize()} should be called only if {@link
+ * GetOptimizeInfoResultProto} shows there is enough resources could be released.
*/
- @GuardedBy("mReadWriteLock")
- private void checkForOptimizeLocked(boolean force) throws AppSearchException {
- ++mOptimizeIntervalCountLocked;
- if (force || mOptimizeIntervalCountLocked >= CHECK_OPTIMIZE_INTERVAL) {
- mOptimizeIntervalCountLocked = 0;
+ public void checkForOptimize() throws AppSearchException {
+ mReadWriteLock.writeLock().lock();
+ try {
GetOptimizeInfoResultProto optimizeInfo = getOptimizeInfoResultLocked();
checkSuccess(optimizeInfo.getStatus());
+ mOptimizeIntervalCountLocked = 0;
// Second threshold, decide when to call optimize().
if (optimizeInfo.getOptimizableDocs() >= OPTIMIZE_THRESHOLD_DOC_COUNT
|| optimizeInfo.getEstimatedOptimizableBytes() >= OPTIMIZE_THRESHOLD_BYTES) {
- // TODO(b/155939114): call optimize in the same thread will slow down api calls
- // significantly. Move this call to background.
- OptimizeResultProto optimizeResultProto = mIcingSearchEngineLocked.optimize();
- checkSuccess(optimizeResultProto.getStatus());
+ optimize();
}
- // TODO(b/147699081): Return OptimizeResultProto & log lost data detail once we add
- // a field to indicate lost_schema and lost_documents in OptimizeResultProto.
- // go/icing-library-apis.
+ } finally {
+ mReadWriteLock.writeLock().unlock();
+ }
+ // TODO(b/147699081): Return OptimizeResultProto & log lost data detail once we add
+ // a field to indicate lost_schema and lost_documents in OptimizeResultProto.
+ // go/icing-library-apis.
+ }
+
+ /**
+ * Triggers {@link IcingSearchEngine#optimize()} directly.
+ *
+ * <p>This method should be only called as a scheduled task in AppSearch Platform backend.
+ */
+ public void optimize() throws AppSearchException {
+ mReadWriteLock.writeLock().lock();
+ try {
+ OptimizeResultProto optimizeResultProto = mIcingSearchEngineLocked.optimize();
+ checkSuccess(optimizeResultProto.getStatus());
+ } finally {
+ mReadWriteLock.writeLock().unlock();
}
}
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 b3f8264dc18a..a501e99db1ef 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
@@ -76,7 +76,7 @@ class AppSearchMigrationHelperImpl implements AppSearchMigrationHelper {
int currentVersion = mCurrentVersionMap.get(schemaType);
int finalVersion = mFinalVersionMap.get(schemaType);
try (FileOutputStream outputStream = new FileOutputStream(mFile)) {
- // TODO(b/151178558) change the output stream so that we can use it in platform
+ // TODO(b/177266929) change the output stream so that we can use it in platform
CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputStream);
SearchResultPage searchResultPage =
mAppSearchImpl.query(
@@ -126,11 +126,13 @@ class AppSearchMigrationHelperImpl implements AppSearchMigrationHelper {
try {
mAppSearchImpl.putDocument(mPackageName, mDatabaseName, document);
} catch (Throwable t) {
- responseBuilder.setFailure(
- document.getSchemaType(),
- document.getNamespace(),
- document.getUri(),
- throwableToFailedResult(t));
+ responseBuilder.addMigrationFailure(
+ new SetSchemaResponse.MigrationFailure.Builder()
+ .setNamespace(document.getNamespace())
+ .setSchemaType(document.getSchemaType())
+ .setUri(document.getUri())
+ .setAppSearchResult(throwableToFailedResult(t))
+ .build());
}
}
mAppSearchImpl.persistToDisk();
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResponseToProtoConverter.java
index e1e7d46d77ea..a0f39ecd68fb 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResultToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResponseToProtoConverter.java
@@ -17,45 +17,41 @@
package com.android.server.appsearch.external.localstorage.converter;
import android.annotation.NonNull;
-import android.app.appsearch.SetSchemaResult;
+import android.app.appsearch.SetSchemaResponse;
import com.android.internal.util.Preconditions;
import com.google.android.icing.proto.SetSchemaResultProto;
/**
- * Translates a {@link SetSchemaResultProto} into {@link SetSchemaResult}.
+ * Translates a {@link SetSchemaResultProto} into {@link SetSchemaResponse}.
*
* @hide
*/
-public class SetSchemaResultToProtoConverter {
+public class SetSchemaResponseToProtoConverter {
- private SetSchemaResultToProtoConverter() {}
+ private SetSchemaResponseToProtoConverter() {}
/**
- * Translate a {@link SetSchemaResultProto} into {@link SetSchemaResult}.
+ * Translate a {@link SetSchemaResultProto} into {@link SetSchemaResponse}.
*
* @param proto The {@link SetSchemaResultProto} containing results.
* @param prefix The prefix need to removed from schemaTypes
- * @return {@link SetSchemaResult} of results.
+ * @return The {@link SetSchemaResponse} object.
*/
@NonNull
- public static SetSchemaResult toSetSchemaResult(
+ public static SetSchemaResponse toSetSchemaResponse(
@NonNull SetSchemaResultProto proto, @NonNull String prefix) {
Preconditions.checkNotNull(proto);
Preconditions.checkNotNull(prefix);
- SetSchemaResult.Builder builder =
- new SetSchemaResult.Builder()
- .setResultCode(
- ResultCodeToProtoConverter.toResultCode(
- proto.getStatus().getCode()));
+ SetSchemaResponse.Builder builder = new SetSchemaResponse.Builder();
for (int i = 0; i < proto.getDeletedSchemaTypesCount(); i++) {
- builder.addDeletedSchemaType(proto.getDeletedSchemaTypes(i).substring(prefix.length()));
+ builder.addDeletedType(proto.getDeletedSchemaTypes(i).substring(prefix.length()));
}
for (int i = 0; i < proto.getIncompatibleSchemaTypesCount(); i++) {
- builder.addIncompatibleSchemaType(
+ builder.addIncompatibleType(
proto.getIncompatibleSchemaTypes(i).substring(prefix.length()));
}
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 12699b7815fb..d076db3b8f82 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-Ibe06fb9c574c8718191f833bb042fa10c300e4e2
+Ia9a8daef1a6d7d9432f7808d440abd64f4797701
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
index eb1623e5dffc..6595d8d4abba 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
@@ -37,8 +37,9 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
- * This test class adapts the AppSearch Framework API to ListenableFuture, so it can be tested via
- * a consistent interface.
+ * This test class adapts the AppSearch Framework API to ListenableFuture, so it can be tested via a
+ * consistent interface.
+ *
* @hide
*/
public class GlobalSearchSessionShimImpl implements GlobalSearchSessionShim {
@@ -47,7 +48,13 @@ public class GlobalSearchSessionShimImpl implements GlobalSearchSessionShim {
@NonNull
public static ListenableFuture<GlobalSearchSessionShim> createGlobalSearchSession() {
- Context context = ApplicationProvider.getApplicationContext();
+ return createGlobalSearchSession(ApplicationProvider.getApplicationContext());
+ }
+
+ /** Only for use when called from a non-instrumented context. */
+ @NonNull
+ public static ListenableFuture<GlobalSearchSessionShim> createGlobalSearchSession(
+ @NonNull Context context) {
AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
SettableFuture<AppSearchResult<GlobalSearchSession>> future = SettableFuture.create();
ExecutorService executor = Executors.newCachedThreadPool();
@@ -62,7 +69,6 @@ public class GlobalSearchSessionShimImpl implements GlobalSearchSessionShim {
@NonNull GlobalSearchSession session, @NonNull ExecutorService executor) {
mGlobalSearchSession = Preconditions.checkNotNull(session);
mExecutor = Preconditions.checkNotNull(executor);
-
}
@NonNull
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
index 8e62c0e57f7d..ea21e19b2bea 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
@@ -77,20 +77,24 @@ public interface AppSearchSessionShim extends Closeable {
* android.app.appsearch.exceptions.AppSearchException} with the {@link
* AppSearchResult#RESULT_INVALID_SCHEMA} error code, all documents which are not
* compatible with the new schema will be deleted and the incompatible schema will be
- * applied.
+ * applied. Incompatible types and deleted types will be set into {@link
+ * SetSchemaResponse#getIncompatibleTypes()} and {@link
+ * SetSchemaResponse#getDeletedTypes()}, respectively.
* <li>Add a {@link android.app.appsearch.AppSearchSchema.Migrator} for each incompatible type
* and make no deletion. The migrator will migrate documents from it's old schema version
- * to the new version. See the migration section below.
+ * to the new version. Migrated types will be set into both {@link
+ * SetSchemaResponse#getIncompatibleTypes()} and {@link
+ * SetSchemaResponse#getMigratedTypes()}. See the migration section below.
* </ul>
*
* <p>It is a no-op to set the same schema as has been previously set; this is handled
* efficiently.
*
- * <p>By default, documents are visible on platform surfaces. To opt out, call {@code
- * SetSchemaRequest.Builder#setPlatformSurfaceable} with {@code surfaceable} as false. Any
- * visibility settings apply only to the schemas that are included in the {@code request}.
- * Visibility settings for a schema type do not apply or persist across {@link
- * SetSchemaRequest}s.
+ * <p>By default, documents are visible on platform surfaces. To opt out, call
+ * {@link SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi} with {@code visible} as
+ * false. Any visibility settings apply only to the schemas that are included in the
+ * {@code request}. Visibility settings for a schema type do not persist across
+ * {@link #setSchema} calls.
*
* <p>Migration: make non-backwards-compatible changes will delete all stored documents in old
* schema. You can save your documents by setting {@link
@@ -109,15 +113,11 @@ public interface AppSearchSessionShim extends Closeable {
* backwards-compatible, and stored documents won't have any observable changes.
*
* @param request The schema update request.
- * @return The pending {@link SetSchemaResponse} of performing this operation. Success if the
- * the schema has been set and any migrations has been done. Otherwise, the failure {@link
- * android.app.appsearch.SetSchemaResponse.MigrationFailure} indicates which document is
- * fail to be migrated.
+ * @return A {@link ListenableFuture} with exception if we hit any error. Or the pending {@link
+ * SetSchemaResponse} of performing this operation, if the schema has been successfully set.
* @see android.app.appsearch.AppSearchSchema.Migrator
* @see android.app.appsearch.AppSearchMigrationHelper.Transformer
*/
- // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are
- // exposed.
@NonNull
ListenableFuture<SetSchemaResponse> setSchema(@NonNull SetSchemaRequest request);
@@ -146,57 +146,79 @@ public interface AppSearchSessionShim extends Closeable {
ListenableFuture<AppSearchBatchResult<String, Void>> put(@NonNull PutDocumentsRequest request);
/**
- * Retrieves {@link GenericDocument}s by URI.
+ * Gets {@link GenericDocument} objects by URIs and namespace from the {@link
+ * AppSearchSessionShim} database.
*
- * @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}.
+ * @param request a request containing URIs and namespace to get documents for.
+ * @return A {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The
+ * keys of the {@link AppSearchBatchResult} represent the input URIs from the {@link
+ * GetByUriRequest} object. The values are either the corresponding {@link GenericDocument}
+ * object for the URI on success, or an {@link AppSearchResult} object on failure. For
+ * example, if a URI is not found, the value for that URI will be set to an {@link
+ * AppSearchResult} object with result code: {@link AppSearchResult#RESULT_NOT_FOUND}.
*/
@NonNull
ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByUri(
@NonNull GetByUriRequest request);
/**
- * Searches for documents based on a given query string.
+ * Retrieves documents from the open {@link AppSearchSessionShim} that match a given query
+ * string and type of search provided.
+ *
+ * <p>Query strings can be empty, contain one term with no operators, or contain multiple terms
+ * and operators.
+ *
+ * <p>For query strings that are empty, all documents that match the {@link SearchSpec} will be
+ * returned.
*
- * <p>Currently we support following features in the raw query format:
+ * <p>For query strings with a single term and no operators, documents that match the provided
+ * query string and {@link SearchSpec} will be returned.
+ *
+ * <p>The following operators are supported:
*
* <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>AND (implicit)
+ * <p>AND is an operator that matches documents that contain <i>all</i> provided terms.
+ * <p><b>NOTE:</b> A space between terms is treated as an "AND" operator. Explicitly
+ * including "AND" in a query string will treat "AND" as a term, returning documents that
+ * also contain "AND".
+ * <p>Example: "apple AND banana" matches documents that contain the terms "apple", "and",
+ * "banana".
+ * <p>Example: "apple banana" matches documents that contain both "apple" and "banana".
+ * <p>Example: "apple banana cherry" matches documents that contain "apple", "banana", and
+ * "cherry".
* <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.
+ * <p>OR is an operator that matches documents that contain <i>any</i> provided term.
+ * <p>Example: "apple OR banana" matches documents that contain either "apple" or
+ * "banana".
+ * <p>Example: "apple OR banana OR cherry" matches documents that contain any of "apple",
+ * "banana", or "cherry".
+ * <li>Exclusion (-)
+ * <p>Exclusion (-) is an operator that matches documents that <i>do not</i> contain the
+ * provided term.
+ * <p>Example: "-apple" matches documents that do not contain "apple".
+ * <li>Grouped Terms
+ * <p>For queries that require multiple operators and terms, terms can be grouped into
+ * subqueries. Subqueries are contained within an open "(" and close ")" parenthesis.
+ * <p>Example: "(donut OR bagel) (coffee OR tea)" matches documents that contain either
+ * "donut" or "bagel" and either "coffee" or "tea".
+ * <li>Property Restricts
+ * <p>For queries that require a term to match a specific {@link AppSearchSchema} property
+ * of a document, a ":" must be included between the property name and the term.
+ * <p>Example: "subject:important" matches documents that contain the term "important" in
+ * the "subject" property.
* </ul>
*
+ * <p>Additional search specifications, such as filtering by {@link AppSearchSchema} type or
+ * adding projection, can be set by calling the corresponding {@link SearchSpec.Builder} setter.
+ *
* <p>This method is lightweight. The heavy work will be done in {@link
* SearchResultsShim#getNextPage()}.
*
- * @param queryExpression Query String to search.
- * @param searchSpec Spec for setting filters, raw query etc.
- * @return The search result of performing this operation.
+ * @param queryExpression query string to search.
+ * @param searchSpec spec for setting document filters, adding projection, setting term match
+ * type, etc.
+ * @return a {@link SearchResultsShim} object for retrieved matched documents.
*/
@NonNull
SearchResultsShim search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
@@ -221,13 +243,22 @@ public interface AppSearchSessionShim extends Closeable {
ListenableFuture<Void> reportUsage(@NonNull ReportUsageRequest request);
/**
- * Removes {@link GenericDocument}s from the index by URI.
+ * Removes {@link GenericDocument} objects by URIs and namespace from the {@link
+ * AppSearchSessionShim} database.
*
- * @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}.
+ * <p>Removed documents will no longer be surfaced by {@link #search} or {@link #getByUri}
+ * calls.
+ *
+ * <p><b>NOTE:</b>By default, documents are removed via a soft delete operation. Once the
+ * document crosses the count threshold or byte usage threshold, the documents will be removed
+ * from disk.
+ *
+ * @param request {@link RemoveByUriRequest} with URIs and namespace to remove from the index.
+ * @return a {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The
+ * keys of the {@link AppSearchBatchResult} represent the input URIs from the {@link
+ * RemoveByUriRequest} object. The values are either {@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}.
*/
@NonNull
ListenableFuture<AppSearchBatchResult<String, Void>> remove(
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 20fb90986f41..37717d6837b9 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
@@ -25,7 +25,6 @@ import android.app.appsearch.GenericDocument;
import android.app.appsearch.GetByUriRequest;
import android.app.appsearch.SearchResult;
import android.app.appsearch.SearchResultsShim;
-import android.app.appsearch.SetSchemaResponse;
import java.util.ArrayList;
import java.util.List;
@@ -43,15 +42,6 @@ public class AppSearchTestUtils {
return result;
}
- // TODO(b/151178558) check setSchemaResponse.xxxtypes for the test need to verify.
- public static void checkIsSetSchemaResponseSuccess(Future<SetSchemaResponse> future)
- throws Exception {
- SetSchemaResponse setSchemaResponse = future.get();
- assertWithMessage("SetSchemaResponse not successful.")
- .that(setSchemaResponse.isSuccess())
- .isTrue();
- }
-
public static List<GenericDocument> doGet(
AppSearchSessionShim session, String namespace, String... uris) throws Exception {
AppSearchBatchResult<String, GenericDocument> result =
@@ -59,7 +49,7 @@ public class AppSearchTestUtils {
session.getByUri(
new GetByUriRequest.Builder()
.setNamespace(namespace)
- .addUri(uris)
+ .addUris(uris)
.build()));
assertThat(result.getSuccesses()).hasSize(uris.length);
assertThat(result.getFailures()).isEmpty();
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java
index b96f99e7cd37..31c934f8bb27 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java
@@ -27,43 +27,26 @@ import java.io.Closeable;
*/
public interface GlobalSearchSessionShim extends Closeable {
/**
- * Searches across all documents in the storage based on a given query string.
+ * Retrieves documents from all AppSearch databases that the querying application has access to.
*
- * <p>Currently we support following features in the raw query format:
+ * <p>Applications can be granted access to documents by specifying {@link
+ * SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage}, or {@link
+ * SetSchemaRequest.Builder#setDocumentClassVisibilityForPackage} when building a schema.
*
- * <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>
+ * <p>Document access can also be granted to system UIs by specifying {@link
+ * SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi}, or {@link
+ * SetSchemaRequest.Builder#setDocumentClassVisibilityForSystemUi} when building a schema.
+ *
+ * <p>See {@link AppSearchSessionShim#search(String, SearchSpec)} for a detailed explanation on
+ * forming a query string.
*
* <p>This method is lightweight. The heavy work will be done in {@link
* SearchResultsShim#getNextPage()}.
*
- * @param queryExpression Query String to search.
- * @param searchSpec Spec for setting filters, raw query etc.
- * @return The search result of performing this operation.
+ * @param queryExpression query string to search.
+ * @param searchSpec spec for setting document filters, adding projection, setting term match
+ * type, etc.
+ * @return a {@link SearchResultsShim} object for retrieved matched documents.
*/
@NonNull
SearchResultsShim search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
diff --git a/apex/blobstore/framework/Android.bp b/apex/blobstore/framework/Android.bp
index 349955368b17..1cb295ba0822 100644
--- a/apex/blobstore/framework/Android.bp
+++ b/apex/blobstore/framework/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "framework-blobstore-sources",
srcs: [
diff --git a/apex/blobstore/service/Android.bp b/apex/blobstore/service/Android.bp
index f6cbac1628da..49cfd07443db 100644
--- a/apex/blobstore/service/Android.bp
+++ b/apex/blobstore/service/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "service-blobstore",
installable: true,
diff --git a/apex/jobscheduler/framework/Android.bp b/apex/jobscheduler/framework/Android.bp
index 6650e677544b..b51c2aae0bb8 100644
--- a/apex/jobscheduler/framework/Android.bp
+++ b/apex/jobscheduler/framework/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "framework-jobscheduler-sources",
srcs: [
@@ -22,6 +31,7 @@ java_library {
],
},
libs: [
+ "app-compat-annotations",
"framework-minus-apex",
"unsupportedappusage",
],
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index 7851087bac19..7c7b21001c3b 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -16,11 +16,14 @@
package android.app;
+import android.Manifest;
import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -183,6 +186,25 @@ public class AlarmManager {
@UnsupportedAppUsage
public static final int FLAG_IDLE_UNTIL = 1<<4;
+ /**
+ * Flag for alarms: Used to provide backwards compatibility for apps with targetSdkVersion less
+ * than {@link Build.VERSION_CODES#S}
+ * @hide
+ */
+ public static final int FLAG_ALLOW_WHILE_IDLE_COMPAT = 1 << 5;
+
+ /**
+ * For apps targeting {@link Build.VERSION_CODES#S} or above, APIs
+ * {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} and
+ * {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} will require holding a new
+ * permission {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM}
+ *
+ * @hide
+ */
+ @ChangeId
+ @Disabled // TODO (b/171306433): Enable starting S.
+ public static final long REQUIRE_EXACT_ALARM_PERMISSION = 171306433L;
+
@UnsupportedAppUsage
private final IAlarmManager mService;
private final Context mContext;
@@ -588,6 +610,11 @@ public class AlarmManager {
* This method is like {@link #setExact(int, long, PendingIntent)}, but implies
* {@link #RTC_WAKEUP}.
*
+ * <p>
+ * Starting from API {@link Build.VERSION_CODES#S}, using this method requires the
+ * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission. Alarms scheduled via this API
+ * will be allowed to start a foreground service even if the app is in the background.
+ *
* @param info
* @param operation Action to perform when the alarm goes off;
* typically comes from {@link PendingIntent#getBroadcast
@@ -603,6 +630,7 @@ public class AlarmManager {
* @see android.content.Context#registerReceiver
* @see android.content.Intent#filterEquals
*/
+ @RequiresPermission(Manifest.permission.SCHEDULE_EXACT_ALARM)
public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) {
setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation,
null, null, null, null, info);
@@ -876,6 +904,12 @@ public class AlarmManager {
* device is idle it may take even more liberties with scheduling in order to optimize
* for battery life.</p>
*
+ * <p>
+ * Starting from API {@link Build.VERSION_CODES#S}, using this method requires the
+ * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission, unless the app is exempt from
+ * battery restrictions. Alarms scheduled via this API will be allowed to start a foreground
+ * service even if the app is in the background.
+ *
* @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).
@@ -895,6 +929,7 @@ public class AlarmManager {
* @see #RTC
* @see #RTC_WAKEUP
*/
+ @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
public void setExactAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
PendingIntent operation) {
setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation,
@@ -1018,6 +1053,18 @@ public class AlarmManager {
}
/**
+ * Called to check if the caller has permission to use alarms set via {@link }
+ * @return
+ */
+ public boolean canScheduleExactAlarms() {
+ try {
+ return mService.canScheduleExactAlarms();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets information about the next alarm clock currently scheduled.
*
* The alarm clocks considered are those scheduled by any application
diff --git a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl
index 2c51935dc446..2f21ce395df5 100644
--- a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl
+++ b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl
@@ -41,4 +41,5 @@ interface IAlarmManager {
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
long currentNetworkTimeMillis();
+ boolean canScheduleExactAlarms();
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 930415fcd8fd..b7a3f1083176 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -59,6 +59,12 @@ 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";
@@ -1461,11 +1467,13 @@ public class JobInfo implements Parcelable {
* possible with stronger guarantees than regular jobs. These "expedited" jobs will:
* <ol>
* <li>Run as soon as possible</li>
- * <li>Be exempted from Doze and battery saver restrictions</li>
+ * <li>Be less restricted during Doze and battery saver</li>
* <li>Have network access</li>
- * <li>Less likely to be killed than regular jobs</li>
+ * <li>Be less likely to be killed than regular jobs</li>
+ * <li>Be subject to background location throttling</li>
* </ol>
*
+ * <p>
* Since these jobs have stronger guarantees than regular jobs, they will be subject to
* stricter quotas. As long as an app has available expedited quota, jobs scheduled with
* this set to true will run with these guarantees. If an app has run out of available
@@ -1475,9 +1483,18 @@ public class JobInfo implements Parcelable {
* will immediately return {@link JobScheduler#RESULT_FAILURE} if the app does not have
* available quota (and the job will not be successfully scheduled).
*
+ * <p>
* Expedited jobs may only set network, storage-not-low, and persistence constraints.
* No other constraints are allowed.
*
+ * <p>
+ * Assuming all constraints remain satisfied (including ideal system load conditions),
+ * expedited jobs are guaranteed to have a minimum allowed runtime of 1 minute. If your
+ * app has remaining expedited job quota, then the expedited job <i>may</i> potentially run
+ * longer until remaining quota is used up. Just like with regular jobs, quota is not
+ * consumed while the app is on top and visible to the user.
+ *
+ * <p>
* Note: Even though expedited jobs are meant to run as soon as possible, they may be
* deferred if the system is under heavy load or requested constraints are not satisfied.
*
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index 283e933e0fa9..df0e157abc6a 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -87,7 +87,7 @@ public class PowerWhitelistManager {
* The list of temp allowlist types.
* @hide
*/
- @IntDef(flag = true, prefix = { "TEMPORARY_ALLOW_TYPE_" }, value = {
+ @IntDef(flag = true, prefix = { "TEMPORARY_ALLOWLIST_TYPE_" }, value = {
TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
})
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index 35bd3175b14b..a4a0b4b29200 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -1,5 +1,14 @@
// Job Scheduler Service jar, which will eventually be put in the jobscheduler mainline apex.
// service-jobscheduler needs to be added to PRODUCT_SYSTEM_SERVER_JARS.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "service-jobscheduler",
installable: true,
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
index 657c368d0aee..3bc7b307c334 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
@@ -26,6 +26,7 @@ import static com.android.server.alarm.AlarmManagerService.clampPositive;
import android.app.AlarmManager;
import android.app.IAlarmListener;
import android.app.PendingIntent;
+import android.os.Bundle;
import android.os.WorkSource;
import android.util.IndentingPrintWriter;
import android.util.TimeUtils;
@@ -92,10 +93,12 @@ class Alarm {
private long mWhenElapsed;
private long mMaxWhenElapsed;
public AlarmManagerService.PriorityClass priorityClass;
+ /** Broadcast options to use when delivering this alarm */
+ public Bundle mIdleOptions;
Alarm(int type, long when, long requestedWhenElapsed, long windowLength, long interval,
PendingIntent op, IAlarmListener rec, String listenerTag, WorkSource ws, int flags,
- AlarmManager.AlarmClockInfo info, int uid, String pkgName) {
+ AlarmManager.AlarmClockInfo info, int uid, String pkgName, Bundle idleOptions) {
this.type = type;
origWhen = when;
wakeup = type == AlarmManager.ELAPSED_REALTIME_WAKEUP
@@ -115,6 +118,7 @@ class Alarm {
alarmClock = info;
this.uid = uid;
packageName = pkgName;
+ mIdleOptions = idleOptions;
sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName;
creatorUid = (operation != null) ? operation.getCreatorUid() : this.uid;
}
@@ -303,6 +307,10 @@ class Alarm {
ipw.print("listener=");
ipw.println(listener.asBinder());
}
+ if (mIdleOptions != null) {
+ ipw.print("idle-options=");
+ ipw.println(mIdleOptions.toString());
+ }
}
public void dumpDebug(ProtoOutputStream proto, long fieldId, long nowElapsed) {
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 f6a1b8af9e49..559a43491c7f 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -20,11 +20,15 @@ import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
+import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
+import static android.app.AlarmManager.INTERVAL_HOUR;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
import static android.os.UserHandle.USER_SYSTEM;
import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
@@ -32,6 +36,7 @@ import static com.android.server.alarm.Alarm.BATTERY_SAVER_POLICY_INDEX;
import static com.android.server.alarm.Alarm.DEVICE_IDLE_POLICY_INDEX;
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.Activity;
@@ -43,12 +48,14 @@ import android.app.IAlarmCompleteListener;
import android.app.IAlarmListener;
import android.app.IAlarmManager;
import android.app.PendingIntent;
+import android.app.compat.CompatChanges;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.PermissionChecker;
import android.content.pm.PackageManagerInternal;
import android.net.Uri;
import android.os.BatteryManager;
@@ -65,6 +72,7 @@ import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
@@ -95,6 +103,8 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsService;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.LocalLog;
@@ -177,6 +187,7 @@ public class AlarmManagerService extends SystemService {
final LocalLog mLog = new LocalLog(TAG);
AppOpsManager mAppOps;
+ IAppOpsService mAppOpsService;
DeviceIdleInternal mLocalDeviceIdleController;
private UsageStatsManagerInternal mUsageStatsManagerInternal;
private ActivityManagerInternal mActivityManagerInternal;
@@ -253,10 +264,8 @@ public class AlarmManagerService extends SystemService {
"REORDER_ALARMS_FOR_STANDBY",
});
- /**
- * Broadcast options to use for FLAG_ALLOW_WHILE_IDLE.
- */
- Bundle mIdleOptions;
+ BroadcastOptions mOptsWithFgs = BroadcastOptions.makeBasic();
+ BroadcastOptions mOptsWithoutFgs = BroadcastOptions.makeBasic();
// TODO(b/172085676): Move inside alarm store.
private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
@@ -410,6 +419,10 @@ public class AlarmManagerService extends SystemService {
@VisibleForTesting
static final String KEY_ALLOW_WHILE_IDLE_QUOTA = "allow_while_idle_quota";
+ @VisibleForTesting
+ static final String KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA = "allow_while_idle_compat_quota";
+ private static final String KEY_ALLOW_WHILE_IDLE_WINDOW = "allow_while_idle_window";
+
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 * DateUtils.DAY_IN_MILLIS;
@@ -433,8 +446,14 @@ public class AlarmManagerService extends SystemService {
private static final boolean DEFAULT_LAZY_BATCHING = true;
private static final boolean DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE = true;
- private static final int DEFAULT_ALLOW_WHILE_IDLE_QUOTA = 7;
- public static final long ALLOW_WHILE_IDLE_WINDOW = 60 * 60 * 1000; // 1 hour.
+ /**
+ * Default quota for pre-S apps. Enough to accommodate the existing policy of an alarm
+ * every ALLOW_WHILE_IDLE_LONG_DELAY, which was 9 minutes.
+ */
+ private static final int DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA = 7;
+ private static final int DEFAULT_ALLOW_WHILE_IDLE_QUOTA = 72;
+
+ private static final long DEFAULT_ALLOW_WHILE_IDLE_WINDOW = 60 * 60 * 1000; // 1 hour.
// Minimum futurity of a new alarm
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
@@ -463,6 +482,19 @@ public class AlarmManagerService extends SystemService {
public int ALLOW_WHILE_IDLE_QUOTA = DEFAULT_ALLOW_WHILE_IDLE_QUOTA;
+ /**
+ * Used to provide backwards compatibility to pre-S apps with a quota equivalent to the
+ * earlier delay throttling mechanism.
+ */
+ public int ALLOW_WHILE_IDLE_COMPAT_QUOTA = DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA;
+
+ /**
+ * The window used for enforcing {@link #ALLOW_WHILE_IDLE_QUOTA} and
+ * {@link #ALLOW_WHILE_IDLE_COMPAT_QUOTA}. Can be configured, but only recommended for
+ * testing.
+ */
+ public long ALLOW_WHILE_IDLE_WINDOW = DEFAULT_ALLOW_WHILE_IDLE_WINDOW;
+
private long mLastAllowWhileIdleWhitelistDuration = -1;
Constants() {
@@ -480,9 +512,11 @@ public class AlarmManagerService extends SystemService {
public void updateAllowWhileIdleWhitelistDurationLocked() {
if (mLastAllowWhileIdleWhitelistDuration != ALLOW_WHILE_IDLE_WHITELIST_DURATION) {
mLastAllowWhileIdleWhitelistDuration = ALLOW_WHILE_IDLE_WHITELIST_DURATION;
- BroadcastOptions opts = BroadcastOptions.makeBasic();
- opts.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION);
- mIdleOptions = opts.toBundle();
+
+ mOptsWithFgs.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION);
+ mOptsWithoutFgs.setTemporaryAppWhitelistDuration(
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
+ ALLOW_WHILE_IDLE_WHITELIST_DURATION);
}
}
@@ -516,6 +550,27 @@ public class AlarmManagerService extends SystemService {
ALLOW_WHILE_IDLE_QUOTA = 1;
}
break;
+ case KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA:
+ ALLOW_WHILE_IDLE_COMPAT_QUOTA = properties.getInt(
+ KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA,
+ DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA);
+ if (ALLOW_WHILE_IDLE_COMPAT_QUOTA <= 0) {
+ Slog.w(TAG, "Cannot have quota lower than 1.");
+ ALLOW_WHILE_IDLE_COMPAT_QUOTA = 1;
+ }
+ break;
+ case KEY_ALLOW_WHILE_IDLE_WINDOW:
+ ALLOW_WHILE_IDLE_WINDOW = properties.getLong(
+ KEY_ALLOW_WHILE_IDLE_WINDOW, DEFAULT_ALLOW_WHILE_IDLE_WINDOW);
+ if (ALLOW_WHILE_IDLE_WINDOW > DEFAULT_ALLOW_WHILE_IDLE_WINDOW) {
+ Slog.w(TAG, "Cannot have allow_while_idle_window > "
+ + DEFAULT_ALLOW_WHILE_IDLE_WINDOW);
+ ALLOW_WHILE_IDLE_WINDOW = DEFAULT_ALLOW_WHILE_IDLE_WINDOW;
+ } else if (ALLOW_WHILE_IDLE_WINDOW < DEFAULT_ALLOW_WHILE_IDLE_WINDOW) {
+ Slog.w(TAG, "Using a non-default allow_while_idle_window = "
+ + ALLOW_WHILE_IDLE_WINDOW);
+ }
+ break;
case KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION:
ALLOW_WHILE_IDLE_WHITELIST_DURATION = properties.getLong(
KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION,
@@ -643,10 +698,14 @@ public class AlarmManagerService extends SystemService {
TimeUtils.formatDuration(LISTENER_TIMEOUT, pw);
pw.println();
- pw.print("allow_while_idle_window=");
+ pw.print(KEY_ALLOW_WHILE_IDLE_WINDOW);
+ pw.print("=");
TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WINDOW, pw);
pw.println();
+ pw.print(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, ALLOW_WHILE_IDLE_COMPAT_QUOTA);
+ pw.println();
+
pw.print(KEY_ALLOW_WHILE_IDLE_QUOTA, ALLOW_WHILE_IDLE_QUOTA);
pw.println();
@@ -830,7 +889,15 @@ public class AlarmManagerService extends SystemService {
if (futurity < MIN_FUZZABLE_INTERVAL) {
futurity = 0;
}
- return clampPositive(triggerAtTime + (long) (.75 * futurity));
+ long maxElapsed = triggerAtTime + (long) (0.75 * futurity);
+ // For non-repeating alarms, window is capped at a maximum of one hour from the requested
+ // delivery time. This allows for inexact-while-idle alarms to be slightly more reliable.
+ // In practice, the delivery window should generally be much smaller than that
+ // when the device is not idling.
+ if (interval == 0) {
+ maxElapsed = Math.min(maxElapsed, triggerAtTime + INTERVAL_HOUR);
+ }
+ return clampPositive(maxElapsed);
}
// The RTC clock has moved arbitrarily, so we need to recalculate all the RTC alarm deliveries.
@@ -998,7 +1065,7 @@ public class AlarmManagerService extends SystemService {
setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
- alarm.packageName);
+ alarm.packageName, null);
// Kernel alarms will be rescheduled as needed in setImplLocked
}
}
@@ -1241,7 +1308,8 @@ public class AlarmManagerService extends SystemService {
mAlarmStore.setAlarmClockRemovalListener(mAlarmClockUpdater);
mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW);
- mAllowWhileIdleHistory = new AppWakeupHistory(Constants.ALLOW_WHILE_IDLE_WINDOW);
+ mAllowWhileIdleHistory = new AppWakeupHistory(
+ Constants.DEFAULT_ALLOW_WHILE_IDLE_WINDOW);
mNextWakeup = mNextNonWakeup = 0;
@@ -1327,6 +1395,28 @@ public class AlarmManagerService extends SystemService {
synchronized (mLock) {
mConstants.start();
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
+ mAppOpsService = mInjector.getAppOpsService();
+ try {
+ mAppOpsService.startWatchingMode(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, null,
+ new IAppOpsCallback.Stub() {
+ @Override
+ public void opChanged(int op, int uid, String packageName)
+ throws RemoteException {
+ if (op != AppOpsManager.OP_SCHEDULE_EXACT_ALARM) {
+ return;
+ }
+ final int mode = mAppOpsService.checkOperation(op, uid,
+ packageName);
+ if (mode != AppOpsManager.MODE_ALLOWED
+ && mode != AppOpsManager.MODE_DEFAULT) {
+ mHandler.obtainMessage(AlarmHandler.REMOVE_EXACT_ALARMS,
+ uid, 0, packageName).sendToTarget();
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ }
+
mLocalDeviceIdleController =
LocalServices.getService(DeviceIdleInternal.class);
mUsageStatsManagerInternal =
@@ -1428,7 +1518,7 @@ public class AlarmManagerService extends SystemService {
void setImpl(int type, long triggerAtTime, long windowLength, long interval,
PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
- int callingUid, String callingPackage) {
+ int callingUid, String callingPackage, Bundle idleOptions) {
if ((operation == null && directReceiver == null)
|| (operation != null && directReceiver != null)) {
Slog.w(TAG, "Alarms must either supply a PendingIntent or an AlarmReceiver");
@@ -1519,17 +1609,18 @@ public class AlarmManagerService extends SystemService {
}
setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, interval, operation,
directReceiver, listenerTag, flags, workSource, alarmClock, callingUid,
- callingPackage);
+ callingPackage, idleOptions);
}
}
private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
long interval, PendingIntent operation, IAlarmListener directReceiver,
String listenerTag, int flags, WorkSource workSource,
- AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage) {
+ AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage,
+ Bundle idleOptions) {
final Alarm a = new Alarm(type, when, whenElapsed, windowLength, interval,
operation, directReceiver, listenerTag, workSource, flags, alarmClock,
- callingUid, callingPackage);
+ callingUid, callingPackage, idleOptions);
if (mActivityManagerInternal.isAppStartModeDisabled(callingUid, callingPackage)) {
Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a
+ " -- package not allowed to start");
@@ -1605,19 +1696,21 @@ public class AlarmManagerService extends SystemService {
return false;
}
- if (!(mAppStateTracker != null && mAppStateTracker.areAlarmsRestrictedByBatterySaver(
- alarm.creatorUid, alarm.sourcePackage))) {
+ if (mAppStateTracker == null || !mAppStateTracker.areAlarmsRestrictedByBatterySaver(
+ alarm.creatorUid, alarm.sourcePackage)) {
return alarm.setPolicyElapsed(BATTERY_SAVER_POLICY_INDEX, nowElapsed);
}
final long batterySaverPolicyElapsed;
- if ((alarm.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)) != 0) {
+ if ((alarm.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)) != 0) {
// Unrestricted.
batterySaverPolicyElapsed = nowElapsed;
- } else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
+ } else if (isAllowedWhileIdleRestricted(alarm)) {
// Allowed but limited.
final int userId = UserHandle.getUserId(alarm.creatorUid);
- final int quota = mConstants.ALLOW_WHILE_IDLE_QUOTA;
+ final int quota = ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0)
+ ? mConstants.ALLOW_WHILE_IDLE_QUOTA
+ : mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
alarm.sourcePackage, userId);
if (dispatchesInWindow < quota) {
@@ -1625,7 +1718,7 @@ public class AlarmManagerService extends SystemService {
batterySaverPolicyElapsed = nowElapsed;
} else {
batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
- alarm.sourcePackage, userId, quota) + Constants.ALLOW_WHILE_IDLE_WINDOW;
+ alarm.sourcePackage, userId, quota) + mConstants.ALLOW_WHILE_IDLE_WINDOW;
}
} else {
// Not allowed.
@@ -1635,6 +1728,16 @@ public class AlarmManagerService extends SystemService {
}
/**
+ * Returns {@code true} if the given alarm has the flag
+ * {@link AlarmManager#FLAG_ALLOW_WHILE_IDLE} or
+ * {@link AlarmManager#FLAG_ALLOW_WHILE_IDLE_COMPAT}
+ *
+ */
+ private static boolean isAllowedWhileIdleRestricted(Alarm a) {
+ return (a.flags & (FLAG_ALLOW_WHILE_IDLE | FLAG_ALLOW_WHILE_IDLE_COMPAT)) != 0;
+ }
+
+ /**
* Adjusts the delivery time of the alarm based on device_idle (doze) rules.
*
* @param alarm The alarm to adjust
@@ -1647,14 +1750,15 @@ public class AlarmManagerService extends SystemService {
}
final long deviceIdlePolicyTime;
- if ((alarm.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED
- | AlarmManager.FLAG_WAKE_FROM_IDLE)) != 0) {
+ if ((alarm.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_WAKE_FROM_IDLE)) != 0) {
// Unrestricted.
deviceIdlePolicyTime = nowElapsed;
- } else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
+ } else if (isAllowedWhileIdleRestricted(alarm)) {
// Allowed but limited.
final int userId = UserHandle.getUserId(alarm.creatorUid);
- final int quota = mConstants.ALLOW_WHILE_IDLE_QUOTA;
+ final int quota = ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0)
+ ? mConstants.ALLOW_WHILE_IDLE_QUOTA
+ : mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
alarm.sourcePackage, userId);
if (dispatchesInWindow < quota) {
@@ -1662,7 +1766,7 @@ public class AlarmManagerService extends SystemService {
deviceIdlePolicyTime = nowElapsed;
} else {
final long whenInQuota = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
- alarm.sourcePackage, userId, quota) + Constants.ALLOW_WHILE_IDLE_WINDOW;
+ alarm.sourcePackage, userId, quota) + mConstants.ALLOW_WHILE_IDLE_WINDOW;
deviceIdlePolicyTime = Math.min(whenInQuota, mPendingIdleUntil.getWhenElapsed());
}
} else {
@@ -1749,7 +1853,7 @@ public class AlarmManagerService extends SystemService {
} else if (mPendingIdleUntil != null) {
adjustDeliveryTimeBasedOnDeviceIdle(a);
}
- if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+ if ((a.flags & FLAG_WAKE_FROM_IDLE) != 0) {
if (mNextWakeFromIdle == null || mNextWakeFromIdle.getWhenElapsed()
> a.getWhenElapsed()) {
mNextWakeFromIdle = a;
@@ -1810,6 +1914,14 @@ public class AlarmManagerService extends SystemService {
}
/**
+ * Returns true if the given uid is on the system or user's power save exclusion list.
+ */
+ boolean isWhitelisted(int uid) {
+ return (mLocalDeviceIdleController == null || mLocalDeviceIdleController.isAppOnWhitelist(
+ UserHandle.getAppId(uid)));
+ }
+
+ /**
* Public-facing binder interface
*/
private final IBinder mService = new IAlarmManager.Stub() {
@@ -1824,6 +1936,54 @@ public class AlarmManagerService extends SystemService {
// wakelock time spent in alarm delivery
mAppOps.checkPackage(callingUid, callingPackage);
+ final boolean allowWhileIdle = (flags & FLAG_ALLOW_WHILE_IDLE) != 0;
+
+ 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.
+ final boolean needsPermission;
+ boolean lowQuota;
+ if (CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
+ callingPackage, UserHandle.getUserHandleForUid(callingUid))) {
+ if (windowLength != AlarmManager.WINDOW_EXACT) {
+ needsPermission = false;
+ lowQuota = true;
+ idleOptions = isWhitelisted(callingUid) ? mOptsWithFgs.toBundle()
+ : mOptsWithoutFgs.toBundle();
+ } else if (alarmClock != null) {
+ needsPermission = true;
+ lowQuota = false;
+ idleOptions = mOptsWithFgs.toBundle();
+ } else {
+ needsPermission = true;
+ lowQuota = false;
+ idleOptions = mOptsWithFgs.toBundle();
+ }
+ } else {
+ needsPermission = false;
+ lowQuota = allowWhileIdle;
+ idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null;
+ }
+ if (needsPermission && !canScheduleExactAlarms()) {
+ if (alarmClock == null && isWhitelisted(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 {
+ final String errorMessage = "Caller needs to hold "
+ + Manifest.permission.SCHEDULE_EXACT_ALARM + " to set "
+ + ((allowWhileIdle) ? "exact, allow-while-idle" : "alarm-clock")
+ + " alarms.";
+ throw new SecurityException(errorMessage);
+ }
+ }
+ if (lowQuota) {
+ flags &= ~FLAG_ALLOW_WHILE_IDLE;
+ flags |= FLAG_ALLOW_WHILE_IDLE_COMPAT;
+ }
+ }
+
// Repeating alarms must use PendingIntent, not direct listener
if (interval != 0) {
if (directReceiver != null) {
@@ -1840,8 +2000,7 @@ public class AlarmManagerService extends SystemService {
// No incoming callers can request either WAKE_FROM_IDLE or
// ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate.
- flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE
- | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED);
+ flags &= ~(FLAG_WAKE_FROM_IDLE | FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED);
// Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm
// manager when to come out of idle mode, which is only for DeviceIdleController.
@@ -1857,22 +2016,32 @@ public class AlarmManagerService extends SystemService {
// If this alarm is for an alarm clock, then it must be standalone and we will
// use it to wake early from idle if needed.
if (alarmClock != null) {
- flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE;
+ flags |= FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE;
// If the caller is a core system component or on the user's whitelist, and not calling
// to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED.
// This means we will allow these alarms to go off as normal even while idle, with no
// timing restrictions.
- } else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
+ } else if (workSource == null && (UserHandle.isCore(callingUid)
|| UserHandle.isSameApp(callingUid, mSystemUiUid)
|| ((mAppStateTracker != null)
&& mAppStateTracker.isUidPowerSaveUserExempt(callingUid)))) {
- flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
- flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
+ flags |= FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
+ flags &= ~FLAG_ALLOW_WHILE_IDLE;
+ flags &= ~FLAG_ALLOW_WHILE_IDLE_COMPAT;
+ idleOptions = null;
}
setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver,
- listenerTag, flags, workSource, alarmClock, callingUid, callingPackage);
+ listenerTag, flags, workSource, alarmClock, callingUid, callingPackage,
+ idleOptions);
+ }
+
+ @Override
+ public boolean canScheduleExactAlarms() {
+ return PermissionChecker.checkCallingOrSelfPermissionForPreflight(getContext(),
+ Manifest.permission.SCHEDULE_EXACT_ALARM)
+ == PermissionChecker.PERMISSION_GRANTED;
}
@Override
@@ -2755,6 +2924,77 @@ 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.
+ */
+ void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) {
+ if (UserHandle.isCore(uid) || uid == mSystemUiUid) {
+ return;
+ }
+ if (isWhitelisted(uid)) {
+ return;
+ }
+ if (!CompatChanges.isChangeEnabled(
+ AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
+ packageName, UserHandle.getUserHandleForUid(uid))) {
+ return;
+ }
+
+ final Predicate<Alarm> whichAlarms =
+ a -> (a.uid == uid && a.packageName.equals(packageName)
+ && ((a.flags & FLAG_ALLOW_WHILE_IDLE) != 0 || a.alarmClock != null));
+ final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms);
+ final boolean didRemove = !removed.isEmpty();
+ if (didRemove) {
+ decrementAlarmCount(uid, removed.size());
+ }
+
+ for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
+ final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
+ for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
+ final Alarm alarm = alarmsForUid.get(j);
+ if (whichAlarms.test(alarm)) {
+ // Don't set didRemove, since this doesn't impact the scheduled alarms.
+ alarmsForUid.remove(j);
+ decrementAlarmCount(alarm.uid, 1);
+ }
+ }
+ if (alarmsForUid.size() == 0) {
+ mPendingBackgroundAlarms.removeAt(i);
+ }
+ }
+ for (int i = mPendingNonWakeupAlarms.size() - 1; i >= 0; i--) {
+ final Alarm a = mPendingNonWakeupAlarms.get(i);
+ if (whichAlarms.test(a)) {
+ // Don't set didRemove, since this doesn't impact the scheduled alarms.
+ mPendingNonWakeupAlarms.remove(i);
+ decrementAlarmCount(a.uid, 1);
+ }
+ }
+
+ if (didRemove) {
+ if (mNextWakeFromIdle != null && whichAlarms.test(mNextWakeFromIdle)) {
+ mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
+ if (mPendingIdleUntil != null) {
+ final boolean idleUntilUpdated = mAlarmStore.updateAlarmDeliveries(alarm -> {
+ if (alarm != mPendingIdleUntil) {
+ return false;
+ }
+ return adjustIdleUntilTime(alarm);
+ });
+ if (idleUntilUpdated) {
+ mAlarmStore.updateAlarmDeliveries(
+ alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm));
+ }
+ }
+ }
+ rescheduleKernelAlarmsLocked();
+ updateNextAlarmClockLocked();
+ }
+ }
+
void removeLocked(PendingIntent operation, IAlarmListener directReceiver) {
if (operation == null && directReceiver == null) {
if (localLOGV) {
@@ -3082,7 +3322,7 @@ public class AlarmManagerService extends SystemService {
}
}
- private boolean isExemptFromBatterySaver(Alarm alarm) {
+ private static boolean isExemptFromBatterySaver(Alarm alarm) {
if (alarm.alarmClock != null) {
return true;
}
@@ -3142,7 +3382,7 @@ public class AlarmManagerService extends SystemService {
alarm.count = 1;
triggerList.add(alarm);
- if ((alarm.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+ if ((alarm.flags & FLAG_WAKE_FROM_IDLE) != 0) {
EventLogTags.writeDeviceIdleWakeFromIdle(mPendingIdleUntil != null ? 1 : 0,
alarm.statsTag);
}
@@ -3180,7 +3420,7 @@ public class AlarmManagerService extends SystemService {
setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
- alarm.packageName);
+ alarm.packageName, null);
}
if (alarm.wakeup) {
@@ -3257,7 +3497,6 @@ public class AlarmManagerService extends SystemService {
mLastAlarmDeliveryTime = nowELAPSED;
for (int i = 0; i < triggerList.size(); i++) {
Alarm alarm = triggerList.get(i);
- final boolean allowWhileIdle = (alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0;
if (alarm.wakeup) {
Trace.traceBegin(Trace.TRACE_TAG_POWER,
"Dispatch wakeup alarm to " + alarm.packageName);
@@ -3273,7 +3512,7 @@ public class AlarmManagerService extends SystemService {
mActivityManagerInternal.noteAlarmStart(alarm.operation, alarm.workSource,
alarm.uid, alarm.statsTag);
}
- mDeliveryTracker.deliverLocked(alarm, nowELAPSED, allowWhileIdle);
+ mDeliveryTracker.deliverLocked(alarm, nowELAPSED);
} catch (RuntimeException e) {
Slog.w(TAG, "Failure sending alarm.", e);
}
@@ -3282,9 +3521,10 @@ public class AlarmManagerService extends SystemService {
}
}
- private boolean isExemptFromAppStandby(Alarm a) {
+ @VisibleForTesting
+ static boolean isExemptFromAppStandby(Alarm a) {
return a.alarmClock != null || UserHandle.isCore(a.creatorUid)
- || (a.flags & FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED) != 0;
+ || (a.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_ALLOW_WHILE_IDLE)) != 0;
}
@VisibleForTesting
@@ -3368,6 +3608,11 @@ public class AlarmManagerService extends SystemService {
MATCH_SYSTEM_ONLY, USER_SYSTEM);
}
+ IAppOpsService getAppOpsService() {
+ return IAppOpsService.Stub.asInterface(
+ ServiceManager.getService(Context.APP_OPS_SERVICE));
+ }
+
ClockReceiver getClockReceiver(AlarmManagerService service) {
return service.new ClockReceiver();
}
@@ -3566,6 +3811,7 @@ public class AlarmManagerService extends SystemService {
public static final int APP_STANDBY_BUCKET_CHANGED = 5;
public static final int CHARGING_STATUS_CHANGED = 6;
public static final int REMOVE_FOR_CANCELED = 7;
+ public static final int REMOVE_EXACT_ALARMS = 8;
AlarmHandler() {
super(Looper.myLooper());
@@ -3645,6 +3891,14 @@ public class AlarmManagerService extends SystemService {
}
break;
+ case REMOVE_EXACT_ALARMS:
+ final int uid = msg.arg1;
+ final String packageName = (String) msg.obj;
+ synchronized (mLock) {
+ removeExactAlarmsOnPermissionRevokedLocked(uid, packageName);
+ }
+ break;
+
default:
// nope, just ignore it
break;
@@ -3720,7 +3974,7 @@ public class AlarmManagerService extends SystemService {
setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
0, null, mTimeTickTrigger, TIME_TICK_TAG, flags, workSource, null,
- Process.myUid(), "android");
+ Process.myUid(), "android", null);
// Finally, remember when we set the tick alarm
synchronized (mLock) {
@@ -3740,7 +3994,7 @@ public class AlarmManagerService extends SystemService {
final WorkSource workSource = null; // Let system take blame for date change events.
setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, null, null,
AlarmManager.FLAG_STANDALONE, workSource, null,
- Process.myUid(), "android");
+ Process.myUid(), "android", null);
}
}
@@ -4112,7 +4366,7 @@ public class AlarmManagerService extends SystemService {
* Deliver an alarm and set up the post-delivery handling appropriately
*/
@GuardedBy("mLock")
- public void deliverLocked(Alarm alarm, long nowELAPSED, boolean allowWhileIdle) {
+ public void deliverLocked(Alarm alarm, long nowELAPSED) {
final long workSourceToken = ThreadLocalWorkSource.setUid(
getAlarmAttributionUid(alarm));
try {
@@ -4122,10 +4376,8 @@ public class AlarmManagerService extends SystemService {
try {
alarm.operation.send(getContext(), 0,
- mBackgroundIntent.putExtra(
- Intent.EXTRA_ALARM_COUNT, alarm.count),
- mDeliveryTracker, mHandler, null,
- allowWhileIdle ? mIdleOptions : null);
+ mBackgroundIntent.putExtra(Intent.EXTRA_ALARM_COUNT, alarm.count),
+ mDeliveryTracker, mHandler, null, alarm.mIdleOptions);
} catch (PendingIntent.CanceledException e) {
if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this
@@ -4194,7 +4446,7 @@ public class AlarmManagerService extends SystemService {
if (inflight.isBroadcast()) {
notifyBroadcastAlarmPendingLocked(alarm.uid);
}
- if (allowWhileIdle) {
+ if (isAllowedWhileIdleRestricted(alarm)) {
final boolean doze = (mPendingIdleUntil != null);
final boolean batterySaver = (mAppStateTracker != null
&& mAppStateTracker.isForceAllAppsStandbyEnabled());
@@ -4204,8 +4456,7 @@ public class AlarmManagerService extends SystemService {
mAllowWhileIdleHistory.recordAlarmForPackage(alarm.sourcePackage,
UserHandle.getUserId(alarm.creatorUid), nowELAPSED);
mAlarmStore.updateAlarmDeliveries(a -> {
- if (a.creatorUid != alarm.creatorUid
- || (a.flags & FLAG_ALLOW_WHILE_IDLE) == 0) {
+ if (a.creatorUid != alarm.creatorUid || !isAllowedWhileIdleRestricted(a)) {
return false;
}
return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a))
diff --git a/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java b/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java
index b7e8cf6e3fc8..7f191d4a306a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java
@@ -26,6 +26,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+
import com.android.server.LocalServices;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -144,13 +145,13 @@ public final class GrantedUriPermissions {
}
// Dumpsys infrastructure
- public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("mGrantFlags=0x"); pw.print(Integer.toHexString(mGrantFlags));
+ public void dump(PrintWriter pw) {
+ pw.print("mGrantFlags=0x"); pw.print(Integer.toHexString(mGrantFlags));
pw.print(" mSourceUserId="); pw.println(mSourceUserId);
- pw.print(prefix); pw.print("mTag="); pw.println(mTag);
- pw.print(prefix); pw.print("mPermissionOwner="); pw.println(mPermissionOwner);
+ pw.print("mTag="); pw.println(mTag);
+ pw.print("mPermissionOwner="); pw.println(mPermissionOwner);
for (int i = 0; i < mUris.size(); i++) {
- pw.print(prefix); pw.print("#"); pw.print(i); pw.print(": ");
+ pw.print("#"); pw.print(i); pw.print(": ");
pw.println(mUris.get(i));
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 18856f782b7d..af9771553063 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -16,33 +16,44 @@
package com.android.server.job;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.UserSwitchObserver;
import android.app.job.JobInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.UserInfo;
import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseIntArray;
+import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.util.StatLogger;
import com.android.server.JobSchedulerBackgroundThread;
+import com.android.server.LocalServices;
import com.android.server.job.controllers.JobStatus;
import com.android.server.job.controllers.StateController;
+import com.android.server.pm.UserManagerInternal;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -54,7 +65,7 @@ import java.util.List;
* and which {@link JobServiceContext} to run each job on.
*/
class JobConcurrencyManager {
- private static final String TAG = JobSchedulerService.TAG;
+ private static final String TAG = JobSchedulerService.TAG + ".Concurrency";
private static final boolean DEBUG = JobSchedulerService.DEBUG;
static final String CONFIG_KEY_PREFIX_CONCURRENCY = "concurrency_";
@@ -65,18 +76,42 @@ class JobConcurrencyManager {
// Try to give higher priority types lower values.
static final int WORK_TYPE_NONE = 0;
static final int WORK_TYPE_TOP = 1 << 0;
- static final int WORK_TYPE_BG = 1 << 1;
- private static final int NUM_WORK_TYPES = 2;
+ static final int WORK_TYPE_EJ = 1 << 1;
+ static final int WORK_TYPE_BG = 1 << 2;
+ static final int WORK_TYPE_BGUSER = 1 << 3;
+ @VisibleForTesting
+ static final int NUM_WORK_TYPES = 4;
+ private static final int ALL_WORK_TYPES =
+ WORK_TYPE_TOP | WORK_TYPE_EJ | WORK_TYPE_BG | WORK_TYPE_BGUSER;
@IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = {
WORK_TYPE_NONE,
WORK_TYPE_TOP,
- WORK_TYPE_BG
+ WORK_TYPE_EJ,
+ WORK_TYPE_BG,
+ WORK_TYPE_BGUSER
})
@Retention(RetentionPolicy.SOURCE)
public @interface WorkType {
}
+ private static String workTypeToString(@WorkType int workType) {
+ switch (workType) {
+ case WORK_TYPE_NONE:
+ return "NONE";
+ case WORK_TYPE_TOP:
+ return "TOP";
+ case WORK_TYPE_EJ:
+ return "EJ";
+ case WORK_TYPE_BG:
+ return "BG";
+ case WORK_TYPE_BGUSER:
+ return "BGUSER";
+ default:
+ return "WORK(" + workType + ")";
+ }
+ }
+
private final Object mLock;
private final JobSchedulerService mService;
private final Context mContext;
@@ -94,49 +129,63 @@ class JobConcurrencyManager {
private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_ON =
new WorkConfigLimitsPerMemoryTrimLevel(
- new WorkTypeConfig("screen_on_normal", 8,
+ new WorkTypeConfig("screen_on_normal", 11,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_EJ, 3),
+ Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 6))),
- new WorkTypeConfig("screen_on_moderate", 8,
+ List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
+ ),
+ new WorkTypeConfig("screen_on_moderate", 9,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2),
+ Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 4))),
- new WorkTypeConfig("screen_on_low", 5,
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
+ ),
+ new WorkTypeConfig("screen_on_low", 6,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1),
+ Pair.create(WORK_TYPE_BG, 1)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 1))),
+ List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+ ),
new WorkTypeConfig("screen_on_critical", 5,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 1)))
+ List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+ )
);
private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF =
new WorkConfigLimitsPerMemoryTrimLevel(
- new WorkTypeConfig("screen_off_normal", 10,
+ new WorkTypeConfig("screen_off_normal", 13,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
+ Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 6))),
- new WorkTypeConfig("screen_off_moderate", 10,
+ List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
+ ),
+ new WorkTypeConfig("screen_off_moderate", 13,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 3),
+ Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 4))),
- new WorkTypeConfig("screen_off_low", 5,
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
+ ),
+ new WorkTypeConfig("screen_off_low", 7,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2),
+ Pair.create(WORK_TYPE_BG, 1)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 1))),
+ List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+ ),
new WorkTypeConfig("screen_off_critical", 5,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 1)))
+ List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+ )
);
/**
@@ -153,10 +202,16 @@ class JobConcurrencyManager {
int[] mRecycledWorkTypeForContext = new int[MAX_JOB_CONTEXTS_COUNT];
+ String[] mRecycledPreemptReasonForContext = new String[MAX_JOB_CONTEXTS_COUNT];
+
+ String[] mRecycledShouldStopJobReason = new String[MAX_JOB_CONTEXTS_COUNT];
+
private final ArraySet<JobStatus> mRunningJobs = new ArraySet<>();
private final WorkCountTracker mWorkCountTracker = new WorkCountTracker();
+ private WorkTypeConfig mWorkTypeConfig = CONFIG_LIMITS_SCREEN_OFF.normal;
+
/** Wait for this long after screen off before adjusting the job concurrency. */
private long mScreenOffAdjustmentDelayMs = DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS;
@@ -171,6 +226,10 @@ class JobConcurrencyManager {
"assignJobsToContexts",
"refreshSystemState",
});
+ @VisibleForTesting
+ GracePeriodObserver mGracePeriodObserver;
+ @VisibleForTesting
+ boolean mShouldRestrictBgUser;
interface Stats {
int ASSIGN_JOBS_TO_CONTEXTS = 0;
@@ -182,9 +241,13 @@ class JobConcurrencyManager {
JobConcurrencyManager(JobSchedulerService service) {
mService = service;
mLock = mService.mLock;
- mContext = service.getContext();
+ mContext = service.getTestableContext();
mHandler = JobSchedulerBackgroundThread.getHandler();
+
+ mGracePeriodObserver = new GracePeriodObserver(mContext);
+ mShouldRestrictBgUser = mContext.getResources().getBoolean(
+ R.bool.config_jobSchedulerRestrictBackgroundUser);
}
public void onSystemReady() {
@@ -193,10 +256,18 @@ class JobConcurrencyManager {
final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
mContext.registerReceiver(mReceiver, filter);
+ try {
+ ActivityManager.getService().registerUserSwitchObserver(mGracePeriodObserver, TAG);
+ } catch (RemoteException e) {
+ }
onInteractiveStateChanged(mPowerManager.isInteractive());
}
+ void onUserRemoved(int userId) {
+ mGracePeriodObserver.onUserRemoved(userId);
+ }
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -224,7 +295,7 @@ class JobConcurrencyManager {
Slog.d(TAG, "Interactive: " + interactive);
}
- final long nowRealtime = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final long nowRealtime = sElapsedRealtimeClock.millis();
if (interactive) {
mLastScreenOnRealtime = nowRealtime;
mEffectiveInteractiveState = true;
@@ -261,7 +332,7 @@ class JobConcurrencyManager {
if (mLastScreenOnRealtime > mLastScreenOffRealtime) {
return;
}
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final long now = sElapsedRealtimeClock.millis();
if ((mLastScreenOffRealtime + mScreenOffAdjustmentDelayMs) > now) {
return;
}
@@ -276,13 +347,14 @@ class JobConcurrencyManager {
}
}
+ /** Return {@code true} if the state was updated. */
@GuardedBy("mLock")
- private void refreshSystemStateLocked() {
+ private boolean refreshSystemStateLocked() {
final long nowUptime = JobSchedulerService.sUptimeMillisClock.millis();
// Only refresh the information every so often.
if (nowUptime < mNextSystemStateRefreshTime) {
- return;
+ return false;
}
final long start = mStatLogger.getTime();
@@ -295,32 +367,34 @@ class JobConcurrencyManager {
}
mStatLogger.logDurationStat(Stats.REFRESH_SYSTEM_STATE, start);
+ return true;
}
@GuardedBy("mLock")
private void updateCounterConfigLocked() {
- refreshSystemStateLocked();
+ if (!refreshSystemStateLocked()) {
+ return;
+ }
final WorkConfigLimitsPerMemoryTrimLevel workConfigs = mEffectiveInteractiveState
? CONFIG_LIMITS_SCREEN_ON : CONFIG_LIMITS_SCREEN_OFF;
- WorkTypeConfig workTypeConfig;
switch (mLastMemoryTrimLevel) {
case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
- workTypeConfig = workConfigs.moderate;
+ mWorkTypeConfig = workConfigs.moderate;
break;
case ProcessStats.ADJ_MEM_FACTOR_LOW:
- workTypeConfig = workConfigs.low;
+ mWorkTypeConfig = workConfigs.low;
break;
case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
- workTypeConfig = workConfigs.critical;
+ mWorkTypeConfig = workConfigs.critical;
break;
default:
- workTypeConfig = workConfigs.normal;
+ mWorkTypeConfig = workConfigs.normal;
break;
}
- mWorkCountTracker.setConfig(workTypeConfig);
+ mWorkCountTracker.setConfig(mWorkTypeConfig);
}
/**
@@ -352,13 +426,20 @@ class JobConcurrencyManager {
boolean[] slotChanged = mRecycledSlotChanged;
int[] preferredUidForContext = mRecycledPreferredUidForContext;
int[] workTypeForContext = mRecycledWorkTypeForContext;
+ String[] preemptReasonForContext = mRecycledPreemptReasonForContext;
+ String[] shouldStopJobReason = mRecycledShouldStopJobReason;
updateCounterConfigLocked();
// Reset everything since we'll re-evaluate the current state.
mWorkCountTracker.resetCounts();
+ // Update the priorities of jobs that aren't running, and also count the pending work types.
+ // Do this before the following loop to hopefully reduce the cost of
+ // shouldStopRunningJobLocked().
+ updateNonRunningPriorities(pendingJobs, true);
+
for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
- final JobServiceContext js = mService.mActiveServices.get(i);
+ final JobServiceContext js = activeServices.get(i);
final JobStatus status = js.getRunningJobLocked();
if ((contextIdToJobMap[i] = status) != null) {
@@ -368,14 +449,13 @@ class JobConcurrencyManager {
slotChanged[i] = false;
preferredUidForContext[i] = js.getPreferredUid();
+ preemptReasonForContext[i] = null;
+ shouldStopJobReason[i] = shouldStopRunningJobLocked(js);
}
if (DEBUG) {
Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
}
- // Next, update the job priorities, and also count the pending FG / BG jobs.
- updateNonRunningPriorities(pendingJobs, true);
-
mWorkCountTracker.onCountDone();
for (int i = 0; i < pendingJobs.size(); i++) {
@@ -385,16 +465,18 @@ class JobConcurrencyManager {
continue;
}
- // TODO(171305774): make sure HPJs aren't pre-empted and add dedicated contexts for them
-
// Find an available slot for nextPending. The context should be available OR
// it should have lowest priority among all running jobs
// (sharing the same Uid as nextPending)
int minPriorityForPreemption = Integer.MAX_VALUE;
int selectedContextId = -1;
- int workType = mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+ int allWorkTypes = getJobWorkTypes(nextPending);
+ int workType = mWorkCountTracker.canJobStart(allWorkTypes);
boolean startingJob = false;
- for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
+ String preemptReason = null;
+ // TODO(141645789): rewrite this to look at empty contexts first so we don't
+ // unnecessarily preempt
+ for (int j = 0; j < MAX_JOB_CONTEXTS_COUNT; j++) {
JobStatus job = contextIdToJobMap[j];
int preferredUid = preferredUidForContext[j];
if (job == null) {
@@ -414,6 +496,15 @@ class JobConcurrencyManager {
continue;
}
if (job.getUid() != nextPending.getUid()) {
+ // Maybe stop the job if it has had its day in the sun.
+ final String reason = shouldStopJobReason[j];
+ if (reason != null && mWorkCountTracker.canJobStart(allWorkTypes,
+ activeServices.get(j).getRunningJobWorkType()) != WORK_TYPE_NONE) {
+ // Right now, the way the code is set up, we don't need to explicitly
+ // assign the new job to this context since we'll reassign when the
+ // preempted job finally stops.
+ preemptReason = reason;
+ }
continue;
}
@@ -427,6 +518,7 @@ class JobConcurrencyManager {
// the lowest-priority running job
minPriorityForPreemption = jobPriority;
selectedContextId = j;
+ preemptReason = "higher priority job found";
// In this case, we're just going to preempt a low priority job, we're not
// actually starting a job, so don't set startingJob.
}
@@ -434,11 +526,12 @@ class JobConcurrencyManager {
if (selectedContextId != -1) {
contextIdToJobMap[selectedContextId] = nextPending;
slotChanged[selectedContextId] = true;
+ preemptReasonForContext[selectedContextId] = preemptReason;
}
if (startingJob) {
// Increase the counters when we're going to start a job.
workTypeForContext[selectedContextId] = workType;
- mWorkCountTracker.stageJob(workType);
+ mWorkCountTracker.stageJob(workType, allWorkTypes);
}
}
if (DEBUG) {
@@ -459,7 +552,7 @@ class JobConcurrencyManager {
+ activeServices.get(i).getRunningJobLocked());
}
// preferredUid will be set to uid of currently running job.
- activeServices.get(i).preemptExecutingJobLocked();
+ activeServices.get(i).preemptExecutingJobLocked(preemptReasonForContext[i]);
preservePreferredUid = true;
} else {
final JobStatus pendingJob = contextIdToJobMap[i];
@@ -533,8 +626,10 @@ class JobConcurrencyManager {
JobStatus highestPriorityJob = null;
int highPriWorkType = workType;
+ int highPriAllWorkTypes = workType;
JobStatus backupJob = null;
int backupWorkType = WORK_TYPE_NONE;
+ int backupAllWorkTypes = WORK_TYPE_NONE;
for (int i = 0; i < pendingJobs.size(); i++) {
final JobStatus nextPending = pendingJobs.get(i);
@@ -544,11 +639,12 @@ class JobConcurrencyManager {
if (worker.getPreferredUid() != nextPending.getUid()) {
if (backupJob == null) {
- int workAsType =
- mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+ int allWorkTypes = getJobWorkTypes(nextPending);
+ int workAsType = mWorkCountTracker.canJobStart(allWorkTypes);
if (workAsType != WORK_TYPE_NONE) {
backupJob = nextPending;
backupWorkType = workAsType;
+ backupAllWorkTypes = allWorkTypes;
}
}
continue;
@@ -566,7 +662,8 @@ class JobConcurrencyManager {
// reserved slots. We should just run the highest priority job we can find,
// though it would be ideal to use an available WorkType slot instead of
// overloading slots.
- final int workAsType = mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+ highPriAllWorkTypes = getJobWorkTypes(nextPending);
+ final int workAsType = mWorkCountTracker.canJobStart(highPriAllWorkTypes);
if (workAsType == WORK_TYPE_NONE) {
// Just use the preempted job's work type since this new one is technically
// replacing it anyway.
@@ -579,7 +676,7 @@ class JobConcurrencyManager {
if (DEBUG) {
Slog.d(TAG, "Running job " + jobStatus + " as preemption");
}
- mWorkCountTracker.stageJob(highPriWorkType);
+ mWorkCountTracker.stageJob(highPriWorkType, highPriAllWorkTypes);
startJobLocked(worker, highestPriorityJob, highPriWorkType);
} else {
if (DEBUG) {
@@ -590,7 +687,7 @@ class JobConcurrencyManager {
if (DEBUG) {
Slog.d(TAG, "Running job " + jobStatus + " instead");
}
- mWorkCountTracker.stageJob(backupWorkType);
+ mWorkCountTracker.stageJob(backupWorkType, backupAllWorkTypes);
startJobLocked(worker, backupJob, backupWorkType);
}
}
@@ -602,6 +699,7 @@ class JobConcurrencyManager {
// find.
JobStatus highestPriorityJob = null;
int highPriWorkType = workType;
+ int highPriAllWorkTypes = workType;
for (int i = 0; i < pendingJobs.size(); i++) {
final JobStatus nextPending = pendingJobs.get(i);
@@ -609,7 +707,8 @@ class JobConcurrencyManager {
continue;
}
- final int workAsType = mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
+ final int allWorkTypes = getJobWorkTypes(nextPending);
+ final int workAsType = mWorkCountTracker.canJobStart(allWorkTypes);
if (workAsType == WORK_TYPE_NONE) {
continue;
}
@@ -618,6 +717,7 @@ class JobConcurrencyManager {
< nextPending.lastEvaluatedPriority) {
highestPriorityJob = nextPending;
highPriWorkType = workAsType;
+ highPriAllWorkTypes = allWorkTypes;
}
}
@@ -627,7 +727,7 @@ class JobConcurrencyManager {
if (DEBUG) {
Slog.d(TAG, "About to run job: " + jobStatus);
}
- mWorkCountTracker.stageJob(highPriWorkType);
+ mWorkCountTracker.stageJob(highPriWorkType, highPriAllWorkTypes);
startJobLocked(worker, highestPriorityJob, highPriWorkType);
}
}
@@ -635,6 +735,91 @@ class JobConcurrencyManager {
noteConcurrency();
}
+ /**
+ * Returns {@code null} if the job can continue running and a non-null String if the job should
+ * be stopped. The non-null String details the reason for stopping the job. A job will generally
+ * be stopped if there similar job types waiting to be run and stopping this job would allow
+ * another job to run, or if system state suggests the job should stop.
+ */
+ @Nullable
+ String shouldStopRunningJobLocked(@NonNull JobServiceContext context) {
+ final JobStatus js = context.getRunningJobLocked();
+ if (js == null) {
+ // This can happen when we try to assign newly found pending jobs to contexts.
+ return null;
+ }
+
+ if (context.isWithinExecutionGuaranteeTime()) {
+ return null;
+ }
+
+ // Update config in case memory usage has changed significantly.
+ updateCounterConfigLocked();
+
+ @WorkType final int workType = context.getRunningJobWorkType();
+
+ // We're over the minimum guaranteed runtime. Stop the job if we're over config limits or
+ // there are pending jobs that could replace this one.
+ if (mRunningJobs.size() > mWorkTypeConfig.getMaxTotal()
+ || mWorkCountTracker.isOverTypeLimit(workType)) {
+ return "too many jobs running";
+ }
+
+ final List<JobStatus> pendingJobs = mService.mPendingJobs;
+ final int numPending = pendingJobs.size();
+ if (numPending == 0) {
+ // All quiet. We can let this job run to completion.
+ return null;
+ }
+
+ // Only expedited jobs can replace expedited jobs.
+ if (js.shouldTreatAsExpeditedJob()) {
+ // Keep fg/bg user distinction.
+ if (workType == WORK_TYPE_BGUSER) {
+ // For now, let any bg user job replace a bg user expedited job.
+ // TODO: limit to ej once we have dedicated bg user ej slots.
+ if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_BGUSER) > 0) {
+ return "blocking " + workTypeToString(workType) + " queue";
+ }
+ } else {
+ if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_EJ) > 0) {
+ return "blocking " + workTypeToString(workType) + " queue";
+ }
+ }
+
+ if (mPowerManager.isPowerSaveMode()) {
+ return "battery saver";
+ }
+ if (mPowerManager.isDeviceIdleMode()) {
+ return "deep doze";
+ }
+ }
+
+ // Easy check. If there are pending jobs of the same work type, then we know that
+ // something will replace this.
+ if (mWorkCountTracker.getPendingJobCount(workType) > 0) {
+ return "blocking " + workTypeToString(workType) + " queue";
+ }
+
+ // Harder check. We need to see if a different work type can replace this job.
+ int remainingWorkTypes = ALL_WORK_TYPES;
+ for (int i = 0; i < numPending; ++i) {
+ final JobStatus pending = pendingJobs.get(i);
+ final int workTypes = getJobWorkTypes(pending);
+ if ((workTypes & remainingWorkTypes) > 0
+ && mWorkCountTracker.canJobStart(workTypes, workType) != WORK_TYPE_NONE) {
+ return "blocking other pending jobs";
+ }
+
+ remainingWorkTypes = remainingWorkTypes & ~workTypes;
+ if (remainingWorkTypes == 0) {
+ break;
+ }
+ }
+
+ return null;
+ }
+
@GuardedBy("mLock")
private String printPendingQueueLocked() {
StringBuilder s = new StringBuilder("Pending queue: ");
@@ -684,17 +869,26 @@ class JobConcurrencyManager {
pw.increaseIndent();
try {
- pw.print("Configuration:");
+ pw.println("Configuration:");
pw.increaseIndent();
pw.print(KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS, mScreenOffAdjustmentDelayMs).println();
+ pw.println();
CONFIG_LIMITS_SCREEN_ON.normal.dump(pw);
+ pw.println();
CONFIG_LIMITS_SCREEN_ON.moderate.dump(pw);
+ pw.println();
CONFIG_LIMITS_SCREEN_ON.low.dump(pw);
+ pw.println();
CONFIG_LIMITS_SCREEN_ON.critical.dump(pw);
+ pw.println();
CONFIG_LIMITS_SCREEN_OFF.normal.dump(pw);
+ pw.println();
CONFIG_LIMITS_SCREEN_OFF.moderate.dump(pw);
+ pw.println();
CONFIG_LIMITS_SCREEN_OFF.low.dump(pw);
+ pw.println();
CONFIG_LIMITS_SCREEN_OFF.critical.dump(pw);
+ pw.println();
pw.decreaseIndent();
pw.print("Screen state: current ");
@@ -713,14 +907,17 @@ class JobConcurrencyManager {
pw.println();
- pw.println("Current max jobs:");
- pw.println(" ");
+ pw.print("Current work counts: ");
pw.println(mWorkCountTracker);
pw.println();
pw.print("mLastMemoryTrimLevel: ");
- pw.print(mLastMemoryTrimLevel);
+ pw.println(mLastMemoryTrimLevel);
+ pw.println();
+
+ pw.print("User Grace Period: ");
+ pw.println(mGracePeriodObserver.mGracePeriodExpiration);
pw.println();
mStatLogger.dump(pw);
@@ -748,15 +945,52 @@ class JobConcurrencyManager {
proto.end(token);
}
+ /**
+ * Decides whether a job is from the current foreground user or the equivalent.
+ */
+ @VisibleForTesting
+ boolean shouldRunAsFgUserJob(JobStatus job) {
+ if (!mShouldRestrictBgUser) return true;
+ int userId = job.getSourceUserId();
+ UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
+ UserInfo userInfo = um.getUserInfo(userId);
+
+ // If the user has a parent user (e.g. a work profile of another user), the user should be
+ // treated equivalent as its parent user.
+ if (userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
+ && userInfo.profileGroupId != userId) {
+ userId = userInfo.profileGroupId;
+ userInfo = um.getUserInfo(userId);
+ }
+
+ int currentUser = LocalServices.getService(ActivityManagerInternal.class)
+ .getCurrentUserId();
+ // A user is treated as foreground user if any of the followings is true:
+ // 1. The user is current user
+ // 2. The user is primary user
+ // 3. The user's grace period has not expired
+ return currentUser == userId || userInfo.isPrimary()
+ || mGracePeriodObserver.isWithinGracePeriodForUser(userId);
+ }
+
int getJobWorkTypes(@NonNull JobStatus js) {
int classification = 0;
- // TODO(171305774): create dedicated work type for EJ and FGS
- if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP
- || js.shouldTreatAsExpeditedJob()) {
- classification |= WORK_TYPE_TOP;
+ // TODO: create dedicated work type for FGS
+ if (shouldRunAsFgUserJob(js)) {
+ if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
+ classification |= WORK_TYPE_TOP;
+ } else {
+ classification |= WORK_TYPE_BG;
+ }
+
+ if (js.shouldTreatAsExpeditedJob()) {
+ classification |= WORK_TYPE_EJ;
+ }
} else {
- classification |= WORK_TYPE_BG;
+ // TODO(171305774): create dedicated slots for EJs of bg user
+ classification |= WORK_TYPE_BGUSER;
}
+
return classification;
}
@@ -765,9 +999,15 @@ class JobConcurrencyManager {
private static final String KEY_PREFIX_MAX_TOTAL =
CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_";
private static final String KEY_PREFIX_MAX_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "max_top_";
+ private static final String KEY_PREFIX_MAX_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "max_ej_";
private static final String KEY_PREFIX_MAX_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_";
+ private static final String KEY_PREFIX_MAX_BGUSER =
+ CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_";
private static final String KEY_PREFIX_MIN_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "min_top_";
+ private static final String KEY_PREFIX_MIN_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "min_ej_";
private static final String KEY_PREFIX_MIN_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bg_";
+ private static final String KEY_PREFIX_MIN_BGUSER =
+ CONFIG_KEY_PREFIX_CONCURRENCY + "min_bguser_";
private final String mConfigIdentifier;
private int mMaxTotal;
@@ -811,10 +1051,18 @@ class JobConcurrencyManager {
properties.getInt(KEY_PREFIX_MAX_TOP + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_TOP, mMaxTotal))));
mMaxAllowedSlots.put(WORK_TYPE_TOP, maxTop);
+ final int maxEj = Math.max(1, Math.min(mMaxTotal,
+ properties.getInt(KEY_PREFIX_MAX_EJ + mConfigIdentifier,
+ mDefaultMaxAllowedSlots.get(WORK_TYPE_EJ, mMaxTotal))));
+ mMaxAllowedSlots.put(WORK_TYPE_EJ, maxEj);
final int maxBg = Math.max(1, Math.min(mMaxTotal,
properties.getInt(KEY_PREFIX_MAX_BG + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_BG, mMaxTotal))));
mMaxAllowedSlots.put(WORK_TYPE_BG, maxBg);
+ final int maxBgUser = Math.max(1, Math.min(mMaxTotal,
+ properties.getInt(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
+ mDefaultMaxAllowedSlots.get(WORK_TYPE_BGUSER, mMaxTotal))));
+ mMaxAllowedSlots.put(WORK_TYPE_BGUSER, maxBgUser);
int remaining = mMaxTotal;
mMinReservedSlots.clear();
@@ -824,11 +1072,23 @@ class JobConcurrencyManager {
mDefaultMinReservedSlots.get(WORK_TYPE_TOP))));
mMinReservedSlots.put(WORK_TYPE_TOP, minTop);
remaining -= minTop;
+ // Ensure ej is in the range [0, min(maxEj, remaining)]
+ final int minEj = Math.max(0, Math.min(Math.min(maxEj, remaining),
+ properties.getInt(KEY_PREFIX_MIN_EJ + mConfigIdentifier,
+ mDefaultMinReservedSlots.get(WORK_TYPE_EJ))));
+ mMinReservedSlots.put(WORK_TYPE_EJ, minEj);
+ remaining -= minEj;
// Ensure bg is in the range [0, min(maxBg, remaining)]
final int minBg = Math.max(0, Math.min(Math.min(maxBg, remaining),
properties.getInt(KEY_PREFIX_MIN_BG + mConfigIdentifier,
mDefaultMinReservedSlots.get(WORK_TYPE_BG))));
mMinReservedSlots.put(WORK_TYPE_BG, minBg);
+ remaining -= minBg;
+ // Ensure bg user is in the range [0, min(maxBgUser, remaining)]
+ final int minBgUser = Math.max(0, Math.min(Math.min(maxBgUser, remaining),
+ properties.getInt(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
+ mDefaultMinReservedSlots.get(WORK_TYPE_BGUSER, 0))));
+ mMinReservedSlots.put(WORK_TYPE_BGUSER, minBgUser);
}
int getMaxTotal() {
@@ -849,10 +1109,18 @@ class JobConcurrencyManager {
.println();
pw.print(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_TOP))
.println();
+ pw.print(KEY_PREFIX_MIN_EJ + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_EJ))
+ .println();
+ pw.print(KEY_PREFIX_MAX_EJ + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_EJ))
+ .println();
pw.print(KEY_PREFIX_MIN_BG + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_BG))
.println();
pw.print(KEY_PREFIX_MAX_BG + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_BG))
.println();
+ pw.print(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
+ mMinReservedSlots.get(WORK_TYPE_BGUSER)).println();
+ pw.print(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
+ mMaxAllowedSlots.get(WORK_TYPE_BGUSER)).println();
}
}
@@ -873,6 +1141,58 @@ class JobConcurrencyManager {
}
/**
+ * This class keeps the track of when a user's grace period expires.
+ */
+ @VisibleForTesting
+ static class GracePeriodObserver extends UserSwitchObserver {
+ // Key is UserId and Value is the time when grace period expires
+ @VisibleForTesting
+ final SparseLongArray mGracePeriodExpiration = new SparseLongArray();
+ private int mCurrentUserId;
+ @VisibleForTesting
+ int mGracePeriod;
+ private final UserManagerInternal mUserManagerInternal;
+ final Object mLock = new Object();
+
+
+ GracePeriodObserver(Context context) {
+ mCurrentUserId = LocalServices.getService(ActivityManagerInternal.class)
+ .getCurrentUserId();
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+ mGracePeriod = Math.max(0, context.getResources().getInteger(
+ R.integer.config_jobSchedulerUserGracePeriod));
+ }
+
+ @Override
+ public void onUserSwitchComplete(int newUserId) {
+ final long expiration = sElapsedRealtimeClock.millis() + mGracePeriod;
+ synchronized (mLock) {
+ if (mCurrentUserId != UserHandle.USER_NULL
+ && mUserManagerInternal.exists(mCurrentUserId)) {
+ mGracePeriodExpiration.append(mCurrentUserId, expiration);
+ }
+ mGracePeriodExpiration.delete(newUserId);
+ mCurrentUserId = newUserId;
+ }
+ }
+
+ void onUserRemoved(int userId) {
+ synchronized (mLock) {
+ mGracePeriodExpiration.delete(userId);
+ }
+ }
+
+ @VisibleForTesting
+ public boolean isWithinGracePeriodForUser(int userId) {
+ synchronized (mLock) {
+ return userId == mCurrentUserId
+ || sElapsedRealtimeClock.millis()
+ < mGracePeriodExpiration.get(userId, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ /**
* This class decides, taking into account the current {@link WorkTypeConfig} and how many jobs
* are running/pending, how many more job can start.
*
@@ -892,20 +1212,21 @@ class JobConcurrencyManager {
private final SparseIntArray mNumPendingJobs = new SparseIntArray(NUM_WORK_TYPES);
private final SparseIntArray mNumRunningJobs = new SparseIntArray(NUM_WORK_TYPES);
private final SparseIntArray mNumStartingJobs = new SparseIntArray(NUM_WORK_TYPES);
- private int mNumUnspecialized = 0;
private int mNumUnspecializedRemaining = 0;
void setConfig(@NonNull WorkTypeConfig workTypeConfig) {
mConfigMaxTotal = workTypeConfig.getMaxTotal();
mConfigNumReservedSlots.put(WORK_TYPE_TOP,
workTypeConfig.getMinReserved(WORK_TYPE_TOP));
+ mConfigNumReservedSlots.put(WORK_TYPE_EJ, workTypeConfig.getMinReserved(WORK_TYPE_EJ));
mConfigNumReservedSlots.put(WORK_TYPE_BG, workTypeConfig.getMinReserved(WORK_TYPE_BG));
+ mConfigNumReservedSlots.put(WORK_TYPE_BGUSER,
+ workTypeConfig.getMinReserved(WORK_TYPE_BGUSER));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_TOP, workTypeConfig.getMax(WORK_TYPE_TOP));
+ mConfigAbsoluteMaxSlots.put(WORK_TYPE_EJ, workTypeConfig.getMax(WORK_TYPE_EJ));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_BG, workTypeConfig.getMax(WORK_TYPE_BG));
+ mConfigAbsoluteMaxSlots.put(WORK_TYPE_BGUSER, workTypeConfig.getMax(WORK_TYPE_BGUSER));
- mNumUnspecialized = mConfigMaxTotal;
- mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_TOP);
- mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BG);
mNumUnspecializedRemaining = mConfigMaxTotal;
for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) {
mNumUnspecializedRemaining -= Math.max(mNumRunningJobs.valueAt(i),
@@ -929,20 +1250,58 @@ class JobConcurrencyManager {
}
void incrementPendingJobCount(int workTypes) {
+ adjustPendingJobCount(workTypes, true);
+ }
+
+ void decrementPendingJobCount(int workTypes) {
+ if (adjustPendingJobCount(workTypes, false) > 1) {
+ // We don't need to adjust reservations if only one work type was modified
+ // because that work type is the one we're using.
+
+ // 0 is WORK_TYPE_NONE.
+ int workType = 1;
+ int rem = workTypes;
+ while (rem > 0) {
+ if ((rem & 1) != 0) {
+ maybeAdjustReservations(workType);
+ }
+ rem = rem >>> 1;
+ workType = workType << 1;
+ }
+ }
+ }
+
+ /** Returns the number of WorkTypes that were modified. */
+ private int adjustPendingJobCount(int workTypes, boolean add) {
+ final int adj = add ? 1 : -1;
+
+ int numAdj = 0;
// We don't know which type we'll classify the job as when we run it yet, so make sure
// we have space in all applicable slots.
if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) {
- mNumPendingJobs.put(WORK_TYPE_TOP, mNumPendingJobs.get(WORK_TYPE_TOP) + 1);
+ mNumPendingJobs.put(WORK_TYPE_TOP, mNumPendingJobs.get(WORK_TYPE_TOP) + adj);
+ numAdj++;
+ }
+ if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) {
+ mNumPendingJobs.put(WORK_TYPE_EJ, mNumPendingJobs.get(WORK_TYPE_EJ) + adj);
+ numAdj++;
}
if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
- mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + 1);
+ mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + adj);
+ numAdj++;
+ }
+ if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) {
+ mNumPendingJobs.put(WORK_TYPE_BGUSER, mNumPendingJobs.get(WORK_TYPE_BGUSER) + adj);
+ numAdj++;
}
+
+ return numAdj;
}
- void stageJob(@WorkType int workType) {
+ void stageJob(@WorkType int workType, int allWorkTypes) {
final int newNumStartingJobs = mNumStartingJobs.get(workType) + 1;
mNumStartingJobs.put(workType, newNumStartingJobs);
- mNumPendingJobs.put(workType, Math.max(0, mNumPendingJobs.get(workType) - 1));
+ decrementPendingJobCount(allWorkTypes);
if (newNumStartingJobs + mNumRunningJobs.get(workType)
> mNumActuallyReservedSlots.get(workType)) {
mNumUnspecializedRemaining--;
@@ -1018,48 +1377,76 @@ class JobConcurrencyManager {
void onCountDone() {
// Calculate how many slots to reserve for each work type. "Unspecialized" slots will
- // be reserved for higher importance types first (ie. top before bg).
- mNumUnspecialized = mConfigMaxTotal;
- final int numTop = mNumRunningJobs.get(WORK_TYPE_TOP)
- + mNumPendingJobs.get(WORK_TYPE_TOP);
- int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop);
- mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop);
- mNumUnspecialized -= resTop;
- final int numBg = mNumRunningJobs.get(WORK_TYPE_BG) + mNumPendingJobs.get(WORK_TYPE_BG);
- int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg);
- mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg);
- mNumUnspecialized -= resBg;
-
- mNumUnspecializedRemaining = mNumUnspecialized;
- // Account for already running jobs after we've assigned the minimum number of slots.
- int unspecializedAssigned;
- int extraRunning = (mNumRunningJobs.get(WORK_TYPE_TOP) - resTop);
- if (extraRunning > 0) {
- unspecializedAssigned = Math.max(0,
- Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP) - resTop,
- extraRunning));
- resTop += unspecializedAssigned;
- mNumUnspecializedRemaining -= extraRunning;
- }
- extraRunning = (mNumRunningJobs.get(WORK_TYPE_BG) - resBg);
- if (extraRunning > 0) {
- unspecializedAssigned = Math.max(0,
- Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG) - resBg, extraRunning));
- resBg += unspecializedAssigned;
- mNumUnspecializedRemaining -= extraRunning;
- }
+ // be reserved for higher importance types first (ie. top before ej before bg).
+ // Steps:
+ // 1. Account for slots for already running jobs
+ // 2. Use remaining unaccounted slots to try and ensure minimum reserved slots
+ // 3. Allocate remaining up to max, based on importance
- // Assign remaining unspecialized based on ranking.
- unspecializedAssigned = Math.max(0,
+ mNumUnspecializedRemaining = mConfigMaxTotal;
+
+ // Step 1
+ int runTop = mNumRunningJobs.get(WORK_TYPE_TOP);
+ int resTop = runTop;
+ mNumUnspecializedRemaining -= resTop;
+ int runEj = mNumRunningJobs.get(WORK_TYPE_EJ);
+ int resEj = runEj;
+ mNumUnspecializedRemaining -= resEj;
+ int runBg = mNumRunningJobs.get(WORK_TYPE_BG);
+ int resBg = runBg;
+ mNumUnspecializedRemaining -= resBg;
+ int runBgUser = mNumRunningJobs.get(WORK_TYPE_BGUSER);
+ int resBgUser = runBgUser;
+ mNumUnspecializedRemaining -= resBgUser;
+
+ // Step 2
+ final int numTop = runTop + mNumPendingJobs.get(WORK_TYPE_TOP);
+ int fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+ Math.min(numTop, mConfigNumReservedSlots.get(WORK_TYPE_TOP) - resTop)));
+ resTop += fillUp;
+ mNumUnspecializedRemaining -= fillUp;
+ final int numEj = runEj + mNumPendingJobs.get(WORK_TYPE_EJ);
+ fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+ Math.min(numEj, mConfigNumReservedSlots.get(WORK_TYPE_EJ) - resEj)));
+ resEj += fillUp;
+ mNumUnspecializedRemaining -= fillUp;
+ final int numBg = runBg + mNumPendingJobs.get(WORK_TYPE_BG);
+ fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+ Math.min(numBg, mConfigNumReservedSlots.get(WORK_TYPE_BG) - resBg)));
+ resBg += fillUp;
+ mNumUnspecializedRemaining -= fillUp;
+ final int numBgUser = runBgUser + mNumPendingJobs.get(WORK_TYPE_BGUSER);
+ fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+ Math.min(numBgUser,
+ mConfigNumReservedSlots.get(WORK_TYPE_BGUSER) - resBgUser)));
+ resBgUser += fillUp;
+ mNumUnspecializedRemaining -= fillUp;
+
+ // Step 3
+ int unspecializedAssigned = Math.max(0,
Math.min(mNumUnspecializedRemaining,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP), numTop) - resTop));
mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop + unspecializedAssigned);
mNumUnspecializedRemaining -= unspecializedAssigned;
+
+ unspecializedAssigned = Math.max(0,
+ Math.min(mNumUnspecializedRemaining,
+ Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ), numEj) - resEj));
+ mNumActuallyReservedSlots.put(WORK_TYPE_EJ, resEj + unspecializedAssigned);
+ mNumUnspecializedRemaining -= unspecializedAssigned;
+
unspecializedAssigned = Math.max(0,
Math.min(mNumUnspecializedRemaining,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), numBg) - resBg));
mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg + unspecializedAssigned);
mNumUnspecializedRemaining -= unspecializedAssigned;
+
+ unspecializedAssigned = Math.max(0,
+ Math.min(mNumUnspecializedRemaining,
+ Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER), numBgUser)
+ - resBgUser));
+ mNumActuallyReservedSlots.put(WORK_TYPE_BGUSER, resBgUser + unspecializedAssigned);
+ mNumUnspecializedRemaining -= unspecializedAssigned;
}
int canJobStart(int workTypes) {
@@ -1072,6 +1459,15 @@ class JobConcurrencyManager {
return WORK_TYPE_TOP;
}
}
+ if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) {
+ final int maxAllowed = Math.min(
+ mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ),
+ mNumActuallyReservedSlots.get(WORK_TYPE_EJ) + mNumUnspecializedRemaining);
+ if (mNumRunningJobs.get(WORK_TYPE_EJ) + mNumStartingJobs.get(WORK_TYPE_EJ)
+ < maxAllowed) {
+ return WORK_TYPE_EJ;
+ }
+ }
if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
final int maxAllowed = Math.min(
mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG),
@@ -1081,13 +1477,53 @@ class JobConcurrencyManager {
return WORK_TYPE_BG;
}
}
+ if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) {
+ final int maxAllowed = Math.min(
+ mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER),
+ mNumActuallyReservedSlots.get(WORK_TYPE_BGUSER)
+ + mNumUnspecializedRemaining);
+ if (mNumRunningJobs.get(WORK_TYPE_BGUSER) + mNumStartingJobs.get(WORK_TYPE_BGUSER)
+ < maxAllowed) {
+ return WORK_TYPE_BGUSER;
+ }
+ }
return WORK_TYPE_NONE;
}
+ int canJobStart(int workTypes, @WorkType int replacingWorkType) {
+ final boolean changedNums;
+ int oldNumRunning = mNumRunningJobs.get(replacingWorkType);
+ if (replacingWorkType != WORK_TYPE_NONE && oldNumRunning > 0) {
+ mNumRunningJobs.put(replacingWorkType, oldNumRunning - 1);
+ // Lazy implementation to avoid lots of processing. Best way would be to go
+ // through the whole process of adjusting reservations, but the processing cost
+ // is likely not worth it.
+ mNumUnspecializedRemaining++;
+ changedNums = true;
+ } else {
+ changedNums = false;
+ }
+
+ final int ret = canJobStart(workTypes);
+ if (changedNums) {
+ mNumRunningJobs.put(replacingWorkType, oldNumRunning);
+ mNumUnspecializedRemaining--;
+ }
+ return ret;
+ }
+
+ int getPendingJobCount(@WorkType final int workType) {
+ return mNumPendingJobs.get(workType, 0);
+ }
+
int getRunningJobCount(@WorkType final int workType) {
return mNumRunningJobs.get(workType, 0);
}
+ boolean isOverTypeLimit(@WorkType final int workType) {
+ return getRunningJobCount(workType) > mConfigAbsoluteMaxSlots.get(workType);
+ }
+
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java
index d05034797f3d..6ffac91d7098 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java
@@ -25,6 +25,7 @@ import android.app.job.JobParameters;
import android.os.UserHandle;
import android.text.format.DateFormat;
import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
@@ -33,8 +34,6 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.util.RingBufferIndices;
import com.android.server.job.controllers.JobStatus;
-import java.io.PrintWriter;
-
public final class JobPackageTracker {
// We batch every 30 minutes.
static final long BATCHING_TIME = 30*60*1000;
@@ -294,53 +293,69 @@ public final class JobPackageTracker {
}
}
- void printDuration(PrintWriter pw, long period, long duration, int count, String suffix) {
+ /** Return {@code true} if text was printed. */
+ boolean printDuration(IndentingPrintWriter pw, long period, long duration, int count,
+ String suffix) {
float fraction = duration / (float) period;
int percent = (int) ((fraction * 100) + .5f);
if (percent > 0) {
- pw.print(" ");
pw.print(percent);
pw.print("% ");
pw.print(count);
pw.print("x ");
pw.print(suffix);
+ return true;
} else if (count > 0) {
- pw.print(" ");
pw.print(count);
pw.print("x ");
pw.print(suffix);
+ return true;
}
+
+ return false;
}
- void dump(PrintWriter pw, String header, String prefix, long now, long nowElapsed,
- int filterUid) {
+ void dump(IndentingPrintWriter pw, String header, long now, long nowElapsed,
+ int filterAppId) {
final long period = getTotalTime(now);
- pw.print(prefix); pw.print(header); pw.print(" at ");
+ pw.print(header); pw.print(" at ");
pw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", mStartClockTime).toString());
pw.print(" (");
TimeUtils.formatDuration(mStartElapsedTime, nowElapsed, pw);
pw.print(") over ");
TimeUtils.formatDuration(period, pw);
pw.println(":");
+ pw.increaseIndent();
+ pw.print("Max concurrency: ");
+ pw.print(mMaxTotalActive); pw.print(" total, ");
+ pw.print(mMaxFgActive); pw.println(" foreground");
+
+ pw.println();
final int NE = mEntries.size();
for (int i = 0; i < NE; i++) {
int uid = mEntries.keyAt(i);
- if (filterUid != -1 && filterUid != UserHandle.getAppId(uid)) {
+ if (filterAppId != -1 && filterAppId != UserHandle.getAppId(uid)) {
continue;
}
ArrayMap<String, PackageEntry> uidMap = mEntries.valueAt(i);
final int NP = uidMap.size();
for (int j = 0; j < NP; j++) {
PackageEntry pe = uidMap.valueAt(j);
- pw.print(prefix); pw.print(" ");
UserHandle.formatUid(pw, uid);
pw.print(" / "); pw.print(uidMap.keyAt(j));
pw.println(":");
- pw.print(prefix); pw.print(" ");
- printDuration(pw, period, pe.getPendingTime(now), pe.pendingCount, "pending");
- printDuration(pw, period, pe.getActiveTime(now), pe.activeCount, "active");
- printDuration(pw, period, pe.getActiveTopTime(now), pe.activeTopCount,
- "active-top");
+
+ pw.increaseIndent();
+ if (printDuration(pw, period,
+ pe.getPendingTime(now), pe.pendingCount, "pending")) {
+ pw.print(" ");
+ }
+ if (printDuration(pw, period,
+ pe.getActiveTime(now), pe.activeCount, "active")) {
+ pw.print(" ");
+ }
+ printDuration(pw, period,
+ pe.getActiveTopTime(now), pe.activeTopCount, "active-top");
if (pe.pendingNesting > 0 || pe.hadPending) {
pw.print(" (pending)");
}
@@ -352,7 +367,6 @@ public final class JobPackageTracker {
}
pw.println();
if (pe.stopReasons.size() > 0) {
- pw.print(prefix); pw.print(" ");
for (int k = 0; k < pe.stopReasons.size(); k++) {
if (k > 0) {
pw.print(", ");
@@ -364,11 +378,10 @@ public final class JobPackageTracker {
}
pw.println();
}
+ pw.decreaseIndent();
}
}
- pw.print(prefix); pw.print(" Max concurrency: ");
- pw.print(mMaxTotalActive); pw.print(" total, ");
- pw.print(mMaxFgActive); pw.println(" foreground");
+ pw.decreaseIndent();
}
private void printPackageEntryState(ProtoOutputStream proto, long fieldId,
@@ -520,7 +533,7 @@ public final class JobPackageTracker {
return time / (float)period;
}
- public void dump(PrintWriter pw, String prefix, int filterUid) {
+ void dump(IndentingPrintWriter pw, int filterAppId) {
final long now = sUptimeMillisClock.millis();
final long nowElapsed = sElapsedRealtimeClock.millis();
final DataSet total;
@@ -533,11 +546,11 @@ public final class JobPackageTracker {
mCurDataSet.addTo(total, now);
for (int i = 1; i < mLastDataSets.length; i++) {
if (mLastDataSets[i] != null) {
- mLastDataSets[i].dump(pw, "Historical stats", prefix, now, nowElapsed, filterUid);
+ mLastDataSets[i].dump(pw, "Historical stats", now, nowElapsed, filterAppId);
pw.println();
}
}
- total.dump(pw, "Current stats", prefix, now, nowElapsed, filterUid);
+ total.dump(pw, "Current stats", now, nowElapsed, filterAppId);
}
public void dump(ProtoOutputStream proto, long fieldId, int filterUid) {
@@ -566,17 +579,19 @@ public final class JobPackageTracker {
proto.end(token);
}
- public boolean dumpHistory(PrintWriter pw, String prefix, int filterUid) {
+ boolean dumpHistory(IndentingPrintWriter pw, int filterAppId) {
final int size = mEventIndices.size();
if (size <= 0) {
return false;
}
- pw.println(" Job history:");
+ pw.increaseIndent();
+ pw.println("Job history:");
+ pw.decreaseIndent();
final long now = sElapsedRealtimeClock.millis();
for (int i=0; i<size; i++) {
final int index = mEventIndices.indexOf(i);
final int uid = mEventUids[index];
- if (filterUid != -1 && filterUid != UserHandle.getAppId(uid)) {
+ if (filterAppId != -1 && filterAppId != UserHandle.getAppId(uid)) {
continue;
}
final int cmd = mEventCmds[index] & EVENT_CMD_MASK;
@@ -591,7 +606,6 @@ public final class JobPackageTracker {
case EVENT_STOP_PERIODIC_JOB: label = " STOP-P"; break;
default: label = " ??"; break;
}
- pw.print(prefix);
TimeUtils.formatDuration(mEventTimes[index]-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
pw.print(" ");
pw.print(label);
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 7ce867c6c850..ac6eb3229a25 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -212,7 +212,7 @@ public class JobSchedulerService extends com.android.server.SystemService
final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
final JobConcurrencyManager mConcurrencyManager;
- static final int MSG_JOB_EXPIRED = 0;
+ static final int MSG_CHECK_INDIVIDUAL_JOB = 0;
static final int MSG_CHECK_JOB = 1;
static final int MSG_STOP_JOB = 2;
static final int MSG_CHECK_JOB_GREEDY = 3;
@@ -339,6 +339,7 @@ public class JobSchedulerService extends com.android.server.SystemService
public void onPropertiesChanged(DeviceConfig.Properties properties) {
boolean apiQuotaScheduleUpdated = false;
boolean concurrencyUpdated = false;
+ boolean runtimeUpdated = false;
for (int controller = 0; controller < mControllers.size(); controller++) {
final StateController sc = mControllers.get(controller);
sc.prepareForUpdatedConstantsLocked();
@@ -377,6 +378,14 @@ public class JobSchedulerService extends com.android.server.SystemService
case Constants.KEY_CONN_PREFETCH_RELAX_FRAC:
mConstants.updateConnectivityConstantsLocked();
break;
+ case Constants.KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS:
+ case Constants.KEY_RUNTIME_MIN_GUARANTEE_MS:
+ case Constants.KEY_RUNTIME_MIN_EJ_GUARANTEE_MS:
+ if (!runtimeUpdated) {
+ mConstants.updateRuntimeConstantsLocked();
+ runtimeUpdated = true;
+ }
+ break;
default:
if (name.startsWith(JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY)
&& !concurrencyUpdated) {
@@ -432,6 +441,11 @@ public class JobSchedulerService extends com.android.server.SystemService
private static final String KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT =
"aq_schedule_return_failure";
+ private static final String KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS =
+ "runtime_free_quota_max_limit_ms";
+ private static final String KEY_RUNTIME_MIN_GUARANTEE_MS = "runtime_min_guarantee_ms";
+ private static final String KEY_RUNTIME_MIN_EJ_GUARANTEE_MS = "runtime_min_ej_guarantee_ms";
+
private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5;
private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS;
private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
@@ -445,6 +459,12 @@ public class JobSchedulerService extends com.android.server.SystemService
private static final long DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS = MINUTE_IN_MILLIS;
private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true;
private static final boolean DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false;
+ @VisibleForTesting
+ public static final long DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = 30 * MINUTE_IN_MILLIS;
+ @VisibleForTesting
+ public static final long DEFAULT_RUNTIME_MIN_GUARANTEE_MS = 10 * MINUTE_IN_MILLIS;
+ @VisibleForTesting
+ public static final long DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS = 3 * MINUTE_IN_MILLIS;
/**
* Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
@@ -509,6 +529,19 @@ public class JobSchedulerService extends com.android.server.SystemService
public boolean API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT =
DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT;
+ /** The maximum amount of time we will let a job run for when quota is "free". */
+ public long RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
+
+ /**
+ * The minimum amount of time we try to guarantee regular jobs will run for.
+ */
+ public long RUNTIME_MIN_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_GUARANTEE_MS;
+
+ /**
+ * The minimum amount of time we try to guarantee EJs will run for.
+ */
+ public long RUNTIME_MIN_EJ_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS;
+
private void updateBatchingConstantsLocked() {
MIN_READY_NON_ACTIVE_JOBS_COUNT = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
@@ -568,6 +601,25 @@ public class JobSchedulerService extends com.android.server.SystemService
DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT);
}
+ private void updateRuntimeConstantsLocked() {
+ DeviceConfig.Properties properties = DeviceConfig.getProperties(
+ DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ KEY_RUNTIME_MIN_GUARANTEE_MS, KEY_RUNTIME_MIN_EJ_GUARANTEE_MS);
+
+ // Make sure min runtime for regular jobs is at least 10 minutes.
+ RUNTIME_MIN_GUARANTEE_MS = Math.max(10 * MINUTE_IN_MILLIS,
+ properties.getLong(
+ KEY_RUNTIME_MIN_GUARANTEE_MS, DEFAULT_RUNTIME_MIN_GUARANTEE_MS));
+ // Make sure min runtime for expedited jobs is at least one minute.
+ RUNTIME_MIN_EJ_GUARANTEE_MS = Math.max(MINUTE_IN_MILLIS,
+ properties.getLong(
+ KEY_RUNTIME_MIN_EJ_GUARANTEE_MS, DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS));
+ RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = Math.max(RUNTIME_MIN_GUARANTEE_MS,
+ properties.getLong(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS));
+ }
+
void dump(IndentingPrintWriter pw) {
pw.println("Settings:");
pw.increaseIndent();
@@ -591,6 +643,11 @@ public class JobSchedulerService extends com.android.server.SystemService
pw.print(KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT,
API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT).println();
+ pw.print(KEY_RUNTIME_MIN_GUARANTEE_MS, RUNTIME_MIN_GUARANTEE_MS).println();
+ pw.print(KEY_RUNTIME_MIN_EJ_GUARANTEE_MS, RUNTIME_MIN_EJ_GUARANTEE_MS).println();
+ pw.print(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, RUNTIME_FREE_QUOTA_MAX_LIMIT_MS)
+ .println();
+
pw.decreaseIndent();
}
@@ -743,6 +800,7 @@ public class JobSchedulerService extends com.android.server.SystemService
mControllers.get(c).onUserRemovedLocked(userId);
}
}
+ mConcurrencyManager.onUserRemoved(userId);
} else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
// Has this package scheduled any jobs, such that we will take action
// if it were to be force-stopped?
@@ -1601,7 +1659,7 @@ public class JobSchedulerService extends com.android.server.SystemService
* time of the job to be the time of completion (i.e. the time at which this function is
* called).
* <p>This could be inaccurate b/c the job can run for as long as
- * {@link com.android.server.job.JobServiceContext#DEFAULT_EXECUTING_TIMESLICE_MILLIS}, but
+ * {@link Constants#DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS}, but
* will lead to underscheduling at least, rather than if we had taken the last execution time
* to be the start of the execution.
*
@@ -1710,6 +1768,12 @@ public class JobSchedulerService extends com.android.server.SystemService
if (DEBUG) {
Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
}
+ JobStatus newJs = mJobs.getJobByUidAndJobId(jobStatus.getUid(), jobStatus.getJobId());
+ if (newJs != null) {
+ // This job was stopped because the app scheduled a new job with the same job ID.
+ // Check if the new job is ready to run.
+ mHandler.obtainMessage(MSG_CHECK_INDIVIDUAL_JOB, newJs).sendToTarget();
+ }
return;
}
@@ -1747,7 +1811,11 @@ public class JobSchedulerService extends com.android.server.SystemService
@Override
public void onRunJobNow(JobStatus jobStatus) {
- mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
+ if (jobStatus == null) {
+ mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
+ } else {
+ mHandler.obtainMessage(MSG_CHECK_INDIVIDUAL_JOB, jobStatus).sendToTarget();
+ }
}
final private class JobHandler extends Handler {
@@ -1763,18 +1831,15 @@ public class JobSchedulerService extends com.android.server.SystemService
return;
}
switch (message.what) {
- case MSG_JOB_EXPIRED: {
- JobStatus runNow = (JobStatus) message.obj;
- // runNow can be null, which is a controller's way of indicating that its
- // state is such that all ready jobs should be run immediately.
- if (runNow != null) {
- if (!isCurrentlyActiveLocked(runNow)
- && isReadyToBeExecutedLocked(runNow)) {
- mJobPackageTracker.notePending(runNow);
- addOrderedItem(mPendingJobs, runNow, sPendingJobComparator);
+ case MSG_CHECK_INDIVIDUAL_JOB: {
+ JobStatus js = (JobStatus) message.obj;
+ if (js != null) {
+ if (isReadyToBeExecutedLocked(js)) {
+ mJobPackageTracker.notePending(js);
+ addOrderedItem(mPendingJobs, js, sPendingJobComparator);
}
} else {
- queueReadyJobsForExecutionLocked();
+ Slog.e(TAG, "Given null job to check individually");
}
} break;
case MSG_CHECK_JOB:
@@ -1908,12 +1973,10 @@ public class JobSchedulerService extends com.android.server.SystemService
// This method will check and capture all ready jobs, so we don't need to keep any messages
// in the queue.
mHandler.removeMessages(MSG_CHECK_JOB_GREEDY);
+ mHandler.removeMessages(MSG_CHECK_INDIVIDUAL_JOB);
// MSG_CHECK_JOB is a weaker form of _GREEDY. Since we're checking and queueing all ready
// jobs, we don't need to keep any MSG_CHECK_JOB messages in the queue.
mHandler.removeMessages(MSG_CHECK_JOB);
- // This method will capture all expired jobs that are ready, so there's no need to keep
- // the _EXPIRED messages in the queue.
- mHandler.removeMessages(MSG_JOB_EXPIRED);
if (DEBUG) {
Slog.d(TAG, "queuing all ready jobs for execution:");
}
@@ -1989,8 +2052,11 @@ public class JobSchedulerService extends com.android.server.SystemService
}
final boolean shouldForceBatchJob;
- // Restricted jobs must always be batched
- if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) {
+ if (job.shouldTreatAsExpeditedJob()) {
+ // Never batch expedited jobs, even for RESTRICTED apps.
+ shouldForceBatchJob = false;
+ } else if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) {
+ // Restricted jobs must always be batched
shouldForceBatchJob = true;
} else if (job.getNumFailures() > 0) {
shouldForceBatchJob = false;
@@ -2204,11 +2270,24 @@ public class JobSchedulerService extends com.android.server.SystemService
return isComponentUsable(job);
}
+ /** Returns the minimum amount of time we should let this job run before timing out. */
+ public long getMinJobExecutionGuaranteeMs(JobStatus job) {
+ synchronized (mLock) {
+ if (job.shouldTreatAsExpeditedJob()) {
+ // Don't guarantee RESTRICTED jobs more than 5 minutes.
+ return job.getEffectiveStandbyBucket() != RESTRICTED_INDEX
+ ? mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS
+ : Math.min(mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 5 * MINUTE_IN_MILLIS);
+ } else {
+ return mConstants.RUNTIME_MIN_GUARANTEE_MS;
+ }
+ }
+ }
+
/** Returns the maximum amount of time this job could run for. */
public long getMaxJobExecutionTimeMs(JobStatus job) {
synchronized (mLock) {
- return Math.min(mQuotaController.getMaxJobExecutionTimeMsLocked(job),
- JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS);
+ return mQuotaController.getMaxJobExecutionTimeMsLocked(job);
}
}
@@ -3012,14 +3091,14 @@ public class JobSchedulerService extends com.android.server.SystemService
}
void dumpInternal(final IndentingPrintWriter pw, int filterUid) {
- final int filterUidFinal = UserHandle.getAppId(filterUid);
+ final int filterAppId = UserHandle.getAppId(filterUid);
final long now = sSystemClock.millis();
final long nowElapsed = sElapsedRealtimeClock.millis();
final long nowUptime = sUptimeMillisClock.millis();
final Predicate<JobStatus> predicate = (js) -> {
- return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
- || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
+ return filterAppId == -1 || UserHandle.getAppId(js.getUid()) == filterAppId
+ || UserHandle.getAppId(js.getSourceUid()) == filterAppId;
};
synchronized (mLock) {
mConstants.dump(pw);
@@ -3031,9 +3110,7 @@ public class JobSchedulerService extends com.android.server.SystemService
pw.println();
for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
- pw.print(" ");
mJobRestrictions.get(i).dumpConstants(pw);
- pw.println();
}
pw.println();
@@ -3044,22 +3121,25 @@ public class JobSchedulerService extends com.android.server.SystemService
pw.print("Registered ");
pw.print(mJobs.size());
pw.println(" jobs:");
+ pw.increaseIndent();
+ boolean jobPrinted = false;
if (mJobs.size() > 0) {
final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
sortJobs(jobs);
for (JobStatus job : jobs) {
- pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
- pw.println(job.toShortStringExceptUniqueId());
-
// Skip printing details if the caller requested a filter
if (!predicate.test(job)) {
continue;
}
+ jobPrinted = true;
- job.dump(pw, " ", true, nowElapsed);
+ pw.print("JOB #"); job.printUniqueId(pw); pw.print(": ");
+ pw.println(job.toShortStringExceptUniqueId());
+ pw.increaseIndent();
+ job.dump(pw, true, nowElapsed);
- pw.print(" Restricted due to:");
+ pw.print("Restricted due to:");
final boolean isRestricted = checkIfRestricted(job) != null;
if (isRestricted) {
for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
@@ -3074,7 +3154,7 @@ public class JobSchedulerService extends com.android.server.SystemService
}
pw.println(".");
- pw.print(" Ready: ");
+ pw.print("Ready: ");
pw.print(isReadyToBeExecutedLocked(job));
pw.print(" (job=");
pw.print(job.isReady());
@@ -3091,10 +3171,15 @@ public class JobSchedulerService extends com.android.server.SystemService
pw.print(" comp=");
pw.print(isComponentUsable(job));
pw.println(")");
+
+ pw.decreaseIndent();
}
- } else {
- pw.println(" None.");
}
+ if (!jobPrinted) {
+ pw.println("None.");
+ }
+ pw.decreaseIndent();
+
for (int i=0; i<mControllers.size(); i++) {
pw.println();
pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
@@ -3102,89 +3187,117 @@ public class JobSchedulerService extends com.android.server.SystemService
mControllers.get(i).dumpControllerStateLocked(pw, predicate);
pw.decreaseIndent();
}
- pw.println();
- pw.println("Uid priority overrides:");
+
+ boolean overridePrinted = false;
for (int i=0; i< mUidPriorityOverride.size(); i++) {
int uid = mUidPriorityOverride.keyAt(i);
- if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
- pw.print(" "); pw.print(UserHandle.formatUid(uid));
+ if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
+ if (!overridePrinted) {
+ overridePrinted = true;
+ pw.println();
+ pw.println("Uid priority overrides:");
+ pw.increaseIndent();
+ }
+ pw.print(UserHandle.formatUid(uid));
pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
}
}
- if (mBackingUpUids.size() > 0) {
- pw.println();
- pw.println("Backing up uids:");
- boolean first = true;
- for (int i = 0; i < mBackingUpUids.size(); i++) {
- int uid = mBackingUpUids.keyAt(i);
- if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
- if (first) {
- pw.print(" ");
- first = false;
- } else {
- pw.print(", ");
- }
- pw.print(UserHandle.formatUid(uid));
+ if (overridePrinted) {
+ pw.decreaseIndent();
+ }
+
+ boolean backingPrinted = false;
+ for (int i = 0; i < mBackingUpUids.size(); i++) {
+ int uid = mBackingUpUids.keyAt(i);
+ if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
+ if (!backingPrinted) {
+ pw.println();
+ pw.println("Backing up uids:");
+ pw.increaseIndent();
+ backingPrinted = true;
+ } else {
+ pw.print(", ");
}
+ pw.print(UserHandle.formatUid(uid));
}
+ }
+ if (backingPrinted) {
+ pw.decreaseIndent();
pw.println();
}
+
pw.println();
- mJobPackageTracker.dump(pw, "", filterUidFinal);
+ mJobPackageTracker.dump(pw, filterAppId);
pw.println();
- if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
+ if (mJobPackageTracker.dumpHistory(pw, filterAppId)) {
pw.println();
}
+
+ boolean pendingPrinted = false;
pw.println("Pending queue:");
+ pw.increaseIndent();
for (int i=0; i<mPendingJobs.size(); i++) {
JobStatus job = mPendingJobs.get(i);
- pw.print(" Pending #"); pw.print(i); pw.print(": ");
+ if (!predicate.test(job)) {
+ continue;
+ }
+ if (!pendingPrinted) {
+ pendingPrinted = true;
+ }
+
+ pw.print("Pending #"); pw.print(i); pw.print(": ");
pw.println(job.toShortString());
- job.dump(pw, " ", false, nowElapsed);
+
+ pw.increaseIndent();
+ job.dump(pw, false, nowElapsed);
int priority = evaluateJobPriorityLocked(job);
- pw.print(" Evaluated priority: ");
+ pw.print("Evaluated priority: ");
pw.println(JobInfo.getPriorityString(priority));
- pw.print(" Tag: "); pw.println(job.getTag());
- pw.print(" Enq: ");
+ pw.print("Tag: "); pw.println(job.getTag());
+ pw.print("Enq: ");
TimeUtils.formatDuration(job.madePending - nowUptime, pw);
+ pw.decreaseIndent();
pw.println();
}
+ if (!pendingPrinted) {
+ pw.println("None");
+ }
+ pw.decreaseIndent();
+
pw.println();
pw.println("Active jobs:");
+ pw.increaseIndent();
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext jsc = mActiveServices.get(i);
- pw.print(" Slot #"); pw.print(i); pw.print(": ");
final JobStatus job = jsc.getRunningJobLocked();
- if (job == null) {
- if (jsc.mStoppedReason != null) {
- pw.print("inactive since ");
- TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
- pw.print(", stopped because: ");
- pw.println(jsc.mStoppedReason);
- } else {
- pw.println("inactive");
- }
+
+ if (job != null && !predicate.test(job)) {
continue;
- } else {
- pw.println(job.toShortString());
- pw.print(" Running for: ");
- TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
- pw.print(", timeout at: ");
- TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
- pw.println();
- job.dump(pw, " ", false, nowElapsed);
- int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
- pw.print(" Evaluated priority: ");
- pw.println(JobInfo.getPriorityString(priority));
+ }
+
+ pw.print("Slot #"); pw.print(i); pw.print(": ");
+ jsc.dumpLocked(pw, nowElapsed);
- pw.print(" Active at ");
+ if (job != null) {
+ pw.increaseIndent();
+
+ pw.increaseIndent();
+ job.dump(pw, false, nowElapsed);
+ pw.decreaseIndent();
+
+ pw.print("Evaluated priority: ");
+ pw.println(JobInfo.getPriorityString(job.lastEvaluatedPriority));
+
+ pw.print("Active at ");
TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
pw.print(", pending for ");
TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
+ pw.decreaseIndent();
pw.println();
}
}
+ pw.decreaseIndent();
if (filterUid == -1) {
pw.println();
pw.print("mReadyToRock="); pw.println(mReadyToRock);
@@ -3203,13 +3316,13 @@ public class JobSchedulerService extends com.android.server.SystemService
void dumpInternalProto(final FileDescriptor fd, int filterUid) {
ProtoOutputStream proto = new ProtoOutputStream(fd);
- final int filterUidFinal = UserHandle.getAppId(filterUid);
+ final int filterAppId = UserHandle.getAppId(filterUid);
final long now = sSystemClock.millis();
final long nowElapsed = sElapsedRealtimeClock.millis();
final long nowUptime = sUptimeMillisClock.millis();
final Predicate<JobStatus> predicate = (js) -> {
- return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
- || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
+ return filterAppId == -1 || UserHandle.getAppId(js.getUid()) == filterAppId
+ || UserHandle.getAppId(js.getSourceUid()) == filterAppId;
};
synchronized (mLock) {
@@ -3282,7 +3395,7 @@ public class JobSchedulerService extends com.android.server.SystemService
}
for (int i=0; i< mUidPriorityOverride.size(); i++) {
int uid = mUidPriorityOverride.keyAt(i);
- if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
+ if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
@@ -3292,15 +3405,15 @@ public class JobSchedulerService extends com.android.server.SystemService
}
for (int i = 0; i < mBackingUpUids.size(); i++) {
int uid = mBackingUpUids.keyAt(i);
- if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
+ if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
}
}
mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
- filterUidFinal);
+ filterAppId);
mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
- filterUidFinal);
+ filterAppId);
for (JobStatus job : mPendingJobs) {
final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
@@ -3340,7 +3453,7 @@ public class JobSchedulerService extends com.android.server.SystemService
job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY,
- evaluateJobPriorityLocked(jsc.getRunningJobLocked()));
+ evaluateJobPriorityLocked(job));
proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
nowUptime - job.madeActive);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index d15bae0274ac..be91947b0445 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -17,9 +17,10 @@
package com.android.server.job;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
-import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.job.IJobCallback;
import android.app.job.IJobService;
import android.app.job.JobInfo;
@@ -42,6 +43,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.WorkSource;
import android.util.EventLog;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.TimeUtils;
@@ -74,14 +76,6 @@ public final class JobServiceContext implements ServiceConnection {
private static final boolean DEBUG_STANDBY = JobSchedulerService.DEBUG_STANDBY;
private static final String TAG = "JobServiceContext";
- /** Amount of time a job is allowed to execute for before being considered timed-out. */
- public static final long DEFAULT_EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins.
- /**
- * Amount of time a RESTRICTED expedited job is allowed to execute for before being considered
- * timed-out.
- */
- public static final long DEFAULT_RESTRICTED_EXPEDITED_JOB_EXECUTING_TIMESLICE_MILLIS =
- DEFAULT_EXECUTING_TIMESLICE_MILLIS / 2;
/** Amount of time the JobScheduler waits for the initial service launch+bind. */
private static final long OP_BIND_TIMEOUT_MILLIS = 18 * 1000;
/** Amount of time the JobScheduler will wait for a response from an app for a message. */
@@ -108,6 +102,7 @@ public final class JobServiceContext implements ServiceConnection {
/** Make callbacks to {@link JobSchedulerService} to inform on job completion status. */
private final JobCompletedListener mCompletedListener;
private final JobConcurrencyManager mJobConcurrencyManager;
+ private final JobSchedulerService mService;
/** Used for service binding, etc. */
private final Context mContext;
private final Object mLock;
@@ -147,6 +142,13 @@ public final class JobServiceContext implements ServiceConnection {
private long mExecutionStartTimeElapsed;
/** Track when job will timeout. */
private long mTimeoutElapsed;
+ /**
+ * The minimum amount of time the context will allow the job to run before checking whether to
+ * stop it or not.
+ */
+ private long mMinExecutionGuaranteeMillis;
+ /** The absolute maximum amount of time the job can run */
+ private long mMaxExecutionTimeMillis;
// Debugging: reason this job was last stopped.
public String mStoppedReason;
@@ -188,6 +190,7 @@ public final class JobServiceContext implements ServiceConnection {
IBatteryStats batteryStats, JobPackageTracker tracker, Looper looper) {
mContext = service.getContext();
mLock = service.getLock();
+ mService = service;
mBatteryStats = batteryStats;
mJobPackageTracker = tracker;
mCallbackHandler = new JobServiceHandler(looper);
@@ -237,6 +240,9 @@ public final class JobServiceContext implements ServiceConnection {
isDeadlineExpired, job.shouldTreatAsExpeditedJob(),
triggeredUris, triggeredAuthorities, job.network);
mExecutionStartTimeElapsed = sElapsedRealtimeClock.millis();
+ mMinExecutionGuaranteeMillis = mService.getMinJobExecutionGuaranteeMs(job);
+ mMaxExecutionTimeMillis =
+ Math.max(mService.getMaxJobExecutionTimeMs(job), mMinExecutionGuaranteeMillis);
final long whenDeferred = job.getWhenStandbyDeferred();
if (whenDeferred > 0) {
@@ -326,6 +332,7 @@ public final class JobServiceContext implements ServiceConnection {
/**
* Used externally to query the running job. Will return null if there is no job running.
*/
+ @Nullable
JobStatus getRunningJobLocked() {
return mRunningJob;
}
@@ -349,8 +356,8 @@ public final class JobServiceContext implements ServiceConnection {
}
@GuardedBy("mLock")
- void preemptExecutingJobLocked() {
- doCancelLocked(JobParameters.REASON_PREEMPT, "cancelled due to preemption");
+ void preemptExecutingJobLocked(@NonNull String reason) {
+ doCancelLocked(JobParameters.REASON_PREEMPT, reason);
}
int getPreferredUid() {
@@ -369,6 +376,11 @@ public final class JobServiceContext implements ServiceConnection {
return mTimeoutElapsed;
}
+ boolean isWithinExecutionGuaranteeTime() {
+ return mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis
+ < sElapsedRealtimeClock.millis();
+ }
+
@GuardedBy("mLock")
boolean timeoutIfExecutingLocked(String pkgName, int userId, boolean matchJobId, int jobId,
String reason) {
@@ -604,7 +616,7 @@ public final class JobServiceContext implements ServiceConnection {
}
@GuardedBy("mLock")
- void doCancelLocked(int arg1, String debugReason) {
+ private void doCancelLocked(int stopReasonCode, String debugReason) {
if (mVerb == VERB_FINISHED) {
if (DEBUG) {
Slog.d(TAG,
@@ -612,8 +624,8 @@ public final class JobServiceContext implements ServiceConnection {
}
return;
}
- mParams.setStopReason(arg1, debugReason);
- if (arg1 == JobParameters.REASON_PREEMPT) {
+ mParams.setStopReason(stopReasonCode, debugReason);
+ if (stopReasonCode == JobParameters.REASON_PREEMPT) {
mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
NO_PREFERRED_UID;
}
@@ -764,11 +776,30 @@ public final class JobServiceContext implements ServiceConnection {
closeAndCleanupJobLocked(true /* needsReschedule */, "timed out while stopping");
break;
case VERB_EXECUTING:
- // Not an error - client ran out of time.
- Slog.i(TAG, "Client timed out while executing (no jobFinished received), " +
- "sending onStop: " + getRunningJobNameLocked());
- mParams.setStopReason(JobParameters.REASON_TIMEOUT, "client timed out");
- sendStopMessageLocked("timeout while executing");
+ final long latestStopTimeElapsed =
+ mExecutionStartTimeElapsed + mMaxExecutionTimeMillis;
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ if (nowElapsed >= latestStopTimeElapsed) {
+ // Not an error - client ran out of time.
+ Slog.i(TAG, "Client timed out while executing (no jobFinished received)."
+ + " Sending onStop: " + getRunningJobNameLocked());
+ mParams.setStopReason(JobParameters.REASON_TIMEOUT, "client timed out");
+ sendStopMessageLocked("timeout while executing");
+ } else {
+ // We've given the app the minimum execution time. See if we should stop it or
+ // let it continue running
+ final String reason = mJobConcurrencyManager.shouldStopRunningJobLocked(this);
+ if (reason != null) {
+ Slog.i(TAG, "Stopping client after min execution time: "
+ + getRunningJobNameLocked() + " because " + reason);
+ mParams.setStopReason(JobParameters.REASON_TIMEOUT, reason);
+ sendStopMessageLocked(reason);
+ } else {
+ Slog.i(TAG, "Letting " + getRunningJobNameLocked()
+ + " continue to run past min execution time");
+ scheduleOpTimeOutLocked();
+ }
+ }
break;
default:
Slog.e(TAG, "Handling timeout for an invalid job state: "
@@ -875,10 +906,16 @@ public final class JobServiceContext implements ServiceConnection {
final long timeoutMillis;
switch (mVerb) {
case VERB_EXECUTING:
- timeoutMillis = mRunningJob.shouldTreatAsExpeditedJob()
- && mRunningJob.getStandbyBucket() == RESTRICTED_INDEX
- ? DEFAULT_RESTRICTED_EXPEDITED_JOB_EXECUTING_TIMESLICE_MILLIS
- : DEFAULT_EXECUTING_TIMESLICE_MILLIS;
+ final long earliestStopTimeElapsed =
+ mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis;
+ final long latestStopTimeElapsed =
+ mExecutionStartTimeElapsed + mMaxExecutionTimeMillis;
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ if (nowElapsed < earliestStopTimeElapsed) {
+ timeoutMillis = earliestStopTimeElapsed - nowElapsed;
+ } else {
+ timeoutMillis = latestStopTimeElapsed - nowElapsed;
+ }
break;
case VERB_BINDING:
@@ -899,8 +936,37 @@ public final class JobServiceContext implements ServiceConnection {
mTimeoutElapsed = sElapsedRealtimeClock.millis() + timeoutMillis;
}
-
private void removeOpTimeOutLocked() {
mCallbackHandler.removeMessages(MSG_TIMEOUT);
}
+
+ void dumpLocked(IndentingPrintWriter pw, final long nowElapsed) {
+ if (mRunningJob == null) {
+ if (mStoppedReason != null) {
+ pw.print("inactive since ");
+ TimeUtils.formatDuration(mStoppedTime, nowElapsed, pw);
+ pw.print(", stopped because: ");
+ pw.println(mStoppedReason);
+ } else {
+ pw.println("inactive");
+ }
+ } else {
+ pw.println(mRunningJob.toShortString());
+
+ pw.increaseIndent();
+ pw.print("Running for: ");
+ TimeUtils.formatDuration(nowElapsed - mExecutionStartTimeElapsed, pw);
+ pw.print(", timeout at: ");
+ TimeUtils.formatDuration(mTimeoutElapsed - nowElapsed, pw);
+ pw.println();
+ pw.print("Remaining execution limits: [");
+ TimeUtils.formatDuration(
+ (mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis) - nowElapsed, pw);
+ pw.print(", ");
+ TimeUtils.formatDuration(
+ (mExecutionStartTimeElapsed + mMaxExecutionTimeMillis) - nowElapsed, pw);
+ pw.println("]");
+ pw.decreaseIndent();
+ }
+ }
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index d249f2ae813c..14484ff441ca 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -325,6 +325,8 @@ public final class ConnectivityController extends RestrictingController implemen
*/
private boolean isInsane(JobStatus jobStatus, Network network,
NetworkCapabilities capabilities, Constants constants) {
+ // Use the maximum possible time since it gives us an upper bound, even though the job
+ // could end up stopping earlier.
final long maxJobExecutionTimeMs = mService.getMaxJobExecutionTimeMs(jobStatus);
final long downloadBytes = jobStatus.getEstimatedNetworkDownloadBytes();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
index 50723c7c2841..131a6d4f4791 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
@@ -107,8 +107,6 @@ public final class ContentObserverController extends StateController {
taskStatus.contentObserverJobInstance.mChangedUris.add(uri);
}
}
- taskStatus.changedAuthorities = null;
- taskStatus.changedUris = null;
}
taskStatus.changedAuthorities = null;
taskStatus.changedUris = null;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 539c3c960f2e..6917fb531ac4 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -35,6 +35,7 @@ import android.os.UserHandle;
import android.provider.MediaStore;
import android.text.format.DateFormat;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import android.util.Pair;
import android.util.Slog;
import android.util.TimeUtils;
@@ -1644,14 +1645,18 @@ public final class JobStatus {
}
}
- private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) {
- pw.print(prefix); pw.print(" #"); pw.print(index); pw.print(": #");
+ private void dumpJobWorkItem(IndentingPrintWriter pw, JobWorkItem work, int index) {
+ pw.increaseIndent();
+ pw.print("#"); pw.print(index); pw.print(": #");
pw.print(work.getWorkId()); pw.print(" "); pw.print(work.getDeliveryCount());
pw.print("x "); pw.println(work.getIntent());
if (work.getGrants() != null) {
- pw.print(prefix); pw.println(" URI grants:");
- ((GrantedUriPermissions)work.getGrants()).dump(pw, prefix + " ");
+ pw.println("URI grants:");
+ pw.increaseIndent();
+ ((GrantedUriPermissions) work.getGrants()).dump(pw);
+ pw.decreaseIndent();
}
+ pw.decreaseIndent();
}
private void dumpJobWorkItem(ProtoOutputStream proto, long fieldId, JobWorkItem work) {
@@ -1695,36 +1700,38 @@ public final class JobStatus {
}
// Dumpsys infrastructure
- public void dump(PrintWriter pw, String prefix, boolean full, long elapsedRealtimeMillis) {
- pw.print(prefix); UserHandle.formatUid(pw, callingUid);
+ public void dump(IndentingPrintWriter pw, boolean full, long elapsedRealtimeMillis) {
+ UserHandle.formatUid(pw, callingUid);
pw.print(" tag="); pw.println(tag);
- pw.print(prefix);
+
pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid());
pw.print(" user="); pw.print(getSourceUserId());
pw.print(" pkg="); pw.println(getSourcePackageName());
if (full) {
- pw.print(prefix); pw.println("JobInfo:");
- pw.print(prefix); pw.print(" Service: ");
+ pw.println("JobInfo:");
+ pw.increaseIndent();
+
+ pw.print("Service: ");
pw.println(job.getService().flattenToShortString());
if (job.isPeriodic()) {
- pw.print(prefix); pw.print(" PERIODIC: interval=");
+ pw.print("PERIODIC: interval=");
TimeUtils.formatDuration(job.getIntervalMillis(), pw);
pw.print(" flex="); TimeUtils.formatDuration(job.getFlexMillis(), pw);
pw.println();
}
if (job.isPersisted()) {
- pw.print(prefix); pw.println(" PERSISTED");
+ pw.println("PERSISTED");
}
if (job.getPriority() != 0) {
- pw.print(prefix); pw.print(" Priority: ");
+ pw.print("Priority: ");
pw.println(JobInfo.getPriorityString(job.getPriority()));
}
if (job.getFlags() != 0) {
- pw.print(prefix); pw.print(" Flags: ");
+ pw.print("Flags: ");
pw.println(Integer.toHexString(job.getFlags()));
}
if (getInternalFlags() != 0) {
- pw.print(prefix); pw.print(" Internal flags: ");
+ pw.print("Internal flags: ");
pw.print(Integer.toHexString(getInternalFlags()));
if ((getInternalFlags()&INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) {
@@ -1732,106 +1739,109 @@ public final class JobStatus {
}
pw.println();
}
- pw.print(prefix); pw.print(" Requires: charging=");
+ pw.print("Requires: charging=");
pw.print(job.isRequireCharging()); pw.print(" batteryNotLow=");
pw.print(job.isRequireBatteryNotLow()); pw.print(" deviceIdle=");
pw.println(job.isRequireDeviceIdle());
if (job.getTriggerContentUris() != null) {
- pw.print(prefix); pw.println(" Trigger content URIs:");
+ pw.println("Trigger content URIs:");
+ pw.increaseIndent();
for (int i = 0; i < job.getTriggerContentUris().length; i++) {
JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i];
- pw.print(prefix); pw.print(" ");
pw.print(Integer.toHexString(trig.getFlags()));
pw.print(' '); pw.println(trig.getUri());
}
+ pw.decreaseIndent();
if (job.getTriggerContentUpdateDelay() >= 0) {
- pw.print(prefix); pw.print(" Trigger update delay: ");
+ pw.print("Trigger update delay: ");
TimeUtils.formatDuration(job.getTriggerContentUpdateDelay(), pw);
pw.println();
}
if (job.getTriggerContentMaxDelay() >= 0) {
- pw.print(prefix); pw.print(" Trigger max delay: ");
+ pw.print("Trigger max delay: ");
TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw);
pw.println();
}
}
if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
- pw.print(prefix); pw.print(" Extras: ");
+ pw.print("Extras: ");
pw.println(job.getExtras().toShortString());
}
if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) {
- pw.print(prefix); pw.print(" Transient extras: ");
+ pw.print("Transient extras: ");
pw.println(job.getTransientExtras().toShortString());
}
if (job.getClipData() != null) {
- pw.print(prefix); pw.print(" Clip data: ");
+ pw.print("Clip data: ");
StringBuilder b = new StringBuilder(128);
b.append(job.getClipData());
pw.println(b);
}
if (uriPerms != null) {
- pw.print(prefix); pw.println(" Granted URI permissions:");
- uriPerms.dump(pw, prefix + " ");
+ pw.println("Granted URI permissions:");
+ uriPerms.dump(pw);
}
if (job.getRequiredNetwork() != null) {
- pw.print(prefix); pw.print(" Network type: ");
+ pw.print("Network type: ");
pw.println(job.getRequiredNetwork());
}
if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
- pw.print(prefix); pw.print(" Network download bytes: ");
+ pw.print("Network download bytes: ");
pw.println(mTotalNetworkDownloadBytes);
}
if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
- pw.print(prefix); pw.print(" Network upload bytes: ");
+ pw.print("Network upload bytes: ");
pw.println(mTotalNetworkUploadBytes);
}
if (job.getMinLatencyMillis() != 0) {
- pw.print(prefix); pw.print(" Minimum latency: ");
+ pw.print("Minimum latency: ");
TimeUtils.formatDuration(job.getMinLatencyMillis(), pw);
pw.println();
}
if (job.getMaxExecutionDelayMillis() != 0) {
- pw.print(prefix); pw.print(" Max execution delay: ");
+ pw.print("Max execution delay: ");
TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw);
pw.println();
}
- pw.print(prefix); pw.print(" Backoff: policy="); pw.print(job.getBackoffPolicy());
+ pw.print("Backoff: policy="); pw.print(job.getBackoffPolicy());
pw.print(" initial="); TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw);
pw.println();
if (job.hasEarlyConstraint()) {
- pw.print(prefix); pw.println(" Has early constraint");
+ pw.println("Has early constraint");
}
if (job.hasLateConstraint()) {
- pw.print(prefix); pw.println(" Has late constraint");
+ pw.println("Has late constraint");
}
+
+ pw.decreaseIndent();
}
- pw.print(prefix); pw.print("Required constraints:");
+
+ pw.print("Required constraints:");
dumpConstraints(pw, requiredConstraints);
pw.println();
- pw.print(prefix);
pw.print("Dynamic constraints:");
dumpConstraints(pw, mDynamicConstraints);
pw.println();
if (full) {
- pw.print(prefix); pw.print("Satisfied constraints:");
+ pw.print("Satisfied constraints:");
dumpConstraints(pw, satisfiedConstraints);
pw.println();
- pw.print(prefix); pw.print("Unsatisfied constraints:");
+ pw.print("Unsatisfied constraints:");
dumpConstraints(pw,
((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints));
pw.println();
if (dozeWhitelisted) {
- pw.print(prefix); pw.println("Doze whitelisted: true");
+ pw.println("Doze whitelisted: true");
}
if (uidActive) {
- pw.print(prefix); pw.println("Uid: active");
+ pw.println("Uid: active");
}
if (job.isExemptedFromAppStandby()) {
- pw.print(prefix); pw.println("Is exempted from app standby");
+ pw.println("Is exempted from app standby");
}
}
if (trackingControllers != 0) {
- pw.print(prefix); pw.print("Tracking:");
+ pw.print("Tracking:");
if ((trackingControllers&TRACKING_BATTERY) != 0) pw.print(" BATTERY");
if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) pw.print(" CONNECTIVITY");
if ((trackingControllers&TRACKING_CONTENT) != 0) pw.print(" CONTENT");
@@ -1842,76 +1852,78 @@ public final class JobStatus {
pw.println();
}
- pw.print(prefix); pw.println("Implicit constraints:");
- pw.print(prefix); pw.print(" readyNotDozing: ");
+ pw.println("Implicit constraints:");
+ pw.increaseIndent();
+ pw.print("readyNotDozing: ");
pw.println(mReadyNotDozing);
- pw.print(prefix); pw.print(" readyNotRestrictedInBg: ");
+ pw.print("readyNotRestrictedInBg: ");
pw.println(mReadyNotRestrictedInBg);
if (!job.isPeriodic() && hasDeadlineConstraint()) {
- pw.print(prefix); pw.print(" readyDeadlineSatisfied: ");
+ pw.print("readyDeadlineSatisfied: ");
pw.println(mReadyDeadlineSatisfied);
}
if (mDynamicConstraints != 0) {
- pw.print(prefix);
- pw.print(" readyDynamicSatisfied: ");
+ pw.print("readyDynamicSatisfied: ");
pw.println(mReadyDynamicSatisfied);
}
- pw.print(prefix);
- pw.print(" readyComponentEnabled: ");
+ pw.print("readyComponentEnabled: ");
pw.println(serviceInfo != null);
if ((getFlags() & JobInfo.FLAG_EXPEDITED) != 0) {
- pw.print(prefix);
- pw.print(" mReadyWithinExpeditedQuota: ");
+ pw.print("readyWithinExpeditedQuota: ");
pw.println(mReadyWithinExpeditedQuota);
}
+ pw.decreaseIndent();
if (changedAuthorities != null) {
- pw.print(prefix); pw.println("Changed authorities:");
+ pw.println("Changed authorities:");
+ pw.increaseIndent();
for (int i=0; i<changedAuthorities.size(); i++) {
- pw.print(prefix); pw.print(" "); pw.println(changedAuthorities.valueAt(i));
+ pw.println(changedAuthorities.valueAt(i));
}
+ pw.decreaseIndent();
}
if (changedUris != null) {
- pw.print(prefix);
pw.println("Changed URIs:");
+ pw.increaseIndent();
for (int i = 0; i < changedUris.size(); i++) {
- pw.print(prefix);
- pw.print(" ");
pw.println(changedUris.valueAt(i));
}
+ pw.decreaseIndent();
}
if (network != null) {
- pw.print(prefix); pw.print("Network: "); pw.println(network);
+ pw.print("Network: "); pw.println(network);
}
if (pendingWork != null && pendingWork.size() > 0) {
- pw.print(prefix); pw.println("Pending work:");
+ pw.println("Pending work:");
for (int i = 0; i < pendingWork.size(); i++) {
- dumpJobWorkItem(pw, prefix, pendingWork.get(i), i);
+ dumpJobWorkItem(pw, pendingWork.get(i), i);
}
}
if (executingWork != null && executingWork.size() > 0) {
- pw.print(prefix); pw.println("Executing work:");
+ pw.println("Executing work:");
for (int i = 0; i < executingWork.size(); i++) {
- dumpJobWorkItem(pw, prefix, executingWork.get(i), i);
+ dumpJobWorkItem(pw, executingWork.get(i), i);
}
}
- pw.print(prefix); pw.print("Standby bucket: ");
+ pw.print("Standby bucket: ");
pw.println(getBucketName());
+ pw.increaseIndent();
if (whenStandbyDeferred != 0) {
- pw.print(prefix); pw.print(" Deferred since: ");
+ pw.print("Deferred since: ");
TimeUtils.formatDuration(whenStandbyDeferred, elapsedRealtimeMillis, pw);
pw.println();
}
if (mFirstForceBatchedTimeElapsed != 0) {
- pw.print(prefix);
- pw.print(" Time since first force batch attempt: ");
+ pw.print("Time since first force batch attempt: ");
TimeUtils.formatDuration(mFirstForceBatchedTimeElapsed, elapsedRealtimeMillis, pw);
pw.println();
}
- pw.print(prefix); pw.print("Enqueue time: ");
+ pw.decreaseIndent();
+
+ pw.print("Enqueue time: ");
TimeUtils.formatDuration(enqueueTime, elapsedRealtimeMillis, pw);
pw.println();
- pw.print(prefix); pw.print("Run time: earliest=");
+ pw.print("Run time: earliest=");
formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, elapsedRealtimeMillis);
pw.print(", latest=");
formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, elapsedRealtimeMillis);
@@ -1920,14 +1932,14 @@ public final class JobStatus {
NO_LATEST_RUNTIME, elapsedRealtimeMillis);
pw.println();
if (numFailures != 0) {
- pw.print(prefix); pw.print("Num failures: "); pw.println(numFailures);
+ pw.print("Num failures: "); pw.println(numFailures);
}
if (mLastSuccessfulRunTime != 0) {
- pw.print(prefix); pw.print("Last successful run: ");
+ pw.print("Last successful run: ");
pw.println(formatTime(mLastSuccessfulRunTime));
}
if (mLastFailedRunTime != 0) {
- pw.print(prefix); pw.print("Last failed run: ");
+ pw.print("Last failed run: ");
pw.println(formatTime(mLastFailedRunTime));
}
}
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 f2d10ac0f7d7..2196b16e0846 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
@@ -72,7 +72,6 @@ import com.android.server.LocalServices;
import com.android.server.PowerAllowlistInternal;
import com.android.server.job.ConstantsProto;
import com.android.server.job.JobSchedulerService;
-import com.android.server.job.JobServiceContext;
import com.android.server.job.StateControllerProto;
import com.android.server.usage.AppStandbyInternal;
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
@@ -770,18 +769,38 @@ public final class QuotaController extends StateController {
/** Returns the maximum amount of time this job could run for. */
public long getMaxJobExecutionTimeMsLocked(@NonNull final JobStatus jobStatus) {
- // If quota is currently "free", then the job can run for the full amount of time.
- if (mChargeTracker.isCharging()
- || isTopStartedJobLocked(jobStatus)
- || isUidInForeground(jobStatus.getSourceUid())) {
- return JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS;
- }
- if (jobStatus.shouldTreatAsExpeditedJob()) {
- return jobStatus.getStandbyBucket() == RESTRICTED_INDEX
- ? JobServiceContext.DEFAULT_RESTRICTED_EXPEDITED_JOB_EXECUTING_TIMESLICE_MILLIS
- : JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS;
- }
- return getRemainingExecutionTimeLocked(jobStatus);
+ // Need to look at current proc state as well in the case where the job hasn't started yet.
+ final boolean isTop = mActivityManagerInternal
+ .getUidProcessState(jobStatus.getSourceUid()) <= ActivityManager.PROCESS_STATE_TOP;
+
+ if (!jobStatus.shouldTreatAsExpeditedJob()) {
+ // If quota is currently "free", then the job can run for the full amount of time.
+ if (mChargeTracker.isCharging()
+ || isTop
+ || isTopStartedJobLocked(jobStatus)
+ || isUidInForeground(jobStatus.getSourceUid())) {
+ return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
+ }
+ return getTimeUntilQuotaConsumedLocked(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
+ }
+
+ // Expedited job.
+ if (mChargeTracker.isCharging()) {
+ return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
+ }
+ if (isTop || isTopStartedJobLocked(jobStatus)) {
+ return Math.max(mEJLimitsMs[ACTIVE_INDEX] / 2,
+ getTimeUntilEJQuotaConsumedLocked(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()));
+ }
+ if (isUidInForeground(jobStatus.getSourceUid())) {
+ return Math.max(mEJLimitsMs[WORKING_INDEX] / 2,
+ getTimeUntilEJQuotaConsumedLocked(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()));
+ }
+ return getTimeUntilEJQuotaConsumedLocked(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
}
/** @return true if the job is within expedited job quota. */
@@ -3577,8 +3596,8 @@ public final class QuotaController extends StateController {
mEJLimitsMs[RARE_INDEX] = newRareLimitMs;
mShouldReevaluateConstraints = true;
}
- // The limit must be in the range [0 minutes, rare limit].
- long newRestrictedLimitMs = Math.max(0,
+ // The limit must be in the range [5 minutes, rare limit].
+ long newRestrictedLimitMs = Math.max(5 * MINUTE_IN_MILLIS,
Math.min(newRareLimitMs, EJ_LIMIT_RESTRICTED_MS));
if (mEJLimitsMs[RESTRICTED_INDEX] != newRestrictedLimitMs) {
mEJLimitsMs[RESTRICTED_INDEX] = newRestrictedLimitMs;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
index 40c8ce0d5c89..954a5b8bdaa8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
@@ -64,7 +64,7 @@ public class ThermalStatusRestriction extends JobRestriction {
@Override
public void dumpConstants(IndentingPrintWriter pw) {
pw.print("In thermal throttling?: ");
- pw.print(mIsThermalRestricted);
+ pw.println(mIsThermalRestricted);
}
@Override
diff --git a/apex/media/Android.bp b/apex/media/Android.bp
index 5f1bd374df00..a75f1aed4ade 100644
--- a/apex/media/Android.bp
+++ b/apex/media/Android.bp
@@ -17,4 +17,17 @@ package {
"//frameworks/av/apex",
"//frameworks/av/apex/testing",
],
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+sdk {
+ name: "media-module-sdk",
+ java_sdk_libs: [
+ "framework-media",
+ ],
}
diff --git a/apex/media/aidl/Android.bp b/apex/media/aidl/Android.bp
index c2b00d5849c4..545a0cd884dc 100644
--- a/apex/media/aidl/Android.bp
+++ b/apex/media/aidl/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "stable-media-aidl-srcs",
srcs: ["stable/**/*.aidl"],
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 5773e4de3f4e..b18a22b408f5 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "updatable-media",
diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt
index 8b9990f9e833..a2366df0660a 100644
--- a/apex/media/framework/api/current.txt
+++ b/apex/media/framework/api/current.txt
@@ -8,9 +8,10 @@ package android.media {
method @NonNull public java.util.List<java.lang.String> getSupportedVideoMimeTypes();
method @NonNull public java.util.List<java.lang.String> getUnsupportedHdrTypes();
method @NonNull public java.util.List<java.lang.String> getUnsupportedVideoMimeTypes();
- method public boolean isHdrTypeSupported(@NonNull String) throws android.media.ApplicationMediaCapabilities.FormatNotFoundException;
+ method public boolean isFormatSpecified(@NonNull String);
+ method public boolean isHdrTypeSupported(@NonNull String);
method public boolean isSlowMotionSupported();
- method public boolean isVideoMimeTypeSupported(@NonNull String) throws android.media.ApplicationMediaCapabilities.FormatNotFoundException;
+ method public boolean isVideoMimeTypeSupported(@NonNull String);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.ApplicationMediaCapabilities> CREATOR;
}
@@ -24,11 +25,8 @@ package android.media {
method @NonNull public android.media.ApplicationMediaCapabilities build();
}
- public static class ApplicationMediaCapabilities.FormatNotFoundException extends android.util.AndroidException {
- ctor public ApplicationMediaCapabilities.FormatNotFoundException(@NonNull String);
- }
-
public class MediaCommunicationManager {
+ method @IntRange(from=1) public int getVersion();
}
public class MediaController2 implements java.lang.AutoCloseable {
diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
index aefeab621778..685cf0dc7f77 100644
--- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
+++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
@@ -22,7 +22,6 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.AndroidException;
import android.util.Log;
import org.xmlpull.v1.XmlPullParser;
@@ -79,17 +78,7 @@ import java.util.Set;
public final class ApplicationMediaCapabilities implements Parcelable {
private static final String TAG = "ApplicationMediaCapabilities";
- /**
- * This exception is thrown when a given format is not specified in the media capabilities.
- */
- public static class FormatNotFoundException extends AndroidException {
- public FormatNotFoundException(@NonNull String format) {
- super(format);
- }
- }
-
/** List of supported video codec mime types. */
- // TODO: init it with avc and mpeg4 as application is assuming to support them.
private Set<String> mSupportedVideoMimeTypes = new HashSet<>();
/** List of unsupported video codec mime types. */
@@ -113,39 +102,54 @@ public final class ApplicationMediaCapabilities implements Parcelable {
/**
* Query if a video codec format is supported by the application.
+ * <p>
+ * If the application has not specified supporting the format or not, this will return false.
+ * Use {@link #isFormatSpecified(String)} to query if a format is specified or not.
+ *
* @param videoMime The mime type of the video codec format. Must be the one used in
* {@link MediaFormat#KEY_MIME}.
* @return true if application supports the video codec format, false otherwise.
- * @throws FormatNotFoundException if the application did not specify the codec either in the
- * supported or unsupported formats.
*/
public boolean isVideoMimeTypeSupported(
- @NonNull String videoMime) throws FormatNotFoundException {
- if (mUnsupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
- return false;
- } else if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
+ @NonNull String videoMime) {
+ if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
return true;
- } else {
- throw new FormatNotFoundException(videoMime);
}
+ return false;
}
/**
* Query if a HDR type is supported by the application.
+ * <p>
+ * If the application has not specified supporting the format or not, this will return false.
+ * Use {@link #isFormatSpecified(String)} to query if a format is specified or not.
+ *
* @param hdrType The type of the HDR format.
* @return true if application supports the HDR format, false otherwise.
- * @throws FormatNotFoundException if the application did not specify the format either in the
- * supported or unsupported formats.
*/
public boolean isHdrTypeSupported(
- @NonNull @MediaFeature.MediaHdrType String hdrType) throws FormatNotFoundException {
- if (mUnsupportedHdrTypes.contains(hdrType)) {
- return false;
- } else if (mSupportedHdrTypes.contains(hdrType)) {
+ @NonNull @MediaFeature.MediaHdrType String hdrType) {
+ if (mSupportedHdrTypes.contains(hdrType)) {
return true;
- } else {
- throw new FormatNotFoundException(hdrType);
}
+ return false;
+ }
+
+ /**
+ * Query if a format is specified by the application.
+ * <p>
+ * The format could be either the video format or the hdr format.
+ *
+ * @param format The name of the format.
+ * @return true if application specifies the format, false otherwise.
+ */
+ public boolean isFormatSpecified(@NonNull String format) {
+ if (mSupportedVideoMimeTypes.contains(format) || mUnsupportedVideoMimeTypes.contains(format)
+ || mSupportedHdrTypes.contains(format) || mUnsupportedHdrTypes.contains(format)) {
+ return true;
+
+ }
+ return false;
}
@Override
diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java
index b8065ef8c597..e686076c871c 100644
--- a/apex/media/framework/java/android/media/MediaCommunicationManager.java
+++ b/apex/media/framework/java/android/media/MediaCommunicationManager.java
@@ -15,6 +15,7 @@
*/
package android.media;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemService;
import android.content.Context;
@@ -30,6 +31,16 @@ import com.android.modules.utils.build.SdkLevel;
public class MediaCommunicationManager {
private static final String TAG = "MediaCommunicationManager";
+ /**
+ * The manager version used from beginning.
+ */
+ private static final int VERSION_1 = 1;
+
+ /**
+ * Current manager version.
+ */
+ private static final int CURRENT_VERSION = VERSION_1;
+
private final Context mContext;
private final IMediaCommunicationService mService;
@@ -43,7 +54,14 @@ public class MediaCommunicationManager {
mContext = context;
mService = IMediaCommunicationService.Stub.asInterface(
MediaFrameworkInitializer.getMediaServiceManager()
- .getMediaCommunicationServiceRegisterer()
- .get());
+ .getMediaCommunicationServiceRegisterer()
+ .get());
+ }
+
+ /**
+ * Gets the version of this {@link MediaCommunicationManager}.
+ */
+ public @IntRange(from = 1) int getVersion() {
+ return CURRENT_VERSION;
}
}
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java
index ce7726a32152..ca5aeb88bf33 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -295,8 +295,8 @@ public final class MediaTranscodeManager {
/* ignore */
}
}
-
- throw new UnsupportedOperationException("Failed to connect to MediaTranscoding service");
+ Log.w(TAG, "Failed to get service");
+ return null;
}
/*
@@ -463,8 +463,7 @@ public final class MediaTranscodeManager {
}
};
- private ITranscodingClient registerClient(IMediaTranscodingService service)
- throws UnsupportedOperationException {
+ private ITranscodingClient registerClient(IMediaTranscodingService service) {
synchronized (mLock) {
try {
// Registers the client with MediaTranscoding service.
@@ -476,13 +475,12 @@ public final class MediaTranscodeManager {
if (mTranscodingClient != null) {
mTranscodingClient.asBinder().linkToDeath(() -> onClientDied(), /* flags */ 0);
}
- return mTranscodingClient;
- } catch (RemoteException re) {
- Log.e(TAG, "Failed to register new client due to exception " + re);
+ } catch (Exception ex) {
+ Log.e(TAG, "Failed to register new client due to exception " + ex);
mTranscodingClient = null;
}
}
- throw new UnsupportedOperationException("Failed to register new client");
+ return mTranscodingClient;
}
/**
@@ -495,7 +493,9 @@ public final class MediaTranscodeManager {
mUid = Os.getuid();
mPid = Os.getpid();
IMediaTranscodingService service = getService(false /*retry*/);
- mTranscodingClient = registerClient(service);
+ if (service != null) {
+ mTranscodingClient = registerClient(service);
+ }
}
public static final class TranscodingRequest {
@@ -1062,14 +1062,8 @@ public final class MediaTranscodeManager {
"Source video format hint must be set!");
}
- boolean supportHevc = false;
- try {
- supportHevc = mClientCaps.isVideoMimeTypeSupported(
- MediaFormat.MIMETYPE_VIDEO_HEVC);
- } catch (ApplicationMediaCapabilities.FormatNotFoundException ex) {
- // Set to false if application did not specify.
- supportHevc = false;
- }
+ boolean supportHevc = mClientCaps.isVideoMimeTypeSupported(
+ MediaFormat.MIMETYPE_VIDEO_HEVC);
if (!supportHevc && MediaFormat.MIMETYPE_VIDEO_HEVC.equals(
mSrcVideoFormatHint.getString(MediaFormat.KEY_MIME))) {
return true;
@@ -1573,6 +1567,10 @@ public final class MediaTranscodeManager {
if (mTranscodingClient == null) {
// Try to register with the service again.
IMediaTranscodingService service = getService(false /*retry*/);
+ if (service == null) {
+ throw new MediaTranscodingException.ServiceNotAvailableException(
+ "Service rebooting. Try again later");
+ }
mTranscodingClient = registerClient(service);
// If still fails, throws an exception to tell client to try later.
if (mTranscodingClient == null) {
diff --git a/api/Android.bp b/api/Android.bp
index 69dce979748e..15c1dfcfe153 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -14,6 +14,14 @@
package {
default_visibility: ["//visibility:private"],
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-MIT
+ // SPDX-license-identifier-Unicode-DFS
+ default_applicable_licenses: ["frameworks_base_license"],
}
genrule {
@@ -28,6 +36,7 @@ genrule {
genrule {
name: "frameworks-base-api-current.txt",
srcs: [
+ ":android.net.ipsec.ike{.public.api.txt}",
":art.module.public.api{.public.api.txt}",
":conscrypt.module.public.api{.public.api.txt}",
":framework-appsearch{.public.api.txt}",
@@ -36,6 +45,7 @@ genrule {
":framework-mediaprovider{.public.api.txt}",
":framework-permission{.public.api.txt}",
":framework-permission-s{.public.api.txt}",
+ ":framework-scheduling{.public.api.txt}",
":framework-sdkextensions{.public.api.txt}",
":framework-statsd{.public.api.txt}",
":framework-tethering{.public.api.txt}",
@@ -62,8 +72,25 @@ genrule {
}
genrule {
+ name: "frameworks-base-api-current-compat",
+ srcs: [
+ ":android.api.public.latest",
+ ":android-incompatibilities.api.public.latest",
+ ":frameworks-base-api-current.txt",
+ ],
+ out: ["stdout.txt"],
+ tools: ["metalava"],
+ cmd: "$(location metalava) --no-banner --format=v2 " +
+ "--check-compatibility:api:released $(location :android.api.public.latest) " +
+ "--baseline:compatibility:released $(location :android-incompatibilities.api.public.latest) " +
+ "$(location :frameworks-base-api-current.txt) " +
+ "> $(genDir)/stdout.txt",
+}
+
+genrule {
name: "frameworks-base-api-current.srcjar",
srcs: [
+ ":android.net.ipsec.ike{.public.stubs.source}",
":api-stubs-docs-non-updatable",
":art.module.public.api{.public.stubs.source}",
":conscrypt.module.public.api{.public.stubs.source}",
@@ -73,6 +100,7 @@ genrule {
":framework-mediaprovider{.public.stubs.source}",
":framework-permission{.public.stubs.source}",
":framework-permission-s{.public.stubs.source}",
+ ":framework-scheduling{.public.stubs.source}",
":framework-sdkextensions{.public.stubs.source}",
":framework-statsd{.public.stubs.source}",
":framework-tethering{.public.stubs.source}",
@@ -88,6 +116,7 @@ genrule {
genrule {
name: "frameworks-base-api-removed.txt",
srcs: [
+ ":android.net.ipsec.ike{.public.removed-api.txt}",
":art.module.public.api{.public.removed-api.txt}",
":conscrypt.module.public.api{.public.removed-api.txt}",
":framework-appsearch{.public.removed-api.txt}",
@@ -96,6 +125,7 @@ genrule {
":framework-mediaprovider{.public.removed-api.txt}",
":framework-permission{.public.removed-api.txt}",
":framework-permission-s{.public.removed-api.txt}",
+ ":framework-scheduling{.public.removed-api.txt}",
":framework-sdkextensions{.public.removed-api.txt}",
":framework-statsd{.public.removed-api.txt}",
":framework-tethering{.public.removed-api.txt}",
@@ -123,12 +153,14 @@ genrule {
genrule {
name: "frameworks-base-api-system-current.txt",
srcs: [
+ ":android.net.ipsec.ike{.system.api.txt}",
":framework-appsearch{.system.api.txt}",
":framework-graphics{.system.api.txt}",
":framework-media{.system.api.txt}",
":framework-mediaprovider{.system.api.txt}",
":framework-permission{.system.api.txt}",
":framework-permission-s{.system.api.txt}",
+ ":framework-scheduling{.system.api.txt}",
":framework-sdkextensions{.system.api.txt}",
":framework-statsd{.system.api.txt}",
":framework-tethering{.system.api.txt}",
@@ -154,14 +186,34 @@ genrule {
}
genrule {
+ name: "frameworks-base-api-system-current-compat",
+ srcs: [
+ ":android.api.system.latest",
+ ":android-incompatibilities.api.system.latest",
+ ":frameworks-base-api-current.txt",
+ ":frameworks-base-api-system-current.txt",
+ ],
+ out: ["stdout.txt"],
+ tools: ["metalava"],
+ cmd: "$(location metalava) --no-banner --format=v2 " +
+ "--check-compatibility:api:released $(location :android.api.system.latest) " +
+ "--check-compatibility:base $(location :frameworks-base-api-current.txt) " +
+ "--baseline:compatibility:released $(location :android-incompatibilities.api.system.latest) " +
+ "$(location :frameworks-base-api-system-current.txt) " +
+ "> $(genDir)/stdout.txt",
+}
+
+genrule {
name: "frameworks-base-api-system-removed.txt",
srcs: [
+ ":android.net.ipsec.ike{.system.removed-api.txt}",
":framework-appsearch{.system.removed-api.txt}",
":framework-graphics{.system.removed-api.txt}",
":framework-media{.system.removed-api.txt}",
":framework-mediaprovider{.system.removed-api.txt}",
":framework-permission{.system.removed-api.txt}",
":framework-permission-s{.system.removed-api.txt}",
+ ":framework-scheduling{.system.removed-api.txt}",
":framework-sdkextensions{.system.removed-api.txt}",
":framework-statsd{.system.removed-api.txt}",
":framework-tethering{.system.removed-api.txt}",
@@ -189,12 +241,14 @@ genrule {
genrule {
name: "frameworks-base-api-module-lib-current.txt",
srcs: [
+ ":android.net.ipsec.ike{.module-lib.api.txt}",
":framework-appsearch{.module-lib.api.txt}",
":framework-graphics{.module-lib.api.txt}",
":framework-media{.module-lib.api.txt}",
":framework-mediaprovider{.module-lib.api.txt}",
":framework-permission{.module-lib.api.txt}",
":framework-permission-s{.module-lib.api.txt}",
+ ":framework-scheduling{.module-lib.api.txt}",
":framework-sdkextensions{.module-lib.api.txt}",
":framework-statsd{.module-lib.api.txt}",
":framework-tethering{.module-lib.api.txt}",
@@ -219,14 +273,37 @@ genrule {
}
genrule {
+ name: "frameworks-base-api-module-lib-current-compat",
+ srcs: [
+ ":android.api.module-lib.latest",
+ ":android-incompatibilities.api.module-lib.latest",
+ ":frameworks-base-api-current.txt",
+ ":frameworks-base-api-module-lib-current.txt",
+ ],
+ out: ["stdout.txt"],
+ tools: ["metalava"],
+ cmd: "$(location metalava) --no-banner --format=v2 " +
+ "--check-compatibility:api:released $(location :android.api.module-lib.latest) " +
+ // Note: having "public" be the base of module-lib is not perfect -- it should
+ // ideally be a merged public+system), but this will help when migrating from
+ // MODULE_LIBS -> public.
+ "--check-compatibility:base $(location :frameworks-base-api-current.txt) " +
+ "--baseline:compatibility:released $(location :android-incompatibilities.api.module-lib.latest) " +
+ "$(location :frameworks-base-api-module-lib-current.txt) " +
+ "> $(genDir)/stdout.txt",
+}
+
+genrule {
name: "frameworks-base-api-module-lib-removed.txt",
srcs: [
+ ":android.net.ipsec.ike{.module-lib.removed-api.txt}",
":framework-appsearch{.module-lib.removed-api.txt}",
":framework-graphics{.module-lib.removed-api.txt}",
":framework-media{.module-lib.removed-api.txt}",
":framework-mediaprovider{.module-lib.removed-api.txt}",
":framework-permission{.module-lib.removed-api.txt}",
":framework-permission-s{.module-lib.removed-api.txt}",
+ ":framework-scheduling{.module-lib.removed-api.txt}",
":framework-sdkextensions{.module-lib.removed-api.txt}",
":framework-statsd{.module-lib.removed-api.txt}",
":framework-tethering{.module-lib.removed-api.txt}",
diff --git a/boot/Android.bp b/boot/Android.bp
index dd4066a7d151..4f7c44eca818 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -12,6 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-MIT
+ // SPDX-license-identifier-Unicode-DFS
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
boot_image {
name: "framework-boot-image",
image_name: "boot",
diff --git a/cmds/am/Android.bp b/cmds/am/Android.bp
index ed73d5596859..7bb9675ede37 100644
--- a/cmds/am/Android.bp
+++ b/cmds/am/Android.bp
@@ -1,6 +1,23 @@
// Copyright 2008 The Android Open Source Project
//
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_am_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_am_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
cc_library_host_static {
name: "libinstrumentation",
srcs: ["**/*.proto"],
diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp
index ba3eea70838c..73949b9ccf85 100644
--- a/cmds/app_process/Android.bp
+++ b/cmds/app_process/Android.bp
@@ -1,3 +1,20 @@
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_app_process_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_app_process_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
cc_binary {
name: "app_process",
diff --git a/cmds/appops/Android.bp b/cmds/appops/Android.bp
index 9f330fa3bb51..80f8ec679247 100644
--- a/cmds/appops/Android.bp
+++ b/cmds/appops/Android.bp
@@ -1,5 +1,22 @@
// Copyright 2014 The Android Open Source Project
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_appops_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_appops_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
sh_binary {
name: "appops",
src: "appops",
diff --git a/cmds/appwidget/Android.bp b/cmds/appwidget/Android.bp
index 487d3e1fc25f..8049038de809 100644
--- a/cmds/appwidget/Android.bp
+++ b/cmds/appwidget/Android.bp
@@ -1,5 +1,22 @@
// Copyright 2014 The Android Open Source Project
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_appwidget_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_appwidget_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
java_binary {
name: "appwidget",
wrapper: "appwidget",
diff --git a/cmds/backup/Android.bp b/cmds/backup/Android.bp
index 287c23b27fbb..5e2a56e6a9cd 100644
--- a/cmds/backup/Android.bp
+++ b/cmds/backup/Android.bp
@@ -1,5 +1,22 @@
// Copyright 2009 The Android Open Source Project
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_backup_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_backup_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
cc_binary {
name: "btool",
diff --git a/cmds/bmgr/Android.bp b/cmds/bmgr/Android.bp
index b64923bcbe1b..14beb5561dba 100644
--- a/cmds/bmgr/Android.bp
+++ b/cmds/bmgr/Android.bp
@@ -1,6 +1,23 @@
// Copyright 2007 The Android Open Source Project
//
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_bmgr_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_bmgr_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
java_binary {
name: "bmgr",
wrapper: "bmgr",
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index 0f569971ff62..b2b66c27f795 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_defaults {
name: "bootanimation_defaults",
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 6d23323c759c..b73e6bf55ebc 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -43,7 +43,7 @@
#include <android-base/properties.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -65,6 +65,8 @@
namespace android {
+using ui::DisplayMode;
+
static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
static const char PRODUCT_BOOTANIMATION_DARK_FILE[] = "/product/media/bootanimation-dark.zip";
static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip";
@@ -347,14 +349,14 @@ public:
continue;
}
- DisplayConfig displayConfig;
- const status_t error = SurfaceComposerClient::getActiveDisplayConfig(
- mBootAnimation->mDisplayToken, &displayConfig);
+ DisplayMode displayMode;
+ const status_t error = SurfaceComposerClient::getActiveDisplayMode(
+ mBootAnimation->mDisplayToken, &displayMode);
if (error != NO_ERROR) {
- SLOGE("Can't get active display configuration.");
+ SLOGE("Can't get active display mode.");
}
- mBootAnimation->resizeSurface(displayConfig.resolution.getWidth(),
- displayConfig.resolution.getHeight());
+ mBootAnimation->resizeSurface(displayMode.resolution.getWidth(),
+ displayMode.resolution.getHeight());
}
}
} while (numEvents > 0);
@@ -403,15 +405,15 @@ status_t BootAnimation::readyToRun() {
if (mDisplayToken == nullptr)
return NAME_NOT_FOUND;
- DisplayConfig displayConfig;
+ DisplayMode displayMode;
const status_t error =
- SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &displayConfig);
+ SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &displayMode);
if (error != NO_ERROR)
return error;
mMaxWidth = android::base::GetIntProperty("ro.surface_flinger.max_graphics_width", 0);
mMaxHeight = android::base::GetIntProperty("ro.surface_flinger.max_graphics_height", 0);
- ui::Size resolution = displayConfig.resolution;
+ ui::Size resolution = displayMode.resolution;
resolution = limitSurfaceSize(resolution.width, resolution.height);
// create the native surface
sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
diff --git a/cmds/bu/Android.bp b/cmds/bu/Android.bp
index 0866ee053107..5b4ec3197cdd 100644
--- a/cmds/bu/Android.bp
+++ b/cmds/bu/Android.bp
@@ -1,6 +1,23 @@
// Copyright 2011 The Android Open Source Project
//
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_bu_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_bu_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
java_binary {
name: "bu",
wrapper: "bu",
diff --git a/cmds/content/Android.bp b/cmds/content/Android.bp
index 96d14696d0a2..c70d01ec8f0b 100644
--- a/cmds/content/Android.bp
+++ b/cmds/content/Android.bp
@@ -1,5 +1,22 @@
// Copyright 2012 The Android Open Source Project
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_content_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_content_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
java_binary {
name: "content",
wrapper: "content",
diff --git a/cmds/device_config/Android.bp b/cmds/device_config/Android.bp
index 67e014a20506..69572d8690c4 100644
--- a/cmds/device_config/Android.bp
+++ b/cmds/device_config/Android.bp
@@ -1,6 +1,17 @@
// Copyright 2018 The Android Open Source Project
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-MIT
+ // SPDX-license-identifier-Unicode-DFS
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
sh_binary {
name: "device_config",
src: "device_config",
diff --git a/cmds/dpm/Android.bp b/cmds/dpm/Android.bp
index 753121e6d886..665abcd7314d 100644
--- a/cmds/dpm/Android.bp
+++ b/cmds/dpm/Android.bp
@@ -1,6 +1,23 @@
// Copyright 2014 The Android Open Source Project
//
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_dpm_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_dpm_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
java_binary {
name: "dpm",
wrapper: "dpm",
diff --git a/cmds/hid/Android.bp b/cmds/hid/Android.bp
index 54c8bf3ef34c..295c71c65368 100644
--- a/cmds/hid/Android.bp
+++ b/cmds/hid/Android.bp
@@ -1,6 +1,23 @@
// Copyright 2015 The Android Open Source Project
//
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_hid_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_hid_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
java_binary {
name: "hid",
wrapper: "hid",
diff --git a/cmds/hid/jni/Android.bp b/cmds/hid/jni/Android.bp
index 2c07de04b6a7..11d92909e44a 100644
--- a/cmds/hid/jni/Android.bp
+++ b/cmds/hid/jni/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_cmds_hid_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_cmds_hid_license"],
+}
+
cc_library_shared {
name: "libhidcommand_jni",
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index e21a6b288fb3..de1bcaea079c 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_defaults {
name: "idmap2_defaults",
tidy: true,
@@ -26,10 +35,24 @@ cc_defaults {
tidy_checks_as_errors: [
"modernize-*",
"-modernize-avoid-c-arrays",
+ "-modernize-pass-by-value",
+ "-modernize-replace-disallow-copy-and-assign-macro",
+ "-modernize-use-equals-default",
+ "-modernize-use-nodiscard",
+ "-modernize-use-override",
"-modernize-use-trailing-return-type",
+ "-modernize-use-using",
"android-*",
"misc-*",
+ "-misc-non-private-member-variables-in-classes",
"readability-*",
+ "-readability-braces-around-statements",
+ "-readability-const-return-type",
+ "-readability-convert-member-functions-to-static",
+ "-readability-else-after-return",
+ "-readability-named-parameter",
+ "-readability-redundant-access-specifiers",
+ "-readability-uppercase-literal-suffix",
],
tidy_flags: [
"-system-headers",
@@ -51,13 +74,18 @@ cc_library {
static: {
enabled: false,
},
+ static_libs: [
+ "libidmap2_protos",
+ ],
shared_libs: [
"libandroidfw",
"libbase",
"libcutils",
+ "libidmap2_policies",
+ "libprotobuf-cpp-lite",
"libutils",
+ "libz",
"libziparchive",
- "libidmap2_policies",
],
},
host: {
@@ -68,15 +96,30 @@ cc_library {
"libandroidfw",
"libbase",
"libcutils",
+ "libidmap2_policies",
+ "libidmap2_protos",
+ "libprotobuf-cpp-lite",
"libutils",
+ "libz",
"libziparchive",
- "libidmap2_policies",
],
},
},
}
cc_library {
+ name: "libidmap2_protos",
+ srcs: [
+ "libidmap2/proto/*.proto",
+ ],
+ host_supported: true,
+ proto: {
+ type: "lite",
+ export_proto_headers: true,
+ },
+}
+
+cc_library {
name: "libidmap2_policies",
defaults: [
"idmap2_defaults",
@@ -88,9 +131,6 @@ cc_library {
enabled: true,
},
android: {
- static: {
- enabled: false,
- },
shared_libs: [
"libandroidfw",
],
@@ -119,6 +159,7 @@ cc_test {
srcs: [
"tests/BinaryStreamVisitorTests.cpp",
"tests/CommandLineOptionsTests.cpp",
+ "tests/FabricatedOverlayTests.cpp",
"tests/FileUtilsTests.cpp",
"tests/Idmap2BinaryTests.cpp",
"tests/IdmapTests.cpp",
@@ -130,23 +171,27 @@ cc_test {
"tests/ResourceUtilsTests.cpp",
"tests/ResultTests.cpp",
"tests/XmlParserTests.cpp",
- "tests/ZipFileTests.cpp",
],
required: [
"idmap2",
],
- static_libs: ["libgmock"],
+ static_libs: [
+ "libgmock",
+ "libidmap2_protos",
+ ],
target: {
android: {
shared_libs: [
"libandroidfw",
"libbase",
"libidmap2",
+ "libidmap2_policies",
"liblog",
+ "libprotobuf-cpp-lite",
"libutils",
"libz",
+ "libz",
"libziparchive",
- "libidmap2_policies",
],
},
host: {
@@ -155,17 +200,28 @@ cc_test {
"libbase",
"libcutils",
"libidmap2",
+ "libidmap2_policies",
"liblog",
+ "libprotobuf-cpp-lite",
"libutils",
"libziparchive",
- "libidmap2_policies",
],
shared_libs: [
"libz",
],
+ data: [
+ ":libz",
+ ":idmap2",
+ ],
},
},
- data: ["tests/data/**/*.apk"],
+ data: [
+ "tests/data/**/*.apk",
+ ],
+ compile_multilib: "first",
+ test_options: {
+ unit_test: true,
+ },
}
cc_binary {
@@ -182,6 +238,9 @@ cc_binary {
"idmap2/Lookup.cpp",
"idmap2/Main.cpp",
],
+ static_libs: [
+ "libidmap2_protos",
+ ],
target: {
android: {
shared_libs: [
@@ -189,9 +248,11 @@ cc_binary {
"libbase",
"libcutils",
"libidmap2",
+ "libidmap2_policies",
+ "libprotobuf-cpp-lite",
"libutils",
+ "libz",
"libziparchive",
- "libidmap2_policies",
],
},
host: {
@@ -200,10 +261,11 @@ cc_binary {
"libbase",
"libcutils",
"libidmap2",
+ "libidmap2_policies",
"liblog",
+ "libprotobuf-cpp-lite",
"libutils",
"libziparchive",
- "libidmap2_policies",
],
shared_libs: [
"libz",
@@ -229,11 +291,14 @@ cc_binary {
"libbinder",
"libcutils",
"libidmap2",
+ "libidmap2_policies",
+ "libprotobuf-cpp-lite",
"libutils",
"libziparchive",
- "libidmap2_policies",
],
static_libs: [
+ "libc++fs",
+ "libidmap2_protos",
"libidmap2daidl",
],
init_rc: ["idmap2d/idmap2d.rc"],
@@ -241,28 +306,41 @@ cc_binary {
cc_library_static {
name: "libidmap2daidl",
- defaults: [
- "idmap2_defaults",
- ],
- tidy: false,
- host_supported: false,
srcs: [
":idmap2_aidl",
+ ":idmap2_core_aidl",
+ ],
+ header_libs: [
+ "libbinder_headers",
],
shared_libs: [
"libbase",
],
aidl: {
export_aidl_headers: true,
+ local_include_dirs: [
+ "idmap2d/aidl/core",
+ "idmap2d/aidl/services/",
+ ],
},
}
filegroup {
+ name: "idmap2_core_aidl",
+ srcs: [
+ "idmap2d/aidl/core/android/os/FabricatedOverlayInternal.aidl",
+ "idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl",
+ "idmap2d/aidl/core/android/os/FabricatedOverlayInfo.aidl",
+ ],
+ path: "idmap2d/aidl/core/",
+}
+
+filegroup {
name: "idmap2_aidl",
srcs: [
- "idmap2d/aidl/android/os/IIdmap2.aidl",
+ "idmap2d/aidl/services/android/os/IIdmap2.aidl",
],
- path: "idmap2d/aidl",
+ path: "idmap2d/aidl/services/",
}
aidl_interface {
@@ -274,7 +352,7 @@ aidl_interface {
filegroup {
name: "overlayable_policy_aidl_files",
srcs: [
- "idmap2d/aidl/android/os/OverlayablePolicy.aidl",
+ "idmap2d/aidl/services/android/os/OverlayablePolicy.aidl",
],
- path: "idmap2d/aidl",
+ path: "idmap2d/aidl/services/",
}
diff --git a/cmds/idmap2/AndroidTest.xml b/cmds/idmap2/AndroidTest.xml
deleted file mode 100644
index 5147f4e6cb4c..000000000000
--- a/cmds/idmap2/AndroidTest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<configuration description="Config for idmap2_tests">
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="idmap2_tests->/data/local/tmp/idmap2_tests" />
- </target_preparer>
- <option name="test-suite-tag" value="idmap2_tests" />
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="idmap2_tests" />
- </test>
-</configuration>
diff --git a/cmds/idmap2/idmap2/CommandUtils.cpp b/cmds/idmap2/idmap2/CommandUtils.cpp
index 09867f3a9c20..bf30a76d581a 100644
--- a/cmds/idmap2/idmap2/CommandUtils.cpp
+++ b/cmds/idmap2/idmap2/CommandUtils.cpp
@@ -25,7 +25,9 @@
using android::idmap2::Error;
using android::idmap2::IdmapHeader;
+using android::idmap2::OverlayResourceContainer;
using android::idmap2::Result;
+using android::idmap2::TargetResourceContainer;
using android::idmap2::Unit;
Result<Unit> Verify(const std::string& idmap_path, const std::string& target_path,
@@ -39,11 +41,20 @@ Result<Unit> Verify(const std::string& idmap_path, const std::string& target_pat
return Error("failed to parse idmap header");
}
- const auto header_ok = header->IsUpToDate(target_path, overlay_path, overlay_name,
- fulfilled_policies, enforce_overlayable);
+ auto target = TargetResourceContainer::FromPath(target_path);
+ if (!target) {
+ return Error("failed to load target '%s'", target_path.c_str());
+ }
+
+ auto overlay = OverlayResourceContainer::FromPath(overlay_path);
+ if (!overlay) {
+ return Error("failed to load overlay '%s'", overlay_path.c_str());
+ }
+
+ const auto header_ok = header->IsUpToDate(**target, **overlay, overlay_name, fulfilled_policies,
+ enforce_overlayable);
if (!header_ok) {
return Error(header_ok.GetError(), "idmap not up to date");
}
-
return Unit{};
}
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index c93c717a15d2..977a0bbadafb 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -20,7 +20,6 @@
#include <fstream>
#include <memory>
#include <ostream>
-#include <string>
#include <vector>
#include "androidfw/ResourceTypes.h"
@@ -31,12 +30,13 @@
#include "idmap2/PolicyUtils.h"
#include "idmap2/SysTrace.h"
-using android::ApkAssets;
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
+using android::idmap2::OverlayResourceContainer;
using android::idmap2::Result;
+using android::idmap2::TargetResourceContainer;
using android::idmap2::Unit;
using android::idmap2::utils::kIdmapFilePermissionMask;
using android::idmap2::utils::PoliciesToBitmaskResult;
@@ -93,18 +93,18 @@ Result<Unit> Create(const std::vector<std::string>& args) {
fulfilled_policies |= PolicyFlags::PUBLIC;
}
- const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- if (!target_apk) {
- return Error("failed to load apk %s", target_apk_path.c_str());
+ const auto target = TargetResourceContainer::FromPath(target_apk_path);
+ if (!target) {
+ return Error("failed to load target '%s'", target_apk_path.c_str());
}
- const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- if (!overlay_apk) {
- return Error("failed to load apk %s", overlay_apk_path.c_str());
+ const auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
+ if (!overlay) {
+ return Error("failed to load apk overlay '%s'", overlay_apk_path.c_str());
}
- const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, overlay_name,
- fulfilled_policies, !ignore_overlayable);
+ const auto idmap = Idmap::FromContainers(**target, **overlay, overlay_name, fulfilled_policies,
+ !ignore_overlayable);
if (!idmap) {
return Error(idmap.GetError(), "failed to create idmap");
}
@@ -112,13 +112,14 @@ Result<Unit> Create(const std::vector<std::string>& args) {
umask(kIdmapFilePermissionMask);
std::ofstream fout(idmap_path);
if (fout.fail()) {
- return Error("failed to open idmap path %s", idmap_path.c_str());
+ return Error("failed to open idmap path '%s'", idmap_path.c_str());
}
+
BinaryStreamVisitor visitor(fout);
(*idmap)->accept(&visitor);
fout.close();
if (fout.fail()) {
- return Error("failed to write to idmap path %s", idmap_path.c_str());
+ return Error("failed to write to idmap path '%s'", idmap_path.c_str());
}
return Unit{};
diff --git a/cmds/idmap2/idmap2/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp
index 5db391caac30..953d99f8dcfb 100644
--- a/cmds/idmap2/idmap2/CreateMultiple.cpp
+++ b/cmds/idmap2/idmap2/CreateMultiple.cpp
@@ -34,13 +34,14 @@
#include "idmap2/PolicyUtils.h"
#include "idmap2/SysTrace.h"
-using android::ApkAssets;
using android::base::StringPrintf;
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
+using android::idmap2::OverlayResourceContainer;
using android::idmap2::Result;
+using android::idmap2::TargetResourceContainer;
using android::idmap2::Unit;
using android::idmap2::utils::kIdmapCacheDir;
using android::idmap2::utils::kIdmapFilePermissionMask;
@@ -91,9 +92,9 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
fulfilled_policies |= PolicyFlags::PUBLIC;
}
- const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- if (!target_apk) {
- return Error("failed to load apk %s", target_apk_path.c_str());
+ const auto target = TargetResourceContainer::FromPath(target_apk_path);
+ if (!target) {
+ return Error("failed to load target '%s'", target_apk_path.c_str());
}
std::vector<std::string> idmap_paths;
@@ -108,14 +109,14 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
// TODO(b/175014391): Support multiple overlay tags in OverlayConfig
if (!Verify(idmap_path, target_apk_path, overlay_apk_path, "", fulfilled_policies,
!ignore_overlayable)) {
- const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- if (!overlay_apk) {
+ const auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
+ if (!overlay) {
LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str();
continue;
}
- const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, "", fulfilled_policies,
- !ignore_overlayable);
+ const auto idmap =
+ Idmap::FromContainers(**target, **overlay, "", fulfilled_policies, !ignore_overlayable);
if (!idmap) {
LOG(WARNING) << "failed to create idmap";
continue;
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 43a1951a5ba9..f41e57cc66d6 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -37,7 +37,6 @@
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
#include "idmap2/XmlParser.h"
-#include "idmap2/ZipFile.h"
#include "utils/String16.h"
#include "utils/String8.h"
@@ -52,10 +51,10 @@ using android::base::StringPrintf;
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::IdmapHeader;
+using android::idmap2::OverlayResourceContainer;
using android::idmap2::ResourceId;
using android::idmap2::Result;
using android::idmap2::Unit;
-using android::idmap2::utils::ExtractOverlayManifestInfo;
namespace {
@@ -195,12 +194,17 @@ Result<Unit> Lookup(const std::vector<std::string>& args) {
}
apk_assets.push_back(std::move(target_apk));
- auto manifest_info = ExtractOverlayManifestInfo(idmap_header->GetOverlayPath(),
- idmap_header->GetOverlayName());
+ auto overlay = OverlayResourceContainer::FromPath(idmap_header->GetOverlayPath());
+ if (!overlay) {
+ return overlay.GetError();
+ }
+
+ auto manifest_info = (*overlay)->FindOverlayInfo(idmap_header->GetOverlayName());
if (!manifest_info) {
return manifest_info.GetError();
}
- target_package_name = manifest_info->target_package;
+
+ target_package_name = (*manifest_info).target_package;
} else if (target_path != idmap_header->GetTargetPath()) {
return Error("different target APKs (expected target APK %s but %s has target APK %s)",
target_path.c_str(), idmap_path.c_str(), idmap_header->GetTargetPath().c_str());
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 93537d32299b..05336baf9217 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -18,10 +18,10 @@
#include <sys/stat.h> // umask
#include <sys/types.h> // umask
-#include <unistd.h>
#include <cerrno>
#include <cstring>
+#include <filesystem>
#include <fstream>
#include <memory>
#include <ostream>
@@ -35,19 +35,20 @@
#include "idmap2/Idmap.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
-#include "idmap2/ZipFile.h"
-#include "utils/String8.h"
using android::IPCThreadState;
using android::base::StringPrintf;
using android::binder::Status;
using android::idmap2::BinaryStreamVisitor;
-using android::idmap2::GetPackageCrc;
+using android::idmap2::FabricatedOverlay;
+using android::idmap2::FabricatedOverlayContainer;
using android::idmap2::Idmap;
using android::idmap2::IdmapHeader;
-using android::idmap2::ZipFile;
+using android::idmap2::OverlayResourceContainer;
+using android::idmap2::TargetResourceContainer;
using android::idmap2::utils::kIdmapCacheDir;
using android::idmap2::utils::kIdmapFilePermissionMask;
+using android::idmap2::utils::RandomStringForPath;
using android::idmap2::utils::UidHasWriteAccessToPath;
using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
@@ -69,39 +70,24 @@ PolicyBitmask ConvertAidlArgToPolicyBitmask(int32_t arg) {
return static_cast<PolicyBitmask>(arg);
}
-Status GetCrc(const std::string& apk_path, uint32_t* out_crc) {
- const auto zip = ZipFile::Open(apk_path);
- if (!zip) {
- return error(StringPrintf("failed to open apk %s", apk_path.c_str()));
- }
-
- const auto crc = GetPackageCrc(*zip);
- if (!crc) {
- return error(crc.GetErrorMessage());
- }
-
- *out_crc = *crc;
- return ok();
-}
-
} // namespace
namespace android::os {
-Status Idmap2Service::getIdmapPath(const std::string& overlay_apk_path,
+Status Idmap2Service::getIdmapPath(const std::string& overlay_path,
int32_t user_id ATTRIBUTE_UNUSED, std::string* _aidl_return) {
assert(_aidl_return);
- SYSTRACE << "Idmap2Service::getIdmapPath " << overlay_apk_path;
- *_aidl_return = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+ SYSTRACE << "Idmap2Service::getIdmapPath " << overlay_path;
+ *_aidl_return = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path);
return ok();
}
-Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path,
- int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
+Status Idmap2Service::removeIdmap(const std::string& overlay_path, int32_t user_id ATTRIBUTE_UNUSED,
+ bool* _aidl_return) {
assert(_aidl_return);
- SYSTRACE << "Idmap2Service::removeIdmap " << overlay_apk_path;
+ SYSTRACE << "Idmap2Service::removeIdmap " << overlay_path;
const uid_t uid = IPCThreadState::self()->getCallingUid();
- const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path);
if (!UidHasWriteAccessToPath(uid, idmap_path)) {
*_aidl_return = false;
return error(base::StringPrintf("failed to unlink %s: calling uid %d lacks write access",
@@ -115,93 +101,88 @@ Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path,
return ok();
}
-Status Idmap2Service::verifyIdmap(const std::string& target_apk_path,
- const std::string& overlay_apk_path, int32_t fulfilled_policies,
+Status Idmap2Service::verifyIdmap(const std::string& target_path, const std::string& overlay_path,
+ const std::string& overlay_name, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED,
bool* _aidl_return) {
- SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_apk_path;
+ SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_path;
assert(_aidl_return);
- const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path);
std::ifstream fin(idmap_path);
const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
fin.close();
if (!header) {
*_aidl_return = false;
- return error("failed to parse idmap header");
+ LOG(WARNING) << "failed to parse idmap header of '" << idmap_path << "'";
+ return ok();
}
- uint32_t target_crc;
- if (target_apk_path == kFrameworkPath && android_crc_) {
- target_crc = *android_crc_;
- } else {
- auto target_crc_status = GetCrc(target_apk_path, &target_crc);
- if (!target_crc_status.isOk()) {
- *_aidl_return = false;
- return target_crc_status;
- }
-
- // Loading the framework zip can take several milliseconds. Cache the crc of the framework
- // resource APK to reduce repeated work during boot.
- if (target_apk_path == kFrameworkPath) {
- android_crc_ = target_crc;
- }
+ const auto target = GetTargetContainer(target_path);
+ if (!target) {
+ *_aidl_return = false;
+ LOG(WARNING) << "failed to load target '" << target_path << "'";
+ return ok();
}
- uint32_t overlay_crc;
- auto overlay_crc_status = GetCrc(overlay_apk_path, &overlay_crc);
- if (!overlay_crc_status.isOk()) {
+ const auto overlay = OverlayResourceContainer::FromPath(overlay_path);
+ if (!overlay) {
*_aidl_return = false;
- return overlay_crc_status;
+ LOG(WARNING) << "failed to load overlay '" << overlay_path << "'";
+ return ok();
}
- // TODO(162841629): Support passing overlay name to idmap2d verify
auto up_to_date =
- header->IsUpToDate(target_apk_path, overlay_apk_path, "", target_crc, overlay_crc,
+ header->IsUpToDate(*GetPointer(*target), **overlay, overlay_name,
ConvertAidlArgToPolicyBitmask(fulfilled_policies), enforce_overlayable);
*_aidl_return = static_cast<bool>(up_to_date);
- return *_aidl_return ? ok() : error(up_to_date.GetErrorMessage());
+ if (!up_to_date) {
+ LOG(WARNING) << "idmap '" << idmap_path
+ << "' not up to date : " << up_to_date.GetErrorMessage();
+ }
+ return ok();
}
-Status Idmap2Service::createIdmap(const std::string& target_apk_path,
- const std::string& overlay_apk_path, int32_t fulfilled_policies,
+Status Idmap2Service::createIdmap(const std::string& target_path, const std::string& overlay_path,
+ const std::string& overlay_name, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED,
std::optional<std::string>* _aidl_return) {
assert(_aidl_return);
- SYSTRACE << "Idmap2Service::createIdmap " << target_apk_path << " " << overlay_apk_path;
+ SYSTRACE << "Idmap2Service::createIdmap " << target_path << " " << overlay_path;
_aidl_return->reset();
const PolicyBitmask policy_bitmask = ConvertAidlArgToPolicyBitmask(fulfilled_policies);
- const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path);
const uid_t uid = IPCThreadState::self()->getCallingUid();
if (!UidHasWriteAccessToPath(uid, idmap_path)) {
return error(base::StringPrintf("will not write to %s: calling uid %d lacks write accesss",
idmap_path.c_str(), uid));
}
- const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- if (!target_apk) {
- return error("failed to load apk " + target_apk_path);
+ // idmap files are mapped with mmap in libandroidfw. Deleting and recreating the idmap guarantees
+ // that existing memory maps will continue to be valid and unaffected. The file must be deleted
+ // before attempting to create the idmap, so that if idmap creation fails, the overlay will no
+ // longer be usable.
+ unlink(idmap_path.c_str());
+
+ const auto target = GetTargetContainer(target_path);
+ if (!target) {
+ return error("failed to load target '%s'" + target_path);
}
- const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- if (!overlay_apk) {
- return error("failed to load apk " + overlay_apk_path);
+ const auto overlay = OverlayResourceContainer::FromPath(overlay_path);
+ if (!overlay) {
+ return error("failed to load apk overlay '%s'" + overlay_path);
}
- // TODO(162841629): Support passing overlay name to idmap2d create
- const auto idmap =
- Idmap::FromApkAssets(*target_apk, *overlay_apk, "", policy_bitmask, enforce_overlayable);
+ const auto idmap = Idmap::FromContainers(*GetPointer(*target), **overlay, overlay_name,
+ policy_bitmask, enforce_overlayable);
if (!idmap) {
return error(idmap.GetErrorMessage());
}
- // idmap files are mapped with mmap in libandroidfw. Deleting and recreating the idmap guarantees
- // that existing memory maps will continue to be valid and unaffected.
- unlink(idmap_path.c_str());
-
umask(kIdmapFilePermissionMask);
std::ofstream fout(idmap_path);
if (fout.fail()) {
@@ -220,4 +201,155 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path,
return ok();
}
+idmap2::Result<Idmap2Service::TargetResourceContainerPtr> Idmap2Service::GetTargetContainer(
+ const std::string& target_path) {
+ if (target_path == kFrameworkPath) {
+ if (framework_apk_cache_ == nullptr) {
+ // Initialize the framework APK cache.
+ auto target = TargetResourceContainer::FromPath(target_path);
+ if (!target) {
+ return target.GetError();
+ }
+ framework_apk_cache_ = std::move(*target);
+ }
+ return {framework_apk_cache_.get()};
+ }
+
+ auto target = TargetResourceContainer::FromPath(target_path);
+ if (!target) {
+ return target.GetError();
+ }
+ return {std::move(*target)};
+}
+
+Status Idmap2Service::createFabricatedOverlay(
+ const os::FabricatedOverlayInternal& overlay,
+ std::optional<os::FabricatedOverlayInfo>* _aidl_return) {
+ idmap2::FabricatedOverlay::Builder builder(overlay.packageName, overlay.overlayName,
+ overlay.targetPackageName);
+ if (!overlay.targetOverlayable.empty()) {
+ builder.SetOverlayable(overlay.targetOverlayable);
+ }
+
+ for (const auto& res : overlay.entries) {
+ builder.SetResourceValue(res.resourceName, res.dataType, res.data);
+ }
+
+ // Generate the file path of the fabricated overlay and ensure it does not collide with an
+ // existing path. Re-registering a fabricated overlay will always result in an updated path.
+ std::string path;
+ std::string file_name;
+ do {
+ constexpr size_t kSuffixLength = 4;
+ const std::string random_suffix = RandomStringForPath(kSuffixLength);
+ file_name = StringPrintf("%s-%s-%s.frro", overlay.packageName.c_str(),
+ overlay.overlayName.c_str(), random_suffix.c_str());
+ path = StringPrintf("%s/%s", kIdmapCacheDir, file_name.c_str());
+
+ // Invoking std::filesystem::exists with a file name greater than 255 characters will cause this
+ // process to abort since the name exceeds the maximum file name size.
+ const size_t kMaxFileNameLength = 255;
+ if (file_name.size() > kMaxFileNameLength) {
+ return error(
+ base::StringPrintf("fabricated overlay file name '%s' longer than %zu characters",
+ file_name.c_str(), kMaxFileNameLength));
+ }
+ } while (std::filesystem::exists(path));
+
+ const uid_t uid = IPCThreadState::self()->getCallingUid();
+ if (!UidHasWriteAccessToPath(uid, path)) {
+ return error(base::StringPrintf("will not write to %s: calling uid %d lacks write access",
+ path.c_str(), uid));
+ }
+
+ // Persist the fabricated overlay.
+ umask(kIdmapFilePermissionMask);
+ std::ofstream fout(path);
+ if (fout.fail()) {
+ return error("failed to open frro path " + path);
+ }
+ const auto frro = builder.Build();
+ if (!frro) {
+ return error(StringPrintf("failed to serialize '%s:%s': %s", overlay.packageName.c_str(),
+ overlay.overlayName.c_str(), frro.GetErrorMessage().c_str()));
+ }
+ auto result = frro->ToBinaryStream(fout);
+ if (!result) {
+ unlink(path.c_str());
+ return error("failed to write to frro path " + path + ": " + result.GetErrorMessage());
+ }
+ if (fout.fail()) {
+ unlink(path.c_str());
+ return error("failed to write to frro path " + path);
+ }
+
+ os::FabricatedOverlayInfo out_info;
+ out_info.packageName = overlay.packageName;
+ out_info.overlayName = overlay.overlayName;
+ out_info.targetPackageName = overlay.targetPackageName;
+ out_info.targetOverlayable = overlay.targetOverlayable;
+ out_info.path = path;
+ *_aidl_return = out_info;
+ return ok();
+}
+
+Status Idmap2Service::getFabricatedOverlayInfos(
+ std::vector<os::FabricatedOverlayInfo>* _aidl_return) {
+ for (const auto& entry : std::filesystem::directory_iterator(kIdmapCacheDir)) {
+ if (!android::IsFabricatedOverlay(entry.path())) {
+ continue;
+ }
+
+ const auto overlay = FabricatedOverlayContainer::FromPath(entry.path());
+ if (!overlay) {
+ // This is a sign something went wrong.
+ LOG(ERROR) << "Failed to open '" << entry.path() << "': " << overlay.GetErrorMessage();
+ continue;
+ }
+
+ const auto info = (*overlay)->GetManifestInfo();
+ os::FabricatedOverlayInfo out_info;
+ out_info.packageName = info.package_name;
+ out_info.overlayName = info.name;
+ out_info.targetPackageName = info.target_package;
+ out_info.targetOverlayable = info.target_name;
+ out_info.path = entry.path();
+ _aidl_return->emplace_back(std::move(out_info));
+ }
+
+ return ok();
+}
+
+binder::Status Idmap2Service::deleteFabricatedOverlay(const std::string& overlay_path,
+ bool* _aidl_return) {
+ SYSTRACE << "Idmap2Service::deleteFabricatedOverlay " << overlay_path;
+ const uid_t uid = IPCThreadState::self()->getCallingUid();
+
+ if (!UidHasWriteAccessToPath(uid, overlay_path)) {
+ *_aidl_return = false;
+ return error(base::StringPrintf("failed to unlink %s: calling uid %d lacks write access",
+ overlay_path.c_str(), uid));
+ }
+
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path);
+ if (!UidHasWriteAccessToPath(uid, idmap_path)) {
+ *_aidl_return = false;
+ return error(base::StringPrintf("failed to unlink %s: calling uid %d lacks write access",
+ idmap_path.c_str(), uid));
+ }
+
+ if (unlink(overlay_path.c_str()) != 0) {
+ *_aidl_return = false;
+ return error("failed to unlink " + overlay_path + ": " + strerror(errno));
+ }
+
+ if (unlink(idmap_path.c_str()) != 0) {
+ *_aidl_return = false;
+ return error("failed to unlink " + idmap_path + ": " + strerror(errno));
+ }
+
+ *_aidl_return = true;
+ return ok();
+}
+
} // namespace android::os
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 0127e874b444..4d16ff39884b 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -18,12 +18,14 @@
#define IDMAP2_IDMAP2D_IDMAP2SERVICE_H_
#include <android-base/unique_fd.h>
+#include <android/os/BnIdmap2.h>
+#include <android/os/FabricatedOverlayInfo.h>
#include <binder/BinderService.h>
+#include <idmap2/ResourceContainer.h>
+#include <idmap2/Result.h>
#include <string>
-#include "android/os/BnIdmap2.h"
-
namespace android::os {
class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 {
@@ -32,27 +34,58 @@ class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 {
return "idmap";
}
- binder::Status getIdmapPath(const std::string& overlay_apk_path, int32_t user_id,
+ binder::Status getIdmapPath(const std::string& overlay_path, int32_t user_id,
std::string* _aidl_return) override;
- binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id,
+ binder::Status removeIdmap(const std::string& overlay_path, int32_t user_id,
bool* _aidl_return) override;
- binder::Status verifyIdmap(const std::string& target_apk_path,
- const std::string& overlay_apk_path, int32_t fulfilled_policies,
+ binder::Status verifyIdmap(const std::string& target_path, const std::string& overlay_path,
+ const std::string& overlay_name, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id,
bool* _aidl_return) override;
- binder::Status createIdmap(const std::string& target_apk_path,
- const std::string& overlay_apk_path, int32_t fulfilled_policies,
+ binder::Status createIdmap(const std::string& target_path, const std::string& overlay_path,
+ const std::string& overlay_name, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id,
std::optional<std::string>* _aidl_return) override;
+ binder::Status createFabricatedOverlay(
+ const os::FabricatedOverlayInternal& overlay,
+ std::optional<os::FabricatedOverlayInfo>* _aidl_return) override;
+
+ binder::Status deleteFabricatedOverlay(const std::string& overlay_path,
+ bool* _aidl_return) override;
+
+ binder::Status getFabricatedOverlayInfos(
+ std::vector<os::FabricatedOverlayInfo>* _aidl_return) override;
+
private:
- // Cache the crc of the android framework package since the crc cannot change without a reboot.
- std::optional<uint32_t> android_crc_;
+ // idmap2d is killed after a period of inactivity, so any information stored on this class should
+ // be able to be recalculated if idmap2 dies and restarts.
+ std::unique_ptr<idmap2::TargetResourceContainer> framework_apk_cache_;
+
+ std::vector<os::FabricatedOverlayInfo> fabricated_overlays_;
+
+ template <typename T>
+ using MaybeUniquePtr = std::variant<std::unique_ptr<T>, T*>;
+
+ using TargetResourceContainerPtr = MaybeUniquePtr<idmap2::TargetResourceContainer>;
+ idmap2::Result<TargetResourceContainerPtr> GetTargetContainer(const std::string& target_path);
+
+ template <typename T>
+ WARN_UNUSED static const T* GetPointer(const MaybeUniquePtr<T>& ptr);
};
+template <typename T>
+const T* Idmap2Service::GetPointer(const MaybeUniquePtr<T>& ptr) {
+ auto u = std::get_if<T*>(&ptr);
+ if (u != nullptr) {
+ return *u;
+ }
+ return std::get<std::unique_ptr<T>>(ptr).get();
+}
+
} // namespace android::os
#endif // IDMAP2_IDMAP2D_IDMAP2SERVICE_H_
diff --git a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInfo.aidl b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInfo.aidl
new file mode 100644
index 000000000000..6375d24845e2
--- /dev/null
+++ b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInfo.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 permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * @hide
+ */
+parcelable FabricatedOverlayInfo {
+ @utf8InCpp String path;
+ @utf8InCpp String packageName;
+ @utf8InCpp String overlayName;
+ @utf8InCpp String targetPackageName;
+ @utf8InCpp String targetOverlayable;
+} \ No newline at end of file
diff --git a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternal.aidl b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternal.aidl
new file mode 100644
index 000000000000..f67d8beeed3a
--- /dev/null
+++ b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternal.aidl
@@ -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 android.os;
+
+import android.os.FabricatedOverlayInternalEntry;
+
+/**
+ * @hide
+ */
+parcelable FabricatedOverlayInternal {
+ @utf8InCpp String packageName;
+ @utf8InCpp String overlayName;
+ @utf8InCpp String targetPackageName;
+ @utf8InCpp String targetOverlayable;
+ List<FabricatedOverlayInternalEntry> entries;
+} \ No newline at end of file
diff --git a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl
new file mode 100644
index 000000000000..6c2af274ae32
--- /dev/null
+++ b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * @hide
+ */
+parcelable FabricatedOverlayInternalEntry {
+ @utf8InCpp String resourceName;
+ int dataType;
+ int data;
+} \ No newline at end of file
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl
index 156f1d70e015..35bca98bcf69 100644
--- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
+++ b/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl
@@ -16,6 +16,9 @@
package android.os;
+import android.os.FabricatedOverlayInternal;
+import android.os.FabricatedOverlayInfo;
+
/**
* @hide
*/
@@ -23,13 +26,18 @@ interface IIdmap2 {
@utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
boolean verifyIdmap(@utf8InCpp String targetApkPath,
- @utf8InCpp String overlayApkPath,
+ @utf8InCpp String overlayApkPath,
+ @utf8InCpp String overlayName,
int fulfilledPolicies,
boolean enforceOverlayable,
int userId);
@nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath,
@utf8InCpp String overlayApkPath,
+ @utf8InCpp String overlayName,
int fulfilledPolicies,
boolean enforceOverlayable,
int userId);
+ @nullable FabricatedOverlayInfo createFabricatedOverlay(in FabricatedOverlayInternal overlay);
+ List<FabricatedOverlayInfo> getFabricatedOverlayInfos();
+ boolean deleteFabricatedOverlay(@utf8InCpp String path);
}
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl b/cmds/idmap2/idmap2d/aidl/services/android/os/OverlayablePolicy.aidl
index 403d8c55de16..403d8c55de16 100644
--- a/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl
+++ b/cmds/idmap2/idmap2d/aidl/services/android/os/OverlayablePolicy.aidl
diff --git a/cmds/idmap2/include/idmap2/FabricatedOverlay.h b/cmds/idmap2/include/idmap2/FabricatedOverlay.h
new file mode 100644
index 000000000000..be687d98081e
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/FabricatedOverlay.h
@@ -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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_FABRICATEDOVERLAY_H
+#define IDMAP2_INCLUDE_IDMAP2_FABRICATEDOVERLAY_H
+
+#include <libidmap2/proto/fabricated_v1.pb.h>
+
+#include <iostream>
+#include <map>
+#include <memory>
+#include <unordered_map>
+
+#include "idmap2/ResourceContainer.h"
+#include "idmap2/Result.h"
+
+namespace android::idmap2 {
+
+struct FabricatedOverlay {
+ struct Builder {
+ Builder(const std::string& package_name, const std::string& name,
+ const std::string& target_package_name);
+
+ Builder& SetOverlayable(const std::string& name);
+
+ Builder& SetResourceValue(const std::string& resource_name, uint8_t data_type,
+ uint32_t data_value);
+
+ WARN_UNUSED Result<FabricatedOverlay> Build();
+
+ private:
+ struct Entry {
+ std::string resource_name;
+ DataType data_type;
+ DataValue data_value;
+ };
+
+ std::string package_name_;
+ std::string name_;
+ std::string target_package_name_;
+ std::string target_overlayable_;
+ std::vector<Entry> entries_;
+ };
+
+ Result<Unit> ToBinaryStream(std::ostream& stream) const;
+ static Result<FabricatedOverlay> FromBinaryStream(std::istream& stream);
+
+ private:
+ struct SerializedData {
+ std::unique_ptr<uint8_t[]> data;
+ size_t data_size;
+ uint32_t crc;
+ };
+
+ Result<SerializedData*> InitializeData() const;
+ Result<uint32_t> GetCrc() const;
+
+ FabricatedOverlay(pb::FabricatedOverlay&& overlay, std::optional<uint32_t> crc_from_disk = {});
+
+ pb::FabricatedOverlay overlay_pb_;
+ std::optional<uint32_t> crc_from_disk_;
+ mutable std::optional<SerializedData> data_;
+
+ friend struct FabricatedOverlayContainer;
+};
+
+struct FabricatedOverlayContainer : public OverlayResourceContainer {
+ static Result<std::unique_ptr<FabricatedOverlayContainer>> FromPath(std::string path);
+ static std::unique_ptr<FabricatedOverlayContainer> FromOverlay(FabricatedOverlay&& overlay);
+
+ WARN_UNUSED OverlayManifestInfo GetManifestInfo() const;
+
+ // inherited from OverlayResourceContainer
+ WARN_UNUSED Result<OverlayManifestInfo> FindOverlayInfo(const std::string& name) const override;
+ WARN_UNUSED Result<OverlayData> GetOverlayData(const OverlayManifestInfo& info) const override;
+
+ // inherited from ResourceContainer
+ WARN_UNUSED Result<uint32_t> GetCrc() const override;
+ WARN_UNUSED const std::string& GetPath() const override;
+ WARN_UNUSED Result<std::string> GetResourceName(ResourceId id) const override;
+
+ ~FabricatedOverlayContainer() override;
+
+ private:
+ FabricatedOverlayContainer(FabricatedOverlay&& overlay, std::string&& path);
+ FabricatedOverlay overlay_;
+ std::string path_;
+};
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_INCLUDE_IDMAP2_FABRICATEDOVERLAY_H
diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h
index c4e0e1fd8ef0..2588688de44c 100644
--- a/cmds/idmap2/include/idmap2/FileUtils.h
+++ b/cmds/idmap2/include/idmap2/FileUtils.h
@@ -17,6 +17,7 @@
#ifndef IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
#define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
+#include <random>
#include <string>
namespace android::idmap2::utils {
@@ -26,6 +27,8 @@ constexpr const mode_t kIdmapFilePermissionMask = 0133; // u=rw,g=r,o=r
bool UidHasWriteAccessToPath(uid_t uid, const std::string& path);
+std::string RandomStringForPath(size_t length);
+
} // namespace android::idmap2::utils
#endif // IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index 1b815c1197ca..58aff42b1e98 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -23,8 +23,8 @@
* debug_info
* data := data_header target_entry* target_inline_entry* overlay_entry*
* string_pool
- * data_header := target_package_id overlay_package_id padding(2) target_entry_count
- * target_inline_entry_count overlay_entry_count string_pool_index
+ * data_header := target_entry_count target_inline_entry_count overlay_entry_count
+ * string_pool_index
* target_entry := target_id overlay_id
* target_inline_entry := target_id Res_value::size padding(1) Res_value::type
* Res_value::value
@@ -68,11 +68,10 @@
#include <vector>
#include "android-base/macros.h"
-#include "androidfw/ApkAssets.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
+#include "idmap2/ResourceContainer.h"
#include "idmap2/ResourceMapping.h"
-#include "idmap2/ZipFile.h"
namespace android::idmap2 {
@@ -85,9 +84,6 @@ static constexpr const uint32_t kIdmapMagic = android::kIdmapMagic;
// current version of the idmap binary format; must be incremented when the format is changed
static constexpr const uint32_t kIdmapCurrentVersion = android::kIdmapCurrentVersion;
-// Retrieves a crc generated using all of the files within the zip that can affect idmap generation.
-Result<uint32_t> GetPackageCrc(const ZipFile& zip_info);
-
class IdmapHeader {
public:
static std::unique_ptr<const IdmapHeader> FromBinaryStream(std::istream& stream);
@@ -135,9 +131,9 @@ class IdmapHeader {
// Invariant: anytime the idmap data encoding is changed, the idmap version
// field *must* be incremented. Because of this, we know that if the idmap
// header is up-to-date the entire file is up-to-date.
- Result<Unit> IsUpToDate(const std::string& target_path, const std::string& overlay_path,
- const std::string& overlay_name, PolicyBitmask fulfilled_policies,
- bool enforce_overlayable) const;
+ Result<Unit> IsUpToDate(const TargetResourceContainer& target,
+ const OverlayResourceContainer& overlay, const std::string& overlay_name,
+ PolicyBitmask fulfilled_policies, bool enforce_overlayable) const;
Result<Unit> IsUpToDate(const std::string& target_path, const std::string& overlay_path,
const std::string& overlay_name, uint32_t target_crc,
@@ -169,14 +165,6 @@ class IdmapData {
public:
static std::unique_ptr<const Header> FromBinaryStream(std::istream& stream);
- inline PackageId GetTargetPackageId() const {
- return target_package_id_;
- }
-
- inline PackageId GetOverlayPackageId() const {
- return overlay_package_id_;
- }
-
inline uint32_t GetTargetEntryCount() const {
return target_entry_count;
}
@@ -196,8 +184,6 @@ class IdmapData {
void accept(Visitor* v) const;
private:
- PackageId target_package_id_;
- PackageId overlay_package_id_;
uint32_t target_entry_count;
uint32_t target_entry_inline_count;
uint32_t overlay_entry_count;
@@ -275,11 +261,10 @@ class Idmap {
// file is used; change this in the next version of idmap to use a named
// package instead; also update FromApkAssets to take additional parameters:
// the target and overlay package names
- static Result<std::unique_ptr<const Idmap>> FromApkAssets(const ApkAssets& target_apk_assets,
- const ApkAssets& overlay_apk_assets,
- const std::string& overlay_name,
- const PolicyBitmask& fulfilled_policies,
- bool enforce_overlayable);
+ static Result<std::unique_ptr<const Idmap>> FromContainers(
+ const TargetResourceContainer& target, const OverlayResourceContainer& overlay,
+ const std::string& overlay_name, const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable);
const std::unique_ptr<const IdmapHeader>& GetHeader() const {
return header_;
diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
index 2b4c76124175..4464201a1f2e 100644
--- a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
@@ -41,9 +41,8 @@ class PrettyPrintVisitor : public Visitor {
private:
std::ostream& stream_;
- AssetManager2 target_am_;
- AssetManager2 overlay_am_;
- std::vector<std::unique_ptr<const ApkAssets>> apk_assets_;
+ std::unique_ptr<TargetResourceContainer> target_;
+ std::unique_ptr<OverlayResourceContainer> overlay_;
};
} // namespace idmap2
diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
index 45835164ef8e..ebd0d1eb2fbc 100644
--- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
@@ -49,10 +49,9 @@ class RawPrintVisitor : public Visitor {
void pad(size_t padding);
std::ostream& stream_;
- std::vector<std::unique_ptr<const ApkAssets>> apk_assets_;
- AssetManager2 target_am_;
- AssetManager2 overlay_am_;
size_t offset_;
+ std::unique_ptr<TargetResourceContainer> target_;
+ std::unique_ptr<OverlayResourceContainer> overlay_;
};
} // namespace idmap2
diff --git a/cmds/idmap2/include/idmap2/ResourceContainer.h b/cmds/idmap2/include/idmap2/ResourceContainer.h
new file mode 100644
index 000000000000..74a6f5666c2a
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/ResourceContainer.h
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_RESOURCECONTAINER_H
+#define IDMAP2_INCLUDE_IDMAP2_RESOURCECONTAINER_H
+
+#include <string>
+#include <variant>
+#include <vector>
+
+#include "idmap2/Policies.h"
+#include "idmap2/ResourceUtils.h"
+
+namespace android::idmap2 {
+
+struct ResourceContainer {
+ WARN_UNUSED virtual Result<uint32_t> GetCrc() const = 0;
+ WARN_UNUSED virtual const std::string& GetPath() const = 0;
+ WARN_UNUSED virtual Result<std::string> GetResourceName(ResourceId id) const = 0;
+
+ virtual ~ResourceContainer() = default;
+};
+
+struct TargetResourceContainer : public ResourceContainer {
+ static Result<std::unique_ptr<TargetResourceContainer>> FromPath(std::string path);
+
+ WARN_UNUSED virtual Result<bool> DefinesOverlayable() const = 0;
+ WARN_UNUSED virtual Result<const android::OverlayableInfo*> GetOverlayableInfo(
+ ResourceId id) const = 0;
+ WARN_UNUSED virtual Result<ResourceId> GetResourceId(const std::string& name) const = 0;
+
+ ~TargetResourceContainer() override = default;
+};
+
+struct OverlayManifestInfo {
+ std::string package_name; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string name; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes)
+ ResourceId resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes)
+};
+
+struct OverlayData {
+ struct ResourceIdValue {
+ // The overlay resource id.
+ ResourceId overlay_id;
+
+ // Whether or not references to the overlay resource id should be rewritten to its corresponding
+ // target id during resource resolution.
+ bool rewrite_id;
+ };
+
+ struct Value {
+ std::string resource_name;
+ std::variant<ResourceIdValue, TargetValue> value;
+ };
+
+ struct InlineStringPoolData {
+ // The binary data of the android::ResStringPool string pool.
+ std::unique_ptr<uint8_t[]> data;
+
+ // The length of the binary data.
+ uint32_t data_length;
+
+ // The offset added to TargetValue#data_value (the index of the string in the inline string
+ // pool) in order to prevent the indices of the overlay resource table string pool from
+ // colliding with the inline string pool indices.
+ uint32_t string_pool_offset;
+ };
+
+ // The overlay's mapping of target resource name to overlaid value. Use a vector to enforce that
+ // the overlay pairs are inserted into the ResourceMapping in the specified ordered.
+ std::vector<Value> pairs;
+
+ // If the overlay maps a target resource to a string literal (not a string resource), then the
+ // this field contains information about the string pool in which the string literal resides so it
+ // can be inlined into an idmap.
+ std::optional<InlineStringPoolData> string_pool_data;
+};
+
+struct OverlayResourceContainer : public ResourceContainer {
+ static Result<std::unique_ptr<OverlayResourceContainer>> FromPath(std::string path);
+
+ WARN_UNUSED virtual Result<OverlayManifestInfo> FindOverlayInfo(
+ const std::string& name) const = 0;
+ WARN_UNUSED virtual Result<OverlayData> GetOverlayData(const OverlayManifestInfo& info) const = 0;
+
+ ~OverlayResourceContainer() override = default;
+};
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCECONTAINER_H
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
index f66916ccb78c..5a0a384f75a3 100644
--- a/cmds/idmap2/include/idmap2/ResourceMapping.h
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -17,30 +17,22 @@
#ifndef IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
#define IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
+#include <androidfw/ApkAssets.h>
+
#include <map>
#include <memory>
#include <utility>
-#include "androidfw/ApkAssets.h"
+#include "idmap2/FabricatedOverlay.h"
#include "idmap2/LogInfo.h"
#include "idmap2/Policies.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/XmlParser.h"
-using android::idmap2::utils::OverlayManifestInfo;
-
using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
namespace android::idmap2 {
-
-struct TargetValue {
- typedef uint8_t DataType;
- typedef uint32_t DataValue;
- DataType data_type;
- DataValue data_value;
-};
-
using TargetResourceMap = std::map<ResourceId, std::variant<ResourceId, TargetValue>>;
using OverlayResourceMap = std::map<ResourceId, ResourceId>;
@@ -49,94 +41,60 @@ class ResourceMapping {
// Creates a ResourceMapping using the target and overlay APKs. Setting enforce_overlayable to
// `false` disables all overlayable and policy enforcement: this is intended for backwards
// compatibility pre-Q and unit tests.
- static Result<ResourceMapping> FromApkAssets(const ApkAssets& target_apk_assets,
- const ApkAssets& overlay_apk_assets,
- const OverlayManifestInfo& overlay_info,
- const PolicyBitmask& fulfilled_policies,
- bool enforce_overlayable, LogInfo& log_info);
+ static Result<ResourceMapping> FromContainers(const TargetResourceContainer& target,
+ const OverlayResourceContainer& overlay,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable, LogInfo& log_info);
// Retrieves the mapping of target resource id to overlay value.
- inline const TargetResourceMap& GetTargetToOverlayMap() const {
- return target_map_;
- }
+ WARN_UNUSED const TargetResourceMap& GetTargetToOverlayMap() const;
// Retrieves the mapping of overlay resource id to target resource id. This allows a reference to
// an overlay resource to appear as a reference to its corresponding target resource at runtime.
- OverlayResourceMap GetOverlayToTargetMap() const;
-
- // Retrieves the build-time package id of the target package.
- inline uint32_t GetTargetPackageId() const {
- return target_package_id_;
- }
-
- // Retrieves the build-time package id of the overlay package.
- inline uint32_t GetOverlayPackageId() const {
- return overlay_package_id_;
- }
+ WARN_UNUSED const OverlayResourceMap& GetOverlayToTargetMap() const;
// Retrieves the offset that was added to the index of inline string overlay values so the indices
// do not collide with the indices of the overlay resource table string pool.
- inline uint32_t GetStringPoolOffset() const {
- return string_pool_offset_;
- }
+ WARN_UNUSED uint32_t GetStringPoolOffset() const;
// Retrieves the raw string pool data from the xml referenced in android:resourcesMap.
- inline const StringPiece GetStringPoolData() const {
- return StringPiece(reinterpret_cast<const char*>(string_pool_data_.get()),
- string_pool_data_length_);
- }
+ WARN_UNUSED StringPiece GetStringPoolData() const;
private:
ResourceMapping() = default;
- // Maps a target resource id to an overlay resource id.
- // If rewrite_overlay_reference is `true` then references to the overlay
- // resource should appear as a reference to its corresponding target resource at runtime.
- Result<Unit> AddMapping(ResourceId target_resource, ResourceId overlay_resource,
- bool rewrite_overlay_reference);
-
- // Maps a target resource id to a data type and value combination.
- // The `data_type` is the runtime format of the data value (see Res_value::dataType).
- Result<Unit> AddMapping(ResourceId target_resource, TargetValue::DataType data_type,
- TargetValue::DataValue data_value);
-
- // Removes the overlay value mapping for the target resource.
- void RemoveMapping(ResourceId target_resource);
-
- // Parses the mapping of target resources to overlay resources to generate a ResourceMapping.
- static Result<ResourceMapping> CreateResourceMapping(const AssetManager2* target_am,
- const LoadedPackage* target_package,
- const LoadedPackage* overlay_package,
- size_t string_pool_offset,
- const XmlParser& overlay_parser,
- LogInfo& log_info);
-
- // Generates a ResourceMapping that maps target resources to overlay resources by name. To overlay
- // a target resource, a resource must exist in the overlay with the same type and entry name as
- // the target resource.
- static Result<ResourceMapping> CreateResourceMappingLegacy(const AssetManager2* target_am,
- const AssetManager2* overlay_am,
- const LoadedPackage* target_package,
- const LoadedPackage* overlay_package,
- LogInfo& log_info);
-
- // Removes resources that do not pass policy or overlayable checks of the target package.
- void FilterOverlayableResources(const AssetManager2* target_am,
- const LoadedPackage* target_package,
- const LoadedPackage* overlay_package,
- const OverlayManifestInfo& overlay_info,
- const PolicyBitmask& fulfilled_policies, LogInfo& log_info);
+ // Maps a target resource id to an overlay resource id or a android::Res_value value.
+ //
+ // If `allow_rewriting_` is true, then the overlay-to-target map will be populated if the target
+ // resource id is mapped to an overlay resource id.
+ Result<Unit> AddMapping(ResourceId target_resource,
+ const std::variant<OverlayData::ResourceIdValue, TargetValue>& value);
TargetResourceMap target_map_;
- std::multimap<ResourceId, ResourceId> overlay_map_;
-
- uint32_t target_package_id_ = 0;
- uint32_t overlay_package_id_ = 0;
+ OverlayResourceMap overlay_map_;
uint32_t string_pool_offset_ = 0;
uint32_t string_pool_data_length_ = 0;
std::unique_ptr<uint8_t[]> string_pool_data_ = nullptr;
};
+inline const TargetResourceMap& ResourceMapping::GetTargetToOverlayMap() const {
+ return target_map_;
+}
+
+inline const OverlayResourceMap& ResourceMapping::GetOverlayToTargetMap() const {
+ return overlay_map_;
+}
+
+inline uint32_t ResourceMapping::GetStringPoolOffset() const {
+ return string_pool_offset_;
+}
+
+inline StringPiece ResourceMapping::GetStringPoolData() const {
+ return StringPiece(reinterpret_cast<const char*>(string_pool_data_.get()),
+ string_pool_data_length_);
+}
+
} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index cd14d3e7254c..a0202dfee473 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -22,19 +22,26 @@
#include "androidfw/AssetManager2.h"
#include "idmap2/Result.h"
-#include "idmap2/ZipFile.h"
namespace android::idmap2 {
-// use typedefs to let the compiler warn us about implicit casts
-typedef uint32_t ResourceId; // 0xpptteeee
-typedef uint8_t PackageId; // pp in 0xpptteeee
-typedef uint8_t TypeId; // tt in 0xpptteeee
-typedef uint16_t EntryId; // eeee in 0xpptteeee
-
#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
+// use typedefs to let the compiler warn us about implicit casts
+using ResourceId = uint32_t; // 0xpptteeee
+using PackageId = uint8_t; // pp in 0xpptteeee
+using TypeId = uint8_t; // tt in 0xpptteeee
+using EntryId = uint16_t; // eeee in 0xpptteeee
+
+using DataType = uint8_t; // Res_value::dataType
+using DataValue = uint32_t; // Res_value::data
+
+struct TargetValue {
+ DataType data_type;
+ DataValue data_value;
+};
+
namespace utils {
// Returns whether the Res_value::data_type represents a dynamic or regular resource reference.
@@ -43,20 +50,10 @@ bool IsReference(uint8_t data_type);
// Converts the Res_value::data_type to a human-readable string representation.
StringPiece DataTypeToString(uint8_t data_type);
-struct OverlayManifestInfo {
- std::string name; // NOLINT(misc-non-private-member-variables-in-classes)
- std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes)
- std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes)
- uint32_t resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes)
-};
-
-Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
- const std::string& name);
-
+// Retrieves the type and entry name of the resource in the AssetManager in the form type/entry.
Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid);
} // namespace utils
-
} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/XmlParser.h b/cmds/idmap2/include/idmap2/XmlParser.h
index 1c74ab3bb691..c968a5e6c04f 100644
--- a/cmds/idmap2/include/idmap2/XmlParser.h
+++ b/cmds/idmap2/include/idmap2/XmlParser.h
@@ -30,8 +30,7 @@
namespace android::idmap2 {
-class XmlParser {
- public:
+struct XmlParser {
using Event = ResXMLParser::event_code_t;
class iterator;
@@ -127,23 +126,19 @@ class XmlParser {
};
// Creates a new xml parser beginning at the first tag.
- static Result<std::unique_ptr<const XmlParser>> Create(const void* data, size_t size,
- bool copy_data = false);
- ~XmlParser();
+ static Result<XmlParser> Create(const void* data, size_t size, bool copy_data = false);
inline iterator tree_iterator() const {
- return iterator(tree_);
+ return iterator(*tree_);
}
inline const ResStringPool& get_strings() const {
- return tree_.getStrings();
+ return tree_->getStrings();
}
private:
- XmlParser() = default;
- mutable ResXMLTree tree_;
-
- DISALLOW_COPY_AND_ASSIGN(XmlParser);
+ explicit XmlParser(std::unique_ptr<ResXMLTree> tree);
+ mutable std::unique_ptr<ResXMLTree> tree_;
};
} // namespace android::idmap2
diff --git a/cmds/idmap2/include/idmap2/ZipFile.h b/cmds/idmap2/include/idmap2/ZipFile.h
deleted file mode 100644
index 8f50e36256f6..000000000000
--- a/cmds/idmap2/include/idmap2/ZipFile.h
+++ /dev/null
@@ -1,60 +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.
- */
-
-#ifndef IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_
-#define IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_
-
-#include <memory>
-#include <string>
-
-#include "android-base/macros.h"
-#include "idmap2/Result.h"
-#include "ziparchive/zip_archive.h"
-
-namespace android::idmap2 {
-
-struct MemoryChunk {
- size_t size;
- uint8_t buf[0];
-
- static std::unique_ptr<MemoryChunk> Allocate(size_t size);
-
- private:
- MemoryChunk() {
- }
-};
-
-class ZipFile {
- public:
- static std::unique_ptr<const ZipFile> Open(const std::string& path);
-
- std::unique_ptr<const MemoryChunk> Uncompress(const std::string& entryPath) const;
- Result<uint32_t> Crc(const std::string& entryPath) const;
-
- ~ZipFile();
-
- private:
- explicit ZipFile(const ::ZipArchiveHandle handle) : handle_(handle) {
- }
-
- const ::ZipArchiveHandle handle_;
-
- DISALLOW_COPY_AND_ASSIGN(ZipFile);
-};
-
-} // namespace android::idmap2
-
-#endif // IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_
diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
index c16310792d12..3bbe9d91deb6 100644
--- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -87,10 +87,6 @@ void BinaryStreamVisitor::visit(const IdmapData& data) {
}
void BinaryStreamVisitor::visit(const IdmapData::Header& header) {
- Write8(header.GetTargetPackageId());
- Write8(header.GetOverlayPackageId());
- Write8(0U); // padding
- Write8(0U); // padding
Write32(header.GetTargetEntryCount());
Write32(header.GetTargetInlineEntryCount());
Write32(header.GetOverlayEntryCount());
diff --git a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp
new file mode 100644
index 000000000000..4f61801aa594
--- /dev/null
+++ b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp
@@ -0,0 +1,302 @@
+/*
+ * 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 "idmap2/FabricatedOverlay.h"
+
+#include <androidfw/ResourceUtils.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <utils/ByteOrder.h>
+#include <zlib.h>
+
+#include <fstream>
+
+namespace android::idmap2 {
+
+namespace {
+bool Read32(std::istream& stream, uint32_t* out) {
+ uint32_t value;
+ if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) {
+ *out = dtohl(value);
+ return true;
+ }
+ return false;
+}
+
+void Write32(std::ostream& stream, uint32_t value) {
+ uint32_t x = htodl(value);
+ stream.write(reinterpret_cast<char*>(&x), sizeof(uint32_t));
+}
+} // namespace
+
+FabricatedOverlay::FabricatedOverlay(pb::FabricatedOverlay&& overlay,
+ std::optional<uint32_t> crc_from_disk)
+ : overlay_pb_(std::forward<pb::FabricatedOverlay>(overlay)), crc_from_disk_(crc_from_disk) {
+}
+
+FabricatedOverlay::Builder::Builder(const std::string& package_name, const std::string& name,
+ const std::string& target_package_name) {
+ package_name_ = package_name;
+ name_ = name;
+ target_package_name_ = target_package_name;
+}
+
+FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetOverlayable(const std::string& name) {
+ target_overlayable_ = name;
+ return *this;
+}
+
+FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
+ const std::string& resource_name, uint8_t data_type, uint32_t data_value) {
+ entries_.emplace_back(Entry{resource_name, data_type, data_value});
+ return *this;
+}
+
+Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() {
+ std::map<std::string, std::map<std::string, std::map<std::string, TargetValue>>> entries;
+ for (const auto& res_entry : entries_) {
+ StringPiece package_substr;
+ StringPiece type_name;
+ StringPiece entry_name;
+ if (!android::ExtractResourceName(StringPiece(res_entry.resource_name), &package_substr,
+ &type_name, &entry_name)) {
+ return Error("failed to parse resource name '%s'", res_entry.resource_name.c_str());
+ }
+
+ std::string package_name =
+ package_substr.empty() ? target_package_name_ : package_substr.to_string();
+ if (type_name.empty()) {
+ return Error("resource name '%s' missing type name", res_entry.resource_name.c_str());
+ }
+
+ if (entry_name.empty()) {
+ return Error("resource name '%s' missing entry name", res_entry.resource_name.c_str());
+ }
+
+ auto package = entries.find(package_name);
+ if (package == entries.end()) {
+ package = entries
+ .insert(std::make_pair<>(
+ package_name, std::map<std::string, std::map<std::string, TargetValue>>()))
+ .first;
+ }
+
+ auto type = package->second.find(type_name.to_string());
+ if (type == package->second.end()) {
+ type =
+ package->second
+ .insert(std::make_pair<>(type_name.to_string(), std::map<std::string, TargetValue>()))
+ .first;
+ }
+
+ auto entry = type->second.find(entry_name.to_string());
+ if (entry == type->second.end()) {
+ entry = type->second.insert(std::make_pair<>(entry_name.to_string(), TargetValue())).first;
+ }
+
+ entry->second = TargetValue{res_entry.data_type, res_entry.data_value};
+ }
+
+ pb::FabricatedOverlay overlay_pb;
+ overlay_pb.set_package_name(package_name_);
+ overlay_pb.set_name(name_);
+ overlay_pb.set_target_package_name(target_package_name_);
+ overlay_pb.set_target_overlayable(target_overlayable_);
+
+ for (const auto& package : entries) {
+ auto package_pb = overlay_pb.add_packages();
+ package_pb->set_name(package.first);
+
+ for (const auto& type : package.second) {
+ auto type_pb = package_pb->add_types();
+ type_pb->set_name(type.first);
+
+ for (const auto& entry : type.second) {
+ auto entry_pb = type_pb->add_entries();
+ entry_pb->set_name(entry.first);
+ pb::ResourceValue* value = entry_pb->mutable_res_value();
+ value->set_data_type(entry.second.data_type);
+ value->set_data_value(entry.second.data_value);
+ }
+ }
+ }
+
+ return FabricatedOverlay(std::move(overlay_pb));
+}
+
+Result<FabricatedOverlay> FabricatedOverlay::FromBinaryStream(std::istream& stream) {
+ uint32_t magic;
+ if (!Read32(stream, &magic)) {
+ return Error("Failed to read fabricated overlay magic.");
+ }
+
+ if (magic != kFabricatedOverlayMagic) {
+ return Error("Not a fabricated overlay file.");
+ }
+
+ uint32_t version;
+ if (!Read32(stream, &version)) {
+ return Error("Failed to read fabricated overlay version.");
+ }
+
+ if (version != 1) {
+ return Error("Invalid fabricated overlay version '%u'.", version);
+ }
+
+ uint32_t crc;
+ if (!Read32(stream, &crc)) {
+ return Error("Failed to read fabricated overlay version.");
+ }
+
+ pb::FabricatedOverlay overlay{};
+ if (!overlay.ParseFromIstream(&stream)) {
+ return Error("Failed read fabricated overlay proto.");
+ }
+
+ // If the proto version is the latest version, then the contents of the proto must be the same
+ // when the proto is re-serialized; otherwise, the crc must be calculated because migrating the
+ // proto to the latest version will likely change the contents of the fabricated overlay.
+ return FabricatedOverlay(std::move(overlay), version == kFabricatedOverlayCurrentVersion
+ ? std::optional<uint32_t>(crc)
+ : std::nullopt);
+}
+
+Result<FabricatedOverlay::SerializedData*> FabricatedOverlay::InitializeData() const {
+ if (!data_.has_value()) {
+ auto size = overlay_pb_.ByteSizeLong();
+ auto data = std::unique_ptr<uint8_t[]>(new uint8_t[size]);
+
+ // Ensure serialization is deterministic
+ google::protobuf::io::ArrayOutputStream array_stream(data.get(), size);
+ google::protobuf::io::CodedOutputStream output_stream(&array_stream);
+ output_stream.SetSerializationDeterministic(true);
+ overlay_pb_.SerializeWithCachedSizes(&output_stream);
+ if (output_stream.HadError() || size != output_stream.ByteCount()) {
+ return Error("Failed to serialize fabricated overlay.");
+ }
+
+ // Calculate the crc using the proto data and the version.
+ uint32_t crc = crc32(0L, Z_NULL, 0);
+ crc = crc32(crc, reinterpret_cast<const uint8_t*>(&kFabricatedOverlayCurrentVersion),
+ sizeof(uint32_t));
+ crc = crc32(crc, data.get(), size);
+ data_ = SerializedData{std::move(data), size, crc};
+ }
+ return &(*data_);
+}
+Result<uint32_t> FabricatedOverlay::GetCrc() const {
+ if (crc_from_disk_.has_value()) {
+ return *crc_from_disk_;
+ }
+ auto data = InitializeData();
+ if (!data) {
+ return data.GetError();
+ }
+ return (*data)->crc;
+}
+
+Result<Unit> FabricatedOverlay::ToBinaryStream(std::ostream& stream) const {
+ auto data = InitializeData();
+ if (!data) {
+ return data.GetError();
+ }
+
+ Write32(stream, kFabricatedOverlayMagic);
+ Write32(stream, kFabricatedOverlayCurrentVersion);
+ Write32(stream, (*data)->crc);
+ stream.write(reinterpret_cast<const char*>((*data)->data.get()), (*data)->data_size);
+ if (stream.bad()) {
+ return Error("Failed to write serialized fabricated overlay.");
+ }
+
+ return Unit{};
+}
+
+using FabContainer = FabricatedOverlayContainer;
+FabContainer::FabricatedOverlayContainer(FabricatedOverlay&& overlay, std::string&& path)
+ : overlay_(std::forward<FabricatedOverlay>(overlay)), path_(std::forward<std::string>(path)) {
+}
+
+FabContainer::~FabricatedOverlayContainer() = default;
+
+Result<std::unique_ptr<FabContainer>> FabContainer::FromPath(std::string path) {
+ std::fstream fin(path);
+ auto overlay = FabricatedOverlay::FromBinaryStream(fin);
+ if (!overlay) {
+ return overlay.GetError();
+ }
+ return std::unique_ptr<FabContainer>(
+ new FabricatedOverlayContainer(std::move(*overlay), std::move(path)));
+}
+
+std::unique_ptr<FabricatedOverlayContainer> FabContainer::FromOverlay(FabricatedOverlay&& overlay) {
+ return std::unique_ptr<FabContainer>(
+ new FabricatedOverlayContainer(std::move(overlay), {} /* path */));
+}
+
+OverlayManifestInfo FabContainer::GetManifestInfo() const {
+ const pb::FabricatedOverlay& overlay_pb = overlay_.overlay_pb_;
+ return OverlayManifestInfo{
+ .package_name = overlay_pb.package_name(),
+ .name = overlay_pb.name(),
+ .target_package = overlay_pb.target_package_name(),
+ .target_name = overlay_pb.target_overlayable(),
+ };
+}
+
+Result<OverlayManifestInfo> FabContainer::FindOverlayInfo(const std::string& name) const {
+ const OverlayManifestInfo info = GetManifestInfo();
+ if (name != info.name) {
+ return Error("Failed to find name '%s' in fabricated overlay", name.c_str());
+ }
+ return info;
+}
+
+Result<OverlayData> FabContainer::GetOverlayData(const OverlayManifestInfo& info) const {
+ const pb::FabricatedOverlay& overlay_pb = overlay_.overlay_pb_;
+ if (info.name != overlay_pb.name()) {
+ return Error("Failed to find name '%s' in fabricated overlay", info.name.c_str());
+ }
+
+ OverlayData result{};
+ for (const auto& package : overlay_pb.packages()) {
+ for (const auto& type : package.types()) {
+ for (const auto& entry : type.entries()) {
+ auto name = base::StringPrintf("%s:%s/%s", package.name().c_str(), type.name().c_str(),
+ entry.name().c_str());
+ const auto& res_value = entry.res_value();
+ result.pairs.emplace_back(OverlayData::Value{
+ name, TargetValue{.data_type = static_cast<uint8_t>(res_value.data_type()),
+ .data_value = res_value.data_value()}});
+ }
+ }
+ }
+ return result;
+}
+
+Result<uint32_t> FabContainer::GetCrc() const {
+ return overlay_.GetCrc();
+}
+
+const std::string& FabContainer::GetPath() const {
+ return path_;
+}
+
+Result<std::string> FabContainer::GetResourceName(ResourceId /* id */) const {
+ return Error("Fabricated overlay does not contain resources.");
+}
+
+} // namespace android::idmap2 \ No newline at end of file
diff --git a/cmds/idmap2/libidmap2/FileUtils.cpp b/cmds/idmap2/libidmap2/FileUtils.cpp
index 3af1f70ebe39..98a4ceabdb94 100644
--- a/cmds/idmap2/libidmap2/FileUtils.cpp
+++ b/cmds/idmap2/libidmap2/FileUtils.cpp
@@ -47,4 +47,19 @@ bool UidHasWriteAccessToPath(uid_t uid ATTRIBUTE_UNUSED, const std::string& path
}
#endif
+std::string RandomStringForPath(const size_t length) {
+ constexpr char kChars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ constexpr size_t kCharLastIndex = sizeof(kChars) - 1;
+
+ std::string out_rand;
+ out_rand.reserve(length);
+
+ std::random_device rd;
+ std::uniform_int_distribution<int> dist(0, kCharLastIndex);
+ for (size_t i = 0; i < length; i++) {
+ out_rand[i] = kChars[dist(rd) % (kCharLastIndex)];
+ }
+ return out_rand;
+}
+
} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index a0cc407fc948..6515d5516d83 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -20,23 +20,16 @@
#include <iostream>
#include <iterator>
#include <limits>
-#include <map>
#include <memory>
-#include <set>
#include <string>
#include <utility>
-#include <vector>
#include "android-base/macros.h"
-#include "android-base/stringprintf.h"
#include "androidfw/AssetManager2.h"
#include "idmap2/ResourceMapping.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
-#include "idmap2/ZipFile.h"
-#include "utils/String16.h"
-#include "utils/String8.h"
namespace android::idmap2 {
@@ -93,22 +86,13 @@ bool WARN_UNUSED ReadString(std::istream& stream, std::string* out) {
} // namespace
-Result<uint32_t> GetPackageCrc(const ZipFile& zip) {
- const Result<uint32_t> a = zip.Crc("resources.arsc");
- const Result<uint32_t> b = zip.Crc("AndroidManifest.xml");
- return a && b
- ? Result<uint32_t>(*a ^ *b)
- : Error("failed to get CRC for \"%s\"", a ? "AndroidManifest.xml" : "resources.arsc");
-}
-
std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) {
std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader());
if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_)) {
return nullptr;
}
- if (idmap_header->magic_ != kIdmapMagic ||
- idmap_header->version_ != kIdmapCurrentVersion) {
+ if (idmap_header->magic_ != kIdmapMagic || idmap_header->version_ != kIdmapCurrentVersion) {
// Do not continue parsing if the file is not a current version idmap.
return nullptr;
}
@@ -127,32 +111,22 @@ std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& s
return std::move(idmap_header);
}
-Result<Unit> IdmapHeader::IsUpToDate(const std::string& target_path,
- const std::string& overlay_path,
+Result<Unit> IdmapHeader::IsUpToDate(const TargetResourceContainer& target,
+ const OverlayResourceContainer& overlay,
const std::string& overlay_name,
PolicyBitmask fulfilled_policies,
bool enforce_overlayable) const {
- const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_path);
- if (!target_zip) {
- return Error("failed to open target %s", target_path.c_str());
- }
-
- const Result<uint32_t> target_crc = GetPackageCrc(*target_zip);
+ const Result<uint32_t> target_crc = target.GetCrc();
if (!target_crc) {
return Error("failed to get target crc");
}
- const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_path);
- if (!overlay_zip) {
- return Error("failed to overlay target %s", overlay_path.c_str());
- }
-
- const Result<uint32_t> overlay_crc = GetPackageCrc(*overlay_zip);
+ const Result<uint32_t> overlay_crc = overlay.GetCrc();
if (!overlay_crc) {
return Error("failed to get overlay crc");
}
- return IsUpToDate(target_path, overlay_path, overlay_name, *target_crc, *overlay_crc,
+ return IsUpToDate(target.GetPath(), overlay.GetPath(), overlay_name, *target_crc, *overlay_crc,
fulfilled_policies, enforce_overlayable);
}
@@ -209,11 +183,7 @@ Result<Unit> IdmapHeader::IsUpToDate(const std::string& target_path,
std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) {
std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
-
- uint8_t padding;
- if (!Read8(stream, &idmap_data_header->target_package_id_) ||
- !Read8(stream, &idmap_data_header->overlay_package_id_) || !Read8(stream, &padding) ||
- !Read8(stream, &padding) || !Read32(stream, &idmap_data_header->target_entry_count) ||
+ if (!Read32(stream, &idmap_data_header->target_entry_count) ||
!Read32(stream, &idmap_data_header->target_entry_inline_count) ||
!Read32(stream, &idmap_data_header->overlay_entry_count) ||
!Read32(stream, &idmap_data_header->string_pool_index_offset)) {
@@ -321,8 +291,6 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
}
std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
- data_header->target_package_id_ = resource_mapping.GetTargetPackageId();
- data_header->overlay_package_id_ = resource_mapping.GetOverlayPackageId();
data_header->target_entry_count = static_cast<uint32_t>(data->target_entries_.size());
data_header->target_entry_inline_count =
static_cast<uint32_t>(data->target_inline_entries_.size());
@@ -332,57 +300,46 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
return {std::move(data)};
}
-Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const ApkAssets& target_apk_assets,
- const ApkAssets& overlay_apk_assets,
- const std::string& overlay_name,
- const PolicyBitmask& fulfilled_policies,
- bool enforce_overlayable) {
+Result<std::unique_ptr<const Idmap>> Idmap::FromContainers(const TargetResourceContainer& target,
+ const OverlayResourceContainer& overlay,
+ const std::string& overlay_name,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable) {
SYSTRACE << "Idmap::FromApkAssets";
- const std::string& target_apk_path = target_apk_assets.GetPath();
- const std::string& overlay_apk_path = overlay_apk_assets.GetPath();
-
- const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_apk_path);
- if (!target_zip) {
- return Error("failed to open target as zip");
- }
-
- const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_apk_path);
- if (!overlay_zip) {
- return Error("failed to open overlay as zip");
- }
-
std::unique_ptr<IdmapHeader> header(new IdmapHeader());
header->magic_ = kIdmapMagic;
header->version_ = kIdmapCurrentVersion;
- Result<uint32_t> crc = GetPackageCrc(*target_zip);
- if (!crc) {
- return Error(crc.GetError(), "failed to get zip CRC for target");
+ const auto target_crc = target.GetCrc();
+ if (!target_crc) {
+ return Error(target_crc.GetError(), "failed to get zip CRC for '%s'", target.GetPath().data());
}
- header->target_crc_ = *crc;
+ header->target_crc_ = *target_crc;
- crc = GetPackageCrc(*overlay_zip);
- if (!crc) {
- return Error(crc.GetError(), "failed to get zip CRC for overlay");
+ const auto overlay_crc = overlay.GetCrc();
+ if (!overlay_crc) {
+ return Error(overlay_crc.GetError(), "failed to get zip CRC for '%s'",
+ overlay.GetPath().data());
}
- header->overlay_crc_ = *crc;
+ header->overlay_crc_ = *overlay_crc;
+
header->fulfilled_policies_ = fulfilled_policies;
header->enforce_overlayable_ = enforce_overlayable;
- header->target_path_ = target_apk_path;
- header->overlay_path_ = overlay_apk_path;
+ header->target_path_ = target.GetPath();
+ header->overlay_path_ = overlay.GetPath();
header->overlay_name_ = overlay_name;
- auto info = utils::ExtractOverlayManifestInfo(overlay_apk_path, overlay_name);
+ auto info = overlay.FindOverlayInfo(overlay_name);
if (!info) {
- return info.GetError();
+ return Error(info.GetError(), "failed to get overlay info for '%s'", overlay.GetPath().data());
}
LogInfo log_info;
- auto resource_mapping =
- ResourceMapping::FromApkAssets(target_apk_assets, overlay_apk_assets, *info,
- fulfilled_policies, enforce_overlayable, log_info);
+ auto resource_mapping = ResourceMapping::FromContainers(
+ target, overlay, *info, fulfilled_policies, enforce_overlayable, log_info);
if (!resource_mapping) {
- return resource_mapping.GetError();
+ return Error(resource_mapping.GetError(), "failed to generate resource map for '%s'",
+ overlay.GetPath().data());
}
auto idmap_data = IdmapData::FromResourceMapping(*resource_mapping);
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index 7e090a983f95..721612cc567b 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -27,8 +27,6 @@
namespace android::idmap2 {
-#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry))
-
#define TAB " "
void PrettyPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
@@ -36,8 +34,8 @@ void PrettyPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
void PrettyPrintVisitor::visit(const IdmapHeader& header) {
stream_ << "Paths:" << std::endl
- << TAB "target apk path : " << header.GetTargetPath() << std::endl
- << TAB "overlay apk path : " << header.GetOverlayPath() << std::endl;
+ << TAB "target path : " << header.GetTargetPath() << std::endl
+ << TAB "overlay path : " << header.GetOverlayPath() << std::endl;
if (!header.GetOverlayName().empty()) {
stream_ << "Overlay name: " << header.GetOverlayName() << std::endl;
@@ -53,14 +51,11 @@ void PrettyPrintVisitor::visit(const IdmapHeader& header) {
}
}
- if (auto target_apk_ = ApkAssets::Load(header.GetTargetPath())) {
- target_am_.SetApkAssets({target_apk_.get()});
- apk_assets_.push_back(std::move(target_apk_));
+ if (auto target = TargetResourceContainer::FromPath(header.GetTargetPath())) {
+ target_ = std::move(*target);
}
-
- if (auto overlay_apk = ApkAssets::Load(header.GetOverlayPath())) {
- overlay_am_.SetApkAssets({overlay_apk.get()});
- apk_assets_.push_back(std::move(overlay_apk));
+ if (auto overlay = OverlayResourceContainer::FromPath(header.GetOverlayPath())) {
+ overlay_ = std::move(*overlay);
}
stream_ << "Mapping:" << std::endl;
@@ -72,23 +67,20 @@ void PrettyPrintVisitor::visit(const IdmapData::Header& header ATTRIBUTE_UNUSED)
void PrettyPrintVisitor::visit(const IdmapData& data) {
static constexpr const char* kUnknownResourceName = "???";
- const bool target_package_loaded = !target_am_.GetApkAssets().empty();
- const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty();
-
const ResStringPool string_pool(data.GetStringPoolData().data(), data.GetStringPoolData().size());
const size_t string_pool_offset = data.GetHeader()->GetStringPoolIndexOffset();
for (const auto& target_entry : data.GetTargetEntries()) {
std::string target_name = kUnknownResourceName;
- if (target_package_loaded) {
- if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) {
+ if (target_ != nullptr) {
+ if (auto name = target_->GetResourceName(target_entry.target_id)) {
target_name = *name;
}
}
std::string overlay_name = kUnknownResourceName;
- if (overlay_package_loaded) {
- if (auto name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id)) {
+ if (overlay_ != nullptr) {
+ if (auto name = overlay_->GetResourceName(target_entry.overlay_id)) {
overlay_name = *name;
}
}
@@ -112,8 +104,8 @@ void PrettyPrintVisitor::visit(const IdmapData& data) {
}
std::string target_name = kUnknownResourceName;
- if (target_package_loaded) {
- if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) {
+ if (target_ != nullptr) {
+ if (auto name = target_->GetResourceName(target_entry.target_id)) {
target_name = *name;
}
}
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index b517aa3a0c01..a016a36a2e3d 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -18,16 +18,13 @@
#include <algorithm>
#include <cstdarg>
-#include <string>
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
-#include "androidfw/ApkAssets.h"
#include "idmap2/PolicyUtils.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
-using android::ApkAssets;
using android::idmap2::policy::PoliciesToDebugString;
namespace android::idmap2 {
@@ -48,27 +45,19 @@ void RawPrintVisitor::visit(const IdmapHeader& header) {
print(header.GetOverlayName(), true /* print_value */, "overlay name");
print(header.GetDebugInfo(), false /* print_value */, "debug info");
- auto target_apk_ = ApkAssets::Load(header.GetTargetPath());
- if (target_apk_) {
- target_am_.SetApkAssets({target_apk_.get()});
- apk_assets_.push_back(std::move(target_apk_));
+ if (auto target = TargetResourceContainer::FromPath(header.GetTargetPath())) {
+ target_ = std::move(*target);
}
-
- auto overlay_apk_ = ApkAssets::Load(header.GetOverlayPath());
- if (overlay_apk_) {
- overlay_am_.SetApkAssets({overlay_apk_.get()});
- apk_assets_.push_back(std::move(overlay_apk_));
+ if (auto overlay = OverlayResourceContainer::FromPath(header.GetOverlayPath())) {
+ overlay_ = std::move(*overlay);
}
}
void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
- const bool target_package_loaded = !target_am_.GetApkAssets().empty();
- const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty();
-
for (auto& target_entry : data.GetTargetEntries()) {
Result<std::string> target_name(Error(""));
- if (target_package_loaded) {
- target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
+ if (target_ != nullptr) {
+ target_name = target_->GetResourceName(target_entry.target_id);
}
if (target_name) {
print(target_entry.target_id, "target id: %s", target_name->c_str());
@@ -77,8 +66,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
}
Result<std::string> overlay_name(Error(""));
- if (overlay_package_loaded) {
- overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id);
+ if (overlay_ != nullptr) {
+ overlay_name = overlay_->GetResourceName(target_entry.overlay_id);
}
if (overlay_name) {
print(target_entry.overlay_id, "overlay id: %s", overlay_name->c_str());
@@ -89,8 +78,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
for (auto& target_entry : data.GetTargetInlineEntries()) {
Result<std::string> target_name(Error(""));
- if (target_package_loaded) {
- target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
+ if (target_ != nullptr) {
+ target_name = target_->GetResourceName(target_entry.target_id);
}
if (target_name) {
print(target_entry.target_id, "target id: %s", target_name->c_str());
@@ -104,10 +93,10 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
utils::DataTypeToString(target_entry.value.data_type).data());
Result<std::string> overlay_name(Error(""));
- if (overlay_package_loaded &&
+ if (overlay_ != nullptr &&
(target_entry.value.data_value == Res_value::TYPE_REFERENCE ||
target_entry.value.data_value == Res_value::TYPE_DYNAMIC_REFERENCE)) {
- overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.value.data_value);
+ overlay_name = overlay_->GetResourceName(target_entry.value.data_value);
}
if (overlay_name) {
@@ -119,8 +108,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
for (auto& overlay_entry : data.GetOverlayEntries()) {
Result<std::string> overlay_name(Error(""));
- if (overlay_package_loaded) {
- overlay_name = utils::ResToTypeEntryName(overlay_am_, overlay_entry.overlay_id);
+ if (overlay_ != nullptr) {
+ overlay_name = overlay_->GetResourceName(overlay_entry.overlay_id);
}
if (overlay_name) {
@@ -130,8 +119,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
}
Result<std::string> target_name(Error(""));
- if (target_package_loaded) {
- target_name = utils::ResToTypeEntryName(target_am_, overlay_entry.target_id);
+ if (target_ != nullptr) {
+ target_name = target_->GetResourceName(overlay_entry.target_id);
}
if (target_name) {
@@ -145,9 +134,6 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
}
void RawPrintVisitor::visit(const IdmapData::Header& header) {
- print(header.GetTargetPackageId(), "target package id");
- print(header.GetOverlayPackageId(), "overlay package id");
- align();
print(header.GetTargetEntryCount(), "target entry count");
print(header.GetTargetInlineEntryCount(), "target inline entry count");
print(header.GetOverlayEntryCount(), "overlay entry count");
diff --git a/cmds/idmap2/libidmap2/ResourceContainer.cpp b/cmds/idmap2/libidmap2/ResourceContainer.cpp
new file mode 100644
index 000000000000..9147ccaaa17a
--- /dev/null
+++ b/cmds/idmap2/libidmap2/ResourceContainer.cpp
@@ -0,0 +1,448 @@
+/*
+ * 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 "idmap2/ResourceContainer.h"
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager.h"
+#include "androidfw/Util.h"
+#include "idmap2/FabricatedOverlay.h"
+#include "idmap2/XmlParser.h"
+
+namespace android::idmap2 {
+namespace {
+#define REWRITE_PACKAGE(resid, package_id) \
+ (((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U))
+
+#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
+
+constexpr ResourceId kAttrName = 0x01010003;
+constexpr ResourceId kAttrResourcesMap = 0x01010609;
+constexpr ResourceId kAttrTargetName = 0x0101044d;
+constexpr ResourceId kAttrTargetPackage = 0x01010021;
+
+// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
+// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
+// this assumption tends to work out. That said, the correct thing to do is to scan
+// resources.arsc for a package with a given name as read from the package manifest instead of
+// relying on a hard-coded index. This however requires storing the package name in the idmap
+// header, which in turn requires incrementing the idmap version. Because the initial version of
+// idmap2 is compatible with idmap, this will have to wait for now.
+const LoadedPackage* GetPackageAtIndex0(const LoadedArsc* loaded_arsc) {
+ const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
+ if (packages.empty()) {
+ return nullptr;
+ }
+ return loaded_arsc->GetPackageById(packages[0]->GetPackageId());
+}
+
+Result<uint32_t> CalculateCrc(const ZipAssetsProvider* zip_assets) {
+ constexpr const char* kResourcesArsc = "resources.arsc";
+ std::optional<uint32_t> res_crc = zip_assets->GetCrc(kResourcesArsc);
+ if (!res_crc) {
+ return Error("failed to get CRC for '%s'", kResourcesArsc);
+ }
+
+ constexpr const char* kManifest = "AndroidManifest.xml";
+ std::optional<uint32_t> man_crc = zip_assets->GetCrc(kManifest);
+ if (!man_crc) {
+ return Error("failed to get CRC for '%s'", kManifest);
+ }
+
+ return *res_crc ^ *man_crc;
+}
+
+Result<XmlParser> OpenXmlParser(const std::string& entry_path, const ZipAssetsProvider* zip) {
+ auto manifest = zip->Open(entry_path);
+ if (manifest == nullptr) {
+ return Error("failed to find %s ", entry_path.c_str());
+ }
+
+ auto size = manifest->getLength();
+ auto buffer = manifest->getIncFsBuffer(true /* aligned */).convert<uint8_t>();
+ if (!buffer.verify(size)) {
+ return Error("failed to read entire %s", entry_path.c_str());
+ }
+
+ return XmlParser::Create(buffer.unsafe_ptr(), size, true /* copyData */);
+}
+
+Result<XmlParser> OpenXmlParser(ResourceId id, const ZipAssetsProvider* zip,
+ const AssetManager2* am) {
+ const auto ref_table = am->GetDynamicRefTableForCookie(0);
+ if (ref_table == nullptr) {
+ return Error("failed to find dynamic ref table for cookie 0");
+ }
+
+ ref_table->lookupResourceId(&id);
+ auto value = am->GetResource(id);
+ if (!value.has_value()) {
+ return Error("failed to find resource for id 0x%08x", id);
+ }
+
+ if (value->type != Res_value::TYPE_STRING) {
+ return Error("resource for is 0x%08x is not a file", id);
+ }
+
+ auto string_pool = am->GetStringPoolForCookie(value->cookie);
+ auto file = string_pool->string8ObjectAt(value->data);
+ if (!file.has_value()) {
+ return Error("failed to find string for index %d", value->data);
+ }
+
+ return OpenXmlParser(file->c_str(), zip);
+}
+
+Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const ZipAssetsProvider* zip,
+ const std::string& name) {
+ Result<XmlParser> xml = OpenXmlParser("AndroidManifest.xml", zip);
+ if (!xml) {
+ return xml.GetError();
+ }
+
+ auto manifest_it = xml->tree_iterator();
+ if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") {
+ return Error("root element tag is not <manifest> in AndroidManifest.xml");
+ }
+
+ std::string package_name;
+ if (auto result_str = manifest_it->GetAttributeStringValue("package")) {
+ package_name = *result_str;
+ } else {
+ return result_str.GetError();
+ }
+
+ for (auto&& it : manifest_it) {
+ if (it.event() != XmlParser::Event::START_TAG || it.name() != "overlay") {
+ continue;
+ }
+
+ OverlayManifestInfo info{};
+ info.package_name = package_name;
+ if (auto result_str = it.GetAttributeStringValue(kAttrName, "android:name")) {
+ if (*result_str != name) {
+ // A value for android:name was found, but either a the name does not match the requested
+ // name, or an <overlay> tag with no name was requested.
+ continue;
+ }
+ info.name = *result_str;
+ } else if (!name.empty()) {
+ // This tag does not have a value for android:name, but an <overlay> tag with a specific name
+ // has been requested.
+ continue;
+ }
+
+ if (auto result_str = it.GetAttributeStringValue(kAttrTargetPackage, "android:targetPackage")) {
+ info.target_package = *result_str;
+ } else {
+ return Error("android:targetPackage missing from <overlay> in AndroidManifest.xml");
+ }
+
+ if (auto result_str = it.GetAttributeStringValue(kAttrTargetName, "android:targetName")) {
+ info.target_name = *result_str;
+ }
+
+ if (auto result_value = it.GetAttributeValue(kAttrResourcesMap, "android:resourcesMap")) {
+ if (utils::IsReference((*result_value).dataType)) {
+ info.resource_mapping = (*result_value).data;
+ } else {
+ return Error("android:resourcesMap is not a reference in AndroidManifest.xml");
+ }
+ }
+ return info;
+ }
+
+ return Error("<overlay> with android:name \"%s\" missing from AndroidManifest.xml", name.c_str());
+}
+
+Result<OverlayData> CreateResourceMapping(ResourceId id, const ZipAssetsProvider* zip,
+ const AssetManager2* overlay_am,
+ const LoadedArsc* overlay_arsc,
+ const LoadedPackage* overlay_package) {
+ auto parser = OpenXmlParser(id, zip, overlay_am);
+ if (!parser) {
+ return parser.GetError();
+ }
+
+ OverlayData overlay_data{};
+ const uint32_t string_pool_offset = overlay_arsc->GetStringPool()->size();
+ const uint8_t package_id = overlay_package->GetPackageId();
+ auto root_it = parser->tree_iterator();
+ if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") {
+ return Error("root element is not <overlay> tag");
+ }
+
+ auto overlay_it_end = root_it.end();
+ for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
+ if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) {
+ return Error("failed to parse overlay xml document");
+ }
+
+ if (overlay_it->event() != XmlParser::Event::START_TAG) {
+ continue;
+ }
+
+ if (overlay_it->name() != "item") {
+ return Error("unexpected tag <%s> in <overlay>", overlay_it->name().c_str());
+ }
+
+ Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target");
+ if (!target_resource) {
+ return Error(R"(<item> tag missing expected attribute "target")");
+ }
+
+ Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value");
+ if (!overlay_resource) {
+ return Error(R"(<item> tag missing expected attribute "value")");
+ }
+
+ if (overlay_resource->dataType == Res_value::TYPE_STRING) {
+ overlay_resource->data += string_pool_offset;
+ }
+
+ if (utils::IsReference(overlay_resource->dataType)) {
+ // Only rewrite resources defined within the overlay package to their corresponding target
+ // resource ids at runtime.
+ bool rewrite_id = package_id == EXTRACT_PACKAGE(overlay_resource->data);
+ overlay_data.pairs.emplace_back(OverlayData::Value{
+ *target_resource, OverlayData::ResourceIdValue{overlay_resource->data, rewrite_id}});
+ } else {
+ overlay_data.pairs.emplace_back(
+ OverlayData::Value{*target_resource, TargetValue{.data_type = overlay_resource->dataType,
+ .data_value = overlay_resource->data}});
+ }
+ }
+
+ const auto& string_pool = parser->get_strings();
+ const uint32_t string_pool_data_length = string_pool.bytes();
+ overlay_data.string_pool_data = OverlayData::InlineStringPoolData{
+ .data = std::unique_ptr<uint8_t[]>(new uint8_t[string_pool_data_length]),
+ .data_length = string_pool_data_length,
+ .string_pool_offset = string_pool_offset,
+ };
+
+ // Overlays should not be incrementally installed, so calling unsafe_ptr is fine here.
+ memcpy(overlay_data.string_pool_data->data.get(), string_pool.data().unsafe_ptr(),
+ string_pool_data_length);
+ return overlay_data;
+}
+
+OverlayData CreateResourceMappingLegacy(const AssetManager2* overlay_am,
+ const LoadedPackage* overlay_package) {
+ OverlayData overlay_data{};
+ for (const ResourceId overlay_resid : *overlay_package) {
+ if (auto name = utils::ResToTypeEntryName(*overlay_am, overlay_resid)) {
+ // Disable rewriting. Overlays did not support internal references before
+ // android:resourcesMap. Do not introduce new behavior.
+ overlay_data.pairs.emplace_back(OverlayData::Value{
+ *name, OverlayData::ResourceIdValue{overlay_resid, false /* rewrite_id */}});
+ }
+ }
+ return overlay_data;
+}
+
+struct ResState {
+ std::unique_ptr<ApkAssets> apk_assets;
+ const LoadedArsc* arsc;
+ const LoadedPackage* package;
+ std::unique_ptr<AssetManager2> am;
+ ZipAssetsProvider* zip_assets;
+
+ static Result<ResState> Initialize(std::unique_ptr<ZipAssetsProvider> zip) {
+ ResState state;
+ state.zip_assets = zip.get();
+ if ((state.apk_assets = ApkAssets::Load(std::move(zip))) == nullptr) {
+ return Error("failed to load apk asset");
+ }
+
+ if ((state.arsc = state.apk_assets->GetLoadedArsc()) == nullptr) {
+ return Error("failed to retrieve loaded arsc");
+ }
+
+ if ((state.package = GetPackageAtIndex0(state.arsc)) == nullptr) {
+ return Error("failed to retrieve loaded package at index 0");
+ }
+
+ state.am = std::make_unique<AssetManager2>();
+ if (!state.am->SetApkAssets({state.apk_assets.get()})) {
+ return Error("failed to create asset manager");
+ }
+
+ return state;
+ }
+};
+
+} // namespace
+
+struct ApkResourceContainer : public TargetResourceContainer, public OverlayResourceContainer {
+ static Result<std::unique_ptr<ApkResourceContainer>> FromPath(const std::string& path);
+
+ // inherited from TargetResourceContainer
+ Result<bool> DefinesOverlayable() const override;
+ Result<const android::OverlayableInfo*> GetOverlayableInfo(ResourceId id) const override;
+ Result<ResourceId> GetResourceId(const std::string& name) const override;
+
+ // inherited from OverlayResourceContainer
+ Result<OverlayData> GetOverlayData(const OverlayManifestInfo& info) const override;
+ Result<OverlayManifestInfo> FindOverlayInfo(const std::string& name) const override;
+
+ // inherited from ResourceContainer
+ Result<uint32_t> GetCrc() const override;
+ Result<std::string> GetResourceName(ResourceId id) const override;
+ const std::string& GetPath() const override;
+
+ ~ApkResourceContainer() override = default;
+
+ private:
+ ApkResourceContainer(std::unique_ptr<ZipAssetsProvider> zip_assets, std::string path);
+
+ Result<const ResState*> GetState() const;
+ ZipAssetsProvider* GetZipAssets() const;
+
+ mutable std::variant<std::unique_ptr<ZipAssetsProvider>, ResState> state_;
+ std::string path_;
+};
+
+ApkResourceContainer::ApkResourceContainer(std::unique_ptr<ZipAssetsProvider> zip_assets,
+ std::string path)
+ : state_(std::move(zip_assets)), path_(std::move(path)) {
+}
+
+Result<std::unique_ptr<ApkResourceContainer>> ApkResourceContainer::FromPath(
+ const std::string& path) {
+ auto zip_assets = ZipAssetsProvider::Create(path);
+ if (zip_assets == nullptr) {
+ return Error("failed to load zip assets");
+ }
+ return std::unique_ptr<ApkResourceContainer>(
+ new ApkResourceContainer(std::move(zip_assets), path));
+}
+
+Result<const ResState*> ApkResourceContainer::GetState() const {
+ if (auto state = std::get_if<ResState>(&state_); state != nullptr) {
+ return state;
+ }
+
+ auto state =
+ ResState::Initialize(std::move(std::get<std::unique_ptr<ZipAssetsProvider>>(state_)));
+ if (!state) {
+ return state.GetError();
+ }
+
+ state_ = std::move(*state);
+ return &std::get<ResState>(state_);
+}
+
+ZipAssetsProvider* ApkResourceContainer::GetZipAssets() const {
+ if (auto zip = std::get_if<std::unique_ptr<ZipAssetsProvider>>(&state_); zip != nullptr) {
+ return zip->get();
+ }
+ return std::get<ResState>(state_).zip_assets;
+}
+
+Result<bool> ApkResourceContainer::DefinesOverlayable() const {
+ auto state = GetState();
+ if (!state) {
+ return state.GetError();
+ }
+ return (*state)->package->DefinesOverlayable();
+}
+
+Result<const android::OverlayableInfo*> ApkResourceContainer::GetOverlayableInfo(
+ ResourceId id) const {
+ auto state = GetState();
+ if (!state) {
+ return state.GetError();
+ }
+ return (*state)->package->GetOverlayableInfo(id);
+}
+
+Result<OverlayManifestInfo> ApkResourceContainer::FindOverlayInfo(const std::string& name) const {
+ return ExtractOverlayManifestInfo(GetZipAssets(), name);
+}
+
+Result<OverlayData> ApkResourceContainer::GetOverlayData(const OverlayManifestInfo& info) const {
+ const auto state = GetState();
+ if (!state) {
+ return state.GetError();
+ }
+
+ if (info.resource_mapping != 0) {
+ return CreateResourceMapping(info.resource_mapping, GetZipAssets(), (*state)->am.get(),
+ (*state)->arsc, (*state)->package);
+ }
+ return CreateResourceMappingLegacy((*state)->am.get(), (*state)->package);
+}
+
+Result<uint32_t> ApkResourceContainer::GetCrc() const {
+ return CalculateCrc(GetZipAssets());
+}
+
+const std::string& ApkResourceContainer::GetPath() const {
+ return path_;
+}
+
+Result<ResourceId> ApkResourceContainer::GetResourceId(const std::string& name) const {
+ auto state = GetState();
+ if (!state) {
+ return state.GetError();
+ }
+ auto id = (*state)->am->GetResourceId(name, "", (*state)->package->GetPackageName());
+ if (!id.has_value()) {
+ return Error("failed to find resource '%s'", name.c_str());
+ }
+
+ // Retrieve the compile-time resource id of the target resource.
+ return REWRITE_PACKAGE(*id, (*state)->package->GetPackageId());
+}
+
+Result<std::string> ApkResourceContainer::GetResourceName(ResourceId id) const {
+ auto state = GetState();
+ if (!state) {
+ return state.GetError();
+ }
+ return utils::ResToTypeEntryName(*(*state)->am, id);
+}
+
+Result<std::unique_ptr<TargetResourceContainer>> TargetResourceContainer::FromPath(
+ std::string path) {
+ auto result = ApkResourceContainer::FromPath(path);
+ if (!result) {
+ return result.GetError();
+ }
+ return std::unique_ptr<TargetResourceContainer>(result->release());
+}
+
+Result<std::unique_ptr<OverlayResourceContainer>> OverlayResourceContainer::FromPath(
+ std::string path) {
+ // Load the path as a fabricated overlay if the file magic indicates this is a fabricated overlay.
+ if (android::IsFabricatedOverlay(path)) {
+ auto result = FabricatedOverlayContainer::FromPath(path);
+ if (!result) {
+ return result.GetError();
+ }
+ return std::unique_ptr<OverlayResourceContainer>(result->release());
+ }
+
+ // Fallback to loading the container as an APK.
+ auto result = ApkResourceContainer::FromPath(path);
+ if (!result) {
+ return result.GetError();
+ }
+ return std::unique_ptr<OverlayResourceContainer>(result->release());
+}
+
+} // namespace android::idmap2 \ No newline at end of file
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index 46eeb8e6ac80..3bbbf248c87d 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -30,19 +30,12 @@
using android::base::StringPrintf;
using android::idmap2::utils::BitmaskToPolicies;
-using android::idmap2::utils::IsReference;
-using android::idmap2::utils::ResToTypeEntryName;
using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
namespace {
-
-#define REWRITE_PACKAGE(resid, package_id) \
- (((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U))
-#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
-
std::string ConcatPolicies(const std::vector<std::string>& policies) {
std::string message;
for (const std::string& policy : policies) {
@@ -55,11 +48,11 @@ std::string ConcatPolicies(const std::vector<std::string>& policies) {
return message;
}
-Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
+Result<Unit> CheckOverlayable(const TargetResourceContainer& target,
const OverlayManifestInfo& overlay_info,
const PolicyBitmask& fulfilled_policies,
const ResourceId& target_resource) {
- static constexpr const PolicyBitmask sDefaultPolicies =
+ constexpr const PolicyBitmask kDefaultPolicies =
PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION | PolicyFlags::SYSTEM_PARTITION |
PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE |
PolicyFlags::CONFIG_SIGNATURE;
@@ -68,8 +61,13 @@ Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
// the overlay is preinstalled, signed with the same signature as the target or signed with the
// same signature as reference package defined in SystemConfig under 'overlay-config-signature'
// tag.
- if (!target_package.DefinesOverlayable()) {
- return (sDefaultPolicies & fulfilled_policies) != 0
+ const Result<bool> defines_overlayable = target.DefinesOverlayable();
+ if (!defines_overlayable) {
+ return Error(defines_overlayable.GetError(), "unable to retrieve overlayable info");
+ }
+
+ if (!*defines_overlayable) {
+ return (kDefaultPolicies & fulfilled_policies) != 0
? Result<Unit>({})
: Error(
"overlay must be preinstalled, signed with the same signature as the target,"
@@ -77,317 +75,92 @@ Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
" <overlay-config-signature>.");
}
- const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource);
- if (overlayable_info == nullptr) {
+ const auto overlayable_info = target.GetOverlayableInfo(target_resource);
+ if (!overlayable_info) {
+ return overlayable_info.GetError();
+ }
+
+ if (*overlayable_info == nullptr) {
// Do not allow non-overlayable resources to be overlaid.
return Error("target resource has no overlayable declaration");
}
- if (overlay_info.target_name != overlayable_info->name) {
+ if (overlay_info.target_name != (*overlayable_info)->name) {
// If the overlay supplies a target overlayable name, the resource must belong to the
// overlayable defined with the specified name to be overlaid.
return Error(R"(<overlay> android:targetName "%s" does not match overlayable name "%s")",
- overlay_info.target_name.c_str(), overlayable_info->name.c_str());
+ overlay_info.target_name.c_str(), (*overlayable_info)->name.c_str());
}
// Enforce policy restrictions if the resource is declared as overlayable.
- if ((overlayable_info->policy_flags & fulfilled_policies) == 0) {
+ if (((*overlayable_info)->policy_flags & fulfilled_policies) == 0) {
return Error(R"(overlay with policies "%s" does not fulfill any overlayable policies "%s")",
ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
- ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str());
+ ConcatPolicies(BitmaskToPolicies((*overlayable_info)->policy_flags)).c_str());
}
return Result<Unit>({});
}
-// TODO(martenkongstad): scan for package name instead of assuming package at index 0
-//
-// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
-// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
-// this assumption tends to work out. That said, the correct thing to do is to scan
-// resources.arsc for a package with a given name as read from the package manifest instead of
-// relying on a hard-coded index. This however requires storing the package name in the idmap
-// header, which in turn requires incrementing the idmap version. Because the initial version of
-// idmap2 is compatible with idmap, this will have to wait for now.
-const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
- const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
- if (packages.empty()) {
- return nullptr;
- }
- int id = packages[0]->GetPackageId();
- return loaded_arsc.GetPackageById(id);
-}
-
-Result<std::unique_ptr<Asset>> OpenNonAssetFromResource(const ResourceId& resource_id,
- const AssetManager2& asset_manager) {
- auto value = asset_manager.GetResource(resource_id);
- if (!value.has_value()) {
- return Error("failed to find resource for id 0x%08x", resource_id);
- }
-
- if (value->type != Res_value::TYPE_STRING) {
- return Error("resource for is 0x%08x is not a file", resource_id);
- }
-
- auto string_pool = asset_manager.GetStringPoolForCookie(value->cookie);
- auto file = string_pool->string8ObjectAt(value->data);
- if (!file.has_value()) {
- return Error("failed to find string for index %d", value->data);
+std::string GetDebugResourceName(const ResourceContainer& container, ResourceId resid) {
+ auto name = container.GetResourceName(resid);
+ if (name) {
+ return *name;
}
-
- // Load the overlay resource mappings from the file specified using android:resourcesMap.
- auto asset = asset_manager.OpenNonAsset(file->c_str(), Asset::AccessMode::ACCESS_BUFFER);
- if (asset == nullptr) {
- return Error("file \"%s\" not found", file->c_str());
- }
-
- return asset;
+ return StringPrintf("0x%08x", resid);
}
-
} // namespace
-Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManager2* target_am,
- const LoadedPackage* target_package,
- const LoadedPackage* overlay_package,
- size_t string_pool_offset,
- const XmlParser& overlay_parser,
- LogInfo& log_info) {
- ResourceMapping resource_mapping;
- auto root_it = overlay_parser.tree_iterator();
- if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") {
- return Error("root element is not <overlay> tag");
- }
-
- const uint8_t target_package_id = target_package->GetPackageId();
- const uint8_t overlay_package_id = overlay_package->GetPackageId();
- auto overlay_it_end = root_it.end();
- for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
- if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) {
- return Error("failed to parse overlay xml document");
- }
-
- if (overlay_it->event() != XmlParser::Event::START_TAG) {
- continue;
- }
-
- if (overlay_it->name() != "item") {
- return Error("unexpected tag <%s> in <overlay>", overlay_it->name().c_str());
- }
-
- Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target");
- if (!target_resource) {
- return Error(R"(<item> tag missing expected attribute "target")");
- }
-
- Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value");
- if (!overlay_resource) {
- return Error(R"(<item> tag missing expected attribute "value")");
- }
-
- auto target_id_result =
- target_am->GetResourceId(*target_resource, "", target_package->GetPackageName());
- if (!target_id_result.has_value()) {
- log_info.Warning(LogMessage() << "failed to find resource \"" << *target_resource
- << "\" in target resources");
+Result<ResourceMapping> ResourceMapping::FromContainers(const TargetResourceContainer& target,
+ const OverlayResourceContainer& overlay,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable,
+ LogInfo& log_info) {
+ auto overlay_data = overlay.GetOverlayData(overlay_info);
+ if (!overlay_data) {
+ return overlay_data.GetError();
+ }
+
+ ResourceMapping mapping;
+ for (const auto& overlay_pair : overlay_data->pairs) {
+ const auto target_resid = target.GetResourceId(overlay_pair.resource_name);
+ if (!target_resid) {
+ log_info.Warning(LogMessage() << target_resid.GetErrorMessage());
continue;
}
- // Retrieve the compile-time resource id of the target resource.
- uint32_t target_id = REWRITE_PACKAGE(*target_id_result, target_package_id);
-
- if (overlay_resource->dataType == Res_value::TYPE_STRING) {
- overlay_resource->data += string_pool_offset;
- }
-
- if (IsReference(overlay_resource->dataType)) {
- // Only rewrite resources defined within the overlay package to their corresponding target
- // resource ids at runtime.
- bool rewrite_reference = overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data);
- resource_mapping.AddMapping(target_id, overlay_resource->data, rewrite_reference);
- } else {
- resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data);
- }
- }
-
- return resource_mapping;
-}
-
-Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy(
- const AssetManager2* target_am, const AssetManager2* overlay_am,
- const LoadedPackage* target_package, const LoadedPackage* overlay_package, LogInfo& log_info) {
- ResourceMapping resource_mapping;
- const uint8_t target_package_id = target_package->GetPackageId();
- const auto end = overlay_package->end();
- for (auto iter = overlay_package->begin(); iter != end; ++iter) {
- const ResourceId overlay_resid = *iter;
- Result<std::string> name = utils::ResToTypeEntryName(*overlay_am, overlay_resid);
- if (!name) {
- continue;
+ if (enforce_overlayable) {
+ // Filter out resources the overlay is not allowed to override.
+ auto overlayable = CheckOverlayable(target, overlay_info, fulfilled_policies, *target_resid);
+ if (!overlayable) {
+ log_info.Warning(LogMessage() << "overlay '" << overlay.GetPath()
+ << "' is not allowed to overlay resource '"
+ << GetDebugResourceName(target, *target_resid)
+ << "' in target: " << overlayable.GetErrorMessage());
+ continue;
+ }
}
- // Find the resource with the same type and entry name within the target package.
- const std::string full_name =
- base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str());
- auto target_resource_result = target_am->GetResourceId(full_name);
- if (!target_resource_result.has_value()) {
- log_info.Warning(LogMessage()
- << "failed to find resource \"" << full_name << "\" in target resources");
- continue;
+ if (auto result = mapping.AddMapping(*target_resid, overlay_pair.value); !result) {
+ return Error(result.GetError(), "failed to add mapping for '%s'",
+ GetDebugResourceName(target, *target_resid).c_str());
}
-
- // Retrieve the compile-time resource id of the target resource.
- ResourceId target_resource = REWRITE_PACKAGE(*target_resource_result, target_package_id);
- resource_mapping.AddMapping(target_resource, overlay_resid,
- false /* rewrite_overlay_reference */);
}
- return resource_mapping;
-}
-
-void ResourceMapping::FilterOverlayableResources(const AssetManager2* target_am,
- const LoadedPackage* target_package,
- const LoadedPackage* overlay_package,
- const OverlayManifestInfo& overlay_info,
- const PolicyBitmask& fulfilled_policies,
- LogInfo& log_info) {
- std::set<ResourceId> remove_ids;
- for (const auto& target_map : target_map_) {
- const ResourceId target_resid = target_map.first;
- Result<Unit> success =
- CheckOverlayable(*target_package, overlay_info, fulfilled_policies, target_resid);
- if (success) {
- continue;
- }
-
- // Attempting to overlay a resource that is not allowed to be overlaid is treated as a
- // warning.
- Result<std::string> name = utils::ResToTypeEntryName(*target_am, target_resid);
- if (!name) {
- name = StringPrintf("0x%08x", target_resid);
- }
-
- log_info.Warning(LogMessage() << "overlay \"" << overlay_package->GetPackageName()
- << "\" is not allowed to overlay resource \"" << *name
- << "\" in target: " << success.GetErrorMessage());
-
- remove_ids.insert(target_resid);
+ auto& string_pool_data = overlay_data->string_pool_data;
+ if (string_pool_data.has_value()) {
+ mapping.string_pool_offset_ = string_pool_data->string_pool_offset;
+ mapping.string_pool_data_ = std::move(string_pool_data->data);
+ mapping.string_pool_data_length_ = string_pool_data->data_length;
}
- for (const ResourceId target_resid : remove_ids) {
- RemoveMapping(target_resid);
- }
+ return std::move(mapping);
}
-Result<ResourceMapping> ResourceMapping::FromApkAssets(const ApkAssets& target_apk_assets,
- const ApkAssets& overlay_apk_assets,
- const OverlayManifestInfo& overlay_info,
- const PolicyBitmask& fulfilled_policies,
- bool enforce_overlayable,
- LogInfo& log_info) {
- AssetManager2 target_asset_manager;
- if (!target_asset_manager.SetApkAssets({&target_apk_assets})) {
- return Error("failed to create target asset manager");
- }
-
- AssetManager2 overlay_asset_manager;
- if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets})) {
- return Error("failed to create overlay asset manager");
- }
-
- const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
- if (target_arsc == nullptr) {
- return Error("failed to load target resources.arsc");
- }
-
- const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
- if (overlay_arsc == nullptr) {
- return Error("failed to load overlay resources.arsc");
- }
-
- const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
- if (target_pkg == nullptr) {
- return Error("failed to load target package from resources.arsc");
- }
-
- const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
- if (overlay_pkg == nullptr) {
- return Error("failed to load overlay package from resources.arsc");
- }
-
- size_t string_pool_data_length = 0U;
- size_t string_pool_offset = 0U;
- std::unique_ptr<uint8_t[]> string_pool_data;
- Result<ResourceMapping> resource_mapping = {{}};
- if (overlay_info.resource_mapping != 0U) {
- // Use the dynamic reference table to find the assigned resource id of the map xml.
- const auto& ref_table = overlay_asset_manager.GetDynamicRefTableForCookie(0);
- uint32_t resource_mapping_id = overlay_info.resource_mapping;
- ref_table->lookupResourceId(&resource_mapping_id);
-
- // Load the overlay resource mappings from the file specified using android:resourcesMap.
- auto asset = OpenNonAssetFromResource(resource_mapping_id, overlay_asset_manager);
- if (!asset) {
- return Error("failed opening xml for android:resourcesMap: %s",
- asset.GetErrorMessage().c_str());
- }
-
- auto parser =
- XmlParser::Create((*asset)->getBuffer(true /* wordAligned*/), (*asset)->getLength());
- if (!parser) {
- return Error("failed opening ResXMLTree");
- }
-
- // Copy the xml string pool data before the parse goes out of scope.
- auto& string_pool = (*parser)->get_strings();
- string_pool_data_length = string_pool.bytes();
- string_pool_data.reset(new uint8_t[string_pool_data_length]);
-
- // Overlays should not be incrementally installed, so calling unsafe_ptr is fine here.
- memcpy(string_pool_data.get(), string_pool.data().unsafe_ptr(), string_pool_data_length);
-
- // Offset string indices by the size of the overlay resource table string pool.
- string_pool_offset = overlay_arsc->GetStringPool()->size();
-
- resource_mapping = CreateResourceMapping(&target_asset_manager, target_pkg, overlay_pkg,
- string_pool_offset, *(*parser), log_info);
- } else {
- // If no file is specified using android:resourcesMap, it is assumed that the overlay only
- // defines resources intended to override target resources of the same type and name.
- resource_mapping = CreateResourceMappingLegacy(&target_asset_manager, &overlay_asset_manager,
- target_pkg, overlay_pkg, log_info);
- }
-
- if (!resource_mapping) {
- return resource_mapping.GetError();
- }
-
- if (enforce_overlayable) {
- // Filter out resources the overlay is not allowed to override.
- (*resource_mapping)
- .FilterOverlayableResources(&target_asset_manager, target_pkg, overlay_pkg, overlay_info,
- fulfilled_policies, log_info);
- }
-
- resource_mapping->target_package_id_ = target_pkg->GetPackageId();
- resource_mapping->overlay_package_id_ = overlay_pkg->GetPackageId();
- resource_mapping->string_pool_offset_ = string_pool_offset;
- resource_mapping->string_pool_data_ = std::move(string_pool_data);
- resource_mapping->string_pool_data_length_ = string_pool_data_length;
- return std::move(*resource_mapping);
-}
-
-OverlayResourceMap ResourceMapping::GetOverlayToTargetMap() const {
- // An overlay resource can override multiple target resources at once. Rewrite the overlay
- // resource as the first target resource it overrides.
- OverlayResourceMap map;
- for (const auto& mappings : overlay_map_) {
- map.insert(std::make_pair(mappings.first, mappings.second));
- }
- return map;
-}
-
-Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource, ResourceId overlay_resource,
- bool rewrite_overlay_reference) {
+Result<Unit> ResourceMapping::AddMapping(
+ ResourceId target_resource,
+ const std::variant<OverlayData::ResourceIdValue, TargetValue>& value) {
if (target_map_.find(target_resource) != target_map_.end()) {
return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
}
@@ -395,49 +168,19 @@ Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource, ResourceId
// TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
// runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
- target_map_.insert(std::make_pair(target_resource, overlay_resource));
-
- if (rewrite_overlay_reference) {
- overlay_map_.insert(std::make_pair(overlay_resource, target_resource));
- }
- return Unit{};
-}
-
-Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
- TargetValue::DataType data_type,
- TargetValue::DataValue data_value) {
- if (target_map_.find(target_resource) != target_map_.end()) {
- return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
+ if (auto overlay_resource = std::get_if<OverlayData::ResourceIdValue>(&value)) {
+ target_map_.insert(std::make_pair(target_resource, overlay_resource->overlay_id));
+ if (overlay_resource->rewrite_id) {
+ // An overlay resource can override multiple target resources at once. Rewrite the overlay
+ // resource as the first target resource it overrides.
+ overlay_map_.insert(std::make_pair(overlay_resource->overlay_id, target_resource));
+ }
+ } else {
+ auto overlay_value = std::get<TargetValue>(value);
+ target_map_.insert(std::make_pair(target_resource, overlay_value));
}
- // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
- // runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
-
- target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
return Unit{};
}
-void ResourceMapping::RemoveMapping(ResourceId target_resource) {
- auto target_iter = target_map_.find(target_resource);
- if (target_iter == target_map_.end()) {
- return;
- }
-
- const auto value = target_iter->second;
- target_map_.erase(target_iter);
-
- const ResourceId* overlay_resource = std::get_if<ResourceId>(&value);
- if (overlay_resource == nullptr) {
- return;
- }
-
- auto overlay_iter = overlay_map_.equal_range(*overlay_resource);
- for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) {
- if (i->second == target_resource) {
- overlay_map_.erase(i);
- return;
- }
- }
-}
-
} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index 4e85e5751300..e809bf1f4b02 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -17,27 +17,16 @@
#include "idmap2/ResourceUtils.h"
#include <memory>
-#include <string>
#include "androidfw/StringPiece.h"
#include "androidfw/Util.h"
#include "idmap2/Result.h"
-#include "idmap2/XmlParser.h"
-#include "idmap2/ZipFile.h"
using android::StringPiece16;
using android::idmap2::Result;
-using android::idmap2::XmlParser;
-using android::idmap2::ZipFile;
using android::util::Utf16ToUtf8;
namespace android::idmap2::utils {
-namespace {
-constexpr ResourceId kAttrName = 0x01010003;
-constexpr ResourceId kAttrResourcesMap = 0x01010609;
-constexpr ResourceId kAttrTargetName = 0x0101044d;
-constexpr ResourceId kAttrTargetPackage = 0x01010021;
-} // namespace
bool IsReference(uint8_t data_type) {
return data_type == Res_value::TYPE_REFERENCE || data_type == Res_value::TYPE_DYNAMIC_REFERENCE;
@@ -97,71 +86,4 @@ Result<std::string> ResToTypeEntryName(const AssetManager2& am, uint32_t resid)
return out;
}
-Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
- const std::string& name) {
- std::unique_ptr<const ZipFile> zip = ZipFile::Open(path);
- if (!zip) {
- return Error("failed to open %s as a zip file", path.c_str());
- }
-
- std::unique_ptr<const MemoryChunk> entry = zip->Uncompress("AndroidManifest.xml");
- if (!entry) {
- return Error("failed to uncompress AndroidManifest.xml from %s", path.c_str());
- }
-
- Result<std::unique_ptr<const XmlParser>> xml = XmlParser::Create(entry->buf, entry->size);
- if (!xml) {
- return Error("failed to parse AndroidManifest.xml from %s", path.c_str());
- }
-
- auto manifest_it = (*xml)->tree_iterator();
- if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") {
- return Error("root element tag is not <manifest> in AndroidManifest.xml of %s", path.c_str());
- }
-
- for (auto&& it : manifest_it) {
- if (it.event() != XmlParser::Event::START_TAG || it.name() != "overlay") {
- continue;
- }
-
- OverlayManifestInfo info{};
- if (auto result_str = it.GetAttributeStringValue(kAttrName, "android:name")) {
- if (*result_str != name) {
- // A value for android:name was found, but either a the name does not match the requested
- // name, or an <overlay> tag with no name was requested.
- continue;
- }
- info.name = *result_str;
- } else if (!name.empty()) {
- // This tag does not have a value for android:name, but an <overlay> tag with a specific name
- // has been requested.
- continue;
- }
-
- if (auto result_str = it.GetAttributeStringValue(kAttrTargetPackage, "android:targetPackage")) {
- info.target_package = *result_str;
- } else {
- return Error("android:targetPackage missing from <overlay> of %s: %s", path.c_str(),
- result_str.GetErrorMessage().c_str());
- }
-
- if (auto result_str = it.GetAttributeStringValue(kAttrTargetName, "android:targetName")) {
- info.target_name = *result_str;
- }
-
- if (auto result_value = it.GetAttributeValue(kAttrResourcesMap, "android:resourcesMap")) {
- if (IsReference((*result_value).dataType)) {
- info.resource_mapping = (*result_value).data;
- } else {
- return Error("android:resourcesMap is not a reference in AndroidManifest.xml of %s",
- path.c_str());
- }
- }
- return info;
- }
-
- return Error("<overlay> with android:name \"%s\" missing from AndroidManifest.xml of %s",
- name.c_str(), path.c_str());
-}
-
} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp
index 00baea46f909..70822c890288 100644
--- a/cmds/idmap2/libidmap2/XmlParser.cpp
+++ b/cmds/idmap2/libidmap2/XmlParser.cpp
@@ -151,16 +151,18 @@ Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string&
return value ? GetStringValue(parser_, *value, name) : value.GetError();
}
-Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, size_t size,
- bool copy_data) {
- auto parser = std::unique_ptr<const XmlParser>(new XmlParser());
- if (parser->tree_.setTo(data, size, copy_data) != NO_ERROR) {
+XmlParser::XmlParser(std::unique_ptr<ResXMLTree> tree) : tree_(std::move(tree)) {
+}
+
+Result<XmlParser> XmlParser::Create(const void* data, size_t size, bool copy_data) {
+ auto tree = std::make_unique<ResXMLTree>();
+ if (tree->setTo(data, size, copy_data) != NO_ERROR) {
return Error("Malformed xml block");
}
// Find the beginning of the first tag.
XmlParser::Event event;
- while ((event = parser->tree_.next()) != XmlParser::Event::BAD_DOCUMENT &&
+ while ((event = tree->next()) != XmlParser::Event::BAD_DOCUMENT &&
event != XmlParser::Event::END_DOCUMENT && event != XmlParser::Event::START_TAG) {
}
@@ -172,11 +174,7 @@ Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, siz
return Error("Bad xml document");
}
- return parser;
-}
-
-XmlParser::~XmlParser() {
- tree_.uninit();
+ return XmlParser{std::move(tree)};
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp
deleted file mode 100644
index 1e1a218163f0..000000000000
--- a/cmds/idmap2/libidmap2/ZipFile.cpp
+++ /dev/null
@@ -1,70 +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.
- */
-
-#include "idmap2/ZipFile.h"
-
-#include <memory>
-#include <string>
-
-#include "idmap2/Result.h"
-
-namespace android::idmap2 {
-
-std::unique_ptr<MemoryChunk> MemoryChunk::Allocate(size_t size) {
- void* ptr = ::operator new(sizeof(MemoryChunk) + size);
- std::unique_ptr<MemoryChunk> chunk(reinterpret_cast<MemoryChunk*>(ptr));
- chunk->size = size;
- return chunk;
-}
-
-std::unique_ptr<const ZipFile> ZipFile::Open(const std::string& path) {
- ::ZipArchiveHandle handle;
- int32_t status = ::OpenArchive(path.c_str(), &handle);
- if (status != 0) {
- ::CloseArchive(handle);
- return nullptr;
- }
- return std::unique_ptr<ZipFile>(new ZipFile(handle));
-}
-
-ZipFile::~ZipFile() {
- ::CloseArchive(handle_);
-}
-
-std::unique_ptr<const MemoryChunk> ZipFile::Uncompress(const std::string& entryPath) const {
- ::ZipEntry entry;
- int32_t status = ::FindEntry(handle_, entryPath, &entry);
- if (status != 0) {
- return nullptr;
- }
- std::unique_ptr<MemoryChunk> chunk = MemoryChunk::Allocate(entry.uncompressed_length);
- status = ::ExtractToMemory(handle_, &entry, chunk->buf, chunk->size);
- if (status != 0) {
- return nullptr;
- }
- return chunk;
-}
-
-Result<uint32_t> ZipFile::Crc(const std::string& entryPath) const {
- ::ZipEntry entry;
- int32_t status = ::FindEntry(handle_, entryPath, &entry);
- if (status != 0) {
- return Error("failed to find zip entry %s", entryPath.c_str());
- }
- return entry.crc32;
-}
-
-} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/proto/fabricated_v1.proto b/cmds/idmap2/libidmap2/proto/fabricated_v1.proto
new file mode 100644
index 000000000000..a392b2b6d856
--- /dev/null
+++ b/cmds/idmap2/libidmap2/proto/fabricated_v1.proto
@@ -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.
+ */
+
+syntax = "proto3";
+
+package android.idmap2.pb;
+
+option optimize_for = LITE_RUNTIME;
+
+// All changes to the proto messages in this file MUST be backwards compatible. Backwards
+// incompatible changes will cause previously fabricated overlays to be considered corrupt by the
+// new proto message specification.
+message FabricatedOverlay {
+ repeated ResourcePackage packages = 1;
+ string name = 2;
+ string package_name = 3;
+ string target_package_name = 4;
+ string target_overlayable = 5;
+}
+
+message ResourcePackage {
+ string name = 1;
+ repeated ResourceType types = 2;
+}
+
+message ResourceType {
+ string name = 1;
+ repeated ResourceEntry entries = 2;
+}
+
+message ResourceEntry {
+ string name = 1;
+ oneof value {
+ ResourceValue res_value = 2;
+ }
+}
+
+message ResourceValue {
+ // Corresponds with android::Res_value::dataType
+ uint32 data_type = 1;
+ // Corresponds with android::Res_value::data
+ uint32 data_value = 2;
+} \ No newline at end of file
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index 524aabcec652..bf6332742787 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -33,7 +33,7 @@ using ::testing::NotNull;
namespace android::idmap2 {
TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
+ std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
std::istringstream raw_stream(raw);
auto result1 = Idmap::FromBinaryStream(raw_stream);
diff --git a/cmds/idmap2/tests/FabricatedOverlayTests.cpp b/cmds/idmap2/tests/FabricatedOverlayTests.cpp
new file mode 100644
index 000000000000..79ab2438af74
--- /dev/null
+++ b/cmds/idmap2/tests/FabricatedOverlayTests.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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 <android-base/file.h>
+#include <gtest/gtest.h>
+#include <idmap2/FabricatedOverlay.h>
+
+#include <fstream>
+
+namespace android::idmap2 {
+
+TEST(FabricatedOverlayTests, OverlayInfo) {
+ auto overlay =
+ FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
+ .SetOverlayable("TestResources")
+ .Build();
+
+ ASSERT_TRUE(overlay);
+ auto container = FabricatedOverlayContainer::FromOverlay(std::move(*overlay));
+ auto info = container->FindOverlayInfo("SandTheme");
+ ASSERT_TRUE(info);
+ EXPECT_EQ("SandTheme", (*info).name);
+ EXPECT_EQ("TestResources", (*info).target_name);
+
+ info = container->FindOverlayInfo("OceanTheme");
+ ASSERT_FALSE(info);
+}
+
+TEST(FabricatedOverlayTests, SetResourceValue) {
+ auto overlay =
+ FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
+ .SetResourceValue("com.example.target:integer/int1", Res_value::TYPE_INT_DEC, 1U)
+ .SetResourceValue("com.example.target.split:integer/int2", Res_value::TYPE_INT_DEC, 2U)
+ .SetResourceValue("string/int3", Res_value::TYPE_REFERENCE, 0x7f010000)
+ .Build();
+ ASSERT_TRUE(overlay);
+ auto container = FabricatedOverlayContainer::FromOverlay(std::move(*overlay));
+ auto info = container->FindOverlayInfo("SandTheme");
+ ASSERT_TRUE(info);
+ ASSERT_TRUE((*info).target_name.empty());
+
+ auto crc = (*container).GetCrc();
+ ASSERT_TRUE(crc) << crc.GetErrorMessage();
+ EXPECT_NE(0U, *crc);
+
+ auto pairs = container->GetOverlayData(*info);
+ ASSERT_TRUE(pairs);
+ EXPECT_FALSE(pairs->string_pool_data.has_value());
+ ASSERT_EQ(3U, pairs->pairs.size());
+
+ auto& it = pairs->pairs[0];
+ ASSERT_EQ("com.example.target:integer/int1", it.resource_name);
+ auto entry = std::get_if<TargetValue>(&it.value);
+ ASSERT_NE(nullptr, entry);
+ ASSERT_EQ(1U, entry->data_value);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->data_type);
+
+ it = pairs->pairs[1];
+ ASSERT_EQ("com.example.target:string/int3", it.resource_name);
+ entry = std::get_if<TargetValue>(&it.value);
+ ASSERT_NE(nullptr, entry);
+ ASSERT_EQ(0x7f010000, entry->data_value);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, entry->data_type);
+
+ it = pairs->pairs[2];
+ ASSERT_EQ("com.example.target.split:integer/int2", it.resource_name);
+ entry = std::get_if<TargetValue>(&it.value);
+ ASSERT_NE(nullptr, entry);
+ ASSERT_EQ(2U, entry->data_value);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->data_type);
+}
+
+TEST(FabricatedOverlayTests, SetResourceValueBadArgs) {
+ {
+ auto builder =
+ FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
+ .SetResourceValue("int1", Res_value::TYPE_INT_DEC, 1U);
+ ASSERT_FALSE(builder.Build());
+ }
+ {
+ auto builder =
+ FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
+ .SetResourceValue("com.example.target:int2", Res_value::TYPE_INT_DEC, 1U);
+ ASSERT_FALSE(builder.Build());
+ }
+}
+
+TEST(FabricatedOverlayTests, SerializeAndDeserialize) {
+ auto overlay =
+ FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
+ .SetOverlayable("TestResources")
+ .SetResourceValue("com.example.target:integer/int1", Res_value::TYPE_INT_DEC, 1U)
+ .Build();
+ ASSERT_TRUE(overlay);
+ TemporaryFile tf;
+ std::ofstream out(tf.path);
+ ASSERT_TRUE((*overlay).ToBinaryStream(out));
+ out.close();
+
+ auto container = OverlayResourceContainer::FromPath(tf.path);
+ ASSERT_TRUE(container) << container.GetErrorMessage();
+ EXPECT_EQ(tf.path, (*container)->GetPath());
+
+ auto crc = (*container)->GetCrc();
+ ASSERT_TRUE(crc) << crc.GetErrorMessage();
+ EXPECT_NE(0U, *crc);
+
+ auto info = (*container)->FindOverlayInfo("SandTheme");
+ ASSERT_TRUE(info) << info.GetErrorMessage();
+ EXPECT_EQ("SandTheme", (*info).name);
+ EXPECT_EQ("TestResources", (*info).target_name);
+
+ auto pairs = (*container)->GetOverlayData(*info);
+ ASSERT_TRUE(pairs) << pairs.GetErrorMessage();
+ EXPECT_EQ(1U, pairs->pairs.size());
+
+ auto& it = pairs->pairs[0];
+ ASSERT_EQ("com.example.target:integer/int1", it.resource_name);
+ auto entry = std::get_if<TargetValue>(&it.value);
+ ASSERT_NE(nullptr, entry);
+ EXPECT_EQ(1U, entry->data_value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, entry->data_type);
+}
+
+} // namespace android::idmap2 \ No newline at end of file
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 16b68f01e8f5..9516ff83d718 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <android-base/file.h>
+
#include <cstdio> // fclose
#include <fstream>
#include <memory>
@@ -61,12 +63,12 @@ TEST(IdmapTests, TestCanonicalIdmapPathFor) {
}
TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) {
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
+ std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
std::istringstream stream(raw);
std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
ASSERT_EQ(header->GetMagic(), 0x504d4449U);
- ASSERT_EQ(header->GetVersion(), 0x07U);
+ ASSERT_EQ(header->GetVersion(), 0x08U);
ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
ASSERT_EQ(header->GetFulfilledPolicies(), 0x11);
@@ -81,7 +83,7 @@ TEST(IdmapTests, IdmapFailParsingDifferentVersion) {
std::stringstream stream;
stream << android::kIdmapMagic;
stream << 0xffffffffU;
- stream << std::string(kJunkSize, (char) 0xffU);
+ stream << std::string(kJunkSize, (char)0xffU);
ASSERT_FALSE(Idmap::FromBinaryStream(stream));
}
@@ -90,14 +92,13 @@ TEST(IdmapTests, IdmapFailParsingDifferentMagic) {
std::stringstream stream;
stream << 0xffffffffU;
stream << android::kIdmapCurrentVersion;
- stream << std::string(kJunkSize, (char) 0xffU);
+ stream << std::string(kJunkSize, (char)0xffU);
ASSERT_FALSE(Idmap::FromBinaryStream(stream));
}
TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
const size_t offset = kIdmapRawDataOffset;
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
- kIdmapRawDataLen - offset);
+ std::string raw(reinterpret_cast<const char*>(kIdmapRawData + offset), kIdmapRawDataLen - offset);
std::istringstream stream(raw);
std::unique_ptr<const IdmapData::Header> header = IdmapData::Header::FromBinaryStream(stream);
@@ -108,8 +109,7 @@ TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
const size_t offset = kIdmapRawDataOffset;
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
- kIdmapRawDataLen - offset);
+ std::string raw(reinterpret_cast<const char*>(kIdmapRawData + offset), kIdmapRawDataLen - offset);
std::istringstream stream(raw);
std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream);
@@ -134,7 +134,7 @@ TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
}
TEST(IdmapTests, CreateIdmapFromBinaryStream) {
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
+ std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
std::istringstream stream(raw);
auto result = Idmap::FromBinaryStream(stream);
@@ -143,7 +143,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) {
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x07U);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), kIdmapRawDataPolicies);
@@ -177,7 +177,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) {
}
TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) {
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data),
+ std::string raw(reinterpret_cast<const char*>(kIdmapRawData),
10); // data too small
std::istringstream stream(raw);
@@ -189,14 +189,14 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- ASSERT_THAT(target_apk, NotNull());
+ auto target = TargetResourceContainer::FromPath(target_apk_path);
+ ASSERT_TRUE(target);
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- ASSERT_THAT(overlay_apk, NotNull());
+ auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
+ ASSERT_TRUE(overlay);
- auto idmap_result = Idmap::FromApkAssets(
- *target_apk, *overlay_apk, TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::PUBLIC,
+ auto idmap_result = Idmap::FromContainers(
+ **target, **overlay, TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
@@ -204,7 +204,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) {
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x07U);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC);
ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC);
@@ -218,15 +218,15 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssets) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- ASSERT_THAT(target_apk, NotNull());
+ auto target = TargetResourceContainer::FromPath(target_apk_path);
+ ASSERT_TRUE(target);
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- ASSERT_THAT(overlay_apk, NotNull());
+ auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
+ ASSERT_TRUE(overlay);
- auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk,
- TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ auto idmap_result = Idmap::FromContainers(
+ **target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
ASSERT_THAT(idmap, NotNull());
@@ -255,25 +255,66 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssets) {
ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay::string::str4, R::target::string::str4);
}
+TEST(IdmapTests, FabricatedOverlay) {
+ std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
+ auto target = TargetResourceContainer::FromPath(target_apk_path);
+ ASSERT_TRUE(target);
+
+ auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target")
+ .SetOverlayable("TestResources")
+ .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U)
+ .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000)
+ .Build();
+
+ ASSERT_TRUE(frro);
+ TemporaryFile tf;
+ std::ofstream out(tf.path);
+ ASSERT_TRUE((*frro).ToBinaryStream(out));
+ out.close();
+
+ auto overlay = OverlayResourceContainer::FromPath(tf.path);
+ ASSERT_TRUE(overlay);
+
+ auto idmap_result = Idmap::FromContainers(**target, **overlay, "SandTheme", PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+ ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
+ auto& idmap = *idmap_result;
+ ASSERT_THAT(idmap, NotNull());
+
+ const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
+ ASSERT_EQ(dataBlocks.size(), 1U);
+
+ const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+ ASSERT_THAT(data, NotNull());
+ ASSERT_EQ(data->GetTargetEntries().size(), 0U);
+ ASSERT_EQ(data->GetOverlayEntries().size(), 0U);
+
+ const auto& target_inline_entries = data->GetTargetInlineEntries();
+ ASSERT_EQ(target_inline_entries.size(), 2U);
+ ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1,
+ Res_value::TYPE_INT_DEC, 2U);
+ ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1,
+ Res_value::TYPE_REFERENCE, 0x7f010000);
+}
+
TEST(IdmapTests, FailCreateIdmapInvalidName) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- ASSERT_THAT(target_apk, NotNull());
+ auto target = TargetResourceContainer::FromPath(target_apk_path);
+ ASSERT_TRUE(target);
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- ASSERT_THAT(overlay_apk, NotNull());
+ auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
+ ASSERT_TRUE(overlay);
{
- auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, "", PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ auto idmap_result = Idmap::FromContainers(**target, **overlay, "", PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_FALSE(idmap_result);
}
{
- auto idmap_result =
- Idmap::FromApkAssets(*target_apk, *overlay_apk, "unknown", PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ auto idmap_result = Idmap::FromContainers(**target, **overlay, "unknown", PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_FALSE(idmap_result);
}
}
@@ -282,15 +323,15 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-shared.apk";
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- ASSERT_THAT(target_apk, NotNull());
+ auto target = TargetResourceContainer::FromPath(target_apk_path);
+ ASSERT_TRUE(target);
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- ASSERT_THAT(overlay_apk, NotNull());
+ auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
+ ASSERT_TRUE(overlay);
- auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk,
- TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ auto idmap_result = Idmap::FromContainers(
+ **target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
ASSERT_THAT(idmap, NotNull());
@@ -328,30 +369,29 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
}
Result<std::unique_ptr<const IdmapData>> TestIdmapDataFromApkAssets(
- const std::string& local_target_apk_path, const std::string& local_overlay_apk_path,
+ const std::string& local_target_path, const std::string& local_overlay_path,
const std::string& overlay_name, const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable) {
- auto overlay_info =
- utils::ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path, overlay_name);
- if (!overlay_info) {
- return overlay_info.GetError();
+ const std::string target_path(GetTestDataPath() + local_target_path);
+ auto target = TargetResourceContainer::FromPath(target_path);
+ if (!target) {
+ return Error(R"(Failed to load target "%s")", target_path.c_str());
}
- const std::string target_apk_path(GetTestDataPath() + local_target_apk_path);
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- if (!target_apk) {
- return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
+ const std::string overlay_path(GetTestDataPath() + local_overlay_path);
+ auto overlay = OverlayResourceContainer::FromPath(overlay_path);
+ if (!overlay) {
+ return Error(R"(Failed to load overlay "%s")", overlay_path.c_str());
}
- const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path);
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- if (!overlay_apk) {
- return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
+ auto overlay_info = (*overlay)->FindOverlayInfo(overlay_name);
+ if (!overlay_info) {
+ return Error(R"(Failed to find overlay name "%s")", overlay_name.c_str());
}
LogInfo log_info;
- auto mapping = ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, *overlay_info,
- fulfilled_policies, enforce_overlayable, log_info);
+ auto mapping = ResourceMapping::FromContainers(**target, **overlay, *overlay_info,
+ fulfilled_policies, enforce_overlayable, log_info);
if (!mapping) {
return mapping.GetError();
}
@@ -360,11 +400,9 @@ Result<std::unique_ptr<const IdmapData>> TestIdmapDataFromApkAssets(
}
TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) {
- auto idmap_data =
- TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", "DifferentPackages",
-
- PolicyFlags::PUBLIC,
- /* enforce_overlayable */ false);
+ auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk",
+ "DifferentPackages", PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ false);
ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
auto& data = *idmap_data;
@@ -417,7 +455,7 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
const uint32_t target_crc = kIdmapRawDataTargetCrc;
const uint32_t overlay_crc = kIdmapRawOverlayCrc;
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
+ std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
std::istringstream raw_stream(raw);
auto result = Idmap::FromBinaryStream(raw_stream);
@@ -468,8 +506,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
ASSERT_THAT(bad_target_crc_header, NotNull());
ASSERT_NE(header->GetTargetCrc(), bad_target_crc_header->GetTargetCrc());
ASSERT_FALSE(bad_target_crc_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
- target_crc, overlay_crc, policies,
- /* enforce_overlayable */ true));
+ target_crc, overlay_crc, policies,
+ /* enforce_overlayable */ true));
// overlay crc: bytes (0xc, 0xf)
std::string bad_overlay_crc_string(stream.str());
@@ -483,8 +521,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
ASSERT_THAT(bad_overlay_crc_header, NotNull());
ASSERT_NE(header->GetOverlayCrc(), bad_overlay_crc_header->GetOverlayCrc());
ASSERT_FALSE(bad_overlay_crc_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
- target_crc, overlay_crc, policies,
- /* enforce_overlayable */ true));
+ target_crc, overlay_crc, policies,
+ /* enforce_overlayable */ true));
// fulfilled policy: bytes (0x10, 0x13)
std::string bad_policy_string(stream.str());
@@ -522,8 +560,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
ASSERT_THAT(bad_target_path_header, NotNull());
ASSERT_NE(header->GetTargetPath(), bad_target_path_header->GetTargetPath());
ASSERT_FALSE(bad_target_path_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
- target_crc, overlay_crc, policies,
- /* enforce_overlayable */ true));
+ target_crc, overlay_crc, policies,
+ /* enforce_overlayable */ true));
// overlay path: bytes (0x2c, 0x37)
std::string bad_overlay_path_string(stream.str());
@@ -576,7 +614,7 @@ class TestVisitor : public Visitor {
};
TEST(IdmapTests, TestVisitor) {
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
+ std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
std::istringstream stream(raw);
const auto idmap = Idmap::FromBinaryStream(stream);
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index 87ce0f13d19e..3d3d82a8c7dd 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -27,35 +27,31 @@
#include "idmap2/Idmap.h"
#include "idmap2/PrettyPrintVisitor.h"
-using android::ApkAssets;
using android::base::StringPrintf;
-using ::testing::NotNull;
-using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- ASSERT_THAT(target_apk, NotNull());
+ auto target = TargetResourceContainer::FromPath(target_apk_path);
+ ASSERT_TRUE(target);
const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- ASSERT_THAT(overlay_apk, NotNull());
+ auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
+ ASSERT_TRUE(overlay);
- const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk,
- TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ const auto idmap = Idmap::FromContainers(**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT,
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
PrettyPrintVisitor visitor(stream);
(*idmap)->accept(&visitor);
- ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
- ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
+ ASSERT_NE(stream.str().find("target path : "), std::string::npos);
+ ASSERT_NE(stream.str().find("overlay path : "), std::string::npos);
ASSERT_NE(stream.str().find(StringPrintf("0x%08x -> 0x%08x (integer/int1 -> integer/int1)\n",
R::target::integer::int1, R::overlay::integer::int1)),
std::string::npos);
@@ -64,7 +60,7 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) {
fclose(stderr); // silence expected warnings from libandroidfw
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
+ std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
std::istringstream raw_stream(raw);
const auto idmap = Idmap::FromBinaryStream(raw_stream);
@@ -74,8 +70,8 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) {
PrettyPrintVisitor visitor(stream);
(*idmap)->accept(&visitor);
- ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
- ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
+ ASSERT_NE(stream.str().find("target path : "), std::string::npos);
+ ASSERT_NE(stream.str().find("overlay path : "), std::string::npos);
ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000 (\?\?\? -> \?\?\?)\n"), std::string::npos);
}
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index 88f85efb0f84..a6371cb74f2e 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -30,17 +30,16 @@
#include "idmap2/RawPrintVisitor.h"
using android::base::StringPrintf;
-using ::testing::NotNull;
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
-#define ASSERT_CONTAINS_REGEX(pattern, str) \
- do { \
- ASSERT_TRUE(std::regex_search(str, std::regex(pattern))) \
- << "pattern '" << pattern << "' not found in\n--------\n" \
- << str << "--------"; \
+#define ASSERT_CONTAINS_REGEX(pattern, str) \
+ do { \
+ ASSERT_TRUE(std::regex_search(str, std::regex(pattern))) \
+ << "pattern '" << (pattern) << "' not found in\n--------\n" \
+ << (str) << "--------"; \
} while (0)
#define ADDRESS "[0-9a-f]{8}: "
@@ -49,16 +48,15 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
fclose(stderr); // silence expected warnings
const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- ASSERT_THAT(target_apk, NotNull());
+ auto target = TargetResourceContainer::FromPath(target_apk_path);
+ ASSERT_TRUE(target);
const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- ASSERT_THAT(overlay_apk, NotNull());
+ auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
+ ASSERT_TRUE(overlay);
- const auto idmap =
- Idmap::FromApkAssets(*target_apk, *overlay_apk, TestConstants::OVERLAY_NAME_DEFAULT,
- PolicyFlags::PUBLIC, /* enforce_overlayable */ true);
+ const auto idmap = Idmap::FromContainers(**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT,
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
@@ -66,7 +64,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
(*idmap)->accept(&visitor);
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "00000007 version\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000008 version\n", stream.str());
ASSERT_CONTAINS_REGEX(
StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
stream.str());
@@ -75,8 +73,6 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 fulfilled policies: public\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 enforce overlayable\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000000 target inline entry count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count", stream.str());
@@ -104,7 +100,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
fclose(stderr); // silence expected warnings from libandroidfw
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
+ std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
std::istringstream raw_stream(raw);
const auto idmap = Idmap::FromBinaryStream(raw_stream);
@@ -115,7 +111,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
(*idmap)->accept(&visitor);
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "00000007 version\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000008 version\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00001234 target crc\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00005678 overlay crc\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000011 fulfilled policies: public|signature\n", stream.str());
@@ -126,8 +122,6 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
ASSERT_CONTAINS_REGEX(ADDRESS "........ overlay path: overlayX.apk\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "0000000b overlay name size\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "........ overlay name: OverlayName\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000003 target entry count\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 target inline entry count\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000003 overlay entry count\n", stream.str());
@@ -140,7 +134,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f030002 target id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 string pool size\n", stream.str());
- ASSERT_CONTAINS_REGEX("000000a8: ........ string pool\n", stream.str());
+ ASSERT_CONTAINS_REGEX("000000a4: ........ string pool\n", stream.str());
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index 0362529c4f3b..5a1d808af06f 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#include <android-base/file.h>
+#include <androidfw/ResourceTypes.h>
+#include <gtest/gtest.h>
+
#include <cstdio> // fclose
#include <fstream>
#include <memory>
@@ -22,14 +26,10 @@
#include "R.h"
#include "TestConstants.h"
#include "TestHelpers.h"
-#include "androidfw/ResourceTypes.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
#include "idmap2/LogInfo.h"
#include "idmap2/ResourceMapping.h"
using android::Res_value;
-using android::idmap2::utils::ExtractOverlayManifestInfo;
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
@@ -41,32 +41,36 @@ namespace android::idmap2 {
ASSERT_TRUE(result) << result.GetErrorMessage(); \
} while (0)
-Result<ResourceMapping> TestGetResourceMapping(const std::string& local_target_apk_path,
- const std::string& local_overlay_apk_path,
+Result<ResourceMapping> TestGetResourceMapping(const std::string& local_target_path,
+ const std::string& local_overlay_path,
const std::string& overlay_name,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable) {
- auto overlay_info =
- ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path, overlay_name);
- if (!overlay_info) {
- return overlay_info.GetError();
+ const std::string target_path = (local_target_path[0] == '/')
+ ? local_target_path
+ : (GetTestDataPath() + "/" + local_target_path);
+ auto target = TargetResourceContainer::FromPath(target_path);
+ if (!target) {
+ return Error(target.GetError(), R"(Failed to load target "%s")", target_path.c_str());
}
- const std::string target_apk_path(GetTestDataPath() + local_target_apk_path);
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- if (!target_apk) {
- return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
+ const std::string overlay_path = (local_overlay_path[0] == '/')
+ ? local_overlay_path
+ : (GetTestDataPath() + "/" + local_overlay_path);
+ auto overlay = OverlayResourceContainer::FromPath(overlay_path);
+ if (!overlay) {
+ return Error(overlay.GetError(), R"(Failed to load overlay "%s")", overlay_path.c_str());
}
- const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path);
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- if (!overlay_apk) {
- return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
+ auto overlay_info = (*overlay)->FindOverlayInfo(overlay_name);
+ if (!overlay_info) {
+ return Error(overlay_info.GetError(), R"(Failed to find overlay name "%s")",
+ overlay_name.c_str());
}
LogInfo log_info;
- return ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, *overlay_info,
- fulfilled_policies, enforce_overlayable, log_info);
+ return ResourceMapping::FromContainers(**target, **overlay, *overlay_info, fulfilled_policies,
+ enforce_overlayable, log_info);
}
Result<Unit> MappingExists(const ResourceMapping& mapping, ResourceId target_resource,
@@ -128,7 +132,7 @@ Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& tar
}
TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) {
- auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-legacy.apk", "",
+ auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay-legacy.apk", "",
PolicyFlags::PUBLIC, /* enforce_overlayable */ false);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
@@ -145,7 +149,7 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) {
}
TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
- auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", "SwapNames",
+ auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", "SwapNames",
PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
@@ -161,7 +165,7 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
}
TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
- auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
+ auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
"DifferentPackages", PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
@@ -176,7 +180,7 @@ TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
}
TEST(ResourceMappingTests, InlineResources) {
- auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", "Inline",
+ auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", "Inline",
PolicyFlags::PUBLIC, /* enforce_overlayable */ false);
constexpr size_t overlay_string_pool_size = 10U;
@@ -189,8 +193,32 @@ TEST(ResourceMappingTests, InlineResources) {
ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U));
}
+TEST(ResourceMappingTests, FabricatedOverlay) {
+ auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target")
+ .SetOverlayable("TestResources")
+ .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U)
+ .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000)
+ .Build();
+
+ ASSERT_TRUE(frro);
+ TemporaryFile tf;
+ std::ofstream out(tf.path);
+ ASSERT_TRUE((*frro).ToBinaryStream(out));
+ out.close();
+
+ auto resources = TestGetResourceMapping("target/target.apk", tf.path, "SandTheme",
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
+ ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, 0x7f010000));
+ ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 2U));
+}
+
TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
- auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
+ auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
TestConstants::OVERLAY_NAME_ALL_POLICIES,
PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
@@ -209,7 +237,7 @@ TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
// Resources that are not declared as overlayable and resources that a protected by policies the
// overlay does not fulfill must not map to overlay resources.
TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
- auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
+ auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
TestConstants::OVERLAY_NAME_ALL_POLICIES,
PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
@@ -229,7 +257,7 @@ TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
// overlay does not fulfilled can map to overlay resources when overlayable enforcement is turned
// off.
TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
- auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
+ auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
TestConstants::OVERLAY_NAME_ALL_POLICIES,
PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
@@ -264,7 +292,7 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnore
// Overlays that do not target an <overlayable> tag can overlay any resource if overlayable
// enforcement is disabled.
TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
- auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-legacy.apk", "",
+ auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay-legacy.apk", "",
PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
@@ -284,10 +312,9 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTarget
// Overlays that are neither pre-installed nor signed with the same signature as the target cannot
// overlay packages that have not defined overlayable resources.
TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
- auto resources =
- TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay.apk",
- "NoTargetName", PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ auto resources = TestGetResourceMapping("target/target-no-overlayable.apk", "overlay/overlay.apk",
+ "NoTargetName", PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
@@ -297,9 +324,9 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
// signed with the same signature as the reference package can overlay packages that have not
// defined overlayable resources.
TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
- auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void {
+ auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) {
auto resources =
- TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay.apk",
+ TestGetResourceMapping("target/target-no-overlayable.apk", "overlay/overlay.apk",
TestConstants::OVERLAY_NAME_ALL_POLICIES, fulfilled_policies,
/* enforce_overlayable */ true);
diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp
index 1f6bf49f5f0e..69142086765c 100644
--- a/cmds/idmap2/tests/ResourceUtilsTests.cpp
+++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp
@@ -17,10 +17,12 @@
#include <memory>
#include <string>
+#include "R.h"
#include "TestHelpers.h"
#include "androidfw/ApkAssets.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "idmap2/ResourceContainer.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
@@ -49,8 +51,8 @@ class ResourceUtilsTests : public Idmap2Tests {
};
TEST_F(ResourceUtilsTests, ResToTypeEntryName) {
- Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000U);
- ASSERT_TRUE(name);
+ Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), R::target::integer::int1);
+ ASSERT_TRUE(name) << name.GetErrorMessage();
ASSERT_EQ(*name, "integer/int1");
}
@@ -60,25 +62,34 @@ TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) {
}
TEST_F(ResourceUtilsTests, InvalidValidOverlayNameInvalidAttributes) {
- auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk",
- "InvalidName");
+ auto overlay =
+ OverlayResourceContainer::FromPath(GetTestDataPath() + "/overlay/overlay-invalid.apk");
+ ASSERT_TRUE(overlay);
+
+ auto info = (*overlay)->FindOverlayInfo("InvalidName");
ASSERT_FALSE(info);
}
TEST_F(ResourceUtilsTests, ValidOverlayNameInvalidAttributes) {
- auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk",
- "ValidName");
+ auto overlay =
+ OverlayResourceContainer::FromPath(GetTestDataPath() + "/overlay/overlay-invalid.apk");
+ ASSERT_TRUE(overlay);
+
+ auto info = (*overlay)->FindOverlayInfo("ValidName");
ASSERT_FALSE(info);
}
TEST_F(ResourceUtilsTests, ValidOverlayNameAndTargetPackageInvalidAttributes) {
- auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk",
- "ValidNameAndTargetPackage");
+ auto overlay =
+ OverlayResourceContainer::FromPath(GetTestDataPath() + "/overlay/overlay-invalid.apk");
+ ASSERT_TRUE(overlay);
+
+ auto info = (*overlay)->FindOverlayInfo("ValidNameAndTargetPackage");
ASSERT_TRUE(info);
ASSERT_EQ("ValidNameAndTargetPackage", info->name);
ASSERT_EQ("Valid", info->target_package);
- ASSERT_EQ("", info->target_name); // Attribute resource id could not be found
- ASSERT_EQ(0, info->resource_mapping); // Attribute resource id could not be found
+ ASSERT_EQ("", info->target_name); // Attribute resource id could not be found
+ ASSERT_EQ(0, info->resource_mapping); // Attribute resource id could not be found
}
-}// namespace android::idmap2
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
index 842af3dd7b3c..6b5f3a8a98eb 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -24,13 +24,13 @@
namespace android::idmap2 {
-const unsigned char idmap_raw_data[] = {
+const unsigned char kIdmapRawData[] = {
// IDMAP HEADER
// 0x0: magic
0x49, 0x44, 0x4d, 0x50,
// 0x4: version
- 0x07, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
// 0x8: target crc
0x34, 0x12, 0x00, 0x00,
@@ -70,81 +70,72 @@ const unsigned char idmap_raw_data[] = {
0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00,
// DATA HEADER
- // 0x54: target_package_id
- 0x7f,
-
- // 0x55: overlay_package_id
- 0x7f,
-
- // 0x56: padding
- 0x00, 0x00,
-
- // 0x58: target_entry_count
+ // 0x54: target_entry_count
0x03, 0x00, 0x00, 0x00,
- // 0x5c: target_inline_entry_count
+ // 0x58: target_inline_entry_count
0x01, 0x00, 0x00, 0x00,
- // 0x60: overlay_entry_count
+ // 0x5c: overlay_entry_count
0x03, 0x00, 0x00, 0x00,
- // 0x64: string_pool_offset
+ // 0x60: string_pool_offset
0x00, 0x00, 0x00, 0x00,
// TARGET ENTRIES
- // 0x68: target id (0x7f020000)
+ // 0x64: target id (0x7f020000)
0x00, 0x00, 0x02, 0x7f,
- // 0x6c: overlay_id (0x7f020000)
+ // 0x68: overlay_id (0x7f020000)
0x00, 0x00, 0x02, 0x7f,
- // 0x70: target id (0x7f030000)
+ // 0x6c: target id (0x7f030000)
0x00, 0x00, 0x03, 0x7f,
- // 0x74: overlay_id (0x7f030000)
+ // 0x70: overlay_id (0x7f030000)
0x00, 0x00, 0x03, 0x7f,
- // 0x78: target id (0x7f030002)
+ // 0x74: target id (0x7f030002)
0x02, 0x00, 0x03, 0x7f,
- // 0x7c: overlay_id (0x7f030001)
+ // 0x78: overlay_id (0x7f030001)
0x01, 0x00, 0x03, 0x7f,
// INLINE TARGET ENTRIES
- // 0x80: target_id
+ // 0x7c: target_id
0x00, 0x00, 0x04, 0x7f,
- // 0x84: Res_value::size (value ignored by idmap)
+ // 0x80: Res_value::size (value ignored by idmap)
0x08, 0x00,
- // 0x87: Res_value::res0 (value ignored by idmap)
+ // 0x82: Res_value::res0 (value ignored by idmap)
0x00,
- // 0x88: Res_value::dataType (TYPE_INT_HEX)
+ // 0x83: Res_value::dataType (TYPE_INT_HEX)
0x11,
- // 0x8c: Res_value::data
+ // 0x84: Res_value::data
0x78, 0x56, 0x34, 0x12,
// OVERLAY ENTRIES
- // 0x90: 0x7f020000 -> 0x7f020000
+ // 0x88: 0x7f020000 -> 0x7f020000
0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
- // 0x98: 0x7f030000 -> 0x7f030000
+ // 0x90: 0x7f030000 -> 0x7f030000
0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
- // 0xa0: 0x7f030001 -> 0x7f030002
+ // 0x98: 0x7f030001 -> 0x7f030002
0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f,
- // 0xa4: string pool
+ // 0xa0: string pool
// string length,
0x04, 0x00, 0x00, 0x00,
- // 0xa8 string contents "test"
+ // 0xa4 string contents "test"
0x74, 0x65, 0x73, 0x74};
-const unsigned int kIdmapRawDataLen = 0xac;
+const unsigned int kIdmapRawDataLen = 0xa8;
const unsigned int kIdmapRawDataOffset = 0x54;
const unsigned int kIdmapRawDataTargetCrc = 0x1234;
const unsigned int kIdmapRawOverlayCrc = 0x5678;
diff --git a/cmds/idmap2/tests/XmlParserTests.cpp b/cmds/idmap2/tests/XmlParserTests.cpp
index 1a7eaca4d67b..eaf10a7d9282 100644
--- a/cmds/idmap2/tests/XmlParserTests.cpp
+++ b/cmds/idmap2/tests/XmlParserTests.cpp
@@ -19,25 +19,25 @@
#include <string>
#include "TestHelpers.h"
-#include "gmock/gmock.h"
+#include "androidfw/AssetsProvider.h"
#include "gtest/gtest.h"
#include "idmap2/XmlParser.h"
-#include "idmap2/ZipFile.h"
namespace android::idmap2 {
-Result<std::unique_ptr<const XmlParser>> CreateTestParser(const std::string& test_file) {
- auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+Result<XmlParser> CreateTestParser(const std::string& test_file) {
+ auto zip = ZipAssetsProvider::Create(GetTestDataPath() + "/target/target.apk");
if (zip == nullptr) {
return Error("Failed to open zip file");
}
- auto data = zip->Uncompress(test_file);
+ auto data = zip->Open(test_file);
if (data == nullptr) {
return Error("Failed to open xml file");
}
- return XmlParser::Create(data->buf, data->size, /* copy_data */ true);
+ return XmlParser::Create(data->getBuffer(true /* aligned*/), data->getLength(),
+ /* copy_data */ true);
}
TEST(XmlParserTests, Create) {
@@ -54,7 +54,7 @@ TEST(XmlParserTests, NextChild) {
auto xml = CreateTestParser("res/xml/test.xml");
ASSERT_TRUE(xml) << xml.GetErrorMessage();
- auto root_iter = (*xml)->tree_iterator();
+ auto root_iter = xml->tree_iterator();
ASSERT_EQ(root_iter->event(), XmlParser::Event::START_TAG);
ASSERT_EQ(root_iter->name(), "a");
@@ -85,7 +85,7 @@ TEST(XmlParserTests, AttributeValues) {
ASSERT_TRUE(xml) << xml.GetErrorMessage();
// Start at the <a> tag.
- auto root_iter = (*xml)->tree_iterator();
+ auto root_iter = xml->tree_iterator();
// Start at the <b> tag.
auto a_iter = root_iter.begin();
@@ -111,8 +111,8 @@ TEST(XmlParserTests, IteratorEquality) {
ASSERT_TRUE(xml) << xml.GetErrorMessage();
// Start at the <a> tag.
- auto root_iter_1 = (*xml)->tree_iterator();
- auto root_iter_2 = (*xml)->tree_iterator();
+ auto root_iter_1 = xml->tree_iterator();
+ auto root_iter_2 = xml->tree_iterator();
ASSERT_EQ(root_iter_1, root_iter_2);
ASSERT_EQ(*root_iter_1, *root_iter_2);
@@ -146,7 +146,7 @@ TEST(XmlParserTests, Backtracking) {
ASSERT_TRUE(xml) << xml.GetErrorMessage();
// Start at the <a> tag.
- auto root_iter_1 = (*xml)->tree_iterator();
+ auto root_iter_1 = xml->tree_iterator();
// Start at the <b> tag.
auto a_iter_1 = root_iter_1.begin();
diff --git a/cmds/idmap2/tests/ZipFileTests.cpp b/cmds/idmap2/tests/ZipFileTests.cpp
deleted file mode 100644
index 3fca43621945..000000000000
--- a/cmds/idmap2/tests/ZipFileTests.cpp
+++ /dev/null
@@ -1,66 +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.
- */
-
-#include <cstdio> // fclose
-#include <string>
-
-#include "TestHelpers.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "idmap2/Result.h"
-#include "idmap2/ZipFile.h"
-
-using ::testing::IsNull;
-using ::testing::NotNull;
-
-namespace android::idmap2 {
-
-TEST(ZipFileTests, BasicOpen) {
- auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
- ASSERT_THAT(zip, NotNull());
-
- fclose(stderr); // silence expected warnings from libziparchive
- auto fail = ZipFile::Open(GetTestDataPath() + "/does-not-exist");
- ASSERT_THAT(fail, IsNull());
-}
-
-TEST(ZipFileTests, Crc) {
- auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
- ASSERT_THAT(zip, NotNull());
-
- Result<uint32_t> crc = zip->Crc("AndroidManifest.xml");
- ASSERT_TRUE(crc);
- ASSERT_EQ(*crc, 0x762f3d24);
-
- Result<uint32_t> crc2 = zip->Crc("does-not-exist");
- ASSERT_FALSE(crc2);
-}
-
-TEST(ZipFileTests, Uncompress) {
- auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
- ASSERT_THAT(zip, NotNull());
-
- auto data = zip->Uncompress("assets/lorem-ipsum.txt");
- ASSERT_THAT(data, NotNull());
- const std::string lorem_ipsum("Lorem ipsum dolor sit amet.\n");
- ASSERT_THAT(data->size, lorem_ipsum.size());
- ASSERT_THAT(std::string(reinterpret_cast<const char*>(data->buf), data->size), lorem_ipsum);
-
- auto fail = zip->Uncompress("does-not-exist");
- ASSERT_THAT(fail, IsNull());
-}
-
-} // namespace android::idmap2
diff --git a/cmds/ime/Android.bp b/cmds/ime/Android.bp
index 76a16c81ca38..6dd3ba1ca7ef 100644
--- a/cmds/ime/Android.bp
+++ b/cmds/ime/Android.bp
@@ -1,6 +1,23 @@
// Copyright 2007 The Android Open Source Project
//
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_ime_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_ime_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
sh_binary {
name: "ime",
src: "ime",
diff --git a/cmds/incident/Android.bp b/cmds/incident/Android.bp
index 94855aa0311f..147885f029c2 100644
--- a/cmds/incident/Android.bp
+++ b/cmds/incident/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_binary {
name: "incident",
diff --git a/cmds/incident_helper/Android.bp b/cmds/incident_helper/Android.bp
index f07743ec2ee6..5b819ebc3dfd 100644
--- a/cmds/incident_helper/Android.bp
+++ b/cmds/incident_helper/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_binary {
name: "incident-helper-cmd",
wrapper: "incident_helper_cmd",
diff --git a/cmds/incidentd/Android.bp b/cmds/incidentd/Android.bp
index c47526abad53..b0b23f569664 100644
--- a/cmds/incidentd/Android.bp
+++ b/cmds/incidentd/Android.bp
@@ -16,6 +16,15 @@
// incidentd
// =========
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_binary {
name: "incidentd",
diff --git a/cmds/input/Android.bp b/cmds/input/Android.bp
index 1ee9dd355290..2e30176971d7 100644
--- a/cmds/input/Android.bp
+++ b/cmds/input/Android.bp
@@ -1,6 +1,23 @@
// Copyright 2008 The Android Open Source Project
//
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_input_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_input_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
sh_binary {
name: "input",
src: "input",
diff --git a/cmds/interrupter/Android.bp b/cmds/interrupter/Android.bp
index d68e7fe37535..d7f744d0834e 100644
--- a/cmds/interrupter/Android.bp
+++ b/cmds/interrupter/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_shared {
name: "interrupter",
host_supported: true,
diff --git a/cmds/locksettings/Android.bp b/cmds/locksettings/Android.bp
index 59ccc5cd082e..3869c8fcdabc 100644
--- a/cmds/locksettings/Android.bp
+++ b/cmds/locksettings/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_binary {
name: "locksettings",
wrapper: "locksettings",
diff --git a/cmds/pm/Android.bp b/cmds/pm/Android.bp
index 0644f6efcb8a..847dbabdf4c0 100644
--- a/cmds/pm/Android.bp
+++ b/cmds/pm/Android.bp
@@ -1,6 +1,23 @@
// Copyright 2007 The Android Open Source Project
//
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_pm_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_pm_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
sh_binary {
name: "pm",
src: "pm",
diff --git a/cmds/requestsync/Android.bp b/cmds/requestsync/Android.bp
index ef2a8a6be3ca..57e8dd355b26 100644
--- a/cmds/requestsync/Android.bp
+++ b/cmds/requestsync/Android.bp
@@ -1,6 +1,23 @@
// Copyright 2012 The Android Open Source Project
//
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_requestsync_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_requestsync_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
java_binary {
name: "requestsync",
wrapper: "requestsync",
diff --git a/cmds/screencap/Android.bp b/cmds/screencap/Android.bp
index fc628a696afa..c009c1f5b08b 100644
--- a/cmds/screencap/Android.bp
+++ b/cmds/screencap/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_binary {
name: "screencap",
diff --git a/cmds/settings/Android.bp b/cmds/settings/Android.bp
index 8a78e5420a5f..cc730062f9df 100644
--- a/cmds/settings/Android.bp
+++ b/cmds/settings/Android.bp
@@ -1,6 +1,17 @@
// Copyright 2011 The Android Open Source Project
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-MIT
+ // SPDX-license-identifier-Unicode-DFS
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
sh_binary {
name: "settings",
src: "settings",
diff --git a/cmds/sm/Android.bp b/cmds/sm/Android.bp
index 11e4e72b435b..ecfacaeecfd9 100644
--- a/cmds/sm/Android.bp
+++ b/cmds/sm/Android.bp
@@ -1,6 +1,23 @@
// Copyright 2015 The Android Open Source Project
//
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_sm_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_sm_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
java_binary {
name: "sm",
wrapper: "sm",
diff --git a/cmds/svc/Android.bp b/cmds/svc/Android.bp
index 68b48f11e5a6..41a3ebd2f514 100644
--- a/cmds/svc/Android.bp
+++ b/cmds/svc/Android.bp
@@ -1,6 +1,23 @@
// Copyright 2007 The Android Open Source Project
//
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_svc_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_svc_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
java_binary {
name: "svc",
wrapper: "svc",
diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
index 227b9e24f173..115c1f23c521 100644
--- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
@@ -55,7 +55,11 @@ public class UsbCommand extends Svc.Command {
+ " svc usb getGadgetHalVersion\n"
+ " Gets current Gadget Hal Version\n"
+ " possible values of Hal version are any of 'unknown', 'V1_0', 'V1_1',\n"
- + " 'V1_2'\n";
+ + " 'V1_2'\n"
+ + " svc usb getUsbHalVersion\n"
+ + " Gets current USB Hal Version\n"
+ + " possible values of Hal version are any of 'unknown', 'V1_0', 'V1_1',\n"
+ + " 'V1_2', 'V1_3'\n";
}
@Override
@@ -111,6 +115,25 @@ public class UsbCommand extends Svc.Command {
System.err.println("Error communicating with UsbManager: " + e);
}
return;
+ } else if ("getUsbHalVersion".equals(args[1])) {
+ try {
+ int version = usbMgr.getUsbHalVersion();
+
+ if (version == 13) {
+ System.err.println("V1_3");
+ } else if (version == 12) {
+ System.err.println("V1_2");
+ } else if (version == 11) {
+ System.err.println("V1_1");
+ } else if (version == 10) {
+ System.err.println("V1_0");
+ } else {
+ System.err.println("unknown");
+ }
+ } catch (RemoteException e) {
+ System.err.println("Error communicating with UsbManager: " + e);
+ }
+ return;
}
}
System.err.println(longHelp());
diff --git a/cmds/telecom/Android.bp b/cmds/telecom/Android.bp
index 56e147c5c8ca..4da79c54477b 100644
--- a/cmds/telecom/Android.bp
+++ b/cmds/telecom/Android.bp
@@ -1,6 +1,23 @@
// Copyright 2015 The Android Open Source Project
//
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_telecom_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_telecom_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
java_binary {
name: "telecom",
wrapper: "telecom",
diff --git a/cmds/uiautomator/Android.bp b/cmds/uiautomator/Android.bp
index f9cb3dd38897..a0bf624c6455 100644
--- a/cmds/uiautomator/Android.bp
+++ b/cmds/uiautomator/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
genrule {
name: "uiautomator-last-released-api",
srcs: ["api/*.txt"],
diff --git a/cmds/uiautomator/cmds/uiautomator/Android.bp b/cmds/uiautomator/cmds/uiautomator/Android.bp
index 68cc5a339107..56e2e70958fa 100644
--- a/cmds/uiautomator/cmds/uiautomator/Android.bp
+++ b/cmds/uiautomator/cmds/uiautomator/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_binary {
name: "uiautomator",
wrapper: "uiautomator",
diff --git a/cmds/uiautomator/instrumentation/Android.bp b/cmds/uiautomator/instrumentation/Android.bp
index 477f0d1f6e10..50e223425f14 100644
--- a/cmds/uiautomator/instrumentation/Android.bp
+++ b/cmds/uiautomator/instrumentation/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test {
name: "uiautomator-instrumentation",
diff --git a/cmds/uiautomator/library/Android.bp b/cmds/uiautomator/library/Android.bp
index 14b74da0c616..469b45201337 100644
--- a/cmds/uiautomator/library/Android.bp
+++ b/cmds/uiautomator/library/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
droidstubs {
name: "uiautomator-stubs",
srcs: [
diff --git a/cmds/vr/Android.bp b/cmds/vr/Android.bp
index cb129bdf6853..893649155fd8 100644
--- a/cmds/vr/Android.bp
+++ b/cmds/vr/Android.bp
@@ -1,6 +1,23 @@
// Copyright 2017 The Android Open Source Project
//
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_vr_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_vr_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
java_binary {
name: "vr",
wrapper: "vr",
diff --git a/cmds/wm/Android.bp b/cmds/wm/Android.bp
index 609f84b89bd8..cf6b0193fe87 100644
--- a/cmds/wm/Android.bp
+++ b/cmds/wm/Android.bp
@@ -1,6 +1,23 @@
// Copyright 2013 The Android Open Source Project
//
+package {
+ default_applicable_licenses: ["frameworks_base_cmds_wm_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_cmds_wm_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
sh_binary {
name: "wm",
src: "wm",
diff --git a/config/Android.bp b/config/Android.bp
index 8dd409b51eee..6a6f848b7d52 100644
--- a/config/Android.bp
+++ b/config/Android.bp
@@ -12,6 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["frameworks_base_config_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_config_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "copyright-header",
+ ],
+}
+
filegroup {
name: "preloaded-classes-denylist",
srcs: ["preloaded-classes-denylist"],
diff --git a/config/hiddenapi-unsupported.txt b/config/hiddenapi-unsupported.txt
index 90a526bcfaf7..48aa8b2c2e30 100644
--- a/config/hiddenapi-unsupported.txt
+++ b/config/hiddenapi-unsupported.txt
@@ -204,7 +204,6 @@ Landroid/os/IRemoteCallback$Stub;-><init>()V
Landroid/os/IUpdateEngine$Stub;-><init>()V
Landroid/os/IUserManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/os/IUserManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IUserManager;
-Landroid/os/IVibratorService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IVibratorService;
Landroid/os/storage/IObbActionListener$Stub;-><init>()V
Landroid/os/storage/IStorageManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/os/storage/IStorageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IStorageManager;
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 0c440e810ea8..c6ec37690d88 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -400,6 +400,9 @@ android.app.INotificationManager
android.app.IProcessObserver$Stub$Proxy
android.app.IProcessObserver$Stub
android.app.IProcessObserver
+android.app.IRequestFinishCallback$Stub$Proxy
+android.app.IRequestFinishCallback$Stub
+android.app.IRequestFinishCallback
android.app.ISearchManager$Stub$Proxy
android.app.ISearchManager$Stub
android.app.ISearchManager
diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist
index 43a8a878a624..da4b25519e80 100644
--- a/config/preloaded-classes-denylist
+++ b/config/preloaded-classes-denylist
@@ -4,7 +4,6 @@ android.os.FileObserver
android.os.NullVibrator
android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
android.widget.Magnifier
-com.android.server.BootReceiver$2
gov.nist.core.net.DefaultNetworkLayer
android.net.rtp.AudioGroup
android.net.rtp.AudioStream
diff --git a/core/api/Android.bp b/core/api/Android.bp
index 00b901992b90..170febb4f766 100644
--- a/core/api/Android.bp
+++ b/core/api/Android.bp
@@ -14,6 +14,14 @@
package {
default_visibility: ["//visibility:private"],
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-MIT
+ // SPDX-license-identifier-Unicode-DFS
+ default_applicable_licenses: ["frameworks_base_license"],
}
filegroup {
diff --git a/core/api/current.txt b/core/api/current.txt
index f5c67c170931..d6c783dc71b0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -88,6 +88,7 @@ package android {
field @Deprecated public static final String GET_TASKS = "android.permission.GET_TASKS";
field public static final String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH";
field public static final String HIDE_OVERLAY_WINDOWS = "android.permission.HIDE_OVERLAY_WINDOWS";
+ field public static final String HIGH_SAMPLING_RATE_SENSORS = "android.permission.HIGH_SAMPLING_RATE_SENSORS";
field public static final String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
field public static final String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES";
field public static final String INSTALL_SHORTCUT = "com.android.launcher.permission.INSTALL_SHORTCUT";
@@ -144,6 +145,7 @@ package android {
field public static final String REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE = "android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE";
field public static final String REQUEST_PASSWORD_COMPLEXITY = "android.permission.REQUEST_PASSWORD_COMPLEXITY";
field @Deprecated public static final String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
+ field public static final String SCHEDULE_EXACT_ALARM = "android.permission.SCHEDULE_EXACT_ALARM";
field public static final String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
field public static final String SEND_SMS = "android.permission.SEND_SMS";
field public static final String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
@@ -435,6 +437,7 @@ package android {
field public static final int clickable = 16842981; // 0x10100e5
field public static final int clipChildren = 16842986; // 0x10100ea
field public static final int clipOrientation = 16843274; // 0x101020a
+ field public static final int clipToOutline = 16844328; // 0x1010628
field public static final int clipToPadding = 16842987; // 0x10100eb
field public static final int closeIcon = 16843905; // 0x1010481
field @Deprecated public static final int codes = 16843330; // 0x1010242
@@ -572,6 +575,7 @@ package android {
field public static final int dropDownWidth = 16843362; // 0x1010262
field public static final int duplicateParentState = 16842985; // 0x10100e9
field public static final int duration = 16843160; // 0x1010198
+ field public static final int edgeEffectType = 16844329; // 0x1010629
field public static final int editTextBackground = 16843602; // 0x1010352
field public static final int editTextColor = 16843601; // 0x1010351
field public static final int editTextPreferenceStyle = 16842898; // 0x1010092
@@ -847,6 +851,7 @@ package android {
field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
field public static final int keycode = 16842949; // 0x10100c5
field public static final int killAfterRestore = 16843420; // 0x101029c
+ field public static final int knownCerts = 16844330; // 0x101062a
field public static final int label = 16842753; // 0x1010001
field public static final int labelFor = 16843718; // 0x10103c6
field @Deprecated public static final int labelTextSize = 16843317; // 0x1010235
@@ -956,6 +961,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 maxRows = 16843059; // 0x1010133
field public static final int maxSdkVersion = 16843377; // 0x1010271
field public static final int maxWidth = 16843039; // 0x101011f
@@ -1100,6 +1107,7 @@ package android {
field public static final int presentationTheme = 16843712; // 0x10103c0
field public static final int preserveLegacyExternalStorage = 16844308; // 0x1010614
field public static final int previewImage = 16843482; // 0x10102da
+ field public static final int previewLayout = 16844327; // 0x1010627
field public static final int primaryContentAlpha = 16844114; // 0x1010552
field public static final int priority = 16842780; // 0x101001c
field public static final int privateImeOptions = 16843299; // 0x1010223
@@ -1181,6 +1189,7 @@ 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
@@ -1291,6 +1300,7 @@ package android {
field public static final int spinnerMode = 16843505; // 0x10102f1
field public static final int spinnerStyle = 16842881; // 0x1010081
field public static final int spinnersShown = 16843595; // 0x101034b
+ field public static final int splashScreenTheme = 16844336; // 0x1010630
field public static final int splitMotionEvents = 16843503; // 0x10102ef
field public static final int splitName = 16844105; // 0x1010549
field public static final int splitTrack = 16843852; // 0x101044c
@@ -1387,6 +1397,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 targetClass = 16842799; // 0x101002f
field @Deprecated public static final int targetDescriptions = 16843680; // 0x10103a0
field public static final int targetId = 16843740; // 0x10103dc
@@ -1610,6 +1622,7 @@ package android {
field public static final int windowAllowReturnTransitionOverlap = 16843835; // 0x101043b
field public static final int windowAnimationStyle = 16842926; // 0x10100ae
field public static final int windowBackground = 16842836; // 0x1010054
+ field public static final int windowBackgroundBlurRadius = 16844331; // 0x101062b
field public static final int windowBackgroundFallback = 16844035; // 0x1010503
field public static final int windowBlurBehindEnabled = 16844316; // 0x101061c
field public static final int windowBlurBehindRadius = 16844315; // 0x101061b
@@ -1650,6 +1663,10 @@ package android {
field public static final int windowShowAnimation = 16842934; // 0x10100b6
field public static final int windowShowWallpaper = 16843410; // 0x1010292
field public static final int windowSoftInputMode = 16843307; // 0x101022b
+ field public static final int windowSplashScreenAnimatedIcon = 16844333; // 0x101062d
+ 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 windowSwipeToDismiss = 16843763; // 0x10103f3
field public static final int windowTitleBackgroundStyle = 16842844; // 0x101005c
@@ -1697,30 +1714,42 @@ package android {
field @Deprecated public static final int secondary_text_dark_nodisable = 17170438; // 0x1060006
field @Deprecated public static final int secondary_text_light = 17170439; // 0x1060007
field @Deprecated public static final int secondary_text_light_nodisable = 17170440; // 0x1060008
- field public static final int system_accent_0 = 17170473; // 0x1060029
- field public static final int system_accent_100 = 17170475; // 0x106002b
- field public static final int system_accent_1000 = 17170484; // 0x1060034
- field public static final int system_accent_200 = 17170476; // 0x106002c
- field public static final int system_accent_300 = 17170477; // 0x106002d
- field public static final int system_accent_400 = 17170478; // 0x106002e
- field public static final int system_accent_50 = 17170474; // 0x106002a
- field public static final int system_accent_500 = 17170479; // 0x106002f
- field public static final int system_accent_600 = 17170480; // 0x1060030
- field public static final int system_accent_700 = 17170481; // 0x1060031
- field public static final int system_accent_800 = 17170482; // 0x1060032
- field public static final int system_accent_900 = 17170483; // 0x1060033
- field public static final int system_main_0 = 17170461; // 0x106001d
- field public static final int system_main_100 = 17170463; // 0x106001f
- field public static final int system_main_1000 = 17170472; // 0x1060028
- field public static final int system_main_200 = 17170464; // 0x1060020
- field public static final int system_main_300 = 17170465; // 0x1060021
- field public static final int system_main_400 = 17170466; // 0x1060022
- field public static final int system_main_50 = 17170462; // 0x106001e
- field public static final int system_main_500 = 17170467; // 0x1060023
- field public static final int system_main_600 = 17170468; // 0x1060024
- field public static final int system_main_700 = 17170469; // 0x1060025
- field public static final int system_main_800 = 17170470; // 0x1060026
- field public static final int system_main_900 = 17170471; // 0x1060027
+ field public static final int system_neutral_0 = 17170485; // 0x1060035
+ field public static final int system_neutral_100 = 17170487; // 0x1060037
+ field public static final int system_neutral_1000 = 17170496; // 0x1060040
+ field public static final int system_neutral_200 = 17170488; // 0x1060038
+ field public static final int system_neutral_300 = 17170489; // 0x1060039
+ field public static final int system_neutral_400 = 17170490; // 0x106003a
+ field public static final int system_neutral_50 = 17170486; // 0x1060036
+ field public static final int system_neutral_500 = 17170491; // 0x106003b
+ field public static final int system_neutral_600 = 17170492; // 0x106003c
+ field public static final int system_neutral_700 = 17170493; // 0x106003d
+ field public static final int system_neutral_800 = 17170494; // 0x106003e
+ field public static final int system_neutral_900 = 17170495; // 0x106003f
+ field public static final int system_primary_0 = 17170461; // 0x106001d
+ field public static final int system_primary_100 = 17170463; // 0x106001f
+ field public static final int system_primary_1000 = 17170472; // 0x1060028
+ field public static final int system_primary_200 = 17170464; // 0x1060020
+ field public static final int system_primary_300 = 17170465; // 0x1060021
+ field public static final int system_primary_400 = 17170466; // 0x1060022
+ field public static final int system_primary_50 = 17170462; // 0x106001e
+ field public static final int system_primary_500 = 17170467; // 0x1060023
+ field public static final int system_primary_600 = 17170468; // 0x1060024
+ field public static final int system_primary_700 = 17170469; // 0x1060025
+ field public static final int system_primary_800 = 17170470; // 0x1060026
+ field public static final int system_primary_900 = 17170471; // 0x1060027
+ field public static final int system_secondary_0 = 17170473; // 0x1060029
+ field public static final int system_secondary_100 = 17170475; // 0x106002b
+ field public static final int system_secondary_1000 = 17170484; // 0x1060034
+ field public static final int system_secondary_200 = 17170476; // 0x106002c
+ field public static final int system_secondary_300 = 17170477; // 0x106002d
+ field public static final int system_secondary_400 = 17170478; // 0x106002e
+ field public static final int system_secondary_50 = 17170474; // 0x106002a
+ field public static final int system_secondary_500 = 17170479; // 0x106002f
+ field public static final int system_secondary_600 = 17170480; // 0x1060030
+ field public static final int system_secondary_700 = 17170481; // 0x1060031
+ field public static final int system_secondary_800 = 17170482; // 0x1060032
+ field public static final int system_secondary_900 = 17170483; // 0x1060033
field public static final int tab_indicator_text = 17170441; // 0x1060009
field @Deprecated public static final int tertiary_text_dark = 17170448; // 0x1060010
field @Deprecated public static final int tertiary_text_light = 17170449; // 0x1060011
@@ -1736,6 +1765,9 @@ package android {
field public static final int dialog_min_width_minor = 17104900; // 0x1050004
field public static final int notification_large_icon_height = 17104902; // 0x1050006
field public static final int notification_large_icon_width = 17104901; // 0x1050005
+ field public static final int system_app_widget_background_radius = 17104904; // 0x1050008
+ field public static final int system_app_widget_inner_radius = 17104905; // 0x1050009
+ field public static final int system_app_widget_internal_padding = 17104906; // 0x105000a
field public static final int thumbnail_height = 17104897; // 0x1050001
field public static final int thumbnail_width = 17104898; // 0x1050002
}
@@ -2935,12 +2967,12 @@ package android.accessibilityservice {
field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28
field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
- field public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43; // 0x2b
field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b
field public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28; // 0x1c
field public static final int GESTURE_2_FINGER_SWIPE_UP = 25; // 0x19
field public static final int GESTURE_2_FINGER_TRIPLE_TAP = 21; // 0x15
+ field public static final int GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD = 43; // 0x2b
field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17
field public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; // 0x29
field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16
@@ -3860,6 +3892,7 @@ package android.app {
method @Nullable public android.net.Uri getReferrer();
method public int getRequestedOrientation();
method public final android.view.SearchEvent getSearchEvent();
+ method @NonNull public final android.window.SplashScreen getSplashScreen();
method public int getTaskId();
method public final CharSequence getTitle();
method public final int getTitleColor();
@@ -4296,16 +4329,17 @@ package android.app {
}
public class AlarmManager {
+ method public boolean canScheduleExactAlarms();
method public void cancel(android.app.PendingIntent);
method public void cancel(android.app.AlarmManager.OnAlarmListener);
method public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock();
method public void set(int, long, android.app.PendingIntent);
method public void set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
- method public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent);
+ 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 public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent);
+ 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);
method @RequiresPermission(android.Manifest.permission.SET_TIME) public void setTime(long);
@@ -5522,6 +5556,7 @@ package android.app {
method public android.graphics.drawable.Icon getSmallIcon();
method public String getSortKey();
method public long getTimeoutAfter();
+ method public boolean hasImage();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
field public static final int BADGE_ICON_LARGE = 2; // 0x2
@@ -5553,15 +5588,19 @@ package android.app {
field public static final int DEFAULT_LIGHTS = 4; // 0x4
field public static final int DEFAULT_SOUND = 1; // 0x1
field public static final int DEFAULT_VIBRATE = 2; // 0x2
+ field public static final String EXTRA_ANSWER_INTENT = "android.answerIntent";
field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
field public static final String EXTRA_BIG_TEXT = "android.bigText";
+ field public static final String EXTRA_CALL_PERSON = "android.callPerson";
field public static final String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
field public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
field public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
field public static final String EXTRA_COLORIZED = "android.colorized";
field public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
field public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+ field public static final String EXTRA_DECLINE_INTENT = "android.declineIntent";
+ field public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent";
field public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
field public static final String EXTRA_INFO_TEXT = "android.infoText";
field public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
@@ -5579,10 +5618,10 @@ package android.app {
field public static final String EXTRA_PROGRESS = "android.progress";
field public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
field public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
- field public static final String EXTRA_PROMOTE_PICTURE = "android.promotePicture";
field public static final String EXTRA_REMOTE_INPUT_DRAFT = "android.remoteInputDraft";
field public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
field @Deprecated public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
+ field public static final String EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED = "android.showBigPictureWhenCollapsed";
field public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
field public static final String EXTRA_SHOW_WHEN = "android.showWhen";
field @Deprecated public static final String EXTRA_SMALL_ICON = "android.icon";
@@ -5593,6 +5632,8 @@ package android.app {
field public static final String EXTRA_TEXT_LINES = "android.textLines";
field public static final String EXTRA_TITLE = "android.title";
field public static final String EXTRA_TITLE_BIG = "android.title.big";
+ field public static final String EXTRA_VERIFICATION_ICON = "android.verificationIcon";
+ field public static final String EXTRA_VERIFICATION_TEXT = "android.verificationText";
field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
field public static final int FLAG_BUBBLE = 4096; // 0x1000
field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
@@ -5841,6 +5882,14 @@ package android.app {
method @NonNull public android.app.Notification.Builder setWhen(long);
}
+ public static class Notification.CallStyle extends android.app.Notification.Style {
+ method @NonNull public static android.app.Notification.CallStyle forIncomingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent);
+ method @NonNull public static android.app.Notification.CallStyle forOngoingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent);
+ method @NonNull public static android.app.Notification.CallStyle forScreeningCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent);
+ method @NonNull public android.app.Notification.CallStyle setVerificationIcon(@Nullable android.graphics.drawable.Icon);
+ method @NonNull public android.app.Notification.CallStyle setVerificationText(@Nullable CharSequence);
+ }
+
public static final class Notification.CarExtender implements android.app.Notification.Extender {
ctor public Notification.CarExtender();
ctor public Notification.CarExtender(android.app.Notification);
@@ -6044,6 +6093,13 @@ package android.app {
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.NotificationChannel> CREATOR;
field public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
+ field public static final String EDIT_CONVERSATION = "convo";
+ field public static final String EDIT_IMPORTANCE = "importance";
+ field public static final String EDIT_LAUNCHER = "launcher";
+ field public static final String EDIT_LOCKED_DEVICE = "locked";
+ field public static final String EDIT_SOUND = "sound";
+ field public static final String EDIT_VIBRATION = "vibration";
+ field public static final String EDIT_ZEN = "dnd";
}
public final class NotificationChannelGroup implements android.os.Parcelable {
@@ -6634,6 +6690,7 @@ package android.app {
method @NonNull public java.time.LocalTime getCustomNightModeEnd();
method @NonNull public java.time.LocalTime getCustomNightModeStart();
method public int getNightMode();
+ method public void setApplicationNightMode(int);
method public void setCustomNightModeEnd(@NonNull java.time.LocalTime);
method public void setCustomNightModeStart(@NonNull java.time.LocalTime);
method public void setNightMode(int);
@@ -6878,6 +6935,7 @@ package android.app.admin {
method public void onLockTaskModeEntering(@NonNull android.content.Context, @NonNull android.content.Intent, @NonNull String);
method public void onLockTaskModeExiting(@NonNull android.content.Context, @NonNull android.content.Intent);
method public void onNetworkLogsAvailable(@NonNull android.content.Context, @NonNull android.content.Intent, long, @IntRange(from=1) int);
+ method public void onOperationSafetyStateChanged(@NonNull android.content.Context, int, boolean);
method @Deprecated public void onPasswordChanged(@NonNull android.content.Context, @NonNull android.content.Intent);
method public void onPasswordChanged(@NonNull android.content.Context, @NonNull android.content.Intent, @NonNull android.os.UserHandle);
method @Deprecated public void onPasswordExpiring(@NonNull android.content.Context, @NonNull android.content.Intent);
@@ -6931,6 +6989,7 @@ package android.app.admin {
method public void addUserRestriction(@NonNull android.content.ComponentName, String);
method public boolean bindDeviceAdminServiceAsUser(@NonNull android.content.ComponentName, android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
method public boolean canAdminGrantSensorsPermissions();
+ method public boolean canUsbDataSignalingBeDisabled();
method public void clearApplicationUserData(@NonNull android.content.ComponentName, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener);
method public void clearCrossProfileIntentFilters(@NonNull android.content.ComponentName);
method @Deprecated public void clearDeviceOwnerApp(String);
@@ -7048,16 +7107,20 @@ package android.app.admin {
method public boolean isManagedProfile(@NonNull android.content.ComponentName);
method public boolean isMasterVolumeMuted(@NonNull android.content.ComponentName);
method public boolean isNetworkLoggingEnabled(@Nullable android.content.ComponentName);
+ method public boolean isNetworkSlicingEnabled();
method public boolean isOrganizationOwnedDeviceWithManagedProfile();
method public boolean isOverrideApnEnabled(@NonNull android.content.ComponentName);
method public boolean isPackageSuspended(@NonNull android.content.ComponentName, String) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean isProfileOwnerApp(String);
method public boolean isProvisioningAllowed(@NonNull String);
method public boolean isResetPasswordTokenActive(android.content.ComponentName);
+ method public boolean isSafeOperation(int);
method public boolean isSecurityLoggingEnabled(@Nullable android.content.ComponentName);
method public boolean isUninstallBlocked(@Nullable android.content.ComponentName, String);
method public boolean isUniqueDeviceAttestationSupported();
+ method public boolean isUsbDataSignalingEnabled();
method public boolean isUsingUnifiedPassword(@NonNull android.content.ComponentName);
+ method @NonNull public java.util.List<android.os.UserHandle> listForegroundAffiliatedUsers();
method public void lockNow();
method public void lockNow(int);
method public int logoutUser(@NonNull android.content.ComponentName);
@@ -7117,6 +7180,7 @@ package android.app.admin {
method public void setMaximumTimeToLock(@NonNull android.content.ComponentName, long);
method @NonNull public java.util.List<java.lang.String> setMeteredDataDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
method public void setNetworkLoggingEnabled(@Nullable android.content.ComponentName, boolean);
+ method public void setNetworkSlicingEnabled(boolean);
method @Deprecated public void setOrganizationColor(@NonNull android.content.ComponentName, int);
method public void setOrganizationId(@NonNull String);
method public void setOrganizationName(@NonNull android.content.ComponentName, @Nullable CharSequence);
@@ -7158,6 +7222,7 @@ package android.app.admin {
method public boolean setTimeZone(@NonNull android.content.ComponentName, String);
method public void setTrustAgentConfiguration(@NonNull android.content.ComponentName, @NonNull android.content.ComponentName, android.os.PersistableBundle);
method public void setUninstallBlocked(@Nullable android.content.ComponentName, String, boolean);
+ method public void setUsbDataSignalingEnabled(boolean);
method public void setUserControlDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
method public void setUserIcon(@NonNull android.content.ComponentName, android.graphics.Bitmap);
method public int startUserInBackground(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
@@ -7226,7 +7291,7 @@ package android.app.admin {
field public static final String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
field public static final String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME";
field public static final String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI";
- field public static final String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR";
+ field @Deprecated public static final String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR";
field public static final String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE";
field public static final String EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT = "android.app.extra.PROVISIONING_PERMISSION_GRANT_OPT_OUT";
field public static final String EXTRA_PROVISIONING_SERIAL_NUMBER = "android.app.extra.PROVISIONING_SERIAL_NUMBER";
@@ -7282,6 +7347,7 @@ package android.app.admin {
field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
field public static final String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
+ field public static final int OPERATION_SAFETY_REASON_DRIVING_DISTRACTION = 1; // 0x1
field public static final int PASSWORD_COMPLEXITY_HIGH = 327680; // 0x50000
field public static final int PASSWORD_COMPLEXITY_LOW = 65536; // 0x10000
field public static final int PASSWORD_COMPLEXITY_MEDIUM = 196608; // 0x30000
@@ -7318,7 +7384,6 @@ package android.app.admin {
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
- field public static final int UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION = 1; // 0x1
field public static final int WIPE_EUICC = 4; // 0x4
field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
@@ -7472,7 +7537,7 @@ package android.app.admin {
public final class UnsafeStateException extends java.lang.IllegalStateException implements android.os.Parcelable {
method public int describeContents();
- method public int getReason();
+ method @NonNull public java.util.List<java.lang.Integer> getReasons();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.UnsafeStateException> CREATOR;
}
@@ -8274,6 +8339,7 @@ package android.appwidget {
public class AppWidgetHostView extends android.widget.FrameLayout {
ctor public AppWidgetHostView(android.content.Context);
ctor public AppWidgetHostView(android.content.Context, int, int);
+ method public void clearCurrentSize();
method public int getAppWidgetId();
method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo();
method public static android.graphics.Rect getDefaultPaddingForWidget(android.content.Context, android.content.ComponentName, android.graphics.Rect);
@@ -8281,11 +8347,13 @@ package android.appwidget {
method protected android.view.View getErrorView();
method protected void prepareView(android.view.View);
method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo);
+ method public void setCurrentSize(@NonNull android.graphics.PointF);
method public void setExecutor(java.util.concurrent.Executor);
method public void setOnLightBackground(boolean);
method public void updateAppWidget(android.widget.RemoteViews);
method public void updateAppWidgetOptions(android.os.Bundle);
- method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int);
+ method @Deprecated public void updateAppWidgetSize(android.os.Bundle, int, int, int, int);
+ method public void updateAppWidgetSize(@NonNull android.os.Bundle, @NonNull java.util.List<android.graphics.PointF>);
}
public class AppWidgetManager {
@@ -8338,6 +8406,7 @@ package android.appwidget {
field public static final String OPTION_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight";
field public static final String OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth";
field public static final String OPTION_APPWIDGET_RESTORE_COMPLETED = "appWidgetRestoreCompleted";
+ field public static final String OPTION_APPWIDGET_SIZES = "appWidgetSizes";
}
public class AppWidgetProvider extends android.content.BroadcastReceiver {
@@ -8381,13 +8450,18 @@ package android.appwidget {
field public int initialKeyguardLayout;
field public int initialLayout;
field @Deprecated public String label;
+ field public int maxResizeHeight;
+ field public int maxResizeWidth;
field public int minHeight;
field public int minResizeHeight;
field public int minResizeWidth;
field public int minWidth;
field public int previewImage;
+ field @IdRes public int previewLayout;
field public android.content.ComponentName provider;
field public int resizeMode;
+ field public int targetCellHeight;
+ field public int targetCellWidth;
field public int updatePeriodMillis;
field public int widgetCategory;
field public int widgetFeatures;
@@ -9798,6 +9872,7 @@ package android.content {
method public int getMimeTypeCount();
method public long getTimestamp();
method public boolean hasMimeType(String);
+ method public boolean isStyledText();
method public void setExtras(android.os.PersistableBundle);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.ClipDescription> CREATOR;
@@ -10204,12 +10279,15 @@ package android.content {
method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.INTERACT_ACROSS_PROFILES}) public boolean bindServiceAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
method @CheckResult(suggest="#enforceCallingOrSelfPermission(String,String)") public abstract int checkCallingOrSelfPermission(@NonNull String);
method @CheckResult(suggest="#enforceCallingOrSelfUriPermission(Uri,int,String)") public abstract int checkCallingOrSelfUriPermission(android.net.Uri, int);
+ method @NonNull public int[] checkCallingOrSelfUriPermissions(@NonNull java.util.List<android.net.Uri>, int);
method @CheckResult(suggest="#enforceCallingPermission(String,String)") public abstract int checkCallingPermission(@NonNull String);
method @CheckResult(suggest="#enforceCallingUriPermission(Uri,int,String)") public abstract int checkCallingUriPermission(android.net.Uri, int);
+ method @NonNull public int[] checkCallingUriPermissions(@NonNull java.util.List<android.net.Uri>, int);
method @CheckResult(suggest="#enforcePermission(String,int,int,String)") public abstract int checkPermission(@NonNull String, int, int);
method public abstract int checkSelfPermission(@NonNull String);
method @CheckResult(suggest="#enforceUriPermission(Uri,int,int,String)") public abstract int checkUriPermission(android.net.Uri, int, int, int);
method @CheckResult(suggest="#enforceUriPermission(Uri,String,String,int,int,int,String)") public abstract int checkUriPermission(@Nullable android.net.Uri, @Nullable String, @Nullable String, int, int, int);
+ method @NonNull public int[] checkUriPermissions(@NonNull java.util.List<android.net.Uri>, int, int, int);
method @Deprecated public abstract void clearWallpaper() throws java.io.IOException;
method @NonNull public android.content.Context createAttributionContext(@Nullable String);
method public abstract android.content.Context createConfigurationContext(@NonNull android.content.res.Configuration);
@@ -10367,6 +10445,7 @@ package android.content {
field public static final int CONTEXT_RESTRICTED = 4; // 0x4
field public static final String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
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 DOWNLOAD_SERVICE = "download";
field public static final String DROPBOX_SERVICE = "dropbox";
@@ -10422,6 +10501,7 @@ package android.content {
field public static final String USAGE_STATS_SERVICE = "usagestats";
field public static final String USB_SERVICE = "usb";
field public static final String USER_SERVICE = "user";
+ 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";
@@ -10962,6 +11042,7 @@ package android.content {
field public static final String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
field public static final String EXTRA_INSTALLER_PACKAGE_NAME = "android.intent.extra.INSTALLER_PACKAGE_NAME";
field public static final String EXTRA_INTENT = "android.intent.extra.INTENT";
+ field public static final String EXTRA_IS_BUBBLED = "android.intent.extra.IS_BUBBLED";
field public static final String EXTRA_KEY_EVENT = "android.intent.extra.KEY_EVENT";
field public static final String EXTRA_LOCAL_ONLY = "android.intent.extra.LOCAL_ONLY";
field public static final String EXTRA_LOCUS_ID = "android.intent.extra.LOCUS_ID";
@@ -12144,6 +12225,7 @@ package android.content.pm {
method public void setAutoRevokePermissionsMode(boolean);
method public void setInstallLocation(int);
method public void setInstallReason(int);
+ method public void setInstallScenario(int);
method public void setMultiPackage();
method public void setOriginatingUid(int);
method public void setOriginatingUri(@Nullable android.net.Uri);
@@ -12366,6 +12448,7 @@ package android.content.pm {
field public static final String FEATURE_INPUT_METHODS = "android.software.input_methods";
field public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
field public static final String FEATURE_IRIS = "android.hardware.biometrics.iris";
+ field public static final String FEATURE_KEYSTORE_APP_ATTEST_KEY = "android.hardware.keystore.app_attest_key";
field public static final String FEATURE_KEYSTORE_LIMITED_USE_KEY = "android.hardware.keystore.limited_use_key";
field public static final String FEATURE_KEYSTORE_SINGLE_USE_KEY = "android.hardware.keystore.single_use_key";
field public static final String FEATURE_LEANBACK = "android.software.leanback";
@@ -12472,6 +12555,10 @@ package android.content.pm {
field public static final int INSTALL_REASON_POLICY = 1; // 0x1
field public static final int INSTALL_REASON_UNKNOWN = 0; // 0x0
field public static final int INSTALL_REASON_USER = 4; // 0x4
+ field public static final int INSTALL_SCENARIO_BULK = 2; // 0x2
+ field public static final int INSTALL_SCENARIO_BULK_SECONDARY = 3; // 0x3
+ field public static final int INSTALL_SCENARIO_DEFAULT = 0; // 0x0
+ field public static final int INSTALL_SCENARIO_FAST = 1; // 0x1
field public static final int MATCH_ALL = 131072; // 0x20000
field public static final int MATCH_APEX = 1073741824; // 0x40000000
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
@@ -12754,6 +12841,7 @@ package android.content.pm {
method @NonNull public android.content.pm.ShortcutInfo.Builder setPersons(@NonNull android.app.Person[]);
method @NonNull public android.content.pm.ShortcutInfo.Builder setRank(int);
method @NonNull public android.content.pm.ShortcutInfo.Builder setShortLabel(@NonNull CharSequence);
+ method @NonNull public android.content.pm.ShortcutInfo.Builder setStartingTheme(int);
}
public class ShortcutManager {
@@ -15795,6 +15883,7 @@ package android.graphics {
method @NonNull public static android.graphics.RenderEffect createColorFilterEffect(@NonNull android.graphics.ColorFilter);
method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float);
method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float, @NonNull android.graphics.RenderEffect);
+ method @NonNull public static android.graphics.RenderEffect createShaderEffect(@NonNull android.graphics.Shader);
}
public final class RenderNode {
@@ -16490,9 +16579,13 @@ 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 {
@@ -17618,6 +17711,7 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SCALER_CROPPING_TYPE;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SCALER_DEFAULT_SECURE_IMAGE_SIZE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP;
@@ -20477,6 +20571,7 @@ package android.media {
field public static final int QUALITY_480P = 4; // 0x4
field public static final int QUALITY_4KDCI = 10; // 0xa
field public static final int QUALITY_720P = 5; // 0x5
+ field public static final int QUALITY_8KUHD = 13; // 0xd
field public static final int QUALITY_CIF = 3; // 0x3
field public static final int QUALITY_HIGH = 1; // 0x1
field public static final int QUALITY_HIGH_SPEED_1080P = 2004; // 0x7d4
@@ -20498,6 +20593,7 @@ package android.media {
field public static final int QUALITY_TIME_LAPSE_480P = 1004; // 0x3ec
field public static final int QUALITY_TIME_LAPSE_4KDCI = 1010; // 0x3f2
field public static final int QUALITY_TIME_LAPSE_720P = 1005; // 0x3ed
+ field public static final int QUALITY_TIME_LAPSE_8KUHD = 1013; // 0x3f5
field public static final int QUALITY_TIME_LAPSE_CIF = 1003; // 0x3eb
field public static final int QUALITY_TIME_LAPSE_HIGH = 1001; // 0x3e9
field public static final int QUALITY_TIME_LAPSE_LOW = 1000; // 0x3e8
@@ -20923,7 +21019,7 @@ package android.media {
method @NonNull public String getDiagnosticInfo();
}
- public final class MediaCodec {
+ public final class MediaCodec implements android.media.metrics.PlaybackComponent {
method public void configure(@Nullable android.media.MediaFormat, @Nullable android.view.Surface, @Nullable android.media.MediaCrypto, int);
method public void configure(@Nullable android.media.MediaFormat, @Nullable android.view.Surface, int, @Nullable android.media.MediaDescrambler);
method @NonNull public static android.media.MediaCodec createByCodecName(@NonNull String) throws java.io.IOException;
@@ -20949,6 +21045,7 @@ package android.media {
method @NonNull public android.media.MediaFormat getOutputFormat(int);
method @NonNull public android.media.MediaCodec.OutputFrame getOutputFrame(int);
method @Nullable public android.media.Image getOutputImage(int);
+ method public String getPlaybackId();
method @NonNull public android.media.MediaCodec.QueueRequest getQueueRequest(int);
method @Nullable public static android.media.Image mapHardwareBuffer(@NonNull android.hardware.HardwareBuffer);
method public void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException;
@@ -20964,6 +21061,7 @@ package android.media {
method public void setOnFrameRenderedListener(@Nullable android.media.MediaCodec.OnFrameRenderedListener, @Nullable android.os.Handler);
method public void setOutputSurface(@NonNull android.view.Surface);
method public void setParameters(@Nullable android.os.Bundle);
+ method public void setPlaybackId(@NonNull String);
method public void setVideoScalingMode(int);
method public void signalEndOfInputStream();
method public void start();
@@ -21021,15 +21119,15 @@ package android.media {
public static final class MediaCodec.CryptoException extends java.lang.RuntimeException {
ctor public MediaCodec.CryptoException(int, @Nullable String);
method public int getErrorCode();
- field public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8
- field public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4
- field public static final int ERROR_INSUFFICIENT_SECURITY = 7; // 0x7
- field public static final int ERROR_KEY_EXPIRED = 2; // 0x2
- field public static final int ERROR_LOST_STATE = 9; // 0x9
- field public static final int ERROR_NO_KEY = 1; // 0x1
- field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3
- field public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5
- field public static final int ERROR_UNSUPPORTED_OPERATION = 6; // 0x6
+ field @Deprecated public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8
+ field @Deprecated public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4
+ field @Deprecated public static final int ERROR_INSUFFICIENT_SECURITY = 7; // 0x7
+ field @Deprecated public static final int ERROR_KEY_EXPIRED = 2; // 0x2
+ field @Deprecated public static final int ERROR_LOST_STATE = 9; // 0x9
+ field @Deprecated public static final int ERROR_NO_KEY = 1; // 0x1
+ field @Deprecated public static final int ERROR_RESOURCE_BUSY = 3; // 0x3
+ field @Deprecated public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5
+ field @Deprecated public static final int ERROR_UNSUPPORTED_OPERATION = 6; // 0x6
}
public static final class MediaCodec.CryptoInfo {
@@ -21564,6 +21662,7 @@ package android.media {
method @android.media.MediaDrm.HdcpLevel public int getConnectedHdcpLevel();
method public android.media.MediaDrm.CryptoSession getCryptoSession(@NonNull byte[], @NonNull String, @NonNull String);
method @NonNull public android.media.MediaDrm.KeyRequest getKeyRequest(@NonNull byte[], @Nullable byte[], @Nullable String, int, @Nullable java.util.HashMap<java.lang.String,java.lang.String>) throws android.media.NotProvisionedException;
+ method @NonNull public java.util.List<android.media.MediaDrm.LogMessage> getLogMessages();
method @android.media.MediaDrm.HdcpLevel public int getMaxHdcpLevel();
method public static int getMaxSecurityLevel();
method public int getMaxSessionCount();
@@ -21647,6 +21746,42 @@ package android.media {
method public boolean verify(@NonNull byte[], @NonNull byte[], @NonNull byte[]);
}
+ public static final class MediaDrm.ErrorCodes {
+ field public static final int ERROR_CERTIFICATE_MALFORMED = 10; // 0xa
+ field public static final int ERROR_CERTIFICATE_MISSING = 11; // 0xb
+ field public static final int ERROR_CRYPTO_LIBRARY = 12; // 0xc
+ field public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8
+ field public static final int ERROR_GENERIC_OEM = 13; // 0xd
+ field public static final int ERROR_GENERIC_PLUGIN = 14; // 0xe
+ field public static final int ERROR_INIT_DATA = 15; // 0xf
+ field public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4
+ field public static final int ERROR_INSUFFICIENT_SECURITY = 7; // 0x7
+ field public static final int ERROR_KEY_EXPIRED = 2; // 0x2
+ field public static final int ERROR_KEY_NOT_LOADED = 16; // 0x10
+ field public static final int ERROR_LICENSE_PARSE = 17; // 0x11
+ field public static final int ERROR_LICENSE_POLICY = 18; // 0x12
+ field public static final int ERROR_LICENSE_RELEASE = 19; // 0x13
+ field public static final int ERROR_LICENSE_REQUEST_REJECTED = 20; // 0x14
+ field public static final int ERROR_LICENSE_RESTORE = 21; // 0x15
+ field public static final int ERROR_LICENSE_STATE = 22; // 0x16
+ field public static final int ERROR_LOST_STATE = 9; // 0x9
+ field public static final int ERROR_MEDIA_FRAMEWORK = 23; // 0x17
+ field public static final int ERROR_NO_KEY = 1; // 0x1
+ field public static final int ERROR_PROVISIONING_CERTIFICATE = 24; // 0x18
+ field public static final int ERROR_PROVISIONING_CONFIG = 25; // 0x19
+ field public static final int ERROR_PROVISIONING_PARSE = 26; // 0x1a
+ field public static final int ERROR_PROVISIONING_RETRY = 27; // 0x1b
+ field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3
+ field public static final int ERROR_RESOURCE_CONTENTION = 28; // 0x1c
+ field public static final int ERROR_SECURE_STOP_RELEASE = 29; // 0x1d
+ field public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5
+ field public static final int ERROR_STORAGE_READ = 30; // 0x1e
+ field public static final int ERROR_STORAGE_WRITE = 31; // 0x1f
+ field public static final int ERROR_UNKNOWN = 0; // 0x0
+ field public static final int ERROR_UNSUPPORTED_OPERATION = 6; // 0x6
+ field public static final int ERROR_ZERO_SUBSAMPLES = 32; // 0x20
+ }
+
@Deprecated @IntDef({android.media.MediaDrm.HDCP_LEVEL_UNKNOWN, android.media.MediaDrm.HDCP_NONE, android.media.MediaDrm.HDCP_V1, android.media.MediaDrm.HDCP_V2, android.media.MediaDrm.HDCP_V2_1, android.media.MediaDrm.HDCP_V2_2, android.media.MediaDrm.HDCP_V2_3, android.media.MediaDrm.HDCP_NO_DIGITAL_OUTPUT}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MediaDrm.HdcpLevel {
}
@@ -21672,8 +21807,16 @@ package android.media {
field public static final int STATUS_USABLE_IN_FUTURE = 5; // 0x5
}
+ public static class MediaDrm.LogMessage {
+ field @NonNull public final String message;
+ field public final int priority;
+ field public final long timestampMillis;
+ }
+
public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException {
method @NonNull public String getDiagnosticInfo();
+ method public int getErrorCode();
+ method public boolean isTransient();
}
public static final class MediaDrm.MetricsConstants {
@@ -21740,9 +21883,10 @@ package android.media {
public static final class MediaDrm.SessionException extends java.lang.RuntimeException {
ctor public MediaDrm.SessionException(int, @Nullable String);
- method public int getErrorCode();
- field public static final int ERROR_RESOURCE_CONTENTION = 1; // 0x1
- field public static final int ERROR_UNKNOWN = 0; // 0x0
+ method @Deprecated public int getErrorCode();
+ method public boolean isTransient();
+ field @Deprecated public static final int ERROR_RESOURCE_CONTENTION = 1; // 0x1
+ field @Deprecated public static final int ERROR_UNKNOWN = 0; // 0x0
}
public class MediaDrmException extends java.lang.Exception {
@@ -21870,6 +22014,7 @@ package android.media {
field public static final String KEY_COLOR_RANGE = "color-range";
field public static final String KEY_COLOR_STANDARD = "color-standard";
field public static final String KEY_COLOR_TRANSFER = "color-transfer";
+ field public static final String KEY_COLOR_TRANSFER_REQUEST = "color-transfer-request";
field public static final String KEY_COMPLEXITY = "complexity";
field public static final String KEY_CREATE_INPUT_SURFACE_SUSPENDED = "create-input-buffers-suspended";
field public static final String KEY_DURATION = "durationUs";
@@ -23012,10 +23157,12 @@ package android.media {
method @Deprecated public int getStreamType();
method public String getTitle(android.content.Context);
method public float getVolume();
+ method public boolean isHapticGeneratorEnabled();
method public boolean isLooping();
method public boolean isPlaying();
method public void play();
method public void setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
+ method public boolean setHapticGeneratorEnabled(boolean);
method public void setLooping(boolean);
method @Deprecated public void setStreamType(int);
method public void setVolume(float);
@@ -24017,13 +24164,213 @@ package android.media.effect {
package android.media.metrics {
+ public abstract class Event {
+ ctor protected Event(long);
+ method @IntRange(from=0xffffffff) public long getTimeSinceCreatedMillis();
+ }
+
public class MediaMetricsManager {
method @NonNull public android.media.metrics.PlaybackSession createPlaybackSession();
+ field public static final long INVALID_TIMESTAMP = -1L; // 0xffffffffffffffffL
+ }
+
+ public final class NetworkEvent extends android.media.metrics.Event implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getNetworkType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.metrics.NetworkEvent> CREATOR;
+ field public static final int NETWORK_TYPE_2G = 4; // 0x4
+ field public static final int NETWORK_TYPE_3G = 5; // 0x5
+ field public static final int NETWORK_TYPE_4G = 6; // 0x6
+ field public static final int NETWORK_TYPE_5G_NSA = 7; // 0x7
+ field public static final int NETWORK_TYPE_5G_SA = 8; // 0x8
+ field public static final int NETWORK_TYPE_ETHERNET = 3; // 0x3
+ field public static final int NETWORK_TYPE_NONE = 0; // 0x0
+ field public static final int NETWORK_TYPE_OTHER = 1; // 0x1
+ field public static final int NETWORK_TYPE_WIFI = 2; // 0x2
+ }
+
+ public static final class NetworkEvent.Builder {
+ ctor public NetworkEvent.Builder();
+ method @NonNull public android.media.metrics.NetworkEvent build();
+ method @NonNull public android.media.metrics.NetworkEvent.Builder setNetworkType(int);
+ method @NonNull public android.media.metrics.NetworkEvent.Builder setTimeSinceCreatedMillis(@IntRange(from=0xffffffff) long);
+ }
+
+ public interface PlaybackComponent {
+ method @NonNull public String getPlaybackId();
+ method public void setPlaybackId(@NonNull String);
+ }
+
+ public final class PlaybackErrorEvent extends android.media.metrics.Event implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getErrorCode();
+ method @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) public int getSubErrorCode();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.metrics.PlaybackErrorEvent> CREATOR;
+ field public static final int ERROR_CODE_OTHER = 1; // 0x1
+ field public static final int ERROR_CODE_RUNTIME = 2; // 0x2
+ field public static final int ERROR_CODE_UNKNOWN = 0; // 0x0
+ }
+
+ public static final class PlaybackErrorEvent.Builder {
+ ctor public PlaybackErrorEvent.Builder();
+ method @NonNull public android.media.metrics.PlaybackErrorEvent build();
+ method @NonNull public android.media.metrics.PlaybackErrorEvent.Builder setErrorCode(int);
+ method @NonNull public android.media.metrics.PlaybackErrorEvent.Builder setException(@NonNull Exception);
+ method @NonNull public android.media.metrics.PlaybackErrorEvent.Builder setSubErrorCode(@IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
+ method @NonNull public android.media.metrics.PlaybackErrorEvent.Builder setTimeSinceCreatedMillis(@IntRange(from=0xffffffff) long);
+ }
+
+ public final class PlaybackMetrics implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getAudioUnderrunCount();
+ method public int getContentType();
+ method public int getDrmType();
+ method @NonNull public long[] getExperimentIds();
+ method @IntRange(from=0xffffffff) public long getLocalBytesRead();
+ method @IntRange(from=0xffffffff) public long getMediaDurationMillis();
+ method @IntRange(from=0xffffffff) public long getNetworkBytesRead();
+ method @IntRange(from=0xffffffff) public long getNetworkTransferDurationMillis();
+ method public int getPlaybackType();
+ method @Nullable public String getPlayerName();
+ method @Nullable public String getPlayerVersion();
+ method public int getStreamSource();
+ method public int getStreamType();
+ method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getVideoFramesDropped();
+ method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getVideoFramesPlayed();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int CONTENT_TYPE_AD = 1; // 0x1
+ field public static final int CONTENT_TYPE_MAIN = 0; // 0x0
+ field public static final int CONTENT_TYPE_OTHER = 2; // 0x2
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.metrics.PlaybackMetrics> CREATOR;
+ field public static final int DRM_TYPE_CLEARKEY = 6; // 0x6
+ field public static final int DRM_TYPE_NONE = 0; // 0x0
+ field public static final int DRM_TYPE_OTHER = 1; // 0x1
+ field public static final int DRM_TYPE_PLAY_READY = 2; // 0x2
+ field public static final int DRM_TYPE_WIDEVINE_L1 = 3; // 0x3
+ field public static final int DRM_TYPE_WIDEVINE_L3 = 4; // 0x4
+ field public static final int DRM_TYPE_WV_L3_FALLBACK = 5; // 0x5
+ field public static final int PLAYBACK_TYPE_LIVE = 1; // 0x1
+ field public static final int PLAYBACK_TYPE_OTHER = 2; // 0x2
+ field public static final int PLAYBACK_TYPE_VOD = 0; // 0x0
+ field public static final int STREAM_SOURCE_DEVICE = 2; // 0x2
+ field public static final int STREAM_SOURCE_MIXED = 3; // 0x3
+ field public static final int STREAM_SOURCE_NETWORK = 1; // 0x1
+ field public static final int STREAM_SOURCE_UNKNOWN = 0; // 0x0
+ field public static final int STREAM_TYPE_DASH = 3; // 0x3
+ field public static final int STREAM_TYPE_HLS = 4; // 0x4
+ field public static final int STREAM_TYPE_OTHER = 1; // 0x1
+ field public static final int STREAM_TYPE_PROGRESSIVE = 2; // 0x2
+ field public static final int STREAM_TYPE_SS = 5; // 0x5
+ field public static final int STREAM_TYPE_UNKNOWN = 0; // 0x0
+ }
+
+ public static final class PlaybackMetrics.Builder {
+ ctor public PlaybackMetrics.Builder();
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder addExperimentId(long);
+ method @NonNull public android.media.metrics.PlaybackMetrics build();
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setAudioUnderrunCount(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setContentType(int);
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setDrmType(int);
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setLocalBytesRead(@IntRange(from=0xffffffff) long);
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setMediaDurationMillis(@IntRange(from=0xffffffff) long);
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setNetworkBytesRead(@IntRange(from=0xffffffff) long);
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setNetworkTransferDurationMillis(@IntRange(from=0xffffffff) long);
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setPlaybackType(int);
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setPlayerName(@NonNull String);
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setPlayerVersion(@NonNull String);
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setStreamSource(int);
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setStreamType(int);
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setVideoFramesDropped(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setVideoFramesPlayed(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
}
public final class PlaybackSession implements java.lang.AutoCloseable {
method public void close();
method @NonNull public String getId();
+ method public void reportNetworkEvent(@NonNull android.media.metrics.NetworkEvent);
+ method public void reportPlaybackErrorEvent(@NonNull android.media.metrics.PlaybackErrorEvent);
+ method public void reportPlaybackMetrics(@NonNull android.media.metrics.PlaybackMetrics);
+ method public void reportPlaybackStateEvent(@NonNull android.media.metrics.PlaybackStateEvent);
+ method public void reportTrackChangeEvent(@NonNull android.media.metrics.TrackChangeEvent);
+ }
+
+ public final class PlaybackStateEvent extends android.media.metrics.Event implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getState();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.metrics.PlaybackStateEvent> CREATOR;
+ field public static final int STATE_ABANDONED = 15; // 0xf
+ field public static final int STATE_BUFFERING = 6; // 0x6
+ field public static final int STATE_ENDED = 11; // 0xb
+ field public static final int STATE_FAILED = 13; // 0xd
+ field public static final int STATE_INTERRUPTED_BY_AD = 14; // 0xe
+ field public static final int STATE_JOINING_BACKGROUND = 1; // 0x1
+ field public static final int STATE_JOINING_FOREGROUND = 2; // 0x2
+ field public static final int STATE_NOT_STARTED = 0; // 0x0
+ field public static final int STATE_PAUSED = 4; // 0x4
+ field public static final int STATE_PAUSED_BUFFERING = 7; // 0x7
+ field public static final int STATE_PLAYING = 3; // 0x3
+ field public static final int STATE_SEEKING = 5; // 0x5
+ field public static final int STATE_STOPPED = 12; // 0xc
+ field public static final int STATE_SUPPRESSED = 9; // 0x9
+ field public static final int STATE_SUPPRESSED_BUFFERING = 10; // 0xa
+ }
+
+ public static final class PlaybackStateEvent.Builder {
+ ctor public PlaybackStateEvent.Builder();
+ method @NonNull public android.media.metrics.PlaybackStateEvent build();
+ method @NonNull public android.media.metrics.PlaybackStateEvent.Builder setState(int);
+ method @NonNull public android.media.metrics.PlaybackStateEvent.Builder setTimeSinceCreatedMillis(@IntRange(from=0xffffffff) long);
+ }
+
+ public final class TrackChangeEvent extends android.media.metrics.Event implements android.os.Parcelable {
+ ctor public TrackChangeEvent(int, int, @Nullable String, @Nullable String, @Nullable String, int, long, int, @Nullable String, @Nullable String, int, int, int, int);
+ method public int describeContents();
+ method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getBitrate();
+ method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getChannelCount();
+ method @Nullable public String getCodecName();
+ method @Nullable public String getContainerMimeType();
+ method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getHeight();
+ method @Nullable public String getLanguage();
+ method @Nullable public String getLanguageRegion();
+ method @Nullable public String getSampleMimeType();
+ method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getSampleRate();
+ method public int getTrackChangeReason();
+ method public int getTrackState();
+ method public int getTrackType();
+ method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getWidth();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.metrics.TrackChangeEvent> CREATOR;
+ field public static final int TRACK_CHANGE_REASON_ADAPTIVE = 4; // 0x4
+ field public static final int TRACK_CHANGE_REASON_INITIAL = 2; // 0x2
+ field public static final int TRACK_CHANGE_REASON_MANUAL = 3; // 0x3
+ field public static final int TRACK_CHANGE_REASON_OTHER = 1; // 0x1
+ field public static final int TRACK_CHANGE_REASON_UNKNOWN = 0; // 0x0
+ field public static final int TRACK_STATE_OFF = 0; // 0x0
+ field public static final int TRACK_STATE_ON = 1; // 0x1
+ field public static final int TRACK_TYPE_AUDIO = 0; // 0x0
+ field public static final int TRACK_TYPE_TEXT = 2; // 0x2
+ field public static final int TRACK_TYPE_VIDEO = 1; // 0x1
+ }
+
+ public static final class TrackChangeEvent.Builder {
+ ctor public TrackChangeEvent.Builder(int);
+ method @NonNull public android.media.metrics.TrackChangeEvent build();
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setBitrate(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setChannelCount(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setCodecName(@NonNull String);
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setContainerMimeType(@NonNull String);
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setHeight(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setLanguage(@NonNull String);
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setLanguageRegion(@NonNull String);
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setSampleMimeType(@NonNull String);
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setSampleRate(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setTimeSinceCreatedMillis(@IntRange(from=0xffffffff) long);
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setTrackChangeReason(int);
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setTrackState(int);
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setWidth(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
}
}
@@ -26664,6 +27011,55 @@ package android.net.sip {
}
+package android.net.vcn {
+
+ public final class VcnConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.Set<android.net.vcn.VcnGatewayConnectionConfig> getGatewayConnectionConfigs();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.vcn.VcnConfig> CREATOR;
+ }
+
+ public static final class VcnConfig.Builder {
+ ctor public VcnConfig.Builder(@NonNull android.content.Context);
+ method @NonNull public android.net.vcn.VcnConfig.Builder addGatewayConnectionConfig(@NonNull android.net.vcn.VcnGatewayConnectionConfig);
+ method @NonNull public android.net.vcn.VcnConfig build();
+ }
+
+ public abstract class VcnControlPlaneConfig {
+ }
+
+ public final class VcnControlPlaneIkeConfig extends android.net.vcn.VcnControlPlaneConfig {
+ ctor public VcnControlPlaneIkeConfig(@NonNull android.net.ipsec.ike.IkeSessionParams, @NonNull android.net.ipsec.ike.TunnelModeChildSessionParams);
+ method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams getChildSessionParams();
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams getIkeSessionParams();
+ }
+
+ public final class VcnGatewayConnectionConfig {
+ method @NonNull public int[] getExposedCapabilities();
+ method @IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) public int getMaxMtu();
+ method @NonNull public int[] getRequiredUnderlyingCapabilities();
+ method @NonNull public long[] getRetryInterval();
+ }
+
+ public static final class VcnGatewayConnectionConfig.Builder {
+ ctor public VcnGatewayConnectionConfig.Builder(@NonNull android.net.vcn.VcnControlPlaneConfig);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addExposedCapability(int);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addRequiredUnderlyingCapability(int);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build();
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeRequiredUnderlyingCapability(int);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) int);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryInterval(@NonNull long[]);
+ }
+
+ public class VcnManager {
+ method @RequiresPermission("carrier privileges") public void clearVcnConfig(@NonNull android.os.ParcelUuid) throws java.io.IOException;
+ method @RequiresPermission("carrier privileges") public void setVcnConfig(@NonNull android.os.ParcelUuid, @NonNull android.net.vcn.VcnConfig) throws java.io.IOException;
+ }
+
+}
+
package android.nfc {
public class FormatException extends java.lang.Exception {
@@ -30589,6 +30985,7 @@ package android.os {
field public static String DIRECTORY_NOTIFICATIONS;
field public static String DIRECTORY_PICTURES;
field public static String DIRECTORY_PODCASTS;
+ field @NonNull public static String DIRECTORY_RECORDINGS;
field public static String DIRECTORY_RINGTONES;
field public static String DIRECTORY_SCREENSHOTS;
field public static final String MEDIA_BAD_REMOVAL = "bad_removal";
@@ -31455,6 +31852,7 @@ package android.os {
method public boolean isQuietModeEnabled(android.os.UserHandle);
method public boolean isSystemUser();
method public boolean isUserAGoat();
+ method public boolean isUserForeground();
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public boolean isUserRunning(android.os.UserHandle);
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public boolean isUserRunningOrStopping(android.os.UserHandle);
method public boolean isUserUnlocked();
@@ -31603,6 +32001,7 @@ package android.os {
method @NonNull public int[] areEffectsSupported(@NonNull int...);
method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...);
method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
+ method public int getId();
method public abstract boolean hasAmplitudeControl();
method public abstract boolean hasVibrator();
method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long);
@@ -31617,10 +32016,12 @@ package android.os {
}
public abstract class VibratorManager {
+ method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
method @NonNull public abstract android.os.Vibrator getDefaultVibrator();
method @NonNull public abstract android.os.Vibrator getVibrator(int);
method @NonNull public abstract int[] getVibratorIds();
- method public abstract void vibrate(@NonNull android.os.CombinedVibrationEffect);
+ method @RequiresPermission(android.Manifest.permission.VIBRATE) public final void vibrate(@NonNull android.os.CombinedVibrationEffect);
+ method @RequiresPermission(android.Manifest.permission.VIBRATE) public final void vibrate(@NonNull android.os.CombinedVibrationEffect, @Nullable android.os.VibrationAttributes);
}
public class WorkSource implements android.os.Parcelable {
@@ -34667,6 +35068,7 @@ package android.provider {
field public static final String EXTRA_AUTHORITIES = "authorities";
field public static final String EXTRA_BATTERY_SAVER_MODE_ENABLED = "android.settings.extra.battery_saver_mode_enabled";
field public static final String EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED = "android.provider.extra.BIOMETRIC_AUTHENTICATORS_ALLOWED";
+ field public static final String EXTRA_CHANNEL_FILTER_LIST = "android.provider.extra.CHANNEL_FILTER_LIST";
field public static final String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
field public static final String EXTRA_CONVERSATION_ID = "android.provider.extra.CONVERSATION_ID";
field public static final String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled";
@@ -35007,30 +35409,18 @@ package android.provider {
public static final class SimPhonebookContract.SimRecords {
method @NonNull public static android.net.Uri getContentUri(int, int);
+ method @WorkerThread public static int getEncodedNameLength(@NonNull android.content.ContentResolver, @NonNull String);
method @NonNull public static android.net.Uri getItemUri(int, int, int);
- method @NonNull @WorkerThread public static android.provider.SimPhonebookContract.SimRecords.NameValidationResult validateName(@NonNull android.content.ContentResolver, int, int, @NonNull String);
field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
field public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
+ field public static final int ERROR_NAME_UNSUPPORTED = -1; // 0xffffffff
field public static final String NAME = "name";
field public static final String PHONE_NUMBER = "phone_number";
field public static final String RECORD_NUMBER = "record_number";
field public static final String SUBSCRIPTION_ID = "subscription_id";
}
- public static final class SimPhonebookContract.SimRecords.NameValidationResult implements android.os.Parcelable {
- ctor public SimPhonebookContract.SimRecords.NameValidationResult(@NonNull String, @NonNull String, int, int);
- method public int describeContents();
- method public int getEncodedLength();
- method public int getMaxEncodedLength();
- method @NonNull public String getName();
- method @NonNull public String getSanitizedName();
- method public boolean isSupportedCharacter(int);
- method public boolean isValid();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.provider.SimPhonebookContract.SimRecords.NameValidationResult> CREATOR;
- }
-
public class SyncStateContract {
ctor public SyncStateContract();
}
@@ -36914,6 +37304,7 @@ package android.security.keystore {
public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
method @Nullable public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec();
+ method @Nullable public String getAttestKeyAlias();
method public byte[] getAttestationChallenge();
method @NonNull public String[] getBlockModes();
method @NonNull public java.util.Date getCertificateNotAfter();
@@ -36948,6 +37339,7 @@ package android.security.keystore {
ctor public KeyGenParameterSpec.Builder(@NonNull String, int);
method @NonNull public android.security.keystore.KeyGenParameterSpec build();
method public android.security.keystore.KeyGenParameterSpec.Builder setAlgorithmParameterSpec(@NonNull java.security.spec.AlgorithmParameterSpec);
+ method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setAttestKeyAlias(@Nullable String);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setAttestationChallenge(byte[]);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setBlockModes(java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotAfter(@NonNull java.util.Date);
@@ -37045,6 +37437,7 @@ package android.security.keystore {
field public static final int ORIGIN_SECURELY_IMPORTED = 8; // 0x8
field public static final int ORIGIN_UNKNOWN = 4; // 0x4
field public static final int PURPOSE_AGREE_KEY = 64; // 0x40
+ field public static final int PURPOSE_ATTEST_KEY = 128; // 0x80
field public static final int PURPOSE_DECRYPT = 2; // 0x2
field public static final int PURPOSE_ENCRYPT = 1; // 0x1
field public static final int PURPOSE_SIGN = 4; // 0x4
@@ -38044,6 +38437,10 @@ package android.service.notification {
method public final void setNotificationsShown(String[]);
method public final void snoozeNotification(String, long);
method public final void updateNotificationChannel(@NonNull String, @NonNull android.os.UserHandle, @NonNull android.app.NotificationChannel);
+ field public static final int FLAG_FILTER_TYPE_ALERTING = 2; // 0x2
+ field public static final int FLAG_FILTER_TYPE_CONVERSATIONS = 1; // 0x1
+ field public static final int FLAG_FILTER_TYPE_ONGOING = 8; // 0x8
+ field public static final int FLAG_FILTER_TYPE_SILENT = 4; // 0x4
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
@@ -38052,6 +38449,7 @@ package android.service.notification {
field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2
field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
+ field public static final String META_DATA_DEFAULT_FILTER_TYPES = "android.service.notification.default_filter_types";
field public static final int NOTIFICATION_CHANNEL_OR_GROUP_ADDED = 1; // 0x1
field public static final int NOTIFICATION_CHANNEL_OR_GROUP_DELETED = 3; // 0x3
field public static final int NOTIFICATION_CHANNEL_OR_GROUP_UPDATED = 2; // 0x2
@@ -38678,7 +39076,9 @@ package android.speech {
field public static final int ERROR_NO_MATCH = 7; // 0x7
field public static final int ERROR_RECOGNIZER_BUSY = 8; // 0x8
field public static final int ERROR_SERVER = 4; // 0x4
+ field public static final int ERROR_SERVER_DISCONNECTED = 11; // 0xb
field public static final int ERROR_SPEECH_TIMEOUT = 6; // 0x6
+ field public static final int ERROR_TOO_MANY_REQUESTS = 10; // 0xa
field public static final String RESULTS_RECOGNITION = "results_recognition";
}
@@ -39079,16 +39479,22 @@ package android.telecom {
}
public static class CallScreeningService.CallResponse {
+ method public int getCallComposerAttachmentsToShow();
method public boolean getDisallowCall();
method public boolean getRejectCall();
method public boolean getSilenceCall();
method public boolean getSkipCallLog();
method public boolean getSkipNotification();
+ field public static final int CALL_COMPOSER_ATTACHMENT_LOCATION = 2; // 0x2
+ field public static final int CALL_COMPOSER_ATTACHMENT_PICTURE = 1; // 0x1
+ field public static final int CALL_COMPOSER_ATTACHMENT_PRIORITY = 8; // 0x8
+ field public static final int CALL_COMPOSER_ATTACHMENT_SUBJECT = 4; // 0x4
}
public static class CallScreeningService.CallResponse.Builder {
ctor public CallScreeningService.CallResponse.Builder();
method public android.telecom.CallScreeningService.CallResponse build();
+ method @NonNull public android.telecom.CallScreeningService.CallResponse.Builder setCallComposerAttachmentsToShow(int);
method public android.telecom.CallScreeningService.CallResponse.Builder setDisallowCall(boolean);
method public android.telecom.CallScreeningService.CallResponse.Builder setRejectCall(boolean);
method @NonNull public android.telecom.CallScreeningService.CallResponse.Builder setSilenceCall(boolean);
@@ -40238,6 +40644,7 @@ package android.telephony {
field public static final String KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY = "gsm_roaming_networks_string_array";
field public static final String KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL = "has_in_call_noise_suppression_bool";
field public static final String KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool";
+ field public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool";
field public static final String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
field public static final String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool";
field public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL = "hide_lte_plus_data_icon_bool";
@@ -40433,7 +40840,6 @@ package android.telephony {
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_ADD_WIFI_MAC_ADDR_TO_NAI_BOOL = "iwlan.add_wifi_mac_addr_to_nai_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";
field public static final String KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = "iwlan.child_session_aes_cbc_key_size_int_array";
@@ -42006,8 +42412,9 @@ package android.telephony {
field public static final int OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO = 2; // 0x2
field public static final int OVERRIDE_NETWORK_TYPE_LTE_CA = 1; // 0x1
field public static final int OVERRIDE_NETWORK_TYPE_NONE = 0; // 0x0
+ field public static final int OVERRIDE_NETWORK_TYPE_NR_ADVANCED = 4; // 0x4
field public static final int OVERRIDE_NETWORK_TYPE_NR_NSA = 3; // 0x3
- field public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4; // 0x4
+ field @Deprecated public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4; // 0x4
}
public class TelephonyManager {
@@ -42058,7 +42465,7 @@ package android.telephony {
method @Deprecated public int getPhoneCount();
method public int getPhoneType();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
- method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState();
method @Nullable public android.telephony.SignalStrength getSignalStrength();
method public int getSimCarrierId();
method @Nullable public CharSequence getSimCarrierIdName();
@@ -42724,7 +43131,11 @@ package android.telephony.ims {
}
public class ImsRcsManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter();
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
}
@@ -42913,6 +43324,16 @@ package android.telephony.ims {
field public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2; // 0x2
}
+ public final class ImsRegistrationAttributes implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getAttributeFlags();
+ method @NonNull public java.util.Set<java.lang.String> getFeatureTags();
+ method public int getTransportType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1; // 0x1
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsRegistrationAttributes> CREATOR;
+ }
+
public class RcsUceAdapter {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
}
@@ -42922,7 +43343,6 @@ package android.telephony.ims {
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
- field public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1; // 0x1
field public static final int REGISTRATION_STATE_NOT_REGISTERED = 0; // 0x0
field public static final int REGISTRATION_STATE_REGISTERED = 2; // 0x2
field public static final int REGISTRATION_STATE_REGISTERING = 1; // 0x1
@@ -42931,9 +43351,9 @@ package android.telephony.ims {
public static class RegistrationManager.RegistrationCallback {
ctor public RegistrationManager.RegistrationCallback();
method @Deprecated public void onRegistered(int);
- method public void onRegistered(int, int);
+ method public void onRegistered(@NonNull android.telephony.ims.ImsRegistrationAttributes);
method @Deprecated public void onRegistering(int);
- method public void onRegistering(int, int);
+ method public void onRegistering(@NonNull android.telephony.ims.ImsRegistrationAttributes);
method public void onTechnologyChangeFailed(int, @NonNull android.telephony.ims.ImsReasonInfo);
method public void onUnregistered(@NonNull android.telephony.ims.ImsReasonInfo);
}
@@ -46317,8 +46737,10 @@ package android.view {
method public long getMetric(int);
field public static final int ANIMATION_DURATION = 2; // 0x2
field public static final int COMMAND_ISSUE_DURATION = 6; // 0x6
+ field public static final int DEADLINE = 13; // 0xd
field public static final int DRAW_DURATION = 4; // 0x4
field public static final int FIRST_DRAW_FRAME = 9; // 0x9
+ field public static final int GPU_DURATION = 12; // 0xc
field public static final int INPUT_HANDLING_DURATION = 1; // 0x1
field public static final int INTENDED_VSYNC_TIMESTAMP = 10; // 0xa
field public static final int LAYOUT_MEASURE_DURATION = 3; // 0x3
@@ -47861,6 +48283,7 @@ package android.view {
method public android.view.View focusSearch(int);
method public void forceHasOverlappingRendering(boolean);
method public void forceLayout();
+ method @Nullable public void generateDisplayHash(@NonNull String, @Nullable android.graphics.Rect, @NonNull java.util.concurrent.Executor, @NonNull android.view.displayhash.DisplayHashResultCallback);
method public static int generateViewId();
method public CharSequence getAccessibilityClassName();
method public android.view.View.AccessibilityDelegate getAccessibilityDelegate();
@@ -49312,6 +49735,7 @@ package android.view {
method public void setAllowEnterTransitionOverlap(boolean);
method public void setAllowReturnTransitionOverlap(boolean);
method public void setAttributes(android.view.WindowManager.LayoutParams);
+ method public void setBackgroundBlurRadius(int);
method public abstract void setBackgroundDrawable(android.graphics.drawable.Drawable);
method public void setBackgroundDrawableResource(@DrawableRes int);
method public void setCallback(android.view.Window.Callback);
@@ -50678,6 +51102,7 @@ package android.view.autofill {
method public void unregisterCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
field public static final String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
+ field public static final String EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET = "android.view.autofill.extra.AUTHENTICATION_RESULT_EPHEMERAL_DATASET";
field public static final String EXTRA_CLIENT_STATE = "android.view.autofill.extra.CLIENT_STATE";
}
@@ -50814,6 +51239,41 @@ package android.view.contentcapture {
}
+package android.view.displayhash {
+
+ public final class DisplayHash implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.displayhash.DisplayHash> CREATOR;
+ }
+
+ public final class DisplayHashManager {
+ method @NonNull public java.util.Set<java.lang.String> getSupportedHashAlgorithms();
+ method @Nullable public android.view.displayhash.VerifiedDisplayHash verifyDisplayHash(@NonNull android.view.displayhash.DisplayHash);
+ }
+
+ public interface DisplayHashResultCallback {
+ method public void onDisplayHashError(int);
+ method public void onDisplayHashResult(@NonNull android.view.displayhash.DisplayHash);
+ field public static final int DISPLAY_HASH_ERROR_INVALID_BOUNDS = -2; // 0xfffffffe
+ field public static final int DISPLAY_HASH_ERROR_MISSING_WINDOW = -3; // 0xfffffffd
+ field public static final int DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN = -4; // 0xfffffffc
+ field public static final int DISPLAY_HASH_ERROR_UNKNOWN = -1; // 0xffffffff
+ }
+
+ public final class VerifiedDisplayHash implements android.os.Parcelable {
+ ctor public VerifiedDisplayHash(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[]);
+ method public int describeContents();
+ method @NonNull public android.graphics.Rect getBoundsInWindow();
+ method @NonNull public String getHashAlgorithm();
+ method @NonNull public byte[] getImageHash();
+ method public long getTimeMillis();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.displayhash.VerifiedDisplayHash> CREATOR;
+ }
+
+}
+
package android.view.inputmethod {
public class BaseInputConnection implements android.view.inputmethod.InputConnection {
@@ -53545,20 +54005,27 @@ package android.widget {
public class EdgeEffect {
ctor public EdgeEffect(android.content.Context);
+ ctor public EdgeEffect(@NonNull android.content.Context, @Nullable android.util.AttributeSet);
method public boolean draw(android.graphics.Canvas);
method public void finish();
method @Nullable public android.graphics.BlendMode getBlendMode();
method @ColorInt public int getColor();
+ method public float getDistance();
method public int getMaxHeight();
+ method public int getType();
method public boolean isFinished();
method public void onAbsorb(int);
method public void onPull(float);
method public void onPull(float, float);
+ method public float onPullDistance(float, float);
method public void onRelease();
method public void setBlendMode(@Nullable android.graphics.BlendMode);
method public void setColor(@ColorInt int);
method public void setSize(int, int);
+ method public void setType(int);
field public static final android.graphics.BlendMode DEFAULT_BLEND_MODE;
+ field public static final int TYPE_GLOW = 0; // 0x0
+ field public static final int TYPE_STRETCH = 1; // 0x1
}
public class EditText extends android.widget.TextView {
@@ -54563,6 +55030,7 @@ package android.widget {
public class RemoteViews implements android.view.LayoutInflater.Filter android.os.Parcelable {
ctor public RemoteViews(String, int);
ctor public RemoteViews(android.widget.RemoteViews, android.widget.RemoteViews);
+ ctor public RemoteViews(@NonNull java.util.Map<android.graphics.PointF,android.widget.RemoteViews>);
ctor public RemoteViews(android.widget.RemoteViews);
ctor public RemoteViews(android.os.Parcel);
method public void addView(@IdRes int, android.widget.RemoteViews);
@@ -54577,6 +55045,7 @@ package android.widget {
method public void setAccessibilityTraversalAfter(@IdRes int, @IdRes int);
method public void setAccessibilityTraversalBefore(@IdRes int, @IdRes int);
method public void setBitmap(@IdRes int, String, android.graphics.Bitmap);
+ method public void setBlendMode(@IdRes int, @NonNull String, @Nullable android.graphics.BlendMode);
method public void setBoolean(@IdRes int, String, boolean);
method public void setBundle(@IdRes int, String, android.os.Bundle);
method public void setByte(@IdRes int, String, byte);
@@ -54607,6 +55076,7 @@ package android.widget {
method public void setLabelFor(@IdRes int, @IdRes int);
method public void setLightBackgroundLayoutId(@LayoutRes int);
method public void setLong(@IdRes int, String, long);
+ method public void setOnCheckedChangeResponse(@IdRes int, @NonNull android.widget.RemoteViews.RemoteResponse);
method public void setOnClickFillInIntent(@IdRes int, android.content.Intent);
method public void setOnClickPendingIntent(@IdRes int, android.app.PendingIntent);
method public void setOnClickResponse(@IdRes int, @NonNull android.widget.RemoteViews.RemoteResponse);
@@ -54625,12 +55095,15 @@ package android.widget {
method public void setTextViewText(@IdRes int, CharSequence);
method public void setTextViewTextSize(@IdRes int, int, float);
method public void setUri(@IdRes int, String, android.net.Uri);
+ method public void setViewOutlinePreferredRadius(@IdRes int, float, int);
+ method public void setViewOutlinePreferredRadiusDimen(@IdRes int, @DimenRes int);
method public void setViewPadding(@IdRes int, @Px int, @Px int, @Px int, @Px int);
method public void setViewVisibility(@IdRes int, int);
method public void showNext(@IdRes int);
method public void showPrevious(@IdRes int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.widget.RemoteViews> CREATOR;
+ field public static final String EXTRA_CHECKED = "android.widget.extra.CHECKED";
field public static final String EXTRA_SHARED_ELEMENT_BOUNDS = "android.widget.extra.SHARED_ELEMENT_BOUNDS";
}
@@ -54649,6 +55122,12 @@ package android.widget {
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE}) public static @interface RemoteViews.RemoteView {
}
+ public static final class RemoteViews.RemoteViewOutlineProvider extends android.view.ViewOutlineProvider {
+ ctor public RemoteViews.RemoteViewOutlineProvider(float);
+ method public void getOutline(@NonNull android.view.View, @NonNull android.graphics.Outline);
+ method public float getRadius();
+ }
+
public abstract class RemoteViewsService extends android.app.Service {
ctor public RemoteViewsService();
method public android.os.IBinder onBind(android.content.Intent);
@@ -55752,6 +56231,25 @@ package android.widget.inline {
}
+package android.window {
+
+ public interface SplashScreen {
+ method public void setOnExitAnimationListener(@Nullable android.window.SplashScreen.OnExitAnimationListener);
+ }
+
+ public static interface SplashScreen.OnExitAnimationListener {
+ method public void onSplashScreenExit(@NonNull android.window.SplashScreenView);
+ }
+
+ public final class SplashScreenView extends android.widget.FrameLayout {
+ method public long getIconAnimationDurationMillis();
+ method public long getIconAnimationStartMillis();
+ method @Nullable public android.view.View getIconView();
+ method public void remove();
+ }
+
+}
+
package javax.microedition.khronos.egl {
public interface EGL {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index bf70803fbc74..de02d0bb7e98 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -73,6 +73,7 @@ package android.hardware.usb {
public class UsbManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int getGadgetHalVersion();
method public int getUsbBandwidth();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int getUsbHalVersion();
field public static final int GADGET_HAL_NOT_SUPPORTED = -1; // 0xffffffff
field public static final int GADGET_HAL_V1_0 = 10; // 0xa
field public static final int GADGET_HAL_V1_1 = 11; // 0xb
@@ -85,6 +86,11 @@ package android.hardware.usb {
field public static final int USB_DATA_TRANSFER_RATE_HIGH_SPEED = 480; // 0x1e0
field public static final int USB_DATA_TRANSFER_RATE_LOW_SPEED = 2; // 0x2
field public static final int USB_DATA_TRANSFER_RATE_UNKNOWN = -1; // 0xffffffff
+ field public static final int USB_HAL_NOT_SUPPORTED = -1; // 0xffffffff
+ field public static final int USB_HAL_V1_0 = 10; // 0xa
+ field public static final int USB_HAL_V1_1 = 11; // 0xb
+ field public static final int USB_HAL_V1_2 = 12; // 0xc
+ field public static final int USB_HAL_V1_3 = 13; // 0xd
}
}
@@ -162,6 +168,7 @@ package android.net {
}
public class ConnectivityManager {
+ 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.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
}
@@ -210,6 +217,7 @@ package android.net {
method @NonNull public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>);
method public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder);
method public void teardownTestNetwork(@NonNull android.net.Network);
+ field public static final String TEST_TAP_PREFIX = "testtap";
}
public final class UnderlyingNetworkInfo implements android.os.Parcelable {
@@ -222,6 +230,14 @@ package android.net {
field @NonNull public final java.util.List<java.lang.String> underlyingIfaces;
}
+ public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
+ ctor public VpnTransportInfo(int);
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR;
+ field public final int type;
+ }
+
}
package android.os {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 7f9aafabcde8..b41f921a5070 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -14,6 +14,7 @@ package android {
field public static final String ACCESS_MTP = "android.permission.ACCESS_MTP";
field public static final String ACCESS_NETWORK_CONDITIONS = "android.permission.ACCESS_NETWORK_CONDITIONS";
field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
+ field public static final String ACCESS_RCS_USER_CAPABILITY_EXCHANGE = "android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE";
field public static final String ACCESS_SHARED_LIBRARIES = "android.permission.ACCESS_SHARED_LIBRARIES";
field public static final String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS";
field public static final String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
@@ -27,6 +28,7 @@ package android {
field public static final String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER";
field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
field public static final String BACKUP = "android.permission.BACKUP";
+ field public static final String BATTERY_PREDICTION = "android.permission.BATTERY_PREDICTION";
field public static final String BIND_ATTENTION_SERVICE = "android.permission.BIND_ATTENTION_SERVICE";
field public static final String BIND_AUGMENTED_AUTOFILL_SERVICE = "android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE";
field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE";
@@ -82,6 +84,7 @@ package android {
field public static final String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION";
field public static final String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE";
field public static final String CONTROL_KEYGUARD_SECURE_NOTIFICATIONS = "android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS";
+ field public static final String CONTROL_OEM_PAID_NETWORK_PREFERENCE = "android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE";
field public static final String CONTROL_VPN = "android.permission.CONTROL_VPN";
field public static final String CREATE_USERS = "android.permission.CREATE_USERS";
field public static final String CRYPT_KEEPER = "android.permission.CRYPT_KEEPER";
@@ -179,6 +182,7 @@ package android {
field public static final String PACKET_KEEPALIVE_OFFLOAD = "android.permission.PACKET_KEEPALIVE_OFFLOAD";
field public static final String PEERS_MAC_ADDRESS = "android.permission.PEERS_MAC_ADDRESS";
field public static final String PERFORM_CDMA_PROVISIONING = "android.permission.PERFORM_CDMA_PROVISIONING";
+ field public static final String PERFORM_IMS_SINGLE_REGISTRATION = "android.permission.PERFORM_IMS_SINGLE_REGISTRATION";
field public static final String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION";
field public static final String POWER_SAVER = "android.permission.POWER_SAVER";
field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
@@ -192,8 +196,10 @@ package android {
field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG";
field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
field public static final String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS";
+ field public static final String READ_NETWORK_DEVICE_CONFIG = "android.permission.READ_NETWORK_DEVICE_CONFIG";
field public static final String READ_NETWORK_USAGE_HISTORY = "android.permission.READ_NETWORK_USAGE_HISTORY";
field public static final String READ_OEM_UNLOCK_STATE = "android.permission.READ_OEM_UNLOCK_STATE";
+ field public static final String READ_PEOPLE_DATA = "android.permission.READ_PEOPLE_DATA";
field public static final String READ_PRINT_SERVICES = "android.permission.READ_PRINT_SERVICES";
field public static final String READ_PRINT_SERVICE_RECOMMENDATIONS = "android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS";
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
@@ -244,6 +250,7 @@ package android {
field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
+ field public static final String SIGNAL_REBOOT_READINESS = "android.permission.SIGNAL_REBOOT_READINESS";
field public static final String SOUND_TRIGGER_RUN_IN_BATTERY_SAVER = "android.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER";
field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND";
field public static final String START_FOREGROUND_SERVICES_FROM_BACKGROUND = "android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND";
@@ -267,6 +274,7 @@ package android {
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
+ field public static final String UWB_PRIVILEGED = "android.permission.UWB_PRIVILEGED";
field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS";
field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS";
field public static final String WIFI_ACCESS_COEX_UNSAFE_CHANNELS = "android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS";
@@ -325,6 +333,8 @@ package android {
}
public static final class R.string {
+ field public static final int config_customMediaKeyDispatcher = 17039404; // 0x104002c
+ field public static final int config_customMediaSessionPolicyProvider = 17039405; // 0x104002d
field public static final int config_defaultAssistant = 17039393; // 0x1040021
field public static final int config_defaultBrowser = 17039394; // 0x1040022
field public static final int config_defaultCallRedirection = 17039397; // 0x1040025
@@ -338,9 +348,10 @@ package android {
field public static final int config_helpPackageNameKey = 17039387; // 0x104001b
field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
- field public static final int config_systemAutomotiveProjection = 17039402; // 0x104002a
+ field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029
+ field public static final int config_systemContacts = 17039403; // 0x104002b
field public static final int config_systemGallery = 17039399; // 0x1040027
- field public static final int config_systemVideoCall = 17039401; // 0x1040029
+ field public static final int config_systemShell = 17039402; // 0x104002a
}
public static final class R.style {
@@ -678,6 +689,7 @@ package android.app {
}
public static class Notification.Action implements android.os.Parcelable {
+ field public static final int SEMANTIC_ACTION_CONVERSATION_IS_PHISHING = 12; // 0xc
field public static final int SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY = 11; // 0xb
}
@@ -870,7 +882,6 @@ package android.app.admin {
}
public class DevicePolicyManager {
- method public boolean canAdminGrantSensorsPermissionsForUser(int);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
@@ -886,6 +897,7 @@ package android.app.admin {
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedKiosk();
+ method public boolean isNetworkSlicingEnabledForUser(@NonNull android.os.UserHandle);
method public boolean isSecondaryLockscreenEnabled(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUnattendedManagedKiosk();
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
@@ -905,8 +917,8 @@ package android.app.admin {
field public static final String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
field public static final String ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE";
field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
- field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI";
- field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
+ field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI";
+ field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
field public static final String EXTRA_PROVISIONING_ORGANIZATION_NAME = "android.app.extra.PROVISIONING_ORGANIZATION_NAME";
field public static final String EXTRA_PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE = "android.app.extra.PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE";
field public static final String EXTRA_PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER = "android.app.extra.PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER";
@@ -1271,6 +1283,14 @@ package android.app.job {
}
+package android.app.people {
+
+ public final class PeopleManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PEOPLE_DATA) public boolean isConversation(@NonNull String, @NonNull String);
+ }
+
+}
+
package android.app.prediction {
public final class AppPredictionContext implements android.os.Parcelable {
@@ -1850,6 +1870,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 @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getSimAccessPermission();
@@ -1990,6 +2011,7 @@ package android.bluetooth {
field public static final int UUID_BYTES_128_BIT = 16; // 0x10
field public static final int UUID_BYTES_16_BIT = 2; // 0x2
field public static final int UUID_BYTES_32_BIT = 4; // 0x4
+ field @NonNull public static final android.os.ParcelUuid VOLUME_CONTROL;
}
public final class BufferConstraint implements android.os.Parcelable {
@@ -2117,6 +2139,7 @@ package android.content {
field public static final String OEM_LOCK_SERVICE = "oem_lock";
field public static final String PERMISSION_SERVICE = "permission";
field public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
+ field public static final String REBOOT_READINESS_SERVICE = "reboot_readiness";
field public static final String ROLLBACK_SERVICE = "rollback";
field public static final String SEARCH_UI_SERVICE = "search_ui";
field public static final String SECURE_ELEMENT_SERVICE = "secure_element";
@@ -2169,6 +2192,7 @@ package android.content {
field public static final String ACTION_PENDING_INCIDENT_REPORTS_CHANGED = "android.intent.action.PENDING_INCIDENT_REPORTS_CHANGED";
field public static final String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
field public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
+ field public static final String ACTION_REBOOT_READY = "android.intent.action.REBOOT_READY";
field public static final String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
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";
@@ -2192,6 +2216,7 @@ package android.content {
field public static final String EXTRA_INSTANT_APP_HOSTNAME = "android.intent.extra.INSTANT_APP_HOSTNAME";
field public static final String EXTRA_INSTANT_APP_SUCCESS = "android.intent.extra.INSTANT_APP_SUCCESS";
field public static final String EXTRA_INSTANT_APP_TOKEN = "android.intent.extra.INSTANT_APP_TOKEN";
+ field public static final String EXTRA_IS_READY_TO_REBOOT = "android.intent.extra.IS_READY_TO_REBOOT";
field public static final String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
field public static final String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID";
field public static final String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
@@ -2522,7 +2547,8 @@ package android.content.pm {
field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub";
field public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur";
- field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
+ field @Deprecated public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
+ field public static final String FEATURE_INCREMENTAL_DELIVERY_VERSION = "android.software.incremental_delivery_version";
field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
field public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION = "android.hardware.telephony.ims.singlereg";
@@ -2631,6 +2657,7 @@ package android.content.pm {
field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000
field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
+ field public static final int PROTECTION_FLAG_KNOWN_SIGNER = 134217728; // 0x8000000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
field public static final int PROTECTION_FLAG_RECENTS = 33554432; // 0x2000000
field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
@@ -2810,16 +2837,49 @@ package android.debug {
package android.graphics.fonts {
+ public final class FontFamilyUpdateRequest {
+ method @NonNull public java.util.List<android.graphics.fonts.FontFamilyUpdateRequest.FontFamily> getFontFamilies();
+ method @NonNull public java.util.List<android.graphics.fonts.FontFileUpdateRequest> getFontFileUpdateRequests();
+ }
+
+ public static final class FontFamilyUpdateRequest.Builder {
+ ctor public FontFamilyUpdateRequest.Builder();
+ method @NonNull public android.graphics.fonts.FontFamilyUpdateRequest.Builder addFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest.FontFamily);
+ method @NonNull public android.graphics.fonts.FontFamilyUpdateRequest.Builder addFontFileUpdateRequest(@NonNull android.graphics.fonts.FontFileUpdateRequest);
+ method @NonNull public android.graphics.fonts.FontFamilyUpdateRequest build();
+ }
+
+ public static final class FontFamilyUpdateRequest.Font {
+ ctor public FontFamilyUpdateRequest.Font(@NonNull String, @NonNull android.graphics.fonts.FontStyle, @NonNull java.util.List<android.graphics.fonts.FontVariationAxis>);
+ method @NonNull public java.util.List<android.graphics.fonts.FontVariationAxis> getAxes();
+ method @NonNull public String getPostScriptName();
+ method @NonNull public android.graphics.fonts.FontStyle getStyle();
+ }
+
+ 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 final class FontFileUpdateRequest {
+ ctor public FontFileUpdateRequest(@NonNull android.os.ParcelFileDescriptor, @NonNull byte[]);
+ method @NonNull public android.os.ParcelFileDescriptor getParcelFileDescriptor();
+ method @NonNull public byte[] getSignature();
+ }
+
public class FontManager {
method @Nullable public android.text.FontConfig getFontConfig();
- method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.os.ParcelFileDescriptor, @NonNull byte[], @IntRange(from=0) int);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.graphics.fonts.FontFileUpdateRequest, @IntRange(from=0) int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.os.ParcelFileDescriptor, @NonNull byte[], @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
field public static final int RESULT_ERROR_FAILED_UPDATE_CONFIG = -6; // 0xfffffffa
+ field public static final int RESULT_ERROR_FONT_NOT_FOUND = -9; // 0xfffffff7
field public static final int RESULT_ERROR_FONT_UPDATER_DISABLED = -7; // 0xfffffff9
field public static final int RESULT_ERROR_INVALID_FONT_FILE = -3; // 0xfffffffd
field public static final int RESULT_ERROR_INVALID_FONT_NAME = -4; // 0xfffffffc
- field public static final int RESULT_ERROR_REMOTE_EXCEPTION = -9; // 0xfffffff7
field public static final int RESULT_ERROR_VERIFICATION_FAILURE = -2; // 0xfffffffe
field public static final int RESULT_ERROR_VERSION_MISMATCH = -8; // 0xfffffff8
field public static final int RESULT_SUCCESS = 0; // 0x0
@@ -3502,6 +3562,7 @@ package android.hardware.location {
field public static final int RESULT_FAILED_BAD_PARAMS = 2; // 0x2
field public static final int RESULT_FAILED_BUSY = 4; // 0x4
field public static final int RESULT_FAILED_HAL_UNAVAILABLE = 8; // 0x8
+ field public static final int RESULT_FAILED_PERMISSION_DENIED = 9; // 0x9
field public static final int RESULT_FAILED_SERVICE_INTERNAL_FAILURE = 7; // 0x7
field public static final int RESULT_FAILED_TIMEOUT = 6; // 0x6
field public static final int RESULT_FAILED_UNINITIALIZED = 3; // 0x3
@@ -4666,6 +4727,7 @@ package android.location {
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String, @Nullable String);
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.UPDATE_APP_OPS_STATS}) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
+ method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerProviderRequestListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.Listener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
@@ -4674,6 +4736,7 @@ package android.location {
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setProviderEnabledForUser(@NonNull String, boolean, @NonNull android.os.UserHandle);
method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean unregisterGnssBatchedLocationCallback(@NonNull android.location.BatchedLocationCallback);
+ method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void unregisterProviderRequestListener(@NonNull android.location.provider.ProviderRequest.Listener);
}
public final class LocationRequest implements android.os.Parcelable {
@@ -4823,6 +4886,10 @@ package android.location.provider {
method @NonNull public android.location.provider.ProviderRequest.Builder setWorkSource(@NonNull android.os.WorkSource);
}
+ public static interface ProviderRequest.Listener {
+ method public void onProviderRequestChanged(@NonNull String, @NonNull android.location.provider.ProviderRequest);
+ }
+
}
package android.media {
@@ -4972,6 +5039,7 @@ package android.media {
method public android.media.PlayerProxy getPlayerProxy();
method public int getPlayerState();
method public int getPlayerType();
+ method @IntRange(from=0) public int getSessionId();
method public boolean isActive();
field public static final int PLAYER_STATE_IDLE = 1; // 0x1
field public static final int PLAYER_STATE_PAUSED = 3; // 0x3
@@ -7036,11 +7104,15 @@ package android.net {
method public long getExpiryTimeMillis();
method public long getRefreshTimeMillis();
method @Nullable public android.net.Uri getUserPortalUrl();
+ method public int getUserPortalUrlSource();
method @Nullable public String getVenueFriendlyName();
method @Nullable public android.net.Uri getVenueInfoUrl();
+ method public int getVenueInfoUrlSource();
method public boolean isCaptive();
method public boolean isSessionExtendable();
method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; // 0x0
+ field public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; // 0x1
field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR;
}
@@ -7054,8 +7126,10 @@ package android.net {
method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long);
method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean);
method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri);
+ method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri, int);
method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String);
method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri);
+ method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri, int);
}
public class ConnectivityManager {
@@ -7069,6 +7143,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(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);
@@ -7090,6 +7165,10 @@ 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();
@@ -7174,6 +7253,7 @@ package android.net {
method public void close();
method @NonNull public String getInterfaceName();
method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void setUnderlyingNetwork(@NonNull android.net.Network) throws java.io.IOException;
}
public static class IpSecTransform.Builder {
@@ -7317,6 +7397,7 @@ package android.net {
method @NonNull public int[] getAdministratorUids();
method @Nullable public String getSsid();
method @NonNull public int[] getTransportTypes();
+ method public boolean isPrivateDnsBroken();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
@@ -7331,6 +7412,7 @@ package android.net {
method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int);
method @NonNull public android.net.NetworkCapabilities build();
+ method @NonNull public android.net.NetworkCapabilities.Builder clearAll();
method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int);
method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int);
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
@@ -7446,6 +7528,26 @@ package android.net {
ctor public NetworkStats.Entry(@Nullable String, int, int, int, int, int, int, long, long, long, long, long);
}
+ public final class OemNetworkPreferences implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getNetworkPreferences();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.OemNetworkPreferences> CREATOR;
+ field public static final int OEM_NETWORK_PREFERENCE_OEM_PAID = 1; // 0x1
+ field public static final int OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK = 2; // 0x2
+ field public static final int OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY = 3; // 0x3
+ field public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4; // 0x4
+ field public static final int OEM_NETWORK_PREFERENCE_UNINITIALIZED = 0; // 0x0
+ }
+
+ public static final class OemNetworkPreferences.Builder {
+ ctor public OemNetworkPreferences.Builder();
+ ctor public OemNetworkPreferences.Builder(@NonNull android.net.OemNetworkPreferences);
+ method @NonNull public android.net.OemNetworkPreferences.Builder addNetworkPreference(@NonNull String, int);
+ method @NonNull public android.net.OemNetworkPreferences build();
+ method @NonNull public android.net.OemNetworkPreferences.Builder clearNetworkPreference(@NonNull String);
+ }
+
public abstract class QosCallback {
ctor public QosCallback();
method public void onError(@NonNull android.net.QosCallbackException);
@@ -8484,7 +8586,7 @@ package android.os {
method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressedForToken(@NonNull String);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSaveEnabled(boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
- method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
+ method @RequiresPermission(anyOf={android.Manifest.permission.BATTERY_PREDICTION, android.Manifest.permission.DEVICE_POWER}) public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setDynamicPowerSaveHint(boolean, int);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setFullPowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setPowerSaveModeEnabled(boolean);
@@ -8554,6 +8656,7 @@ package android.os {
public class SystemConfigManager {
method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
+ method @NonNull @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public int[] getSystemPermissionUids(@NonNull String);
}
public class SystemProperties {
@@ -8644,6 +8747,7 @@ package android.os {
method @NonNull public static String formatUid(int);
method public static int getAppId(int);
method public int getIdentifier();
+ method public static int getUid(@NonNull android.os.UserHandle, int);
method @Deprecated public boolean isOwner();
method public boolean isSystem();
method public static int myUserId();
@@ -8863,6 +8967,16 @@ package android.os.storage {
package android.permission {
+ public final class AdminPermissionControlParams implements android.os.Parcelable {
+ method public boolean canAdminGrantSensorsPermissions();
+ method public int describeContents();
+ method public int getGrantState();
+ method @NonNull public String getGranteePackageName();
+ method @NonNull public String getPermission();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.permission.AdminPermissionControlParams> CREATOR;
+ }
+
public final class PermissionControllerManager {
method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS}) public void applyStagedRuntimePermissionBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getRuntimePermissionBackup(@NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<byte[]>);
@@ -8894,7 +9008,8 @@ package android.permission {
method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable);
method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
- method @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @Deprecated @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @BinderThread public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull android.permission.AdminPermissionControlParams, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
method @BinderThread public void onUpdateUserSensitivePermissionFlags(int, @NonNull java.util.concurrent.Executor, @NonNull Runnable);
method @BinderThread public void onUpdateUserSensitivePermissionFlags(int, @NonNull Runnable);
@@ -9365,22 +9480,8 @@ package android.provider {
method @RequiresPermission(android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, boolean);
}
- public final class SimPhonebookContract {
- method @NonNull public static String getEfUriPath(int);
- field public static final String SUBSCRIPTION_ID_PATH_SEGMENT = "subid";
- }
-
- public static final class SimPhonebookContract.ElementaryFiles {
- field public static final String EF_ADN_PATH_SEGMENT = "adn";
- field public static final String EF_FDN_PATH_SEGMENT = "fdn";
- field public static final String EF_SDN_PATH_SEGMENT = "sdn";
- field public static final String ELEMENTARY_FILES_PATH_SEGMENT = "elementary_files";
- }
-
public static final class SimPhonebookContract.SimRecords {
- field public static final String EXTRA_NAME_VALIDATION_RESULT = "android.provider.extra.NAME_VALIDATION_RESULT";
field public static final String QUERY_ARG_PIN2 = "android:query-arg-pin2";
- field public static final String VALIDATE_NAME_PATH_SEGMENT = "validate_name";
}
public static final class Telephony.Carriers implements android.provider.BaseColumns {
@@ -9503,10 +9604,12 @@ package android.security.keystore {
}
public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+ method @Nullable public int[] getAttestationIds();
method public int getNamespace();
}
public static final class KeyGenParameterSpec.Builder {
+ method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setAttestationIds(@NonNull int[]);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setNamespace(int);
method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
}
@@ -9885,6 +9988,18 @@ package android.service.dataloader {
}
+package android.service.displayhash {
+
+ 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 @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";
+ }
+
+}
+
package android.service.euicc {
public final class DownloadSubscriptionResult implements android.os.Parcelable {
@@ -10069,6 +10184,7 @@ package android.service.notification {
method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel);
method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel, @NonNull android.service.notification.NotificationListenerService.RankingMap);
method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean);
+ method public void onNotificationFeedbackReceived(@NonNull String, @NonNull android.service.notification.NotificationListenerService.RankingMap, @NonNull android.os.Bundle);
method public abstract void onNotificationSnoozedUntilContext(@NonNull android.service.notification.StatusBarNotification, @NonNull String);
method public void onNotificationVisibilityChanged(@NonNull String, boolean);
method public void onNotificationsSeen(@NonNull java.util.List<java.lang.String>);
@@ -10076,6 +10192,7 @@ package android.service.notification {
method public void onPanelRevealed(int);
method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int);
method public final void unsnoozeNotification(@NonNull String);
+ field public static final String FEEDBACK_RATING = "feedback.rating";
field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
field public static final int SOURCE_FROM_APP = 0; // 0x0
field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
@@ -10245,30 +10362,6 @@ package android.service.rotationresolver {
}
-package android.service.screenshot {
-
- public final class ScreenshotHash implements android.os.Parcelable {
- ctor public ScreenshotHash(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[], @NonNull byte[]);
- method public int describeContents();
- method @NonNull public android.graphics.Rect getBoundsInWindow();
- method @NonNull public String getHashingAlgorithm();
- method @NonNull public byte[] getHmac();
- method @NonNull public byte[] getImageHash();
- method public long getScreenshotTimeMillis();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.service.screenshot.ScreenshotHash> CREATOR;
- }
-
- public abstract class ScreenshotHasherService extends android.app.Service {
- ctor public ScreenshotHasherService();
- method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
- method @Nullable public abstract android.service.screenshot.ScreenshotHash onGenerateScreenshotHash(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String);
- method public abstract boolean onVerifyScreenshotHash(@NonNull byte[], @NonNull android.service.screenshot.ScreenshotHash);
- field public static final String SERVICE_INTERFACE = "android.service.screenshot.ScreenshotHasherService";
- }
-
-}
-
package android.service.search {
public abstract class SearchUiService extends android.app.Service {
@@ -10339,6 +10432,7 @@ package android.service.storage {
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onEndSession(@NonNull String) throws java.io.IOException;
method public void onFreeCache(@NonNull java.util.UUID, long) throws java.io.IOException;
+ method public long onGetAnrDelayMillis(@NonNull String, int);
method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException;
method public abstract void onVolumeStateChanged(@NonNull android.os.storage.StorageVolume) throws java.io.IOException;
field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2
@@ -10671,7 +10765,7 @@ package android.telecom {
method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
method @Nullable public final String getTelecomCallId();
method @Deprecated public void onAudioStateChanged(android.telecom.AudioState);
- method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean);
+ method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, boolean);
method public final void resetConnectionTime();
method public void setCallDirection(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long);
@@ -10848,7 +10942,7 @@ package android.telecom {
}
public final class RemoteConnection {
- method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean);
+ method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, boolean);
method @Deprecated public void setAudioState(android.telecom.AudioState);
}
@@ -11030,6 +11124,8 @@ package android.telephony {
}
public static final class CarrierConfigManager.Wifi {
+ field public static final String KEY_AVOID_5GHZ_SOFTAP_FOR_LAA_BOOL = "wifi.avoid_5ghz_softap_for_laa_bool";
+ field public static final String KEY_AVOID_5GHZ_WIFI_DIRECT_FOR_LAA_BOOL = "wifi.avoid_5ghz_wifi_direct_for_laa_bool";
field public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT = "wifi.hotspot_maximum_client_count";
field public static final String KEY_PREFIX = "wifi.";
field public static final String KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED = "wifi.suggestion_ssid_list_with_mac_randomization_disabled";
@@ -11768,7 +11864,7 @@ package android.telephony {
}
public class TelephonyManager {
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void bootstrapAuthenticationRequest(int, @NonNull android.net.Uri, @NonNull android.telephony.gba.UaSecurityProtocolIdentifier, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.BootstrapAuthenticationCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) @WorkerThread public void bootstrapAuthenticationRequest(int, @NonNull android.net.Uri, @NonNull android.telephony.gba.UaSecurityProtocolIdentifier, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.BootstrapAuthenticationCallback);
method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult changeIccLockPin(@NonNull String, @NonNull String);
method public int checkCarrierPrivilegesForPackage(String);
@@ -11860,6 +11956,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean matchesCurrentSimOperator(@NonNull String, int, @Nullable String);
method public boolean needsOtaServiceProvisioning();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
+ method @RequiresPermission(android.Manifest.permission.REBOOT) public int prepareForUnattendedReboot();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
@@ -11924,12 +12021,14 @@ package android.telephony {
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
field public static final int ALLOWED_NETWORK_TYPES_REASON_CARRIER = 2; // 0x2
+ field public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3; // 0x3
field public static final int ALLOWED_NETWORK_TYPES_REASON_POWER = 1; // 0x1
field public static final int ALLOWED_NETWORK_TYPES_REASON_USER = 0; // 0x0
field public static final int CALL_WAITING_STATUS_DISABLED = 2; // 0x2
field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1
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_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
@@ -11984,6 +12083,9 @@ package android.telephony {
field public static final int NR_DUAL_CONNECTIVITY_DISABLE = 2; // 0x2
field public static final int NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE = 3; // 0x3
field public static final int NR_DUAL_CONNECTIVITY_ENABLE = 1; // 0x1
+ field public static final int PREPARE_UNATTENDED_REBOOT_ERROR = 2; // 0x2
+ field public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1; // 0x1
+ field public static final int PREPARE_UNATTENDED_REBOOT_SUCCESS = 0; // 0x0
field public static final int RADIO_POWER_OFF = 0; // 0x0
field public static final int RADIO_POWER_ON = 1; // 0x1
field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
@@ -12950,10 +13052,31 @@ package android.telephony.ims {
method @Deprecated public void onRegistering(int);
}
+ public class ImsRcsManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnAvailabilityChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.OnAvailabilityChangedListener) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int, int) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnAvailabilityChangedListener(@NonNull android.telephony.ims.ImsRcsManager.OnAvailabilityChangedListener);
+ }
+
+ public static interface ImsRcsManager.OnAvailabilityChangedListener {
+ method public void onAvailabilityChanged(int);
+ }
+
public final class ImsReasonInfo implements android.os.Parcelable {
field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service";
}
+ public final class ImsRegistrationAttributes implements android.os.Parcelable {
+ method public int getRegistrationTechnology();
+ }
+
+ public static final class ImsRegistrationAttributes.Builder {
+ ctor public ImsRegistrationAttributes.Builder(int);
+ method @NonNull public android.telephony.ims.ImsRegistrationAttributes build();
+ method @NonNull public android.telephony.ims.ImsRegistrationAttributes.Builder setFeatureTags(@NonNull java.util.Set<java.lang.String>);
+ }
+
public class ImsService extends android.app.Service {
ctor public ImsService();
method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
@@ -13189,19 +13312,19 @@ package android.telephony.ims {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(int, int);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isRcsVolteSingleRegistrationCapable() throws android.telephony.ims.ImsException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isRcsVolteSingleRegistrationCapable() throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerRcsProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void registerRcsProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(int, int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRcsClientConfiguration(@NonNull android.telephony.ims.RcsClientConfiguration) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void setRcsClientConfiguration(@NonNull android.telephony.ims.RcsClientConfiguration) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void triggerRcsReconfiguration();
+ method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerRcsReconfiguration();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterRcsProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback);
- field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE = "android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE";
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void unregisterRcsProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback);
+ field @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public static final String ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE = "android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE";
field public static final String EXTRA_STATUS = "android.telephony.ims.extra.STATUS";
field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.ims.extra.SUBSCRIPTION_ID";
field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43
@@ -13333,8 +13456,8 @@ package android.telephony.ims {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnPublishStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnPublishStateChangedListener(@NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestAvailability(@NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestAvailability(@NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, android.Manifest.permission.READ_CONTACTS}) public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1
@@ -13466,10 +13589,10 @@ package android.telephony.ims {
}
public class SipDelegateManager {
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException;
- method public void triggerFullNetworkRegistration(@NonNull android.telephony.ims.SipDelegateConnection, @IntRange(from=100, to=699) int, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isSupported() throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerFullNetworkRegistration(@NonNull android.telephony.ims.SipDelegateConnection, @IntRange(from=100, to=699) int, @Nullable String);
field public static final int DENIED_REASON_INVALID = 4; // 0x4
field public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; // 0x1
field public static final int DENIED_REASON_NOT_ALLOWED = 2; // 0x2
@@ -13590,20 +13713,39 @@ package android.telephony.ims.feature {
ctor public RcsFeature(@NonNull java.util.concurrent.Executor);
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener);
+ method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
method public void onFeatureReady();
method public void onFeatureRemoved();
+ method public boolean queryCapabilityConfiguration(int, int);
+ method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus();
method public void removeCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase);
}
+ public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
+ ctor public RcsFeature.RcsImsCapabilities(int);
+ method public void addCapabilities(int);
+ method public boolean isCapable(int);
+ method public void removeCapabilities(int);
+ field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
+ field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
+ field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
+ }
+
}
package android.telephony.ims.stub {
public interface CapabilityExchangeEventListener {
+ method public void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.List<java.lang.String>, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener.OptionsRequestCallback) throws android.telephony.ims.ImsException;
method public void onRequestPublishCapabilities(int) throws android.telephony.ims.ImsException;
method public void onUnpublish() throws android.telephony.ims.ImsException;
}
+ public static interface CapabilityExchangeEventListener.OptionsRequestCallback {
+ method public default void onRespondToCapabilityRequest(@NonNull android.telephony.ims.RcsContactUceCapability, boolean);
+ method public void onRespondToCapabilityRequestWithError(@IntRange(from=100, to=699) int, @NonNull String);
+ }
+
public interface DelegateConnectionMessageCallback {
method public void onMessageReceived(@NonNull android.telephony.ims.SipMessage);
method public void onMessageSendFailure(@NonNull String, int);
@@ -13724,7 +13866,9 @@ package android.telephony.ims.stub {
ctor public ImsRegistrationImplBase();
method public final void onDeregistered(android.telephony.ims.ImsReasonInfo);
method public final void onRegistered(int);
+ method public final void onRegistered(@NonNull android.telephony.ims.ImsRegistrationAttributes);
method public final void onRegistering(int);
+ method public final void onRegistering(@NonNull android.telephony.ims.ImsRegistrationAttributes);
method public final void onSubscriberAssociatedUriChanged(android.net.Uri[]);
method public final void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo);
method public void triggerFullNetworkRegistration(@IntRange(from=100, to=699) int, @Nullable String);
@@ -13789,6 +13933,7 @@ package android.telephony.ims.stub {
public class RcsCapabilityExchangeImplBase {
ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor);
method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback);
+ method public void sendOptionsCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.List<java.lang.String>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback);
method public void subscribeForCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback);
field public static final int COMMAND_CODE_FETCH_ERROR = 3; // 0x3
field public static final int COMMAND_CODE_GENERIC_FAILURE = 1; // 0x1
@@ -13803,6 +13948,11 @@ package android.telephony.ims.stub {
field public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; // 0x0
}
+ public static interface RcsCapabilityExchangeImplBase.OptionsResponseCallback {
+ method public void onCommandError(int) throws android.telephony.ims.ImsException;
+ method public void onNetworkResponse(int, @NonNull String, @NonNull java.util.List<java.lang.String>) throws android.telephony.ims.ImsException;
+ }
+
public static interface RcsCapabilityExchangeImplBase.PublishResponseCallback {
method public void onCommandError(int) throws android.telephony.ims.ImsException;
method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
@@ -14086,10 +14236,10 @@ package android.uwb {
}
public final class RangingSession implements java.lang.AutoCloseable {
- method public void close();
- method public void reconfigure(@NonNull android.os.PersistableBundle);
- method public void start(@NonNull android.os.PersistableBundle);
- method public void stop();
+ method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void close();
+ method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void reconfigure(@NonNull android.os.PersistableBundle);
+ method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void start(@NonNull android.os.PersistableBundle);
+ method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void stop();
}
public static interface RangingSession.Callback {
@@ -14125,18 +14275,18 @@ package android.uwb {
}
public final class UwbManager {
- method public long elapsedRealtimeResolutionNanos();
- method public int getAngleOfArrivalSupport();
- method public int getMaxRemoteDevicesPerInitiatorSession();
- method public int getMaxRemoteDevicesPerResponderSession();
- method public int getMaxSimultaneousSessions();
- method @NonNull public android.os.PersistableBundle getSpecificationInfo();
- method @NonNull public java.util.List<java.lang.Integer> getSupportedChannelNumbers();
- method @NonNull public java.util.Set<java.lang.Integer> getSupportedPreambleCodeIndices();
- method public boolean isRangingSupported();
- method @NonNull public AutoCloseable openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
- method public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback);
- method public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback);
+ method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos();
+ method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getAngleOfArrivalSupport();
+ method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxRemoteDevicesPerInitiatorSession();
+ method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxRemoteDevicesPerResponderSession();
+ method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxSimultaneousSessions();
+ method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.PersistableBundle getSpecificationInfo();
+ method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public java.util.List<java.lang.Integer> getSupportedChannelNumbers();
+ method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public java.util.Set<java.lang.Integer> getSupportedPreambleCodeIndices();
+ method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public boolean isRangingSupported();
+ 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 @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);
field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D = 2; // 0x2
field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 3; // 0x3
field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 4; // 0x4
@@ -14244,6 +14394,21 @@ package android.view.contentcapture {
}
+package android.view.displayhash {
+
+ public final class DisplayHash implements android.os.Parcelable {
+ ctor public DisplayHash(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[], @NonNull byte[]);
+ method public int describeContents();
+ method @NonNull public android.graphics.Rect getBoundsInWindow();
+ method @NonNull public String getHashAlgorithm();
+ method @NonNull public byte[] getHmac();
+ method @NonNull public byte[] getImageHash();
+ method public long getTimeMillis();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ }
+
+}
+
package android.view.translation {
public final class UiTranslationManager {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e39b2b856661..e0e84537368f 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -14,6 +14,7 @@ package android {
field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS";
field public static final String CONTROL_DEVICE_STATE = "android.permission.CONTROL_DEVICE_STATE";
field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
+ field public static final String KEEP_UNINSTALLED_PACKAGES = "android.permission.KEEP_UNINSTALLED_PACKAGES";
field @Deprecated public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final String MANAGE_ACTIVITY_TASKS = "android.permission.MANAGE_ACTIVITY_TASKS";
field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES";
@@ -53,9 +54,8 @@ package android {
field public static final int config_defaultAssistant = 17039393; // 0x1040021
field public static final int config_defaultDialer = 17039395; // 0x1040023
field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
- field public static final int config_systemAutomotiveProjection = 17039402; // 0x104002a
+ field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029
field public static final int config_systemGallery = 17039399; // 0x1040027
- field public static final int config_systemVideoCall = 17039401; // 0x1040029
}
}
@@ -300,6 +300,7 @@ package android.app {
method public void clickNotification(@Nullable String, int, int, boolean);
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void collapsePanels();
method public void expandNotificationsPanel();
+ method public void sendNotificationFeedback(@Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
}
@@ -391,11 +392,11 @@ package android.app.admin {
method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
method public boolean isCurrentInputMethodSetByOwner();
method public boolean isFactoryResetProtectionPolicySupported();
+ method @NonNull public static String operationSafetyReasonToString(int);
method @NonNull public static String operationToString(int);
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
- method @NonNull public static String unsafeOperationReasonToString(int);
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
@@ -425,6 +426,7 @@ package android.app.admin {
field public static final int OPERATION_REMOVE_KEY_PAIR = 28; // 0x1c
field public static final int OPERATION_REMOVE_USER = 6; // 0x6
field public static final int OPERATION_REQUEST_BUGREPORT = 29; // 0x1d
+ field public static final int OPERATION_SAFETY_REASON_NONE = -1; // 0xffffffff
field public static final int OPERATION_SET_ALWAYS_ON_VPN_PACKAGE = 30; // 0x1e
field public static final int OPERATION_SET_APPLICATION_HIDDEN = 15; // 0xf
field public static final int OPERATION_SET_APPLICATION_RESTRICTIONS = 16; // 0x10
@@ -460,7 +462,6 @@ package android.app.admin {
field public static final int PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED = 4; // 0x4
field public static final int PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED = 7; // 0x7
field public static final int PROVISIONING_RESULT_STARTING_PROFILE_FAILED = 5; // 0x5
- field public static final int UNSAFE_OPERATION_REASON_NONE = -1; // 0xffffffff
}
public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
@@ -472,6 +473,7 @@ package android.app.admin {
method @NonNull public String getOwnerName();
method @Nullable public String getTimeZone();
method public boolean isLeaveAllSystemAppsEnabled();
+ method public void logParams(@NonNull String);
method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FullyManagedDeviceProvisioningParams> CREATOR;
}
@@ -495,6 +497,7 @@ package android.app.admin {
method public boolean isKeepAccountMigrated();
method public boolean isLeaveAllSystemAppsEnabled();
method public boolean isOrganizationOwnedProvisioning();
+ method public void logParams(@NonNull String);
method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.ManagedProfileProvisioningParams> CREATOR;
}
@@ -691,6 +694,7 @@ package android.content.pm {
method @Nullable public String getSystemTextClassifierPackageName();
method @Nullable public String getWellbeingPackageName();
method public void holdLock(android.os.IBinder, int);
+ method @RequiresPermission(android.Manifest.permission.KEEP_UNINSTALLED_PACKAGES) public void setKeepUninstalledPackages(@NonNull java.util.List<java.lang.String>);
field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
@@ -834,14 +838,16 @@ package android.graphics.fonts {
public class FontManager {
method @Nullable public android.text.FontConfig getFontConfig();
- method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.os.ParcelFileDescriptor, @NonNull byte[], @IntRange(from=0) int);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.graphics.fonts.FontFileUpdateRequest, @IntRange(from=0) int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.os.ParcelFileDescriptor, @NonNull byte[], @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
field public static final int RESULT_ERROR_FAILED_UPDATE_CONFIG = -6; // 0xfffffffa
+ field public static final int RESULT_ERROR_FONT_NOT_FOUND = -9; // 0xfffffff7
field public static final int RESULT_ERROR_FONT_UPDATER_DISABLED = -7; // 0xfffffff9
field public static final int RESULT_ERROR_INVALID_FONT_FILE = -3; // 0xfffffffd
field public static final int RESULT_ERROR_INVALID_FONT_NAME = -4; // 0xfffffffc
- field public static final int RESULT_ERROR_REMOTE_EXCEPTION = -9; // 0xfffffff7
field public static final int RESULT_ERROR_VERIFICATION_FAILURE = -2; // 0xfffffffe
field public static final int RESULT_ERROR_VERSION_MISMATCH = -8; // 0xfffffff8
field public static final int RESULT_SUCCESS = 0; // 0x0
@@ -911,6 +917,8 @@ package android.hardware.devicestate {
method @NonNull public int[] getSupportedStates();
method public void removeDeviceStateListener(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener);
method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
+ field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff
+ field public static final int MINIMUM_DEVICE_STATE = 0; // 0x0
}
public static interface DeviceStateManager.DeviceStateListener {
@@ -1225,11 +1233,6 @@ package android.media {
method public void forceResourceLost();
}
- public final class MediaCodec implements android.media.metrics.PlaybackComponent {
- method public String getPlaybackId();
- method public void setPlaybackId(@NonNull String);
- }
-
public static final class MediaCodecInfo.VideoCapabilities.PerformancePoint {
ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(int, int, int, int, @NonNull android.util.Size);
ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(@NonNull android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint, @NonNull android.util.Size);
@@ -1304,15 +1307,6 @@ package android.media.audiopolicy {
}
-package android.media.metrics {
-
- public interface PlaybackComponent {
- method @NonNull public String getPlaybackId();
- method public void setPlaybackId(@NonNull String);
- }
-
-}
-
package android.media.tv {
public final class TvInputManager {
@@ -1343,6 +1337,12 @@ package android.net {
field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
}
+ public class NetworkPolicyManager {
+ method public boolean getRestrictBackground();
+ method @NonNull public static String resolveNetworkId(@NonNull android.net.wifi.WifiConfiguration);
+ method public void setRestrictBackground(boolean);
+ }
+
public class NetworkStack {
method public static void setServiceForTest(@Nullable android.os.IBinder);
}
@@ -1369,6 +1369,32 @@ package android.os {
field public static final int RESOURCES_SDK_INT;
}
+ public abstract class CombinedVibrationEffect implements android.os.Parcelable {
+ method public abstract long getDuration();
+ }
+
+ public static final class CombinedVibrationEffect.Mono extends android.os.CombinedVibrationEffect {
+ method public long getDuration();
+ method @NonNull public android.os.VibrationEffect getEffect();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.os.CombinedVibrationEffect.Mono> CREATOR;
+ }
+
+ public static final class CombinedVibrationEffect.Sequential extends android.os.CombinedVibrationEffect {
+ method @NonNull public java.util.List<java.lang.Integer> getDelays();
+ method public long getDuration();
+ method @NonNull public java.util.List<android.os.CombinedVibrationEffect> getEffects();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.os.CombinedVibrationEffect.Sequential> CREATOR;
+ }
+
+ public static final class CombinedVibrationEffect.Stereo extends android.os.CombinedVibrationEffect {
+ method public long getDuration();
+ method @NonNull public android.util.SparseArray<android.os.VibrationEffect> getEffects();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.os.CombinedVibrationEffect.Stereo> CREATOR;
+ }
+
public class DeviceIdleManager {
method @NonNull public String[] getSystemPowerWhitelist();
method @NonNull public String[] getSystemPowerWhitelistExceptIdle();
@@ -2531,6 +2557,7 @@ package android.view.inputmethod {
public final class InputMethodManager {
method public int getDisplayId();
+ method public boolean hasActiveInputConnection(@Nullable android.view.View);
method public boolean isInputMethodPickerShown();
}
@@ -2692,6 +2719,10 @@ package android.window {
field public static final int FEATURE_WINDOW_TOKENS = 2; // 0x2
}
+ public final class SplashScreenView extends android.widget.FrameLayout {
+ method @Nullable public android.view.View getBrandingView();
+ }
+
public final class StartingWindowInfo implements android.os.Parcelable {
ctor public StartingWindowInfo();
method public int describeContents();
@@ -2711,6 +2742,7 @@ package android.window {
public class TaskOrganizer extends android.window.WindowOrganizer {
ctor public TaskOrganizer();
method @BinderThread public void addStartingWindow(@NonNull android.window.StartingWindowInfo, @NonNull android.os.IBinder);
+ method @BinderThread public void copySplashScreenView(int);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void createRootTask(int, int, @Nullable android.os.IBinder);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public boolean deleteRootTask(@NonNull android.window.WindowContainerToken);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getChildTasks(@NonNull android.window.WindowContainerToken, @NonNull int[]);
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 23a40b8c28d5..c8046b145a08 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -487,6 +487,8 @@ GetterSetterNames: android.location.LocationRequest#isLocationSettingsIgnored():
GetterSetterNames: android.location.LocationRequest#isLowPowerMode():
+GetterSetterNames: android.net.NetworkPolicyManager#getRestrictBackground():
+ Symmetric method for `setRestrictBackground` must be named `isRestrictBackground`; was `getRestrictBackground`
GetterSetterNames: android.os.IncidentReportArgs#isAll():
GetterSetterNames: android.service.notification.NotificationStats#setDirectReplied():
diff --git a/core/java/Android.bp b/core/java/Android.bp
index fb27f74211fb..2fdf9c146ff6 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -1,3 +1,14 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-BSD
+ // legacy_unencumbered
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "IKeyAttestationApplicationIdProvider.aidl",
srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"],
@@ -7,3 +18,8 @@ filegroup {
name: "IDropBoxManagerService.aidl",
srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"],
}
+
+filegroup {
+ name: "ITracingServiceProxy.aidl",
+ srcs: ["android/tracing/ITracingServiceProxy.aidl"],
+}
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 4b2d74160006..768ec3851991 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -20,12 +20,12 @@ package android.accessibilityservice;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
-import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
@@ -97,10 +97,10 @@ public final class AccessibilityGestureEvent implements Parcelable {
GESTURE_UNKNOWN,
GESTURE_TOUCH_EXPLORATION,
GESTURE_2_FINGER_SINGLE_TAP,
- GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD,
GESTURE_2_FINGER_DOUBLE_TAP,
GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD,
GESTURE_2_FINGER_TRIPLE_TAP,
+ GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD,
GESTURE_3_FINGER_SINGLE_TAP,
GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD,
GESTURE_3_FINGER_DOUBLE_TAP,
@@ -232,8 +232,8 @@ public final class AccessibilityGestureEvent implements Parcelable {
case GESTURE_PASSTHROUGH: return "GESTURE_PASSTHROUGH";
case GESTURE_TOUCH_EXPLORATION: return "GESTURE_TOUCH_EXPLORATION";
case GESTURE_2_FINGER_SINGLE_TAP: return "GESTURE_2_FINGER_SINGLE_TAP";
- case GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD:
- return "GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD";
+ case GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD:
+ return "GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD";
case GESTURE_2_FINGER_DOUBLE_TAP: return "GESTURE_2_FINGER_DOUBLE_TAP";
case GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD:
return "GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD";
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 1fa7fa2b744f..dab4a5dc316c 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -445,8 +445,8 @@ public abstract class AccessibilityService extends Service {
/** The user has performed a three-finger double tap and hold gesture on the touch screen. */
public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41;
- /** The user has performed a two-finger single-tap and hold gesture on the touch screen. */
- public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43;
+ /** The user has performed a two-finger triple-tap and hold gesture on the touch screen. */
+ public static final int GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD = 43;
/** The user has performed a three-finger single-tap and hold gesture on the touch screen. */
public static final int GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD = 44;
diff --git a/core/java/android/accounts/OWNERS b/core/java/android/accounts/OWNERS
index ea5fd36702f9..8dcc04a27af6 100644
--- a/core/java/android/accounts/OWNERS
+++ b/core/java/android/accounts/OWNERS
@@ -3,7 +3,6 @@ dementyev@google.com
sandrakwan@google.com
hackbod@google.com
svetoslavganov@google.com
-moltmann@google.com
fkupolov@google.com
yamasani@google.com
omakoto@google.com
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 4728f11a402d..a73fe71cfe1a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -139,6 +139,8 @@ import android.view.translation.UiTranslationController;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.Toolbar;
+import android.window.SplashScreen;
+import android.window.SplashScreenView;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -154,6 +156,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -961,6 +964,10 @@ public class Activity extends ContextThemeWrapper
private UiTranslationController mUiTranslationController;
+ private SplashScreen mSplashScreen;
+ /** @hide */
+ SplashScreenView mSplashScreenView;
+
private final WindowControllerCallback mWindowControllerCallback =
new WindowControllerCallback() {
/**
@@ -1603,6 +1610,23 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Get the interface that activity use to talk to the splash screen.
+ * @see SplashScreen
+ */
+ public final @NonNull SplashScreen getSplashScreen() {
+ return getOrCreateSplashScreen();
+ }
+
+ private SplashScreen getOrCreateSplashScreen() {
+ synchronized (this) {
+ if (mSplashScreen == null) {
+ mSplashScreen = new SplashScreen.SplashScreenImpl(this);
+ }
+ return mSplashScreen;
+ }
+ }
+
+ /**
* 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>.
@@ -3788,6 +3812,22 @@ public class Activity extends ContextThemeWrapper
return false;
}
+ private static final class RequestFinishCallback extends IRequestFinishCallback.Stub {
+ private final WeakReference<Activity> mActivityRef;
+
+ RequestFinishCallback(WeakReference<Activity> activityRef) {
+ mActivityRef = activityRef;
+ }
+
+ @Override
+ public void requestFinish() {
+ Activity activity = mActivityRef.get();
+ if (activity != null) {
+ activity.mHandler.post(activity::finishAfterTransition);
+ }
+ }
+ }
+
/**
* Called when the activity has detected the user's press of the back
* key. The default implementation simply finishes the current activity,
@@ -3811,7 +3851,8 @@ public class Activity extends ContextThemeWrapper
// Inform activity task manager that the activity received a back press while at the
// root of the task. This call allows ActivityTaskManager to intercept or move the task
// to the back.
- ActivityClient.getInstance().onBackPressedOnTaskRoot(mToken);
+ ActivityClient.getInstance().onBackPressedOnTaskRoot(mToken,
+ new RequestFinishCallback(new WeakReference<>(this)));
// Activity was launched when user tapped a link in the Autofill Save UI - Save UI must
// be restored now.
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index 401f8cc13bad..fbabfac706e1 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -46,9 +46,9 @@ public class ActivityClient {
}
/** Reports {@link Activity#onResume()} is done. */
- public void activityResumed(IBinder token) {
+ public void activityResumed(IBinder token, boolean handleSplashScreenExit) {
try {
- getActivityClientController().activityResumed(token);
+ getActivityClientController().activityResumed(token, handleSplashScreenExit);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -480,9 +480,20 @@ public class ActivityClient {
}
}
- void onBackPressedOnTaskRoot(IBinder token) {
+ void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) {
try {
- getActivityClientController().onBackPressedOnTaskRoot(token);
+ getActivityClientController().onBackPressedOnTaskRoot(token, callback);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Reports the splash screen view has attached to client.
+ */
+ void reportSplashScreenAttached(IBinder token) {
+ try {
+ getActivityClientController().splashScreenAttached(token);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 43d0269beae2..e2426d116319 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -50,7 +50,9 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.drawable.Icon;
+import android.hardware.HardwareBuffer;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Build;
@@ -76,6 +78,7 @@ import android.util.Singleton;
import android.util.Size;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
+import android.window.TaskSnapshot;
import com.android.internal.app.LocalePicker;
import com.android.internal.app.procstats.ProcessStats;
@@ -1740,6 +1743,62 @@ public class ActivityManager {
*/
public static class RecentTaskInfo extends TaskInfo implements Parcelable {
/**
+ * @hide
+ */
+ public static class PersistedTaskSnapshotData {
+ /**
+ * The bounds of the task when the last snapshot was taken, may be null if the task is
+ * not yet attached to the hierarchy.
+ * @see {@link android.window.TaskSnapshot#mTaskSize}.
+ * @hide
+ */
+ public @Nullable Point taskSize;
+
+ /**
+ * The content insets of the task when the task snapshot was taken.
+ * @see {@link android.window.TaskSnapshot#mContentInsets}.
+ * @hide
+ */
+ public @Nullable Rect contentInsets;
+
+ /**
+ * The size of the last snapshot taken, may be null if there is no associated snapshot.
+ * @see {@link android.window.TaskSnapshot#mSnapshot}.
+ * @hide
+ */
+ public @Nullable Point bufferSize;
+
+ /**
+ * Sets the data from the other data.
+ * @hide
+ */
+ public void set(PersistedTaskSnapshotData other) {
+ taskSize = other.taskSize;
+ contentInsets = other.contentInsets;
+ bufferSize = other.bufferSize;
+ }
+
+ /**
+ * Sets the data from the provided {@param snapshot}.
+ * @hide
+ */
+ public void set(TaskSnapshot snapshot) {
+ if (snapshot == null) {
+ taskSize = null;
+ contentInsets = null;
+ bufferSize = null;
+ return;
+ }
+ final HardwareBuffer buffer = snapshot.getHardwareBuffer();
+ taskSize = new Point(snapshot.getTaskSize());
+ contentInsets = new Rect(snapshot.getContentInsets());
+ bufferSize = buffer != null
+ ? new Point(buffer.getWidth(), buffer.getHeight())
+ : null;
+ }
+ }
+
+ /**
* If this task is currently running, this is the identifier for it.
* If it is not running, this will be -1.
*
@@ -1782,6 +1841,12 @@ public class ActivityManager {
*/
public ArrayList<RecentTaskInfo> childrenTaskInfos = new ArrayList<>();
+ /**
+ * Information about the last snapshot taken for this task.
+ * @hide
+ */
+ public PersistedTaskSnapshotData lastSnapshotData = new PersistedTaskSnapshotData();
+
public RecentTaskInfo() {
}
@@ -1798,6 +1863,9 @@ public class ActivityManager {
id = source.readInt();
persistentId = source.readInt();
childrenTaskInfos = source.readArrayList(RecentTaskInfo.class.getClassLoader());
+ lastSnapshotData.taskSize = source.readTypedObject(Point.CREATOR);
+ lastSnapshotData.contentInsets = source.readTypedObject(Rect.CREATOR);
+ lastSnapshotData.bufferSize = source.readTypedObject(Point.CREATOR);
super.readFromParcel(source);
}
@@ -1806,6 +1874,9 @@ public class ActivityManager {
dest.writeInt(id);
dest.writeInt(persistentId);
dest.writeList(childrenTaskInfos);
+ dest.writeTypedObject(lastSnapshotData.taskSize, flags);
+ dest.writeTypedObject(lastSnapshotData.contentInsets, flags);
+ dest.writeTypedObject(lastSnapshotData.bufferSize, flags);
super.writeToParcel(dest, flags);
}
@@ -1825,7 +1896,6 @@ public class ActivityManager {
public void dump(PrintWriter pw, String indent) {
pw.println(); pw.print(" ");
pw.print(" id="); pw.print(persistentId);
- pw.print(" stackId="); pw.print(stackId);
pw.print(" userId="); pw.print(userId);
pw.print(" hasTask="); pw.print((id != -1));
pw.print(" lastActiveTime="); pw.println(lastActiveTime);
@@ -1872,6 +1942,12 @@ public class ActivityManager {
pw.print(Integer.toHexString(td.getBackgroundColorFloating()));
pw.println(" }");
}
+ pw.print(" ");
+ pw.print(" lastSnapshotData {");
+ pw.print(" taskSize=" + lastSnapshotData.taskSize);
+ pw.print(" contentInsets=" + lastSnapshotData.contentInsets);
+ pw.print(" bufferSize=" + lastSnapshotData.bufferSize);
+ pw.println(" }");
}
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 6d3a5581504a..86917a76c5ab 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -539,6 +539,12 @@ public abstract class ActivityManagerInternal {
*/
public abstract long getBootTimeTempAllowListDuration();
+ /** Register an {@link AnrController} to control the ANR dialog behavior */
+ public abstract void registerAnrController(AnrController controller);
+
+ /** Unregister an {@link AnrController} */
+ public abstract void unregisterAnrController(AnrController controller);
+
// Starts a process as empty.
public abstract int startActivityAsUserEmpty(Bundle options);
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 28da1c3a3eb7..73cc13c82bcb 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -160,6 +160,12 @@ public class ActivityOptions {
public static final String KEY_ANIM_START_LISTENER = "android:activity.animStartListener";
/**
+ * Specific a theme for a splash screen window.
+ * @hide
+ */
+ public static final String KEY_SPLASH_SCREEN_THEME = "android.activity.splashScreenTheme";
+
+ /**
* Callback for when the last frame of the animation is played.
* @hide
*/
@@ -398,6 +404,7 @@ public class ActivityOptions {
private IBinder mLaunchCookie;
private IRemoteTransition mRemoteTransition;
private boolean mOverrideTaskTransition;
+ private int mSplashScreenThemeResId;
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -1147,6 +1154,7 @@ public class ActivityOptions {
mRemoteTransition = IRemoteTransition.Stub.asInterface(opts.getBinder(
KEY_REMOTE_TRANSITION));
mOverrideTaskTransition = opts.getBoolean(KEY_OVERRIDE_TASK_TRANSITION);
+ mSplashScreenThemeResId = opts.getInt(KEY_SPLASH_SCREEN_THEME);
}
/**
@@ -1333,6 +1341,14 @@ public class ActivityOptions {
}
/**
+ * Gets whether the activity want to be launched as other theme for the splash screen.
+ * @hide
+ */
+ public int getSplashScreenThemeResId() {
+ return mSplashScreenThemeResId;
+ }
+
+ /**
* Sets whether the activity is to be launched into LockTask mode.
*
* Use this option to start an activity in LockTask mode. Note that only apps permitted by
@@ -1838,6 +1854,9 @@ public class ActivityOptions {
if (mOverrideTaskTransition) {
b.putBoolean(KEY_OVERRIDE_TASK_TRANSITION, mOverrideTaskTransition);
}
+ if (mSplashScreenThemeResId != 0) {
+ b.putInt(KEY_SPLASH_SCREEN_THEME, mSplashScreenThemeResId);
+ }
return b;
}
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 70fa4445479b..233f737b8e0f 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -19,6 +19,7 @@ package android.app;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -37,6 +38,7 @@ import android.os.ServiceManager;
import android.util.DisplayMetrics;
import android.util.Singleton;
import android.view.RemoteAnimationDefinition;
+import android.window.SplashScreenView.SplashScreenViewParcelable;
import java.util.List;
@@ -243,6 +245,22 @@ public class ActivityTaskManager {
}
/**
+ * Notify the server that splash screen of the given task has been copied"
+ *
+ * @param taskId Id of task to handle the material to reconstruct the splash screen view.
+ * @param parcelable Used to reconstruct the view, null means the surface is un-copyable.
+ * @hide
+ */
+ public void onSplashScreenViewCopyFinished(int taskId,
+ @Nullable SplashScreenViewParcelable parcelable) {
+ try {
+ getService().onSplashScreenViewCopyFinished(taskId, parcelable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Return the default limit on the number of recents that an app can make.
* @hide
*/
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0af74d09cfd1..0777311a12e5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -172,12 +172,15 @@ import android.view.View;
import android.view.ViewDebug;
import android.view.ViewManager;
import android.view.ViewRootImpl;
+import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.autofill.AutofillId;
import android.view.translation.TranslationSpec;
import android.webkit.WebView;
+import android.window.SplashScreen;
+import android.window.SplashScreenView;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -186,6 +189,7 @@ import com.android.internal.content.ReferrerIntent;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
import com.android.internal.os.SomeArgs;
+import com.android.internal.policy.DecorView;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.Preconditions;
@@ -228,6 +232,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@@ -286,6 +291,7 @@ 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.
*/
@@ -487,6 +493,8 @@ public final class ActivityThread extends ClientTransactionHandler {
final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
= new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>();
+ private SplashScreen.SplashScreenManagerGlobal mSplashScreenGlobal;
+
final GcIdler mGcIdler = new GcIdler();
final PurgeIdler mPurgeIdler = new PurgeIdler();
@@ -1931,6 +1939,8 @@ 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) {
@@ -1977,6 +1987,8 @@ 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);
@@ -2170,6 +2182,9 @@ 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) {
@@ -2290,11 +2305,12 @@ public final class ActivityThread extends ClientTransactionHandler {
* Creates the top level resources for the given package. Will return an existing
* Resources if one has already been created.
*/
- Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
- String[] libDirs, LoadedApk pkgInfo, Configuration overrideConfig) {
- return mResourcesManager.getResources(null, resDir, splitResDirs, overlayDirs, libDirs,
- null, overrideConfig, pkgInfo.getCompatibilityInfo(), pkgInfo.getClassLoader(),
- null);
+ Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] legacyOverlayDirs,
+ String[] overlayPaths, String[] libDirs, LoadedApk pkgInfo,
+ Configuration overrideConfig) {
+ return mResourcesManager.getResources(null, resDir, splitResDirs, legacyOverlayDirs,
+ overlayPaths, libDirs, null, overrideConfig, pkgInfo.getCompatibilityInfo(),
+ pkgInfo.getClassLoader(), null);
}
@UnsupportedAppUsage
@@ -2463,12 +2479,15 @@ public final class ActivityThread extends ClientTransactionHandler {
private static boolean isLoadedApkResourceDirsUpToDate(LoadedApk loadedApk,
ApplicationInfo appInfo) {
Resources packageResources = loadedApk.mResources;
- String[] overlayDirs = ArrayUtils.defeatNullable(loadedApk.getOverlayDirs());
- String[] resourceDirs = ArrayUtils.defeatNullable(appInfo.resourceDirs);
+ boolean resourceDirsUpToDate = Arrays.equals(
+ ArrayUtils.defeatNullable(appInfo.resourceDirs),
+ ArrayUtils.defeatNullable(loadedApk.getOverlayDirs()));
+ boolean overlayPathsUpToDate = Arrays.equals(
+ ArrayUtils.defeatNullable(appInfo.overlayPaths),
+ ArrayUtils.defeatNullable(loadedApk.getOverlayPaths()));
return (packageResources == null || packageResources.getAssets().isUpToDate())
- && overlayDirs.length == resourceDirs.length
- && ArrayUtils.containsAll(overlayDirs, resourceDirs);
+ && resourceDirsUpToDate && overlayPathsUpToDate;
}
@UnsupportedAppUsage
@@ -3952,6 +3971,106 @@ public final class ActivityThread extends ClientTransactionHandler {
}
/**
+ * Register a splash screen manager to this process.
+ */
+ public void registerSplashScreenManager(
+ @NonNull SplashScreen.SplashScreenManagerGlobal manager) {
+ synchronized (this) {
+ mSplashScreenGlobal = manager;
+ }
+ }
+
+ @Override
+ public boolean isHandleSplashScreenExit(@NonNull IBinder token) {
+ synchronized (this) {
+ return mSplashScreenGlobal != null && mSplashScreenGlobal.containsExitListener(token);
+ }
+ }
+
+ @Override
+ public void handleAttachSplashScreenView(@NonNull ActivityClientRecord r,
+ @Nullable SplashScreenView.SplashScreenViewParcelable parcelable) {
+ final DecorView decorView = (DecorView) r.window.peekDecorView();
+ if (parcelable != null && decorView != null) {
+ createSplashScreen(r, decorView, parcelable);
+ } else {
+ // shouldn't happen!
+ Slog.e(TAG, "handleAttachSplashScreenView failed, unable to attach");
+ }
+ }
+
+ private void createSplashScreen(ActivityClientRecord r, DecorView decorView,
+ SplashScreenView.SplashScreenViewParcelable parcelable) {
+ final SplashScreenView.Builder builder = new SplashScreenView.Builder(r.activity);
+ final SplashScreenView view = builder.createFromParcel(parcelable).build();
+ decorView.addView(view);
+ view.cacheRootWindow(r.window);
+ view.makeSystemUIColorsTransparent();
+ r.activity.mSplashScreenView = view;
+ view.requestLayout();
+ // Ensure splash screen view is shown before remove the splash screen window.
+ final ViewRootImpl impl = decorView.getViewRootImpl();
+ final boolean hardwareEnabled = impl != null && impl.isHardwareEnabled();
+ final AtomicBoolean notified = new AtomicBoolean();
+ if (hardwareEnabled) {
+ final Runnable frameCommit = new Runnable() {
+ @Override
+ public void run() {
+ view.post(() -> {
+ if (!notified.get()) {
+ view.getViewTreeObserver().unregisterFrameCommitCallback(this);
+ ActivityClient.getInstance().reportSplashScreenAttached(
+ r.token);
+ notified.set(true);
+ }
+ });
+ }
+ };
+ view.getViewTreeObserver().registerFrameCommitCallback(frameCommit);
+ } else {
+ final ViewTreeObserver.OnDrawListener onDrawListener =
+ new ViewTreeObserver.OnDrawListener() {
+ @Override
+ public void onDraw() {
+ view.post(() -> {
+ if (!notified.get()) {
+ view.getViewTreeObserver().removeOnDrawListener(this);
+ ActivityClient.getInstance().reportSplashScreenAttached(
+ r.token);
+ notified.set(true);
+ }
+ });
+ }
+ };
+ view.getViewTreeObserver().addOnDrawListener(onDrawListener);
+ }
+ }
+
+ @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,
+ r.activity.mSplashScreenView);
+ }
+ }
+ }
+ }
+
+ /**
+ * 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.
@@ -5171,6 +5290,11 @@ public final class ActivityThread extends ClientTransactionHandler {
r.setState(ON_DESTROY);
mLastReportedWindowingMode.remove(r.activity.getActivityToken());
schedulePurgeIdler();
+ synchronized (this) {
+ if (mSplashScreenGlobal != null) {
+ mSplashScreenGlobal.tokenDestroyed(r.token);
+ }
+ }
// updatePendingActivityConfiguration() reads from mActivities to update
// ActivityClientRecord which runs in a different thread. Protect modifications to
// mActivities to avoid race.
@@ -5686,6 +5810,13 @@ public final class ActivityThread extends ClientTransactionHandler {
final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
amOverrideConfig, contextThemeWrapperOverrideConfig);
mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId);
+ final Resources res = activity.getResources();
+ if (res.hasOverrideDisplayAdjustments()) {
+ // If fixed rotation is applied while the activity is visible (e.g. PiP), the rotated
+ // configuration of activity may be sent later than the adjustments. In this case, the
+ // adjustments need to be updated for the consistency of display info.
+ res.getDisplayAdjustments().getConfiguration().updateFrom(finalOverrideConfig);
+ }
activity.mConfigChangeFlags = 0;
activity.mCurrentConfig = new Configuration(newConfig);
diff --git a/core/java/android/app/AnrController.java b/core/java/android/app/AnrController.java
new file mode 100644
index 000000000000..cfc9d2715720
--- /dev/null
+++ b/core/java/android/app/AnrController.java
@@ -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 android.app;
+
+/**
+ * Interface to control the ANR dialog within the activity manager
+ * {@hide}
+ */
+public interface AnrController {
+ /**
+ * Returns the delay in milliseconds for an ANR dialog that is about to be shown for
+ * {@code packageName}.
+ */
+ long getAnrDelayMillis(String packageName, int uid);
+}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index b85b1861aa02..1906ee4d85d2 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -795,7 +795,8 @@ public class AppOpsManager {
// when adding one of these:
// - increment _NUM_OP
// - define an OPSTR_* constant (marked as @SystemApi)
- // - add rows to sOpToSwitch, sOpToString, sOpNames, sOpToPerms, sOpDefault
+ // - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode, sOpDisableReset,
+ // sOpRestrictions, sOpAllowSystemRestrictionBypass
// - add descriptive strings to Settings/res/values/arrays.xml
// - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app)
@@ -1174,13 +1175,19 @@ public class AppOpsManager {
*
* @hide
*/
- // TODO: Add as AppProtoEnums
- public static final int OP_RECORD_AUDIO_OUTPUT = 106;
+ public static final int OP_RECORD_AUDIO_OUTPUT = AppProtoEnums.APP_OP_RECORD_AUDIO_OUTPUT;
+
+ /**
+ * App can schedule exact alarm to perform timing based background work
+ *
+ * @hide
+ */
+ public static final int OP_SCHEDULE_EXACT_ALARM = AppProtoEnums.APP_OP_SCHEDULE_EXACT_ALARM;
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 107;
+ public static final int _NUM_OP = 108;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1553,6 +1560,13 @@ public class AppOpsManager {
*/
public static final String OPSTR_RECORD_AUDIO_OUTPUT = "android:record_audio_output";
+ /**
+ * App can schedule exact alarm to perform timing based background work.
+ *
+ * @hide
+ */
+ public static final String OPSTR_SCHEDULE_EXACT_ALARM = "android:schedule_exact_alarm";
+
/** {@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} */
@@ -1633,6 +1647,7 @@ public class AppOpsManager {
OP_LOADER_USAGE_STATS,
OP_MANAGE_ONGOING_CALLS,
OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
+ OP_SCHEDULE_EXACT_ALARM,
};
/**
@@ -1751,6 +1766,7 @@ public class AppOpsManager {
OP_MANAGE_CREDENTIALS, // MANAGE_CREDENTIALS
OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
OP_RECORD_AUDIO_OUTPUT, // RECORD_AUDIO_OUTPUT
+ OP_SCHEDULE_EXACT_ALARM, // SCHEDULE_EXACT_ALARM
};
/**
@@ -1864,6 +1880,7 @@ public class AppOpsManager {
OPSTR_MANAGE_CREDENTIALS,
OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
OPSTR_RECORD_AUDIO_OUTPUT,
+ OPSTR_SCHEDULE_EXACT_ALARM,
};
/**
@@ -1978,6 +1995,7 @@ public class AppOpsManager {
"MANAGE_CREDENTIALS",
"USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER",
"RECORD_AUDIO_OUTPUT",
+ "SCHEDULE_EXACT_ALARM",
};
/**
@@ -2093,6 +2111,7 @@ public class AppOpsManager {
null, // no permission for OP_MANAGE_CREDENTIALS
Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
null, // no permission for OP_RECORD_AUDIO_OUTPUT
+ Manifest.permission.SCHEDULE_EXACT_ALARM,
};
/**
@@ -2208,6 +2227,7 @@ public class AppOpsManager {
null, // MANAGE_CREDENTIALS
null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
null, // RECORD_AUDIO_OUTPUT
+ null, // SCHEDULE_EXACT_ALARM
};
/**
@@ -2322,6 +2342,7 @@ public class AppOpsManager {
null, // MANAGE_CREDENTIALS
null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
null, // RECORD_AUDIO_OUTPUT
+ null, // SCHEDULE_EXACT_ALARM
};
/**
@@ -2435,6 +2456,7 @@ public class AppOpsManager {
AppOpsManager.MODE_DEFAULT, // MANAGE_CREDENTIALS
AppOpsManager.MODE_DEFAULT, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO_OUTPUT
+ AppOpsManager.MODE_DEFAULT, // SCHEDULE_EXACT_ALARM
};
/**
@@ -2552,6 +2574,7 @@ public class AppOpsManager {
false, // MANAGE_CREDENTIALS
true, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
false, // RECORD_AUDIO_OUTPUT
+ false, // SCHEDULE_EXACT_ALARM
};
/**
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index 7410a1ca04c8..dfc105a2811a 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -428,6 +428,13 @@ public final class ApplicationExitInfo implements Parcelable {
*/
private IAppTraceRetriever mAppTraceRetriever;
+ /**
+ * ParcelFileDescriptor pointing to a native tombstone.
+ *
+ * @see #getTraceInputStream
+ */
+ private IParcelFileDescriptorRetriever mNativeTombstoneRetriever;
+
/** @hide */
@IntDef(prefix = { "REASON_" }, value = {
REASON_UNKNOWN,
@@ -603,22 +610,38 @@ public final class ApplicationExitInfo implements Parcelable {
* prior to the death of the process; typically it'll be available when
* the reason is {@link #REASON_ANR}, though if the process gets an ANR
* but recovers, and dies for another reason later, this trace will be included
- * in the record of {@link ApplicationExitInfo} still.
+ * in the record of {@link ApplicationExitInfo} still. Beginning with API 31,
+ * tombstone traces will be returned for
+ * {@link #REASON_CRASH_NATIVE}, with an InputStream containing a protobuf with
+ * <a href="https://android.googlesource.com/platform/system/core/+/refs/heads/master/debuggerd/proto/tombstone.proto">this schema</a>.
+ * Note thatbecause these traces are kept in a separate global circular buffer, crashes may be
+ * overwritten by newer crashes (including from other applications), so this may still return
+ * null.
*
* @return The input stream to the traces that was taken by the system
* prior to the death of the process.
*/
public @Nullable InputStream getTraceInputStream() throws IOException {
- if (mAppTraceRetriever == null) {
+ if (mAppTraceRetriever == null && mNativeTombstoneRetriever == null) {
return null;
}
+
try {
- final ParcelFileDescriptor fd = mAppTraceRetriever.getTraceFileDescriptor(
- mPackageName, mPackageUid, mPid);
- if (fd == null) {
- return null;
+ if (mNativeTombstoneRetriever != null) {
+ final ParcelFileDescriptor pfd = mNativeTombstoneRetriever.getPfd();
+ if (pfd == null) {
+ return null;
+ }
+
+ return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ } else {
+ final ParcelFileDescriptor fd = mAppTraceRetriever.getTraceFileDescriptor(
+ mPackageName, mPackageUid, mPid);
+ if (fd == null) {
+ return null;
+ }
+ return new GZIPInputStream(new ParcelFileDescriptor.AutoCloseInputStream(fd));
}
- return new GZIPInputStream(new ParcelFileDescriptor.AutoCloseInputStream(fd));
} catch (RemoteException e) {
return null;
}
@@ -849,6 +872,15 @@ public final class ApplicationExitInfo implements Parcelable {
mAppTraceRetriever = retriever;
}
+ /**
+ * @see mNativeTombstoneRetriever
+ *
+ * @hide
+ */
+ public void setNativeTombstoneRetriever(final IParcelFileDescriptorRetriever retriever) {
+ mNativeTombstoneRetriever = retriever;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -878,6 +910,12 @@ public final class ApplicationExitInfo implements Parcelable {
} else {
dest.writeInt(0);
}
+ if (mNativeTombstoneRetriever != null) {
+ dest.writeInt(1);
+ dest.writeStrongBinder(mNativeTombstoneRetriever.asBinder());
+ } else {
+ dest.writeInt(0);
+ }
}
/** @hide */
@@ -906,6 +944,7 @@ public final class ApplicationExitInfo implements Parcelable {
mState = other.mState;
mTraceFile = other.mTraceFile;
mAppTraceRetriever = other.mAppTraceRetriever;
+ mNativeTombstoneRetriever = other.mNativeTombstoneRetriever;
}
private ApplicationExitInfo(@NonNull Parcel in) {
@@ -928,6 +967,10 @@ public final class ApplicationExitInfo implements Parcelable {
if (in.readInt() == 1) {
mAppTraceRetriever = IAppTraceRetriever.Stub.asInterface(in.readStrongBinder());
}
+ if (in.readInt() == 1) {
+ mNativeTombstoneRetriever = IParcelFileDescriptorRetriever.Stub.asInterface(
+ in.readStrongBinder());
+ }
}
public @NonNull static final Creator<ApplicationExitInfo> CREATOR =
@@ -986,6 +1029,7 @@ public final class ApplicationExitInfo implements Parcelable {
sb.append(" state=").append(ArrayUtils.isEmpty(mState)
? "empty" : Integer.toString(mState.length) + " bytes");
sb.append(" trace=").append(mTraceFile);
+
return sb.toString();
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 8ac91396a6b0..062cab457ebe 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1728,7 +1728,7 @@ public class ApplicationPackageManager extends PackageManager {
final Resources r = mContext.mMainThread.getTopLevelResources(
sameUid ? app.sourceDir : app.publicSourceDir,
sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
- app.resourceDirs, app.sharedLibraryFiles,
+ app.resourceDirs, app.overlayPaths, app.sharedLibraryFiles,
mContext.mPackageInfo, configuration);
if (r != null) {
return r;
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 0e1c827145d2..cf5fd148c030 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -28,6 +28,7 @@ import android.content.res.Configuration;
import android.os.IBinder;
import android.util.MergedConfiguration;
import android.view.DisplayAdjustments.FixedRotationAdjustments;
+import android.window.SplashScreenView.SplashScreenViewParcelable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.ReferrerIntent;
@@ -158,6 +159,16 @@ public abstract class ClientTransactionHandler {
/** Request that an activity enter picture-in-picture. */
public abstract void handlePictureInPictureRequested(@NonNull ActivityClientRecord r);
+ /** Whether the activity want to handle splash screen exit animation */
+ public abstract boolean isHandleSplashScreenExit(@NonNull IBinder token);
+
+ /** Attach a splash screen window view to the top of the activity */
+ public abstract void handleAttachSplashScreenView(@NonNull ActivityClientRecord r,
+ @NonNull SplashScreenViewParcelable parcelable);
+
+ /** Hand over the splash screen window view to the activity */
+ public abstract void handOverSplashScreenView(@NonNull ActivityClientRecord r);
+
/** Perform activity launch. */
public abstract Activity handleLaunchActivity(@NonNull ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4ddeb8fbfbef..85fb543a3967 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -16,6 +16,7 @@
package android.app;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.StrictMode.vmIncorrectContextUseEnabled;
@@ -105,6 +106,7 @@ import java.lang.annotation.RetentionPolicy;
import java.nio.ByteOrder;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -2181,6 +2183,18 @@ class ContextImpl extends Context {
}
}
+ @NonNull
+ @Override
+ public int[] checkUriPermissions(@NonNull List<Uri> uris, int pid, int uid,
+ int modeFlags) {
+ try {
+ return ActivityManager.getService().checkUriPermissions(uris, pid, uid, modeFlags,
+ null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
@Override
public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) {
@@ -2207,12 +2221,30 @@ class ContextImpl extends Context {
return PackageManager.PERMISSION_DENIED;
}
+ @NonNull
+ @Override
+ public int[] checkCallingUriPermissions(@NonNull List<Uri> uris, int modeFlags) {
+ int pid = Binder.getCallingPid();
+ if (pid != Process.myPid()) {
+ return checkUriPermissions(uris, pid, Binder.getCallingUid(), modeFlags);
+ }
+ int[] res = new int[uris.size()];
+ Arrays.fill(res, PERMISSION_DENIED);
+ return res;
+ }
+
@Override
public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) {
return checkUriPermission(uri, Binder.getCallingPid(),
Binder.getCallingUid(), modeFlags);
}
+ @NonNull
+ @Override
+ public int[] checkCallingOrSelfUriPermissions(@NonNull List<Uri> uris, int modeFlags) {
+ return checkUriPermissions(uris, Binder.getCallingPid(), Binder.getCallingUid(), modeFlags);
+ }
+
@Override
public int checkUriPermission(Uri uri, String readPermission,
String writePermission, int pid, int uid, int modeFlags) {
@@ -2345,6 +2377,7 @@ class ContextImpl extends Context {
pi.getResDir(),
splitResDirs,
pi.getOverlayDirs(),
+ pi.getOverlayPaths(),
pi.getApplicationInfo().sharedLibraryFiles,
overrideDisplayId,
overrideConfig,
@@ -2442,6 +2475,7 @@ class ContextImpl extends Context {
mPackageInfo.getResDir(),
paths,
mPackageInfo.getOverlayDirs(),
+ mPackageInfo.getOverlayPaths(),
mPackageInfo.getApplicationInfo().sharedLibraryFiles,
mForceDisplayOverrideInResources ? getDisplayId() : null,
null,
@@ -2558,7 +2592,8 @@ class ContextImpl extends Context {
Resources createWindowContextResources() {
final String resDir = mPackageInfo.getResDir();
final String[] splitResDirs = mPackageInfo.getSplitResDirs();
- final String[] overlayDirs = mPackageInfo.getOverlayDirs();
+ final String[] legacyOverlayDirs = mPackageInfo.getOverlayDirs();
+ final String[] overlayPaths = mPackageInfo.getOverlayPaths();
final String[] libDirs = mPackageInfo.getApplicationInfo().sharedLibraryFiles;
final int displayId = getDisplayId();
final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
@@ -2567,7 +2602,7 @@ class ContextImpl extends Context {
final List<ResourcesLoader> loaders = mResources.getLoaders();
return mResourcesManager.createBaseTokenResources(mToken, resDir, splitResDirs,
- overlayDirs, libDirs, displayId, null /* overrideConfig */,
+ legacyOverlayDirs, overlayPaths, libDirs, displayId, null /* overrideConfig */,
compatInfo, mClassLoader, loaders);
}
@@ -2855,6 +2890,7 @@ class ContextImpl extends Context {
packageInfo.getResDir(),
splitDirs,
packageInfo.getOverlayDirs(),
+ packageInfo.getOverlayPaths(),
packageInfo.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfiguration,
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 96751dba04f4..033cffe38069 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -52,6 +52,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
private boolean mSharedElementTransitionStarted;
private Activity mActivity;
+ private boolean mIsTaskRoot;
private boolean mHasStopped;
private boolean mIsCanceled;
private ObjectAnimator mBackgroundAnimator;
@@ -252,7 +253,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
cancel();
break;
case MSG_ALLOW_RETURN_TRANSITION:
- if (!mIsCanceled) {
+ if (!mIsCanceled && !mIsTaskRoot) {
mPendingExitNames = mAllSharedElementNames;
}
break;
@@ -343,6 +344,9 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
if (mActivity == null || decorView == null) {
return;
}
+
+ mIsTaskRoot = mActivity.isTaskRoot();
+
if (!isCrossTask()) {
mActivity.overridePendingTransition(0, 0);
}
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index f7097fab6b9e..cd84e5671753 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -551,7 +551,7 @@ public class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
@Override
public boolean isReturnTransitionAllowed() {
- return !mActivity.isTopOfTask();
+ return true;
}
@Override
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 8b6570f1241f..ac1fa1ec6837 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -16,7 +16,9 @@
package android.app;
+import android.Manifest;
import android.annotation.IntDef;
+import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.UserHandleAware;
import android.content.Context;
@@ -73,8 +75,8 @@ public final class GameManager {
/**
* Returns the game mode for the given package.
*/
- // TODO(b/178111358): Add @RequiresPermission.
@UserHandleAware
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public @GameMode int getGameMode(String packageName) {
try {
return mService.getGameMode(packageName, mContext.getUserId());
@@ -86,8 +88,8 @@ public final class GameManager {
/**
* Sets the game mode for the given package.
*/
- // TODO(b/178111358): Add @RequiresPermission.
@UserHandleAware
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public void setGameMode(String packageName, @GameMode int gameMode) {
try {
mService.setGameMode(packageName, gameMode, mContext.getUserId());
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index 9d3286fa271c..573931ed228e 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -17,6 +17,7 @@
package android.app;
import android.app.ActivityManager;
+import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.Intent;
@@ -34,7 +35,7 @@ import com.android.internal.policy.IKeyguardDismissCallback;
*/
interface IActivityClientController {
oneway void activityIdle(in IBinder token, in Configuration config, in boolean stopProfiling);
- oneway void activityResumed(in IBinder token);
+ oneway void activityResumed(in IBinder token, in boolean handleSplashScreenExit);
oneway void activityTopResumedStateLost();
/**
* Notifies that the activity has completed paused. This call is not one-way because it can make
@@ -141,5 +142,9 @@ interface IActivityClientController {
* Reports that an Activity received a back key press when there were no additional activities
* on the back stack.
*/
- oneway void onBackPressedOnTaskRoot(in IBinder token);
+ oneway void onBackPressedOnTaskRoot(in IBinder activityToken,
+ in IRequestFinishCallback callback);
+
+ /** Reports that the splash screen view has attached to activity. */
+ oneway void splashScreenAttached(in IBinder token);
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index e73636fe7fd0..4ad13e1932dd 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -220,6 +220,8 @@ interface IActivityManager {
int getProcessLimit();
int checkUriPermission(in Uri uri, int pid, int uid, int mode, int userId,
in IBinder callerToken);
+ int[] checkUriPermissions(in List<Uri> uris, int pid, int uid, int mode,
+ in IBinder callerToken);
void grantUriPermission(in IApplicationThread caller, in String targetPkg, in Uri uri,
int mode, int userId);
void revokeUriPermission(in IApplicationThread caller, in String targetPkg, in Uri uri,
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 38a3e70b3742..542f754ce364 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -70,6 +70,7 @@ import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationAdapter;
import android.window.IWindowOrganizerController;
+import android.window.SplashScreenView;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.IResultReceiver;
@@ -348,4 +349,10 @@ interface IActivityTaskManager {
* Clears launch params for given packages.
*/
void clearLaunchParamsForPackages(in List<String> packageNames);
+
+ /**
+ * A splash screen view has copied.
+ */
+ void onSplashScreenViewCopyFinished(int taskId,
+ in SplashScreenView.SplashScreenViewParcelable material);
}
diff --git a/core/java/android/app/ILocalWallpaperColorConsumer.aidl b/core/java/android/app/ILocalWallpaperColorConsumer.aidl
new file mode 100644
index 000000000000..28b11ec04d96
--- /dev/null
+++ b/core/java/android/app/ILocalWallpaperColorConsumer.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+import android.app.WallpaperColors;
+import android.graphics.RectF;
+
+/**
+ * @hide
+ */
+oneway interface ILocalWallpaperColorConsumer {
+ void onColorsChanged(in RectF area, in WallpaperColors colors);
+} \ No newline at end of file
diff --git a/core/java/android/app/IParcelFileDescriptorRetriever.aidl b/core/java/android/app/IParcelFileDescriptorRetriever.aidl
new file mode 100644
index 000000000000..7e808e74bd5d
--- /dev/null
+++ b/core/java/android/app/IParcelFileDescriptorRetriever.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * An interface used to lazily provide a ParcelFileDescriptor to apps.
+ *
+ * @hide
+ */
+interface IParcelFileDescriptorRetriever {
+ /**
+ * Retrieve the ParcelFileDescriptor.
+ */
+ ParcelFileDescriptor getPfd();
+}
diff --git a/core/java/android/app/IRequestFinishCallback.aidl b/core/java/android/app/IRequestFinishCallback.aidl
new file mode 100644
index 000000000000..22c20c840e99
--- /dev/null
+++ b/core/java/android/app/IRequestFinishCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+/**
+ * This callback allows ActivityTaskManager to ask the calling Activity
+ * to finish in response to a call to onBackPressedOnTaskRoot.
+ *
+ * {@hide}
+ */
+oneway interface IRequestFinishCallback {
+ void requestFinish();
+}
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index a9e28bb635be..b1c39d39d414 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -209,4 +209,10 @@ oneway interface ITaskStackListener {
* @param taskInfo info about the task which moved
*/
void onTaskMovedToBack(in ActivityManager.RunningTaskInfo taskInfo);
+
+ /**
+ * Called when the lock task mode changes. See ActivityManager#LOCK_TASK_MODE_* and
+ * LockTaskController.
+ */
+ void onLockTaskModeChanged(int mode);
}
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index 0ba5beccbf32..f71eebdc66ec 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -62,6 +62,16 @@ interface IUiModeManager {
int getNightMode();
/**
+ * Sets the dark mode for the given application. This setting is persisted and will override the
+ * system configuration for this application.
+ * 1 - notnight mode
+ * 2 - night mode
+ * 3 - automatic mode switching
+ * @throws RemoteException
+ */
+ void setApplicationNightMode(in int mode);
+
+ /**
* Tells if UI mode is locked or not.
*/
boolean isUiModeLocked();
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 101917bc2e07..5402381b7207 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -17,9 +17,11 @@
package android.app;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.app.IWallpaperManagerCallback;
+import android.app.ILocalWallpaperColorConsumer;
import android.app.WallpaperInfo;
import android.content.ComponentName;
import android.app.WallpaperColors;
@@ -162,6 +164,18 @@ interface IWallpaperManager {
WallpaperColors getWallpaperColors(int which, int userId, int displayId);
/**
+ * @hide
+ */
+ void removeOnLocalColorsChangedListener(
+ in ILocalWallpaperColorConsumer callback, int which, int userId, int displayId);
+
+ /**
+ * @hide
+ */
+ void addOnLocalColorsChangedListener(in ILocalWallpaperColorConsumer callback,
+ in List<RectF> regions, int which, int userId, int displayId);
+
+ /**
* Register a callback to receive color updates from a display
*/
void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index c01b5a32b98b..be426aa7ed2b 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -113,7 +113,8 @@ public final class LoadedApk {
private String mAppDir;
@UnsupportedAppUsage
private String mResDir;
- private String[] mOverlayDirs;
+ private String[] mLegacyOverlayDirs;
+ private String[] mOverlayPaths;
@UnsupportedAppUsage
private String mDataDir;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -222,7 +223,8 @@ public final class LoadedApk {
mSplitAppDirs = null;
mSplitResDirs = null;
mSplitClassLoaderNames = null;
- mOverlayDirs = null;
+ mLegacyOverlayDirs = null;
+ mOverlayPaths = null;
mDataDir = null;
mDataDirFile = null;
mDeviceProtectedDataDirFile = null;
@@ -364,8 +366,8 @@ public final class LoadedApk {
}
mResources = ResourcesManager.getInstance().getResources(null, mResDir,
- splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
- null, null, getCompatibilityInfo(),
+ splitPaths, mLegacyOverlayDirs, mOverlayPaths,
+ mApplicationInfo.sharedLibraryFiles, null, null, getCompatibilityInfo(),
getClassLoader(), mApplication == null ? null
: mApplication.getResources().getLoaders());
}
@@ -379,7 +381,8 @@ public final class LoadedApk {
mApplicationInfo = aInfo;
mAppDir = aInfo.sourceDir;
mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir;
- mOverlayDirs = aInfo.resourceDirs;
+ mLegacyOverlayDirs = aInfo.resourceDirs;
+ mOverlayPaths = aInfo.overlayPaths;
mDataDir = aInfo.dataDir;
mLibDir = aInfo.nativeLibraryDir;
mDataDirFile = FileUtils.newFileOrNull(aInfo.dataDir);
@@ -1213,9 +1216,19 @@ public final class LoadedApk {
return mSplitResDirs;
}
+ /**
+ * Corresponds to {@link ApplicationInfo#resourceDirs}.
+ */
@UnsupportedAppUsage
public String[] getOverlayDirs() {
- return mOverlayDirs;
+ return mLegacyOverlayDirs;
+ }
+
+ /**
+ * Corresponds to {@link ApplicationInfo#overlayPaths}.
+ */
+ public String[] getOverlayPaths() {
+ return mOverlayPaths;
}
public String getDataDir() {
@@ -1252,8 +1265,8 @@ public final class LoadedApk {
}
mResources = ResourcesManager.getInstance().getResources(null, mResDir,
- splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
- null, null, getCompatibilityInfo(),
+ splitPaths, mLegacyOverlayDirs, mOverlayPaths,
+ mApplicationInfo.sharedLibraryFiles, null, null, getCompatibilityInfo(),
getClassLoader(), null);
}
return mResources;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 49f508d83f91..899cdb5eb572 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -22,7 +22,10 @@ import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.ColorInt;
+import android.annotation.ColorRes;
import android.annotation.DimenRes;
import android.annotation.Dimension;
import android.annotation.DrawableRes;
@@ -33,6 +36,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.StringRes;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -403,6 +407,7 @@ public class Notification implements Parcelable
STANDARD_LAYOUTS.add(R.layout.notification_template_material_conversation);
STANDARD_LAYOUTS.add(R.layout.notification_template_material_media);
STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media);
+ STANDARD_LAYOUTS.add(R.layout.notification_template_material_call);
STANDARD_LAYOUTS.add(R.layout.notification_template_header);
}
@@ -649,7 +654,7 @@ public class Notification implements Parcelable
private static final List<Class<? extends Style>> PLATFORM_STYLE_CLASSES = Arrays.asList(
BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
- MessagingStyle.class);
+ MessagingStyle.class, CallStyle.class);
/** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {FLAG_SHOW_LIGHTS, FLAG_ONGOING_EVENT,
@@ -1203,7 +1208,8 @@ public class Notification implements Parcelable
* of a {@link BigPictureStyle} notification. This will replace a
* {@link Builder#setLargeIcon(Icon) large icon} in that state if one was provided.
*/
- public static final String EXTRA_PROMOTE_PICTURE = "android.promotePicture";
+ public static final String EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED =
+ "android.showBigPictureWhenCollapsed";
/**
* {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded
@@ -1318,6 +1324,53 @@ public class Notification implements Parcelable
public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
/**
+ * {@link #extras} key: the type of call represented by the
+ * {@link android.app.Notification.CallStyle} notification. This extra is an int.
+ * @hide
+ */
+ public static final String EXTRA_CALL_TYPE = "android.callType";
+
+ /**
+ * {@link #extras} key: the person to be displayed as calling for the
+ * {@link android.app.Notification.CallStyle} notification. This extra is a {@link Person}.
+ */
+ public static final String EXTRA_CALL_PERSON = "android.callPerson";
+
+ /**
+ * {@link #extras} key: the icon to be displayed as a verification status of the caller on a
+ * {@link android.app.Notification.CallStyle} notification. This extra is an {@link Icon}.
+ */
+ public static final String EXTRA_VERIFICATION_ICON = "android.verificationIcon";
+
+ /**
+ * {@link #extras} key: the text to be displayed as a verification status of the caller on a
+ * {@link android.app.Notification.CallStyle} notification. This extra is a
+ * {@link CharSequence}.
+ */
+ public static final String EXTRA_VERIFICATION_TEXT = "android.verificationText";
+
+ /**
+ * {@link #extras} key: the intent to be sent when the users answers a
+ * {@link android.app.Notification.CallStyle} notification. This extra is a
+ * {@link PendingIntent}.
+ */
+ public static final String EXTRA_ANSWER_INTENT = "android.answerIntent";
+
+ /**
+ * {@link #extras} key: the intent to be sent when the users declines a
+ * {@link android.app.Notification.CallStyle} notification. This extra is a
+ * {@link PendingIntent}.
+ */
+ public static final String EXTRA_DECLINE_INTENT = "android.declineIntent";
+
+ /**
+ * {@link #extras} key: the intent to be sent when the users hangs up a
+ * {@link android.app.Notification.CallStyle} notification. This extra is a
+ * {@link PendingIntent}.
+ */
+ public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent";
+
+ /**
* {@link #extras} key: whether the notification should be colorized as
* supplied to {@link Builder#setColorized(boolean)}.
*/
@@ -1551,6 +1604,14 @@ public class Notification implements Parcelable
@SystemApi
public static final int SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY = 11;
+ /**
+ * {@code SemanticAction}: Mark content as a potential phishing attempt.
+ * Note that this is only for use by the notification assistant services.
+ * @hide
+ */
+ @SystemApi
+ public static final int SEMANTIC_ACTION_CONVERSATION_IS_PHISHING = 12;
+
private final Bundle mExtras;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private Icon mIcon;
@@ -2262,7 +2323,8 @@ public class Notification implements Parcelable
SEMANTIC_ACTION_THUMBS_UP,
SEMANTIC_ACTION_THUMBS_DOWN,
SEMANTIC_ACTION_CALL,
- SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY
+ SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY,
+ SEMANTIC_ACTION_CONVERSATION_IS_PHISHING
})
@Retention(RetentionPolicy.SOURCE)
public @interface SemanticAction {}
@@ -3562,7 +3624,7 @@ public class Notification implements Parcelable
private Bundle mUserExtras = new Bundle();
private Style mStyle;
@UnsupportedAppUsage
- private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
+ private ArrayList<Action> mActions = new ArrayList<>(MAX_ACTION_BUTTONS);
private ArrayList<Person> mPersonList = new ArrayList<>();
private ContrastColorUtil mColorUtil;
private boolean mIsLegacy;
@@ -4816,6 +4878,16 @@ public class Notification implements Parcelable
return this;
}
+ private void bindPhishingAlertIcon(RemoteViews contentView, StandardTemplateParams p) {
+ // TODO(b/180334837): Get buy-in on this color, or make sure to give this the
+ // accent color, while still accommodating the colorized state.
+ contentView.setDrawableTint(
+ R.id.phishing_alert,
+ false /* targetBackground */,
+ getPrimaryTextColor(p),
+ PorterDuff.Mode.SRC_ATOP);
+ }
+
private Drawable getProfileBadgeDrawable() {
if (mContext.getUserId() == UserHandle.USER_SYSTEM) {
// This user can never be a badged profile,
@@ -5217,6 +5289,7 @@ public class Notification implements Parcelable
hasTextToLeft |= bindHeaderAppName(contentView, p, true /* force */);
}
bindHeaderChronometerAndTime(contentView, p, hasTextToLeft);
+ bindPhishingAlertIcon(contentView, p);
bindProfileBadge(contentView, p);
bindAlertedIcon(contentView, p);
bindExpandButton(contentView, p);
@@ -5412,15 +5485,18 @@ public class Notification implements Parcelable
RemoteViews.MARGIN_BOTTOM, bottomMarginDimen);
}
- private static List<Notification.Action> filterOutContextualActions(
- List<Notification.Action> actions) {
- List<Notification.Action> nonContextualActions = new ArrayList<>();
- for (Notification.Action action : actions) {
+ /**
+ * Returns the actions that are not contextual.
+ */
+ private @NonNull List<Notification.Action> getNonContextualActions() {
+ if (mActions == null) return Collections.emptyList();
+ List<Notification.Action> contextualActions = new ArrayList<>();
+ for (Notification.Action action : mActions) {
if (!action.isContextual()) {
- nonContextualActions.add(action);
+ contextualActions.add(action);
}
}
- return nonContextualActions;
+ return contextualActions;
}
private RemoteViews applyStandardTemplateWithActions(int layoutId,
@@ -5431,9 +5507,9 @@ public class Notification implements Parcelable
boolean validRemoteInput = false;
- // In the UI contextual actions appear separately from the standard actions, so we
+ // In the UI, contextual actions appear separately from the standard actions, so we
// filter them out here.
- List<Notification.Action> nonContextualActions = filterOutContextualActions(mActions);
+ List<Notification.Action> nonContextualActions = getNonContextualActions();
int N = nonContextualActions.size();
boolean emphazisedMode = mN.fullScreenIntent != null;
@@ -5876,11 +5952,11 @@ public class Notification implements Parcelable
return summary;
}
- private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
+ private RemoteViews generateActionButton(Action action, boolean emphasizedMode,
StandardTemplateParams p) {
final boolean tombstone = (action.actionIntent == null);
RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
- emphazisedMode ? getEmphasizedActionLayoutResource()
+ emphasizedMode ? getEmphasizedActionLayoutResource()
: tombstone ? getActionTombstoneLayoutResource()
: getActionLayoutResource());
if (!tombstone) {
@@ -5890,43 +5966,42 @@ public class Notification implements Parcelable
if (action.mRemoteInputs != null) {
button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
}
- if (emphazisedMode) {
+ if (emphasizedMode) {
// change the background bgColor
CharSequence title = action.title;
- ColorStateList[] outResultColor = null;
+ ColorStateList[] outResultColor = new ColorStateList[1];
int background = resolveBackgroundColor(p);
if (isLegacy()) {
title = ContrastColorUtil.clearColorSpans(title);
} else {
- outResultColor = new ColorStateList[1];
title = ensureColorSpanContrast(title, background, outResultColor);
}
button.setTextViewText(R.id.action0, processTextSpans(title));
- setTextViewColorPrimary(button, R.id.action0, p);
- int rippleColor;
- boolean hasColorOverride = outResultColor != null && outResultColor[0] != null;
+ int textColor = getPrimaryTextColor(p);
+ boolean hasColorOverride = outResultColor[0] != null;
if (hasColorOverride) {
// There's a span spanning the full text, let's take it and use it as the
// background color
background = outResultColor[0].getDefaultColor();
- int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
+ textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
background, mInNightMode);
- button.setTextColor(R.id.action0, textColor);
- rippleColor = textColor;
} else if (getRawColor(p) != COLOR_DEFAULT && !isColorized(p)
&& mTintActionButtons && !mInNightMode) {
- rippleColor = resolveContrastColor(p);
- button.setTextColor(R.id.action0, rippleColor);
- } else {
- rippleColor = getPrimaryTextColor(p);
+ textColor = resolveContrastColor(p);
}
+ button.setTextColor(R.id.action0, textColor);
// We only want about 20% alpha for the ripple
- rippleColor = (rippleColor & 0x00ffffff) | 0x33000000;
+ final int rippleColor = (textColor & 0x00ffffff) | 0x33000000;
button.setColorStateList(R.id.action0, "setRippleColor",
ColorStateList.valueOf(rippleColor));
button.setColorStateList(R.id.action0, "setButtonBackground",
ColorStateList.valueOf(background));
button.setBoolean(R.id.action0, "setHasStroke", !hasColorOverride);
+ if (p.mAllowActionIcons) {
+ button.setImageViewIcon(R.id.action0, action.getIcon());
+ boolean priority = action.getExtras().getBoolean(CallStyle.KEY_ACTION_PRIORITY);
+ button.setBoolean(R.id.action0, "setWrapModePriority", priority);
+ }
} else {
button.setTextViewText(R.id.action0, processTextSpans(
processLegacyText(action.title)));
@@ -5936,8 +6011,12 @@ public class Notification implements Parcelable
button.setTextColor(R.id.action0, resolveContrastColor(p));
}
}
- button.setIntTag(R.id.action0, R.id.notification_action_index_tag,
- mActions.indexOf(action));
+ // CallStyle notifications add action buttons which don't actually exist in mActions,
+ // so we have to omit the index in that case.
+ int actionIndex = mActions.indexOf(action);
+ if (actionIndex != -1) {
+ button.setIntTag(R.id.action0, R.id.notification_action_index_tag, actionIndex);
+ }
return button;
}
@@ -6371,6 +6450,10 @@ 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;
}
@@ -6684,6 +6767,31 @@ public class Notification implements Parcelable
}
/**
+ * @return true if the notification has image
+ */
+ public boolean hasImage() {
+ if (MessagingStyle.class.equals(getNotificationStyle()) && extras != null) {
+ final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
+ if (!ArrayUtils.isEmpty(messages)) {
+ for (MessagingStyle.Message m : MessagingStyle.Message
+ .getMessagesFromBundleArray(messages)) {
+ if (m.getDataUri() != null
+ && m.getDataMimeType() != null
+ && m.getDataMimeType().startsWith("image/")) {
+ return true;
+ }
+ }
+ }
+ } else if (hasLargeIcon()) {
+ return true;
+ } else if (extras.containsKey(EXTRA_BACKGROUND_IMAGE_URI)) {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
* @removed
*/
@SystemApi
@@ -7000,7 +7108,7 @@ public class Notification implements Parcelable
private Icon mBigLargeIcon;
private boolean mBigLargeIconSet = false;
private CharSequence mPictureContentDescription;
- private boolean mPromotePicture;
+ private boolean mShowBigPictureWhenCollapsed;
public BigPictureStyle() {
}
@@ -7065,7 +7173,7 @@ public class Notification implements Parcelable
*/
@NonNull
public BigPictureStyle showBigPictureWhenCollapsed(boolean show) {
- mPromotePicture = show;
+ mShowBigPictureWhenCollapsed = show;
return this;
}
@@ -7136,7 +7244,7 @@ public class Notification implements Parcelable
*/
@Override
public RemoteViews makeContentView(boolean increasedHeight) {
- if (mPicture == null || !mPromotePicture) {
+ if (mPicture == null || !mShowBigPictureWhenCollapsed) {
return super.makeContentView(increasedHeight);
}
@@ -7166,7 +7274,7 @@ public class Notification implements Parcelable
*/
@Override
public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
- if (mPicture == null || !mPromotePicture) {
+ if (mPicture == null || !mShowBigPictureWhenCollapsed) {
return super.makeHeadsUpContentView(increasedHeight);
}
@@ -7250,7 +7358,7 @@ public class Notification implements Parcelable
extras.putCharSequence(EXTRA_PICTURE_CONTENT_DESCRIPTION,
mPictureContentDescription);
}
- extras.putBoolean(EXTRA_PROMOTE_PICTURE, mPromotePicture);
+ extras.putBoolean(EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED, mShowBigPictureWhenCollapsed);
extras.putParcelable(EXTRA_PICTURE, mPicture);
}
@@ -7271,7 +7379,7 @@ public class Notification implements Parcelable
extras.getCharSequence(EXTRA_PICTURE_CONTENT_DESCRIPTION);
}
- mPromotePicture = extras.getBoolean(EXTRA_PROMOTE_PICTURE);
+ mShowBigPictureWhenCollapsed = extras.getBoolean(EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED);
mPicture = extras.getParcelable(EXTRA_PICTURE);
}
@@ -8039,6 +8147,10 @@ public class Notification implements Parcelable
: mBuilder.getMessagingLayoutResource(),
p,
bindResult);
+ if (isConversationLayout) {
+ mBuilder.setTextViewColorPrimary(contentView, R.id.conversation_text, p);
+ mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
+ }
addExtras(mBuilder.mN.extras);
if (!isConversationLayout) {
@@ -8925,6 +9037,441 @@ public class Notification implements Parcelable
}
}
+
+
+ /**
+ * Helper class for generating large-format notifications that include a large image attachment.
+ *
+ * Here's how you'd set the <code>CallStyle</code> on a notification:
+ * <pre class="prettyprint">
+ * Notification notif = new Notification.Builder(mContext)
+ * .setSmallIcon(R.drawable.new_post)
+ * .setStyle(Notification.CallStyle.forIncomingCall(caller, declineIntent, answerIntent))
+ * .build();
+ * </pre>
+ */
+ public static class CallStyle extends Style {
+ private static final int CALL_TYPE_INCOMING = 1;
+ private static final int CALL_TYPE_ONGOING = 2;
+ private static final int CALL_TYPE_SCREENING = 3;
+
+ /**
+ * This is a key used privately on the action.extras to give spacing priority
+ * to the required call actions
+ */
+ private static final String KEY_ACTION_PRIORITY = "key_action_priority";
+
+ private int mCallType;
+ private Person mPerson;
+ private PendingIntent mAnswerIntent;
+ private PendingIntent mDeclineIntent;
+ private PendingIntent mHangUpIntent;
+ private Icon mVerificationIcon;
+ private CharSequence mVerificationText;
+
+ CallStyle() {
+ }
+
+ /**
+ * Create a CallStyle for an incoming call.
+ * This notification will have a decline and an answer action, will allow a single
+ * custom {@link Builder#addAction(Action) action}, and will have a default
+ * {@link Builder#setContentText(CharSequence) content text} for an incoming call.
+ *
+ * @param person The person displayed as the caller.
+ * The person also needs to have a non-empty name associated with it.
+ * @param declineIntent The intent to be sent when the user taps the decline action
+ * @param answerIntent The intent to be sent when the user taps the answer action
+ */
+ @NonNull
+ public static CallStyle forIncomingCall(@NonNull Person person,
+ @NonNull PendingIntent declineIntent, @NonNull PendingIntent answerIntent) {
+ return new CallStyle(CALL_TYPE_INCOMING, person,
+ null /* hangUpIntent */,
+ requireNonNull(declineIntent, "declineIntent is required"),
+ requireNonNull(answerIntent, "answerIntent is required")
+ );
+ }
+
+ /**
+ * Create a CallStyle for an ongoing call.
+ * This notification will have a hang up action, will allow up to two
+ * custom {@link Builder#addAction(Action) actions}, and will have a default
+ * {@link Builder#setContentText(CharSequence) content text} for an ongoing call.
+ *
+ * @param person The person displayed as being on the other end of the call.
+ * The person also needs to have a non-empty name associated with it.
+ * @param hangUpIntent The intent to be sent when the user taps the hang up action
+ */
+ @NonNull
+ public static CallStyle forOngoingCall(@NonNull Person person,
+ @NonNull PendingIntent hangUpIntent) {
+ return new CallStyle(CALL_TYPE_ONGOING, person,
+ requireNonNull(hangUpIntent, "hangUpIntent is required"),
+ null /* declineIntent */,
+ null /* answerIntent */
+ );
+ }
+
+ /**
+ * Create a CallStyle for a call that is being screened.
+ * This notification will have a hang up and an answer action, will allow a single
+ * custom {@link Builder#addAction(Action) action}, and will have a default
+ * {@link Builder#setContentText(CharSequence) content text} for a call that is being
+ * screened.
+ *
+ * @param person The person displayed as the caller.
+ * The person also needs to have a non-empty name associated with it.
+ * @param hangUpIntent The intent to be sent when the user taps the hang up action
+ * @param answerIntent The intent to be sent when the user taps the answer action
+ */
+ @NonNull
+ public static CallStyle forScreeningCall(@NonNull Person person,
+ @NonNull PendingIntent hangUpIntent, @NonNull PendingIntent answerIntent) {
+ return new CallStyle(CALL_TYPE_SCREENING, person,
+ requireNonNull(hangUpIntent, "hangUpIntent is required"),
+ null /* declineIntent */,
+ requireNonNull(answerIntent, "answerIntent is required")
+ );
+ }
+
+ /**
+ * @param person The person displayed for the incoming call.
+ * The user also needs to have a non-empty name associated with it.
+ * @param hangUpIntent The intent to be sent when the user taps the hang up action
+ * @param declineIntent The intent to be sent when the user taps the decline action
+ * @param answerIntent The intent to be sent when the user taps the answer action
+ */
+ private CallStyle(int callType, @NonNull Person person,
+ @Nullable PendingIntent hangUpIntent, @Nullable PendingIntent declineIntent,
+ @Nullable PendingIntent answerIntent) {
+ if (person == null || TextUtils.isEmpty(person.getName())) {
+ throw new IllegalArgumentException("person must have a non-empty a name");
+ }
+ mCallType = callType;
+ mPerson = person;
+ mAnswerIntent = answerIntent;
+ mDeclineIntent = declineIntent;
+ mHangUpIntent = hangUpIntent;
+ }
+
+ /**
+ * Optional icon to be displayed with {@link #setVerificationText(CharSequence) text}
+ * as a verification status of the caller.
+ */
+ @NonNull
+ public CallStyle setVerificationIcon(@Nullable Icon verificationIcon) {
+ mVerificationIcon = verificationIcon;
+ return this;
+ }
+
+ /**
+ * Optional text to be displayed with an {@link #setVerificationIcon(Icon) icon}
+ * as a verification status of the caller.
+ */
+ @NonNull
+ public CallStyle setVerificationText(@Nullable CharSequence verificationText) {
+ mVerificationText = safeCharSequence(verificationText);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean displayCustomViewInline() {
+ // This is a lie; True is returned to make sure that the custom view is not used
+ // instead of the template, but it will not actually be included.
+ return true;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void purgeResources() {
+ super.purgeResources();
+ if (mVerificationIcon != null) {
+ mVerificationIcon.convertToAshmem();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void reduceImageSizes(Context context) {
+ super.reduceImageSizes(context);
+ if (mVerificationIcon != null) {
+ int rightIconSize = context.getResources().getDimensionPixelSize(
+ ActivityManager.isLowRamDeviceStatic()
+ ? R.dimen.notification_right_icon_size_low_ram
+ : R.dimen.notification_right_icon_size);
+ mVerificationIcon.scaleDownIfNecessary(rightIconSize, rightIconSize);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public RemoteViews makeContentView(boolean increasedHeight) {
+ return makeCallLayout();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
+ return makeCallLayout();
+ }
+
+ /**
+ * @hide
+ */
+ public RemoteViews makeBigContentView() {
+ return makeCallLayout();
+ }
+
+ @NonNull
+ private Action makeNegativeAction() {
+ if (mDeclineIntent == null) {
+ return makeAction(R.drawable.ic_call_decline,
+ R.string.call_notification_hang_up_action,
+ R.color.call_notification_decline_color, mHangUpIntent);
+ } else {
+ return makeAction(R.drawable.ic_call_decline,
+ R.string.call_notification_decline_action,
+ R.color.call_notification_decline_color, mDeclineIntent);
+ }
+ }
+
+ @Nullable
+ private Action makeAnswerAction() {
+ return mAnswerIntent == null ? null : makeAction(R.drawable.ic_call_answer,
+ R.string.call_notification_answer_action,
+ R.color.call_notification_answer_color, mAnswerIntent);
+ }
+
+ @NonNull
+ private Action makeAction(@DrawableRes int icon, @StringRes int title,
+ @ColorRes int colorRes, PendingIntent intent) {
+ Action action = new Action.Builder(Icon.createWithResource("", icon),
+ new SpannableStringBuilder().append(mBuilder.mContext.getString(title),
+ new ForegroundColorSpan(mBuilder.mContext.getColor(colorRes)),
+ SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE),
+ intent).build();
+ action.getExtras().putBoolean(KEY_ACTION_PRIORITY, true);
+ return action;
+ }
+
+ private ArrayList<Action> makeActionsList() {
+ final Action negativeAction = makeNegativeAction();
+ final Action answerAction = makeAnswerAction();
+
+ 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;
+ }
+ actions.add(action);
+ }
+ actions.add(lastAction);
+ return actions;
+ }
+
+ private RemoteViews makeCallLayout() {
+ Bundle extras = mBuilder.mN.extras;
+ CharSequence text = mBuilder.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
+ if (text == null) {
+ text = getDefaultText();
+ }
+
+ // Bind standard template
+ StandardTemplateParams p = mBuilder.mParams.reset()
+ .viewType(StandardTemplateParams.VIEW_TYPE_BIG)
+ .allowActionIcons(true)
+ .hideLargeIcon(true)
+ .text(text)
+ .summaryText(mBuilder.processLegacyText(mVerificationText));
+ // TODO(b/179178086): hide the snooze button
+ RemoteViews contentView = mBuilder.applyStandardTemplate(
+ mBuilder.getCallLayoutResource(), p, null /* result */);
+
+ // Bind actions.
+ mBuilder.resetStandardTemplateWithActions(contentView);
+ bindCallActions(contentView, p);
+
+ // 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);
+ bindCallerVerification(contentView, p);
+
+ // Bind some custom CallLayout properties
+ contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
+ mBuilder.isColorized(p)
+ ? mBuilder.getPrimaryTextColor(p)
+ : mBuilder.resolveContrastColor(p));
+ contentView.setInt(R.id.status_bar_latest_event_content,
+ "setNotificationBackgroundColor", mBuilder.resolveBackgroundColor(p));
+ contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
+ mBuilder.mN.mLargeIcon);
+ contentView.setBundle(R.id.status_bar_latest_event_content, "setData",
+ mBuilder.mN.extras);
+
+ 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()) {
+ 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) {
+ if (mVerificationIcon != null) {
+ contentView.setImageViewIcon(R.id.verification_icon, mVerificationIcon);
+ contentView.setDrawableTint(R.id.verification_icon, false /* targetBackground */,
+ mBuilder.getSecondaryTextColor(p), PorterDuff.Mode.SRC_ATOP);
+ contentView.setViewVisibility(R.id.verification_icon, View.VISIBLE);
+ } else {
+ contentView.setViewVisibility(R.id.verification_icon, View.GONE);
+ }
+ if (!TextUtils.isEmpty(mVerificationText)) {
+ contentView.setTextViewText(R.id.verification_text, mVerificationText);
+ mBuilder.setTextViewColorSecondary(contentView, R.id.verification_text, p);
+ contentView.setViewVisibility(R.id.verification_text, View.VISIBLE);
+ } else {
+ contentView.setViewVisibility(R.id.verification_text, View.GONE);
+ }
+ }
+
+ @Nullable
+ private String getDefaultText() {
+ switch (mCallType) {
+ case CALL_TYPE_INCOMING:
+ return mBuilder.mContext.getString(R.string.call_notification_incoming_text);
+ case CALL_TYPE_ONGOING:
+ return mBuilder.mContext.getString(R.string.call_notification_ongoing_text);
+ case CALL_TYPE_SCREENING:
+ return mBuilder.mContext.getString(R.string.call_notification_screening_text);
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public void addExtras(Bundle extras) {
+ super.addExtras(extras);
+ extras.putInt(EXTRA_CALL_TYPE, mCallType);
+ extras.putParcelable(EXTRA_CALL_PERSON, mPerson);
+ if (mVerificationIcon != null) {
+ extras.putParcelable(EXTRA_VERIFICATION_ICON, mVerificationIcon);
+ }
+ if (mVerificationText != null) {
+ extras.putCharSequence(EXTRA_VERIFICATION_TEXT, mVerificationText);
+ }
+ if (mAnswerIntent != null) {
+ extras.putParcelable(EXTRA_ANSWER_INTENT, mAnswerIntent);
+ }
+ if (mDeclineIntent != null) {
+ extras.putParcelable(EXTRA_DECLINE_INTENT, mDeclineIntent);
+ }
+ if (mHangUpIntent != null) {
+ extras.putParcelable(EXTRA_HANG_UP_INTENT, mHangUpIntent);
+ }
+ fixTitleAndTextExtras(extras);
+ }
+
+ private void fixTitleAndTextExtras(Bundle extras) {
+ CharSequence sender = mPerson != null ? mPerson.getName() : null;
+ if (sender != null) {
+ extras.putCharSequence(EXTRA_TITLE, sender);
+ }
+ if (extras.getCharSequence(EXTRA_TEXT) == null) {
+ extras.putCharSequence(EXTRA_TEXT, getDefaultText());
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ protected void restoreFromExtras(Bundle extras) {
+ super.restoreFromExtras(extras);
+ mCallType = extras.getInt(EXTRA_CALL_TYPE);
+ mPerson = extras.getParcelable(EXTRA_CALL_PERSON);
+ mVerificationIcon = extras.getParcelable(EXTRA_VERIFICATION_ICON);
+ mVerificationText = extras.getCharSequence(EXTRA_VERIFICATION_TEXT);
+ mAnswerIntent = extras.getParcelable(EXTRA_ANSWER_INTENT);
+ mDeclineIntent = extras.getParcelable(EXTRA_DECLINE_INTENT);
+ mHangUpIntent = extras.getParcelable(EXTRA_HANG_UP_INTENT);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean hasSummaryInHeader() {
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean areNotificationsVisiblyDifferent(Style other) {
+ if (other == null || getClass() != other.getClass()) {
+ return true;
+ }
+ CallStyle otherS = (CallStyle) other;
+ return !Objects.equals(mCallType, otherS.mCallType)
+ || !Objects.equals(mPerson, otherS.mPerson)
+ || !Objects.equals(mVerificationText, otherS.mVerificationText);
+ }
+ }
+
/**
* Notification style for custom views that are decorated by the system
*
@@ -9474,6 +10021,15 @@ public class Notification implements Parcelable
* <p>The shortcut activity will be used when the bubble is expanded. This will display
* the shortcut activity in a floating window over the existing foreground activity.</p>
*
+ * <p>When the shortcut is displayed in a bubble, there will be an intent
+ * extra set on the activity, {@link Intent#EXTRA_IS_BUBBLED}
+ * with {@code true}. You may check this in the onCreate of your activity via:
+ *
+ * <pre class="prettyprint">
+ * boolean isBubbled = getIntent().getBooleanExtra(Intent.EXTRA_IS_BUBBLED, false);
+ * </pre>
+ * </p>
+ *
* <p>If the shortcut has not been published when the bubble notification is sent,
* no bubble will be produced. If the shortcut is deleted while the bubble is active,
* the bubble will be removed.</p>
@@ -9502,6 +10058,15 @@ public class Notification implements Parcelable
* app content in a floating window over the existing foreground activity. The intent
* should point to a resizable activity. </p>
*
+ * <p>When the activity is displayed in a bubble, there will be an intent
+ * extra set on the activity, {@link Intent#EXTRA_IS_BUBBLED}
+ * with {@code true}. You may check this in the onCreate of your activity via:
+ *
+ * <pre class="prettyprint">
+ * boolean isBubbled = getIntent().getBooleanExtra(Intent.EXTRA_IS_BUBBLED, false);
+ * </pre>
+ * </p>
+ *
* @throws NullPointerException if intent is null.
* @throws NullPointerException if icon is null.
*/
@@ -11376,6 +11941,7 @@ public class Notification implements Parcelable
boolean mHideActions;
boolean mHideProgress;
boolean mPromotePicture;
+ boolean mAllowActionIcons;
CharSequence title;
CharSequence text;
CharSequence headerTextSecondary;
@@ -11392,6 +11958,7 @@ public class Notification implements Parcelable
mHideActions = false;
mHideProgress = false;
mPromotePicture = false;
+ mAllowActionIcons = false;
title = null;
text = null;
summaryText = null;
@@ -11431,6 +11998,11 @@ public class Notification implements Parcelable
return this;
}
+ final StandardTemplateParams allowActionIcons(boolean allowActionIcons) {
+ this.mAllowActionIcons = allowActionIcons;
+ return this;
+ }
+
final StandardTemplateParams promotePicture(boolean promotePicture) {
this.mPromotePicture = promotePicture;
return this;
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 323af8211d76..685c222539c6 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -80,6 +80,48 @@ public final class NotificationChannel implements Parcelable {
public static final String PLACEHOLDER_CONVERSATION_ID = ":placeholder_id";
/**
+ * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
+ * that have to do with editing sound, like a tone picker
+ * ({@link #setSound(Uri, AudioAttributes)}).
+ */
+ public static final String EDIT_SOUND = "sound";
+ /**
+ * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
+ * that have to do with editing vibration ({@link #enableVibration(boolean)},
+ * {@link #setVibrationPattern(long[])}).
+ */
+ public static final String EDIT_VIBRATION = "vibration";
+ /**
+ * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
+ * that have to do with editing importance ({@link #setImportance(int)}) and/or conversation
+ * priority.
+ */
+ public static final String EDIT_IMPORTANCE = "importance";
+ /**
+ * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
+ * that have to do with editing behavior on devices that are locked or have a turned off
+ * display ({@link #setLockscreenVisibility(int)}, {@link #enableLights(boolean)},
+ * {@link #setLightColor(int)}).
+ */
+ public static final String EDIT_LOCKED_DEVICE = "locked";
+ /**
+ * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
+ * that have to do with editing do not disturb bypass {(@link #setBypassDnd(boolean)}) .
+ */
+ public static final String EDIT_ZEN = "dnd";
+ /**
+ * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
+ * that have to do with editing conversation settings (demoting or restoring a channel to
+ * be a Conversation, changing bubble behavior, or setting the priority of a conversation).
+ */
+ public static final String EDIT_CONVERSATION = "convo";
+ /**
+ * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields
+ * that have to do with editing launcher behavior (showing badges)}.
+ */
+ public static final String EDIT_LAUNCHER = "launcher";
+
+ /**
* The maximum length for text fields in a NotificationChannel. Fields will be truncated at this
* limit.
*/
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index e6aa7a77357c..1ff64dbe6d2e 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -59,7 +59,7 @@ per-file IInstantAppResolver.aidl = file:/services/core/java/com/android/server/
per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS
# ResourcesManager
-per-file ResourcesManager.java = rtmitchell@google.com, toddke@google.com
+per-file ResourcesManager.java = file:RESOURCES_OWNERS
# VoiceInteraction
per-file *VoiceInteract* = file:/core/java/android/service/voice/OWNERS
diff --git a/core/java/android/app/RESOURCES_OWNERS b/core/java/android/app/RESOURCES_OWNERS
new file mode 100644
index 000000000000..21c39a8828ad
--- /dev/null
+++ b/core/java/android/app/RESOURCES_OWNERS
@@ -0,0 +1,2 @@
+rtmitchell@google.com
+toddke@google.com
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 772833cc6d2d..ac8d3a261ac6 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -39,6 +39,7 @@ import android.os.IBinder;
import android.os.Process;
import android.os.Trace;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
@@ -60,6 +61,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.WeakHashMap;
@@ -174,8 +176,8 @@ public class ResourcesManager {
* based on.
*
* @see #activityResources
- * @see #getResources(IBinder, String, String[], String[], String[], Integer, Configuration,
- * CompatibilityInfo, ClassLoader, List)
+ * @see #getResources(IBinder, String, String[], String[], String[], String[], Integer,
+ * Configuration, CompatibilityInfo, ClassLoader, List)
*/
public final Configuration overrideConfig = new Configuration();
@@ -482,8 +484,8 @@ public class ResourcesManager {
}
}
- if (key.mOverlayDirs != null) {
- for (final String idmapPath : key.mOverlayDirs) {
+ if (key.mOverlayPaths != null) {
+ for (final String idmapPath : key.mOverlayPaths) {
apkKeys.add(new ApkKey(idmapPath, false /*sharedLib*/, true /*overlay*/));
}
}
@@ -783,14 +785,16 @@ public class ResourcesManager {
/**
* Creates base resources for a binder token. Calls to
- * {@link #getResources(IBinder, String, String[], String[], String[], Integer, Configuration,
- * CompatibilityInfo, ClassLoader, List)} with the same binder token will have their override
- * configurations merged with the one specified here.
+ *
+ * {@link #getResources(IBinder, String, String[], String[], String[], String[], Integer,
+ * Configuration, CompatibilityInfo, ClassLoader, List)} with the same binder token will have
+ * their override configurations merged with the one specified here.
*
* @param token Represents an {@link Activity} or {@link WindowContext}.
* @param resDir The base resource path. Can be null (only framework resources will be loaded).
* @param splitResDirs An array of split resource paths. Can be null.
- * @param overlayDirs An array of overlay paths. Can be null.
+ * @param legacyOverlayDirs An array of overlay APK paths. Can be null.
+ * @param overlayPaths An array of overlay APK and non-APK paths. Can be null.
* @param libDirs An array of resource library paths. Can be null.
* @param displayId The ID of the display for which to create the resources.
* @param overrideConfig The configuration to apply on top of the base configuration. Can be
@@ -804,7 +808,8 @@ public class ResourcesManager {
public @Nullable Resources createBaseTokenResources(@NonNull IBinder token,
@Nullable String resDir,
@Nullable String[] splitResDirs,
- @Nullable String[] overlayDirs,
+ @Nullable String[] legacyOverlayDirs,
+ @Nullable String[] overlayPaths,
@Nullable String[] libDirs,
int displayId,
@Nullable Configuration overrideConfig,
@@ -817,7 +822,7 @@ public class ResourcesManager {
final ResourcesKey key = new ResourcesKey(
resDir,
splitResDirs,
- overlayDirs,
+ combinedOverlayPaths(legacyOverlayDirs, overlayPaths),
libDirs,
displayId,
overrideConfig,
@@ -1043,7 +1048,8 @@ public class ResourcesManager {
* @param activityToken Represents an Activity. If null, global resources are assumed.
* @param resDir The base resource path. Can be null (only framework resources will be loaded).
* @param splitResDirs An array of split resource paths. Can be null.
- * @param overlayDirs An array of overlay paths. Can be null.
+ * @param legacyOverlayDirs An array of overlay APK paths. Can be null.
+ * @param overlayPaths An array of overlay APK and non-APK paths. Can be null.
* @param libDirs An array of resource library paths. Can be null.
* @param overrideDisplayId The ID of the display for which the returned Resources should be
* based. This will cause display-based configuration properties to override those of the base
@@ -1063,7 +1069,8 @@ public class ResourcesManager {
@Nullable IBinder activityToken,
@Nullable String resDir,
@Nullable String[] splitResDirs,
- @Nullable String[] overlayDirs,
+ @Nullable String[] legacyOverlayDirs,
+ @Nullable String[] overlayPaths,
@Nullable String[] libDirs,
@Nullable Integer overrideDisplayId,
@Nullable Configuration overrideConfig,
@@ -1075,7 +1082,7 @@ public class ResourcesManager {
final ResourcesKey key = new ResourcesKey(
resDir,
splitResDirs,
- overlayDirs,
+ combinedOverlayPaths(legacyOverlayDirs, overlayPaths),
libDirs,
overrideDisplayId != null ? overrideDisplayId : INVALID_DISPLAY,
overrideConfig,
@@ -1250,7 +1257,7 @@ public class ResourcesManager {
// Create the new ResourcesKey with the rebased override config.
final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
- oldKey.mSplitResDirs, oldKey.mOverlayDirs, oldKey.mLibDirs,
+ oldKey.mSplitResDirs, oldKey.mOverlayPaths, oldKey.mLibDirs,
displayId, rebasedOverrideConfig, oldKey.mCompatInfo, oldKey.mLoaders);
if (DEBUG) {
@@ -1393,7 +1400,7 @@ public class ResourcesManager {
updatedResourceKeys.put(impl, new ResourcesKey(
key.mResDir,
key.mSplitResDirs,
- key.mOverlayDirs,
+ key.mOverlayPaths,
newLibAssets,
key.mDisplayId,
key.mOverrideConfiguration,
@@ -1423,7 +1430,8 @@ public class ResourcesManager {
// ApplicationInfo is mutable, so clone the arrays to prevent outside modification
String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs);
- String[] copiedResourceDirs = ArrayUtils.cloneOrNull(appInfo.resourceDirs);
+ String[] copiedResourceDirs = combinedOverlayPaths(appInfo.resourceDirs,
+ appInfo.overlayPaths);
final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
final int implCount = mResourceImpls.size();
@@ -1458,6 +1466,39 @@ public class ResourcesManager {
}
}
+ /**
+ * Creates an array with the contents of {@param overlayPaths} and the unique elements of
+ * {@param resourceDirs}.
+ *
+ * {@link ApplicationInfo#resourceDirs} only contains paths of overlays APKs.
+ * {@link ApplicationInfo#overlayPaths} was created to contain paths of overlay of varying file
+ * formats. It also contains the contents of {@code resourceDirs} because the order of loaded
+ * overlays matter. In case {@code resourceDirs} contains overlay APK paths that are not present
+ * in overlayPaths (perhaps an app inserted an additional overlay path into a
+ * {@code resourceDirs}), this method is used to combine the contents of {@code resourceDirs}
+ * that do not exist in {@code overlayPaths}} and {@code overlayPaths}}.
+ */
+ @Nullable
+ private static String[] combinedOverlayPaths(@Nullable String[] resourceDirs,
+ @Nullable String[] overlayPaths) {
+ if (resourceDirs == null) {
+ return ArrayUtils.cloneOrNull(overlayPaths);
+ } else if(overlayPaths == null) {
+ return ArrayUtils.cloneOrNull(resourceDirs);
+ } else {
+ final ArrayList<String> paths = new ArrayList<>();
+ for (final String path : overlayPaths) {
+ paths.add(path);
+ }
+ for (final String path : resourceDirs) {
+ if (!paths.contains(path)) {
+ paths.add(path);
+ }
+ }
+ return paths.toArray(new String[0]);
+ }
+ }
+
private void redirectResourcesToNewImplLocked(
@NonNull final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys) {
// Bail early if there is no work to do.
@@ -1559,7 +1600,7 @@ public class ResourcesManager {
final ResourcesKey newKey = new ResourcesKey(
oldKey.mResDir,
oldKey.mSplitResDirs,
- oldKey.mOverlayDirs,
+ oldKey.mOverlayPaths,
oldKey.mLibDirs,
oldKey.mDisplayId,
oldKey.mOverrideConfiguration,
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 7d2bc1a5f8e2..afcf63b0f1e2 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -27,6 +27,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -286,6 +287,23 @@ public class StatusBarManager {
}
/**
+ * Simulate notification feedback for testing
+ *
+ * @hide
+ */
+ @TestApi
+ public void sendNotificationFeedback(@Nullable String key, @Nullable Bundle feedback) {
+ try {
+ final IStatusBarService svc = getService();
+ if (svc != null) {
+ svc.onNotificationFeedbackReceived(key, feedback);
+ }
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Expand the notifications panel.
*
* @hide
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 7404e53bd8b3..ffaaa578cdd2 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -128,11 +128,13 @@ import android.net.EthernetManager;
import android.net.IEthernetManager;
import android.net.IIpSecService;
import android.net.INetworkPolicyManager;
+import android.net.IVpnManager;
import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
import android.net.NetworkWatchlistManager;
import android.net.TetheringManager;
+import android.net.VpnManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
import android.net.nsd.INsdManager;
@@ -167,9 +169,11 @@ import android.os.StatsFrameworkInitializer;
import android.os.SystemConfigManager;
import android.os.SystemUpdateManager;
import android.os.SystemVibrator;
+import android.os.SystemVibratorManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
+import android.os.VibratorManager;
import android.os.health.SystemHealthManager;
import android.os.image.DynamicSystemManager;
import android.os.image.IDynamicSystemService;
@@ -181,6 +185,7 @@ import android.permission.PermissionControllerManager;
import android.permission.PermissionManager;
import android.print.IPrintManager;
import android.print.PrintManager;
+import android.scheduling.SchedulingFrameworkInitializer;
import android.security.FileIntegrityManager;
import android.security.IFileIntegrityService;
import android.service.oemlock.IOemLockService;
@@ -206,6 +211,7 @@ import android.view.autofill.AutofillManager;
import android.view.autofill.IAutoFillManager;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.IContentCaptureManager;
+import android.view.displayhash.DisplayHashManager;
import android.view.inputmethod.InputMethodManager;
import android.view.textclassifier.TextClassificationManager;
import android.view.textservice.TextServicesManager;
@@ -382,6 +388,15 @@ public final class SystemServiceRegistry {
ctx, () -> ServiceManager.getService(Context.TETHERING_SERVICE));
}});
+ registerService(Context.VPN_MANAGEMENT_SERVICE, VpnManager.class,
+ new CachedServiceFetcher<VpnManager>() {
+ @Override
+ public VpnManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getService(Context.VPN_MANAGEMENT_SERVICE);
+ IVpnManager service = IVpnManager.Stub.asInterface(b);
+ return new VpnManager(ctx, service);
+ }});
+
registerService(Context.VCN_MANAGEMENT_SERVICE, VcnManager.class,
new CachedServiceFetcher<VcnManager>() {
@Override
@@ -699,6 +714,13 @@ public final class SystemServiceRegistry {
}
});
+ registerService(Context.VIBRATOR_MANAGER_SERVICE, VibratorManager.class,
+ new CachedServiceFetcher<VibratorManager>() {
+ @Override
+ public VibratorManager createService(ContextImpl ctx) {
+ return new SystemVibratorManager(ctx);
+ }});
+
registerService(Context.VIBRATOR_SERVICE, Vibrator.class,
new CachedServiceFetcher<Vibrator>() {
@Override
@@ -1405,6 +1427,13 @@ public final class SystemServiceRegistry {
}
});
+ registerService(Context.DISPLAY_HASH_SERVICE, DisplayHashManager.class,
+ new CachedServiceFetcher<DisplayHashManager>() {
+ @Override
+ public DisplayHashManager createService(ContextImpl ctx) {
+ return new DisplayHashManager();
+ }});
+
sInitializing = true;
try {
// Note: the following functions need to be @SystemApis, once they become mainline
@@ -1420,6 +1449,7 @@ public final class SystemServiceRegistry {
MediaFrameworkPlatformInitializer.registerServiceWrappers();
MediaFrameworkInitializer.registerServiceWrappers();
RoleFrameworkInitializer.registerServiceWrappers();
+ SchedulingFrameworkInitializer.registerServiceWrappers();
} finally {
// If any of the above code throws, we're in a pretty bad shape and the process
// will likely crash, but we'll reset it just in case there's an exception handler...
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 390d9219ef2a..9019ddf941d9 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -53,13 +53,6 @@ public class TaskInfo {
public int userId;
/**
- * The id of the ActivityStack that currently contains this task.
- * @hide
- */
- @UnsupportedAppUsage
- public int stackId;
-
- /**
* The identifier for this task.
*/
public int taskId;
@@ -350,7 +343,9 @@ public class TaskInfo {
// TopActivityToken and bounds are important if top activity is in size compat
&& (!topActivityInSizeCompat || topActivityToken.equals(that.topActivityToken))
&& (!topActivityInSizeCompat || configuration.windowConfiguration.getBounds()
- .equals(that.configuration.windowConfiguration.getBounds()));
+ .equals(that.configuration.windowConfiguration.getBounds()))
+ && (!topActivityInSizeCompat || configuration.getLayoutDirection()
+ == that.configuration.getLayoutDirection());
}
/**
@@ -358,7 +353,6 @@ public class TaskInfo {
*/
void readFromParcel(Parcel source) {
userId = source.readInt();
- stackId = source.readInt();
taskId = source.readInt();
displayId = source.readInt();
isRunning = source.readBoolean();
@@ -394,7 +388,6 @@ public class TaskInfo {
*/
void writeToParcel(Parcel dest, int flags) {
dest.writeInt(userId);
- dest.writeInt(stackId);
dest.writeInt(taskId);
dest.writeInt(displayId);
dest.writeBoolean(isRunning);
@@ -428,7 +421,7 @@ public class TaskInfo {
@Override
public String toString() {
- return "TaskInfo{userId=" + userId + " stackId=" + stackId + " taskId=" + taskId
+ return "TaskInfo{userId=" + userId + " taskId=" + taskId
+ " displayId=" + displayId
+ " isRunning=" + isRunning
+ " baseIntent=" + baseIntent + " baseActivity=" + baseActivity
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 517ae24b75a6..1e382307a1a3 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -193,4 +193,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub {
@Override
public void onTaskMovedToBack(RunningTaskInfo taskInfo) {
}
+
+ @Override
+ public void onLockTaskModeChanged(int mode) {
+ }
}
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index e1c262caf44f..9b99ab8e31cb 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -477,11 +477,13 @@ public class UiModeManager {
* Changes to night mode take effect globally and will result in a configuration change
* (and potentially an Activity lifecycle event) being applied to all running apps.
* Developers interested in an app-local implementation of night mode should consider using
- * {@link android.support.v7.app.AppCompatDelegate#setDefaultNightMode(int)} to manage the
- * -night qualifier locally.
+ * {@link #setApplicationNightMode(int)} to set and persist the -night qualifier locally or
+ * {@link android.support.v7.app.AppCompatDelegate#setDefaultNightMode(int)} for the
+ * backward compatible implementation.
*
* @param mode the night mode to set
* @see #getNightMode()
+ * @see #setApplicationNightMode(int)
*/
public void setNightMode(@NightMode int mode) {
if (mService != null) {
@@ -494,6 +496,44 @@ public class UiModeManager {
}
/**
+ * Sets and persist the night mode for this application.
+ * <p>
+ * The mode can be one of:
+ * <ul>
+ * <li><em>{@link #MODE_NIGHT_NO}<em> sets the device into
+ * {@code notnight} mode</li>
+ * <li><em>{@link #MODE_NIGHT_YES}</em> sets the device into
+ * {@code night} mode</li>
+ * <li><em>{@link #MODE_NIGHT_CUSTOM}</em> automatically switches between
+ * {@code night} and {@code notnight} based on the custom time set (or default)</li>
+ * <li><em>{@link #MODE_NIGHT_AUTO}</em> automatically switches between
+ * {@code night} and {@code notnight} based on the device's current
+ * location and certain other sensors</li>
+ * </ul>
+ * <p>
+ * Changes to night mode take effect locally and will result in a configuration change
+ * (and potentially an Activity lifecycle event) being applied to this application. The mode
+ * is persisted for this application until it is either modified by the application, the
+ * user clears the data for the application, or this application is uninstalled.
+ * <p>
+ * Developers interested in a non-persistent app-local implementation of night mode should
+ * consider using {@link android.support.v7.app.AppCompatDelegate#setDefaultNightMode(int)}
+ * to manage the -night qualifier locally.
+ *
+ * @param mode the night mode to set
+ * @see #setNightMode(int)
+ */
+ public void setApplicationNightMode(@NightMode int mode) {
+ if (mService != null) {
+ try {
+ mService.setApplicationNightMode(mode);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Returns the currently configured night mode.
* <p>
* May be one of:
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index f50e33976959..6682175db2d9 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -66,6 +66,7 @@ import android.os.RemoteException;
import android.os.StrictMode;
import android.os.SystemProperties;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.view.Display;
@@ -107,6 +108,8 @@ public class WallpaperManager {
private static boolean DEBUG = false;
private float mWallpaperXStep = -1;
private float mWallpaperYStep = -1;
+ private static final @NonNull RectF LOCAL_COLOR_BOUNDS =
+ new RectF(0, 0, 1, 1);
/** {@hide} */
private static final String PROP_WALLPAPER = "ro.config.wallpaper";
@@ -114,6 +117,9 @@ public class WallpaperManager {
private static final String PROP_LOCK_WALLPAPER = "ro.config.lock_wallpaper";
/** {@hide} */
private static final String PROP_WALLPAPER_COMPONENT = "ro.config.wallpaper_component";
+ /** {@hide} */
+ private static final String VALUE_CMF_COLOR =
+ android.os.SystemProperties.get("ro.boot.hardware.color");
/**
* Activity Action: Show settings for choosing wallpaper. Do not use directly to construct
@@ -309,6 +315,8 @@ public class WallpaperManager {
private int mCachedWallpaperUserId;
private Bitmap mDefaultWallpaper;
private Handler mMainLooperHandler;
+ private ArrayMap<LocalWallpaperColorConsumer, ILocalWallpaperColorConsumer>
+ mLocalColorCallbacks = new ArrayMap<>();
Globals(IWallpaperManager service, Looper looper) {
mService = service;
@@ -350,6 +358,40 @@ 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) {
+ try {
+ mService.addOnLocalColorsChangedListener(wrap(callback) , regions, which,
+ userId, displayId);
+ } catch (RemoteException e) {
+ // Can't get colors, connection lost.
+ }
+ }
+
+ public void removeOnColorsChangedListener(
+ @NonNull LocalWallpaperColorConsumer callback, int which, int userId,
+ int displayId) {
+ ILocalWallpaperColorConsumer callback2 = mLocalColorCallbacks.remove(callback);
+ if (callback2 == null) return;
+ try {
+ mService.removeOnLocalColorsChangedListener(
+ callback2, which, userId, displayId);
+ } catch (RemoteException e) {
+ // Can't get colors, connection lost.
+ }
+ }
+
/**
* Stop listening to wallpaper color events.
*
@@ -1057,6 +1099,29 @@ public class WallpaperManager {
}
/**
+ * @hide
+ */
+ public void addOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback,
+ List<RectF> regions) throws IllegalArgumentException {
+ for (RectF region : regions) {
+ if (!LOCAL_COLOR_BOUNDS.contains(region)) {
+ throw new IllegalArgumentException("Regions must be within bounds "
+ + LOCAL_COLOR_BOUNDS);
+ }
+ }
+ sGlobals.addOnColorsChangedListener(callback, regions, FLAG_SYSTEM,
+ mContext.getUserId(), mContext.getDisplayId());
+ }
+
+ /**
+ * @hide
+ */
+ public void removeOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback) {
+ sGlobals.removeOnColorsChangedListener(callback, FLAG_SYSTEM, mContext.getUserId(),
+ mContext.getDisplayId());
+ }
+
+ /**
* Version of {@link #getWallpaperFile(int)} that can access the wallpaper data
* for a given user. The caller must hold the INTERACT_ACROSS_USERS_FULL
* permission to access another user's wallpaper data.
@@ -2002,7 +2067,11 @@ public class WallpaperManager {
return null;
} else {
whichProp = PROP_WALLPAPER;
- defaultResId = com.android.internal.R.drawable.default_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;
}
final String path = SystemProperties.get(whichProp);
if (!TextUtils.isEmpty(path)) {
@@ -2202,4 +2271,18 @@ public class WallpaperManager {
onColorsChanged(colors, which);
}
}
+
+ /**
+ * Callback to update a consumer with a local color change
+ * @hide
+ */
+ public interface LocalWallpaperColorConsumer {
+
+ /**
+ * Gets called when a color of an area gets updated
+ * @param area
+ * @param colors
+ */
+ void onColorsChanged(RectF area, WallpaperColors colors);
+ }
}
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index d175a66e90ea..4dbff0c06423 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -16,6 +16,8 @@
package android.app.admin;
+import static android.app.admin.DevicePolicyManager.OperationSafetyReason;
+
import android.accounts.AccountManager;
import android.annotation.BroadcastBehavior;
import android.annotation.IntDef;
@@ -35,6 +37,7 @@ import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
import android.security.KeyChain;
+import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -72,8 +75,8 @@ import java.lang.annotation.RetentionPolicy;
* </div>
*/
public class DeviceAdminReceiver extends BroadcastReceiver {
- private static String TAG = "DevicePolicy";
- private static boolean localLOGV = false;
+ private static final String TAG = "DevicePolicy";
+ private static final boolean LOCAL_LOGV = false;
/**
* This is the primary action that a device administrator must implement to be
@@ -509,6 +512,36 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
public static final String EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE =
"android.app.extra.TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE";
+ /**
+ * Broadcast action: notify the admin that the state of operations that can be unsafe because
+ * of a given reason (specified by the {@link #EXTRA_OPERATION_SAFETY_REASON} {@code int} extra)
+ * has changed (the new value is specified by the {@link #EXTRA_OPERATION_SAFETY_STATE}
+ * {@code boolean} extra).
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_OPERATION_SAFETY_STATE_CHANGED =
+ "android.app.action.OPERATION_SAFETY_STATE_CHANGED";
+
+ /**
+ * An {@code int} extra specifying an {@link OperationSafetyReason}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_OPERATION_SAFETY_REASON =
+ "android.app.extra.OPERATION_SAFETY_REASON";
+
+ /**
+ * An {@code boolean} extra specifying whether an operation will fail due to a
+ * {@link OperationSafetyReason}. {@code true} means operations that rely on that reason are
+ * safe, while {@code false} means they're unsafe.
+ *
+ * @hide
+ */
+ public static final String EXTRA_OPERATION_SAFETY_STATE =
+ "android.app.extra.OPERATION_SAFETY_STATE";
+
private DevicePolicyManager mManager;
private ComponentName mWho;
@@ -1018,6 +1051,51 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
}
/**
+ * Called to notify the state of operations that can be unsafe to execute has changed.
+ *
+ * <p><b>Note:/b> notice that the operation safety state might change between the time this
+ * callback is received and the operation's method on {@link DevicePolicyManager} is called, so
+ * calls to the latter could still throw a {@link UnsafeStateException} even when this method
+ * is called with {@code isSafe} as {@code true}
+ *
+ * @param context the running context as per {@link #onReceive}
+ * @param reason the reason an operation could be unsafe.
+ * @param isSafe whether the operation is safe to be executed.
+ */
+ public void onOperationSafetyStateChanged(@NonNull Context context,
+ @OperationSafetyReason int reason, boolean isSafe) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, String.format("onOperationSafetyStateChanged(): %s=%b",
+ DevicePolicyManager.operationSafetyReasonToString(reason), isSafe));
+ }
+ }
+
+ private void onOperationSafetyStateChanged(Context context, Intent intent) {
+ if (!hasRequiredExtra(intent, EXTRA_OPERATION_SAFETY_REASON)
+ || !hasRequiredExtra(intent, EXTRA_OPERATION_SAFETY_STATE)) {
+ return;
+ }
+
+ int reason = intent.getIntExtra(EXTRA_OPERATION_SAFETY_REASON,
+ DevicePolicyManager.OPERATION_SAFETY_REASON_NONE);
+ if (!DevicePolicyManager.isValidOperationSafetyReason(reason)) {
+ Log.wtf(TAG, "Received invalid reason on " + intent.getAction() + ": " + reason);
+ return;
+ }
+ boolean isSafe = intent.getBooleanExtra(EXTRA_OPERATION_SAFETY_STATE,
+ /* defaultValue=*/ false);
+
+ onOperationSafetyStateChanged(context, reason, isSafe);
+ }
+
+ private boolean hasRequiredExtra(Intent intent, String extra) {
+ if (intent.hasExtra(extra)) return true;
+
+ Log.wtf(TAG, "Missing '" + extra + "' on intent " + intent);
+ return false;
+ }
+
+ /**
* Intercept standard device administrator broadcasts. Implementations
* should not override this method; it is better to implement the
* convenience callbacks for each action.
@@ -1025,6 +1103,9 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
@Override
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
String action = intent.getAction();
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "onReceive(): received " + action + " on user " + context.getUserId());
+ }
if (ACTION_PASSWORD_CHANGED.equals(action)) {
onPasswordChanged(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
@@ -1092,6 +1173,8 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
} else if (ACTION_AFFILIATED_PROFILE_TRANSFER_OWNERSHIP_COMPLETE.equals(action)) {
onTransferAffiliatedProfileOwnershipComplete(context,
intent.getParcelableExtra(Intent.EXTRA_USER));
+ } else if (ACTION_OPERATION_SAFETY_STATE_CHANGED.equals(action)) {
+ onOperationSafetyStateChanged(context, intent);
}
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 82255c87c971..59e5144113c9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -206,7 +206,6 @@ public class DevicePolicyManager {
* {@link android.os.Build.VERSION_CODES#N}</li>
* <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_SKIP_USER_CONSENT}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_DISCLAIMERS}, optional</li>
@@ -250,7 +249,6 @@ public class DevicePolicyManager {
* <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
* </ul>
*
* <p>If provisioning fails, the device returns to its previous state.
@@ -289,7 +287,6 @@ public class DevicePolicyManager {
* <li>{@link #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_DISCLAIMERS}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS}, optional</li>
* </ul>
@@ -388,8 +385,6 @@ public class DevicePolicyManager {
* <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_LOCAL_TIME} (convert to String), optional</li>
* <li>{@link #EXTRA_PROVISIONING_TIME_ZONE}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_LOCALE}, optional</li>
@@ -438,8 +433,6 @@ public class DevicePolicyManager {
* <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_SUPPORT_URL}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_ORGANIZATION_NAME}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
@@ -654,7 +647,10 @@ public class DevicePolicyManager {
*
* <p>Use with {@link #ACTION_PROVISION_MANAGED_PROFILE} or
* {@link #ACTION_PROVISION_MANAGED_DEVICE}.
+ *
+ * @deprecated Color customization is no longer supported in the provisioning flow.
*/
+ @Deprecated
public static final String EXTRA_PROVISIONING_MAIN_COLOR =
"android.app.extra.PROVISIONING_MAIN_COLOR";
@@ -905,8 +901,10 @@ public class DevicePolicyManager {
* <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
* or {@link #ACTION_PROVISION_FINANCED_DEVICE}
*
+ * @deprecated This extra is no longer respected in the provisioning flow.
* @hide
*/
+ @Deprecated
@SystemApi
public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL =
"android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
@@ -930,9 +928,11 @@ public class DevicePolicyManager {
* <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
* or {@link #ACTION_PROVISION_FINANCED_DEVICE}
*
+ * @deprecated This extra is no longer respected in the provisioning flow.
* @hide
*/
@SystemApi
+ @Deprecated
public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI =
"android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI";
@@ -1300,10 +1300,11 @@ public class DevicePolicyManager {
* A value for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning is
* organization-owned.
*
- * <p>Using this value will cause the admin app's {@link #ACTION_GET_PROVISIONING_MODE}
- * activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra
- * contain {@link #PROVISIONING_MODE_MANAGED_PROFILE} and {@link
- * #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}.
+ * <p>Using this value indicates the admin app can only be provisioned in either a
+ * fully-managed device or a corporate-owned work profile. This will cause the admin app's
+ * {@link #ACTION_GET_PROVISIONING_MODE} activity to have the {@link
+ * #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link
+ * #PROVISIONING_MODE_MANAGED_PROFILE} and {@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}.
*
* <p>Also, if this value is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity
* will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link
@@ -1868,13 +1869,13 @@ public class DevicePolicyManager {
/**
* Grants access to {@link #setNetworkLoggingEnabled}, {@link #isNetworkLoggingEnabled} and
* {@link #retrieveNetworkLogs}. Once granted the delegated app will start receiving
- * DelegatedAdminReceiver.onNetworkLogsAvailable() callback, and Device owner will no longer
- * receive the DeviceAdminReceiver.onNetworkLogsAvailable() callback.
+ * DelegatedAdminReceiver.onNetworkLogsAvailable() callback, and Device owner or Profile Owner
+ * will no longer receive the DeviceAdminReceiver.onNetworkLogsAvailable() callback.
* There can be at most one app that has this delegation.
* If another app already had delegated network logging access,
* it will lose the delegation when a new app is delegated.
*
- * <p> Can only be granted by Device Owner.
+ * <p> Can only be granted by Device Owner or Profile Owner of a managed profile.
*/
public static final String DELEGATION_NETWORK_LOGGING = "delegation-network-logging";
@@ -2922,33 +2923,61 @@ public class DevicePolicyManager {
return DebugUtils.constantToString(DevicePolicyManager.class, PREFIX_OPERATION, operation);
}
- private static final String PREFIX_UNSAFE_OPERATION_REASON = "UNSAFE_OPERATION_REASON_";
+ private static final String PREFIX_OPERATION_SAFETY_REASON = "OPERATION_SAFETY_REASON_";
/** @hide */
- @IntDef(prefix = PREFIX_UNSAFE_OPERATION_REASON, value = {
- UNSAFE_OPERATION_REASON_NONE,
- UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION
+ @IntDef(prefix = PREFIX_OPERATION_SAFETY_REASON, value = {
+ OPERATION_SAFETY_REASON_NONE,
+ OPERATION_SAFETY_REASON_DRIVING_DISTRACTION
})
@Retention(RetentionPolicy.SOURCE)
- public static @interface UnsafeOperationReason {
+ public static @interface OperationSafetyReason {
}
/** @hide */
@TestApi
- public static final int UNSAFE_OPERATION_REASON_NONE = -1;
+ public static final int OPERATION_SAFETY_REASON_NONE = -1;
/**
* Indicates that a {@link UnsafeStateException} was thrown because the operation would distract
* the driver of the vehicle.
*/
- public static final int UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION = 1;
+ public static final int OPERATION_SAFETY_REASON_DRIVING_DISTRACTION = 1;
/** @hide */
@NonNull
@TestApi
- public static String unsafeOperationReasonToString(@UnsafeOperationReason int reason) {
+ public static String operationSafetyReasonToString(@OperationSafetyReason int reason) {
return DebugUtils.constantToString(DevicePolicyManager.class,
- PREFIX_UNSAFE_OPERATION_REASON, reason);
+ PREFIX_OPERATION_SAFETY_REASON, reason);
+ }
+
+ /** @hide */
+ public static boolean isValidOperationSafetyReason(@OperationSafetyReason int reason) {
+ return reason == OPERATION_SAFETY_REASON_DRIVING_DISTRACTION;
+ }
+
+ /**
+ * 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
+ * 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}.
+ *
+ * @param reason currently, only supported reason is
+ * {@link #OPERATION_SAFETY_REASON_DRIVING_DISTRACTION}.
+ *
+ * @return whether it's safe to run operations that can be affected by the given {@code reason}.
+ */
+ // TODO(b/173541467): should it throw SecurityException if caller is not admin?
+ public boolean isSafeOperation(@OperationSafetyReason int reason) {
+ if (mService == null) return false;
+
+ try {
+ return mService.isSafeOperation(reason);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** @hide */
@@ -9822,6 +9851,84 @@ public class DevicePolicyManager {
}
/**
+ * Sets whether 5g slicing is enabled on the work profile.
+ *
+ * Slicing allows operators to virtually divide their networks in portions and use different
+ * portions for specific use cases; for example, a corporation can have a deal/agreement with
+ * a carrier that all of its employees’ devices use data on a slice dedicated for enterprise
+ * use.
+ *
+ * By default, 5g slicing is enabled on the work profile on supported carriers and devices.
+ * Admins can explicitly disable it with this API.
+ *
+ * <p>This method can only be called by the profile owner of a managed profile.
+ *
+ * @param enabled whether 5g Slice should be enabled.
+ * @throws SecurityException if the caller is not the profile owner.
+ **/
+ public void setNetworkSlicingEnabled(boolean enabled) {
+ throwIfParentInstance("setNetworkSlicingEnabled");
+ if (mService != null) {
+ try {
+ mService.setNetworkSlicingEnabled(enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Indicates whether 5g slicing is enabled.
+ *
+ * <p>This method can be called by the profile owner of a managed profile.
+ *
+ * @return whether 5g Slice is enabled.
+ * @throws SecurityException if the caller is not the profile owner.
+ */
+ public boolean isNetworkSlicingEnabled() {
+ throwIfParentInstance("isNetworkSlicingEnabled");
+ if (mService == null) {
+ return false;
+ }
+ try {
+ return mService.isNetworkSlicingEnabled(myUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Indicates whether 5g slicing is enabled for specific user.
+ *
+ * This method can be called with permission
+ * {@link android.Manifest.permission#READ_NETWORK_DEVICE_CONFIG} by the profile owner of
+ * a managed profile. And the caller must hold the
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission if query for
+ * other users.
+ *
+ * @param userHandle indicates the user to query the state
+ * @return indicates whether 5g Slice is enabled.
+ * @throws SecurityException if the caller is not granted the permission
+ * {@link android.Manifest.permission#READ_NETWORK_DEVICE_CONFIG}
+ * and not profile owner of a managed profile, and not granted the permission
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} if query for
+ * other users.
+ * @hide
+ */
+ @SystemApi
+ public boolean isNetworkSlicingEnabledForUser(@NonNull UserHandle userHandle) {
+ throwIfParentInstance("isNetworkSlicingEnabledForUser");
+ if (mService == null) {
+ return false;
+ }
+ try {
+ return mService.isNetworkSlicingEnabled(userHandle.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* This method is mostly deprecated.
* Most of the settings that still have an effect have dedicated setter methods or user
* restrictions. See individual settings for details.
@@ -11689,8 +11796,11 @@ public class DevicePolicyManager {
}
/**
- * Called by a device owner or delegated app with {@link #DELEGATION_NETWORK_LOGGING} to
- * control the network logging feature.
+ * Called by a device owner, profile owner of a managed profile or delegated app with
+ * {@link #DELEGATION_NETWORK_LOGGING} to control the network logging feature.
+ *
+ * <p> When network logging is enabled by a profile owner, the network logs will only include
+ * work profile network activity, not activity on the personal profile.
*
* <p> Network logs contain DNS lookup and connect() library call events. The following library
* functions are recorded while network logging is active:
@@ -11730,7 +11840,7 @@ public class DevicePolicyManager {
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
* {@code null} if called by a delegated app.
* @param enabled whether network logging should be enabled or not.
- * @throws SecurityException if {@code admin} is not a device owner.
+ * @throws SecurityException if {@code admin} is not a device owner or profile owner.
* @see #setAffiliationIds
* @see #retrieveNetworkLogs
*/
@@ -11744,14 +11854,16 @@ public class DevicePolicyManager {
}
/**
- * Return whether network logging is enabled by a device owner.
+ * Return whether network logging is enabled by a device owner or profile owner of
+ * a managed profile.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with. Can only
* be {@code null} if the caller is a delegated app with {@link #DELEGATION_NETWORK_LOGGING}
* or has MANAGE_USERS permission.
- * @return {@code true} if network logging is enabled by device owner, {@code false} otherwise.
- * @throws SecurityException if {@code admin} is not a device owner and caller has
- * no MANAGE_USERS permission
+ * @return {@code true} if network logging is enabled by device owner or profile owner,
+ * {@code false} otherwise.
+ * @throws SecurityException if {@code admin} is not a device owner or profile owner and
+ * caller has no MANAGE_USERS permission
*/
public boolean isNetworkLoggingEnabled(@Nullable ComponentName admin) {
throwIfParentInstance("isNetworkLoggingEnabled");
@@ -11763,9 +11875,14 @@ public class DevicePolicyManager {
}
/**
- * Called by device owner or delegated app with {@link #DELEGATION_NETWORK_LOGGING} to retrieve
- * the most recent batch of network logging events.
- * A device owner has to provide a batchToken provided as part of
+ * Called by device owner, profile owner of a managed profile or delegated app with
+ * {@link #DELEGATION_NETWORK_LOGGING} to retrieve the most recent batch of
+ * network logging events.
+ *
+ * <p> When network logging is enabled by a profile owner, the network logs will only include
+ * work profile network activity, not activity on the personal profile.
+ *
+ * A device owner or profile owner has to provide a batchToken provided as part of
* {@link DeviceAdminReceiver#onNetworkLogsAvailable} callback. If the token doesn't match the
* token of the most recent available batch of logs, {@code null} will be returned.
*
@@ -11777,11 +11894,11 @@ public class DevicePolicyManager {
* after the device device owner has been notified via
* {@link DeviceAdminReceiver#onNetworkLogsAvailable}.
*
- * <p>If a secondary user or profile is created, calling this method will throw a
- * {@link SecurityException} until all users become affiliated again. It will also no longer be
- * possible to retrieve the network logs batch with the most recent batchToken provided
- * by {@link DeviceAdminReceiver#onNetworkLogsAvailable}. See
- * {@link DevicePolicyManager#setAffiliationIds}.
+ * <p>If the caller is not a profile owner and a secondary user or profile is created, calling
+ * this method will throw a {@link SecurityException} until all users become affiliated again.
+ * It will also no longer be possible to retrieve the network logs batch with the most recent
+ * batchToken provided by {@link DeviceAdminReceiver#onNetworkLogsAvailable}.
+ * See {@link DevicePolicyManager#setAffiliationIds}.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
* {@code null} if called by a delegated app.
@@ -11789,8 +11906,9 @@ public class DevicePolicyManager {
* @return A new batch of network logs which is a list of {@link NetworkEvent}. Returns
* {@code null} if the batch represented by batchToken is no longer available or if
* logging is disabled.
- * @throws SecurityException if {@code admin} is not a device owner, or there is at least one
- * profile or secondary user that is not affiliated with the device.
+ * @throws SecurityException if {@code admin} is not a device owner, profile owner or if the
+ * {@code admin} is not a profile owner and there is at least one profile or secondary user
+ * that is not affiliated with the device.
* @see #setAffiliationIds
* @see DeviceAdminReceiver#onNetworkLogsAvailable
*/
@@ -11909,11 +12027,12 @@ public class DevicePolicyManager {
}
/**
- * Called by the system to get the time at which the device owner last retrieved network logging
- * events.
+ * Called by the system to get the time at which the device owner or profile owner of a
+ * managed profile last retrieved network logging events.
*
- * @return the time at which the device owner most recently retrieved network logging events, in
- * milliseconds since epoch; -1 if network logging events were never retrieved.
+ * @return the time at which the device owner or profile owner most recently retrieved network
+ * logging events, in milliseconds since epoch; -1 if network logging events were
+ * never retrieved.
* @throws SecurityException if the caller is not the device owner, does not hold the
* MANAGE_USERS permission and is not the system.
*
@@ -13157,7 +13276,7 @@ public class DevicePolicyManager {
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS)
public void setNextOperationSafety(@DevicePolicyOperation int operation,
- @UnsafeOperationReason int reason) {
+ @OperationSafetyReason int reason) {
if (mService != null) {
try {
mService.setNextOperationSafety(operation, reason);
@@ -13316,6 +13435,7 @@ public class DevicePolicyManager {
}
}
}
+
/**
* Returns true if the caller is running on a device where the admin can grant
* permissions related to device sensors.
@@ -13328,24 +13448,110 @@ public class DevicePolicyManager {
* @return true if the app can grant device sensors-related permissions, false otherwise.
*/
public boolean canAdminGrantSensorsPermissions() {
- return canAdminGrantSensorsPermissionsForUser(myUserId());
+ throwIfParentInstance("canAdminGrantSensorsPermissions");
+ if (mService == null) {
+ return false;
+ }
+ try {
+ return mService.canAdminGrantSensorsPermissionsForUser(myUserId());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
/**
- * Returns true if the admin can control grants of sensors-related permissions, for
- * a given user.
+ * Called by device owner or profile owner of an organization-owned managed profile to
+ * enable or disable USB data signaling for the device. When disabled, USB data connections
+ * (except from charging functions) are prohibited.
*
+ * <p> This API is not supported on all devices, the caller should call
+ * {@link #canUsbDataSignalingBeDisabled()} to check whether enabling or disabling USB data
+ * signaling is supported on the device.
+ *
+ * @param enabled whether USB data signaling should be enabled or not.
+ * @throws SecurityException if the caller is not a device owner or a profile owner on
+ * an organization-owned managed profile.
+ * @throws IllegalStateException if disabling USB data signaling is not supported or
+ * if USB data signaling fails to be enabled/disabled.
+ */
+ public void setUsbDataSignalingEnabled(boolean enabled) {
+ throwIfParentInstance("setUsbDataSignalingEnabled");
+ if (mService != null) {
+ try {
+ mService.setUsbDataSignalingEnabled(mContext.getPackageName(), enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Called by device owner or profile owner of an organization-owned managed profile to return
+ * whether USB data signaling is currently enabled by the admin.
+ *
+ * @return {@code true} if USB data signaling is enabled, {@code false} otherwise.
+ */
+ public boolean isUsbDataSignalingEnabled() {
+ throwIfParentInstance("isUsbDataSignalingEnabled");
+ if (mService != null) {
+ try {
+ return mService.isUsbDataSignalingEnabled(mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Called by the system to check whether USB data signaling is currently enabled for this user.
+ *
+ * @param userId which user to check for.
+ * @return {@code true} if USB data signaling is enabled, {@code false} otherwise.
* @hide
- * @param userId The ID of the user to check.
- * @return if the admin may grant these permissions, false otherwise.
*/
- @SystemApi
- public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
- if (mService == null) {
- return false;
+ public boolean isUsbDataSignalingEnabledForUser(@UserIdInt int userId) {
+ throwIfParentInstance("isUsbDataSignalingEnabledForUser");
+ if (mService != null) {
+ try {
+ return mService.isUsbDataSignalingEnabledForUser(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns whether enabling or disabling USB data signaling is supported on the device.
+ *
+ * @return {@code true} if the device supports enabling and disabling USB data signaling.
+ */
+ public boolean canUsbDataSignalingBeDisabled() {
+ throwIfParentInstance("canUsbDataSignalingBeDisabled");
+ if (mService != null) {
+ try {
+ return mService.canUsbDataSignalingBeDisabled();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
+ return false;
+ }
+
+ /**
+ * Gets the list of {@link #isAffiliatedUser() affiliated} users running on foreground.
+ *
+ * @return list of {@link #isAffiliatedUser() affiliated} users running on foreground.
+ *
+ * @throws SecurityException if the calling application is not a device owner
+ */
+ @NonNull
+ public List<UserHandle> listForegroundAffiliatedUsers() {
+ if (mService == null) return Collections.emptyList();
+
try {
- return mService.canAdminGrantSensorsPermissionsForUser(userId);
+ return mService.listForegroundAffiliatedUsers();
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index a0d2977cf09a..67f5c366bc14 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -18,6 +18,7 @@ package android.app.admin;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.admin.DevicePolicyManager.OperationSafetyReason;
import android.content.ComponentName;
import android.content.Intent;
import android.os.UserHandle;
@@ -255,4 +256,14 @@ public abstract class DevicePolicyManagerInternal {
* {@link #supportsResetOp(int)} is true.
*/
public abstract void resetOp(int op, String packageName, @UserIdInt int userId);
+
+ /**
+ * Notifies the system that an unsafe operation reason has changed.
+ *
+ * @throws IllegalArgumentException if {@code checker} is not the same as set on
+ * {@code DevicePolicyManagerService}.
+ */
+ public abstract void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker,
+ @OperationSafetyReason int reason, boolean isSafe);
+
}
diff --git a/core/java/android/app/admin/DevicePolicySafetyChecker.java b/core/java/android/app/admin/DevicePolicySafetyChecker.java
index 6c6f2aa15ab7..17b74b1ab400 100644
--- a/core/java/android/app/admin/DevicePolicySafetyChecker.java
+++ b/core/java/android/app/admin/DevicePolicySafetyChecker.java
@@ -17,7 +17,7 @@ package android.app.admin;
import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
-import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
+import android.app.admin.DevicePolicyManager.OperationSafetyReason;
import com.android.internal.os.IResultReceiver;
@@ -31,15 +31,20 @@ public interface DevicePolicySafetyChecker {
/**
* Returns whether the given {@code operation} can be safely executed at the moment.
*/
- @UnsafeOperationReason
+ @OperationSafetyReason
int getUnsafeOperationReason(@DevicePolicyOperation int operation);
/**
+ * Return whether it's safe to run operations that can be affected by the given {@code reason}.
+ */
+ boolean isSafeOperation(@OperationSafetyReason int reason);
+
+ /**
* Returns a new exception for when the given {@code operation} cannot be safely executed.
*/
@NonNull
default UnsafeStateException newUnsafeStateException(@DevicePolicyOperation int operation,
- @UnsafeOperationReason int reason) {
+ @OperationSafetyReason int reason) {
return new UnsafeStateException(operation, reason);
}
diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
index 5e1cbadb458e..9eb9a7bdad07 100644
--- a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
+++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
@@ -25,6 +25,7 @@ import android.annotation.TestApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
+import android.stats.devicepolicy.DevicePolicyEnums;
import java.util.Locale;
@@ -35,6 +36,13 @@ import java.util.Locale;
*/
@TestApi
public final class FullyManagedDeviceProvisioningParams implements Parcelable {
+ private static final String LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM =
+ "LEAVE_ALL_SYSTEM_APPS_ENABLED";
+ private static final String CAN_DEVICE_OWNER_GRANT_SENSOR_PERMISSIONS_PARAM =
+ "CAN_DEVICE_OWNER_GRANT_SENSOR_PERMISSIONS";
+ private static final String TIME_ZONE_PROVIDED_PARAM = "TIME_ZONE_PROVIDED";
+ private static final String LOCALE_PROVIDED_PARAM = "LOCALE_PROVIDED";
+
@NonNull private final ComponentName mDeviceAdminComponentName;
@NonNull private final String mOwnerName;
private final boolean mLeaveAllSystemAppsEnabled;
@@ -121,6 +129,29 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
}
/**
+ * Logs the provisioning params using {@link DevicePolicyEventLogger}.
+ */
+ public void logParams(@NonNull String callerPackage) {
+ requireNonNull(callerPackage);
+
+ logParam(callerPackage, LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM, mLeaveAllSystemAppsEnabled);
+ logParam(callerPackage, CAN_DEVICE_OWNER_GRANT_SENSOR_PERMISSIONS_PARAM,
+ mDeviceOwnerCanGrantSensorsPermissions);
+ logParam(callerPackage, TIME_ZONE_PROVIDED_PARAM, /* value= */ mTimeZone != null);
+ logParam(callerPackage, LOCALE_PROVIDED_PARAM, /* value= */ mLocale != null);
+ }
+
+ private void logParam(String callerPackage, String param, boolean value) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_PARAM)
+ .setStrings(callerPackage)
+ .setAdmin(mDeviceAdminComponentName)
+ .setStrings(param)
+ .setBoolean(value)
+ .write();
+ }
+
+ /**
* Builder class for {@link FullyManagedDeviceProvisioningParams} objects.
*/
public static final class Builder {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 89f30cc821ab..8a87b16b760b 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -267,6 +267,9 @@ interface IDevicePolicyManager {
void setSecondaryLockscreenEnabled(in ComponentName who, boolean enabled);
boolean isSecondaryLockscreenEnabled(in UserHandle userHandle);
+ void setNetworkSlicingEnabled(in boolean enabled);
+ boolean isNetworkSlicingEnabled(int userHandle);
+
void setLockTaskPackages(in ComponentName who, in String[] packages);
String[] getLockTaskPackages(in ComponentName who);
boolean isLockTaskPermitted(in String pkg);
@@ -492,6 +495,7 @@ interface IDevicePolicyManager {
boolean canProfileOwnerResetPasswordWhenLocked(int userId);
void setNextOperationSafety(int operation, int reason);
+ boolean isSafeOperation(int reason);
String getEnrollmentSpecificId(String callerPackage);
void setOrganizationIdForUser(in String callerPackage, in String enterpriseId, int userId);
@@ -501,4 +505,11 @@ interface IDevicePolicyManager {
void resetDefaultCrossProfileIntentFilters(int userId);
boolean canAdminGrantSensorsPermissionsForUser(int userId);
+
+ void setUsbDataSignalingEnabled(String callerPackage, boolean enabled);
+ boolean isUsbDataSignalingEnabled(String callerPackage);
+ boolean isUsbDataSignalingEnabledForUser(int userId);
+ boolean canUsbDataSignalingBeDisabled();
+
+ List<UserHandle> listForegroundAffiliatedUsers();
}
diff --git a/core/java/android/app/admin/ManagedProfileProvisioningParams.java b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
index 5fe63d142021..1a6099a7f570 100644
--- a/core/java/android/app/admin/ManagedProfileProvisioningParams.java
+++ b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
@@ -25,6 +25,7 @@ import android.annotation.TestApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
+import android.stats.devicepolicy.DevicePolicyEnums;
/**
* Params required to provision a managed profile, see
@@ -34,6 +35,13 @@ import android.os.Parcelable;
*/
@TestApi
public final class ManagedProfileProvisioningParams implements Parcelable {
+ private static final String LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM =
+ "LEAVE_ALL_SYSTEM_APPS_ENABLED";
+ private static final String ORGANIZATION_OWNED_PROVISIONING_PARAM =
+ "ORGANIZATION_OWNED_PROVISIONING";
+ private static final String ACCOUNT_TO_MIGRATE_PROVIDED_PARAM = "ACCOUNT_TO_MIGRATE_PROVIDED";
+ private static final String KEEP_MIGRATED_ACCOUNT_PARAM = "KEEP_MIGRATED_ACCOUNT";
+
@NonNull private final ComponentName mProfileAdminComponentName;
@NonNull private final String mOwnerName;
@Nullable private final String mProfileName;
@@ -93,6 +101,30 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
}
/**
+ * Logs the provisioning params using {@link DevicePolicyEventLogger}.
+ */
+ public void logParams(@NonNull String callerPackage) {
+ requireNonNull(callerPackage);
+
+ logParam(callerPackage, LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM, mLeaveAllSystemAppsEnabled);
+ logParam(callerPackage, ORGANIZATION_OWNED_PROVISIONING_PARAM,
+ mOrganizationOwnedProvisioning);
+ logParam(callerPackage, KEEP_MIGRATED_ACCOUNT_PARAM, mKeepAccountMigrated);
+ logParam(callerPackage, ACCOUNT_TO_MIGRATE_PROVIDED_PARAM,
+ /* value= */ mAccountToMigrate != null);
+ }
+
+ private void logParam(String callerPackage, String param, boolean value) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_PARAM)
+ .setStrings(callerPackage)
+ .setAdmin(mProfileAdminComponentName)
+ .setStrings(param)
+ .setBoolean(value)
+ .write();
+ }
+
+ /**
* Builder class for {@link ManagedProfileProvisioningParams} objects.
*/
public static final class Builder {
diff --git a/core/java/android/app/admin/UnsafeStateException.java b/core/java/android/app/admin/UnsafeStateException.java
index 56eeb06e8cc0..f1f652601c66 100644
--- a/core/java/android/app/admin/UnsafeStateException.java
+++ b/core/java/android/app/admin/UnsafeStateException.java
@@ -15,18 +15,20 @@
*/
package android.app.admin;
-import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION;
-import static android.app.admin.DevicePolicyManager.unsafeOperationReasonToString;
+import static android.app.admin.DevicePolicyManager.isValidOperationSafetyReason;
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
-import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
+import android.app.admin.DevicePolicyManager.OperationSafetyReason;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.Preconditions;
+import java.util.Arrays;
+import java.util.List;
+
/**
* Exception thrown when a {@link android.app.admin.DevicePolicyManager} operation failed because it
* was not safe to be executed at that moment.
@@ -39,17 +41,15 @@ import com.android.internal.util.Preconditions;
public final class UnsafeStateException extends IllegalStateException implements Parcelable {
private final @DevicePolicyOperation int mOperation;
- private final @UnsafeOperationReason int mReason;
+ private final @OperationSafetyReason int mReason;
/** @hide */
@TestApi
public UnsafeStateException(@DevicePolicyOperation int operation,
- @UnsafeOperationReason int reason) {
+ @OperationSafetyReason int reason) {
super();
- Preconditions.checkArgument(reason == UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION,
- "invalid reason %d, must be %d (%s)", reason,
- UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION,
- unsafeOperationReasonToString(UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION));
+ Preconditions.checkArgument(isValidOperationSafetyReason(reason), "invalid reason %d",
+ reason);
mOperation = operation;
mReason = reason;
}
@@ -61,19 +61,20 @@ public final class UnsafeStateException extends IllegalStateException implements
}
/**
- * Gets the reason the operation is unsafe.
+ * Gets the reasons the operation is unsafe.
*
* @return currently, only valid reason is
- * {@link android.app.admin.DevicePolicyManager#UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION}.
+ * {@link android.app.admin.DevicePolicyManager#OPERATION_SAFETY_REASON_DRIVING_DISTRACTION}.
*/
- public @UnsafeOperationReason int getReason() {
- return mReason;
+ @NonNull
+ public List<Integer> getReasons() {
+ return Arrays.asList(mReason);
}
/** @hide */
@Override
public String getMessage() {
- return DevicePolicyManager.unsafeOperationReasonToString(mReason);
+ return DevicePolicyManager.operationSafetyReasonToString(mReason);
}
@Override
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 7e232acbdcdd..85cfe835c28d 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -401,7 +401,8 @@ public abstract class BackupAgent extends ContextWrapper {
* @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long)
*/
public void onFullBackup(FullBackupDataOutput data) throws IOException {
- FullBackup.BackupScheme backupScheme = FullBackup.getBackupScheme(this);
+ FullBackup.BackupScheme backupScheme = FullBackup.getBackupScheme(this,
+ mOperationType);
if (!isDeviceToDeviceMigration() && !backupScheme.isFullBackupContentEnabled()) {
return;
}
@@ -624,7 +625,8 @@ public abstract class BackupAgent extends ContextWrapper {
if (includeMap == null || includeMap.size() == 0) {
// Do entire sub-tree for the provided token.
fullBackupFileTree(packageName, domainToken,
- FullBackup.getBackupScheme(this).tokenToDirectoryPath(domainToken),
+ FullBackup.getBackupScheme(this, mOperationType)
+ .tokenToDirectoryPath(domainToken),
filterSet, traversalExcludeSet, data);
} else if (includeMap.get(domainToken) != null) {
// This will be null if the xml parsing didn't yield any rules for
@@ -795,7 +797,8 @@ public abstract class BackupAgent extends ContextWrapper {
ArraySet<String> systemExcludes,
FullBackupDataOutput output) {
// Pull out the domain and set it aside to use when making the tarball.
- String domainPath = FullBackup.getBackupScheme(this).tokenToDirectoryPath(domain);
+ String domainPath = FullBackup.getBackupScheme(this, mOperationType)
+ .tokenToDirectoryPath(domain);
if (domainPath == null) {
// Should never happen.
return;
@@ -911,7 +914,7 @@ public abstract class BackupAgent extends ContextWrapper {
return true;
}
- FullBackup.BackupScheme bs = FullBackup.getBackupScheme(this);
+ FullBackup.BackupScheme bs = FullBackup.getBackupScheme(this, mOperationType);
if (!bs.isFullBackupContentEnabled()) {
if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
Log.v(FullBackup.TAG_XML_PARSER,
@@ -985,7 +988,8 @@ public abstract class BackupAgent extends ContextWrapper {
+ " domain=" + domain + " relpath=" + path + " mode=" + mode
+ " mtime=" + mtime);
- basePath = FullBackup.getBackupScheme(this).tokenToDirectoryPath(domain);
+ basePath = FullBackup.getBackupScheme(this, mOperationType).tokenToDirectoryPath(
+ domain);
if (domain.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)) {
mode = -1; // < 0 is a token to skip attempting a chmod()
}
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 742d05c1ffa4..f7ed6f1f2feb 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -16,6 +16,9 @@
package android.app.backup;
+import static android.app.backup.BackupManager.OperationType;
+
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -41,6 +44,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@@ -90,27 +94,61 @@ public class FullBackup {
"fakeClientSideEncryption";
/**
+ * Identify {@link BackupScheme} object by package and operation type
+ * (see {@link OperationType}) it corresponds to.
+ */
+ private static class BackupSchemeId {
+ final String mPackageName;
+ @OperationType final int mOperationType;
+
+ BackupSchemeId(String packageName, @OperationType int operationType) {
+ mPackageName = packageName;
+ mOperationType = operationType;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPackageName, mOperationType);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object == null || getClass() != object.getClass()) {
+ return false;
+ }
+ BackupSchemeId that = (BackupSchemeId) object;
+ return Objects.equals(mPackageName, that.mPackageName) &&
+ Objects.equals(mOperationType, that.mOperationType);
+ }
+ }
+
+ /**
* @hide
*/
@UnsupportedAppUsage
static public native int backupToTar(String packageName, String domain,
String linkdomain, String rootpath, String path, FullBackupDataOutput output);
- private static final Map<String, BackupScheme> kPackageBackupSchemeMap =
- new ArrayMap<String, BackupScheme>();
+ private static final Map<BackupSchemeId, BackupScheme> kPackageBackupSchemeMap =
+ new ArrayMap<>();
- static synchronized BackupScheme getBackupScheme(Context context) {
+ static synchronized BackupScheme getBackupScheme(Context context,
+ @OperationType int operationType) {
+ BackupSchemeId backupSchemeId = new BackupSchemeId(context.getPackageName(), operationType);
BackupScheme backupSchemeForPackage =
- kPackageBackupSchemeMap.get(context.getPackageName());
+ kPackageBackupSchemeMap.get(backupSchemeId);
if (backupSchemeForPackage == null) {
- backupSchemeForPackage = new BackupScheme(context);
- kPackageBackupSchemeMap.put(context.getPackageName(), backupSchemeForPackage);
+ backupSchemeForPackage = new BackupScheme(context, operationType);
+ kPackageBackupSchemeMap.put(backupSchemeId, backupSchemeForPackage);
}
return backupSchemeForPackage;
}
public static BackupScheme getBackupSchemeForTest(Context context) {
- BackupScheme testing = new BackupScheme(context);
+ BackupScheme testing = new BackupScheme(context, OperationType.BACKUP);
testing.mExcludes = new ArraySet();
testing.mIncludes = new ArrayMap();
return testing;
@@ -236,6 +274,7 @@ public class FullBackup {
private final static String TAG_EXCLUDE = "exclude";
final int mFullBackupContent;
+ @OperationType final int mOperationType;
final PackageManager mPackageManager;
final StorageManager mStorageManager;
final String mPackageName;
@@ -354,8 +393,9 @@ public class FullBackup {
*/
ArraySet<PathWithRequiredFlags> mExcludes;
- BackupScheme(Context context) {
+ BackupScheme(Context context, @OperationType int operationType) {
mFullBackupContent = context.getApplicationInfo().fullBackupContent;
+ mOperationType = operationType;
mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
mPackageManager = context.getPackageManager();
mPackageName = context.getPackageName();
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
index 28b73406b877..ab38832458d6 100644
--- a/core/java/android/app/compat/CompatChanges.java
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -20,8 +20,16 @@ import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.compat.Compatibility;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
+import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.IPlatformCompat;
+
+import java.util.Map;
+
/**
* CompatChanges APIs - to be used by platform code only (including mainline
* modules).
@@ -89,4 +97,25 @@ public final class CompatChanges {
return QUERY_CACHE.query(ChangeIdStateQuery.byUid(changeId, uid));
}
+ /**
+ * Set an app compat override for a given package. This will check whether the caller is allowed
+ * to perform this operation on the given apk and build. Only the installer package is allowed
+ * to set overrides on a non-debuggable final build and a non-test apk.
+ *
+ * @param packageName The package name of the app in question.
+ * @param overrides A map from changeId to the override applied for this change id.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG)
+ public static void setPackageOverride(String packageName,
+ Map<Long, PackageOverride> overrides) {
+ IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+ CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overrides);
+ try {
+ platformCompat.setOverridesFromInstaller(config, packageName);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/compat/OWNERS b/core/java/android/app/compat/OWNERS
new file mode 100644
index 000000000000..f8c3520e9fa8
--- /dev/null
+++ b/core/java/android/app/compat/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/compat/OWNERS
diff --git a/core/java/android/app/compat/PackageOverride.java b/core/java/android/app/compat/PackageOverride.java
new file mode 100644
index 000000000000..9f97cd41128a
--- /dev/null
+++ b/core/java/android/app/compat/PackageOverride.java
@@ -0,0 +1,211 @@
+/*
+ * 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.compat;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An app compat override applied to a given package and change id pairing.
+ *
+ * A package override contains a list of version ranges with the desired boolean value of
+ * the override for the app in this version range. Ranges can be open ended in either direction.
+ * An instance of PackageOverride gets created via {@link Builder} and is immutable once created.
+ *
+ * @hide
+ */
+public class PackageOverride implements Parcelable {
+
+ @IntDef({
+ VALUE_UNDEFINED,
+ VALUE_ENABLED,
+ VALUE_DISABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ /** @hide */
+ public @interface EvaluatedOverride {
+ }
+
+ /**
+ * Return value of {@link #evaluate(long)} and {@link #evaluateForAllVersions()} indicating that
+ * this PackageOverride does not define the value of the override for the given version.
+ * @hide
+ */
+ public static final int VALUE_UNDEFINED = 0;
+ /**
+ * Return value of {@link #evaluate(long)} and {@link #evaluateForAllVersions()} indicating that
+ * the override evaluates to {@code true} for the given version.
+ * @hide
+ */
+ public static final int VALUE_ENABLED = 1;
+ /**
+ * Return value of {@link #evaluate(long)} and {@link #evaluateForAllVersions()} indicating that
+ * the override evaluates to {@code fakse} for the given version.
+ * @hide
+ */
+ public static final int VALUE_DISABLED = 2;
+
+ private final long mMinVersionCode;
+ private final long mMaxVersionCode;
+ private final boolean mEnabled;
+
+ private PackageOverride(long minVersionCode,
+ long maxVersionCode,
+ boolean enabled) {
+ this.mMinVersionCode = minVersionCode;
+ this.mMaxVersionCode = maxVersionCode;
+ this.mEnabled = enabled;
+ }
+
+ private PackageOverride(Parcel in) {
+ this(in.readLong(), in.readLong(), in.readBoolean());
+ }
+
+ /**
+ * Evaluate the override for the given {@code versionCode}. If no override is defined for
+ * the specified version code, {@link #VALUE_UNDEFINED} is returned.
+ * @hide
+ */
+ public @EvaluatedOverride int evaluate(long versionCode) {
+ if (versionCode >= mMinVersionCode && versionCode <= mMaxVersionCode) {
+ return mEnabled ? VALUE_ENABLED : VALUE_DISABLED;
+ }
+ return VALUE_UNDEFINED;
+ }
+
+ /**
+ * Evaluate the override independent of version code, i.e. only return an evaluated value if
+ * this range covers all versions, otherwise {@link #VALUE_UNDEFINED} is returned.
+ * @hide
+ */
+ public int evaluateForAllVersions() {
+ if (mMinVersionCode == Long.MIN_VALUE && mMaxVersionCode == Long.MAX_VALUE) {
+ return mEnabled ? VALUE_ENABLED : VALUE_DISABLED;
+ }
+ return VALUE_UNDEFINED;
+ }
+
+ /** Returns the minimum version code the override applies to. */
+ public long getMinVersionCode() {
+ return mMinVersionCode;
+ }
+
+ /** Returns the minimum version code the override applies from. */
+ public long getMaxVersionCode() {
+ return mMaxVersionCode;
+ }
+
+ /** Returns the enabled value for the override. */
+ public boolean getEnabled() {
+ return mEnabled;
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mMinVersionCode);
+ dest.writeLong(mMaxVersionCode);
+ dest.writeBoolean(mEnabled);
+ }
+
+ /** @hide */
+ @Override
+ public String toString() {
+ if (mMinVersionCode == Long.MIN_VALUE && mMaxVersionCode == Long.MAX_VALUE) {
+ return Boolean.toString(mEnabled);
+ }
+ return String.format("[%d,%d,%b]", mMinVersionCode, mMaxVersionCode, mEnabled);
+ }
+
+ /** @hide */
+ public static final Creator<PackageOverride> CREATOR =
+ new Creator<PackageOverride>() {
+
+ @Override
+ public PackageOverride createFromParcel(Parcel in) {
+ return new PackageOverride(in);
+ }
+
+ @Override
+ public PackageOverride[] newArray(int size) {
+ return new PackageOverride[size];
+ }
+ };
+
+ /**
+ * Builder to construct a PackageOverride.
+ */
+ public static class Builder {
+ private long mMinVersionCode = Long.MIN_VALUE;
+ private long mMaxVersionCode = Long.MAX_VALUE;
+ private boolean mEnabled;
+
+ /**
+ * Sets the minimum version code the override should apply from.
+ *
+ * default value: {@code Long.MIN_VALUE}.
+ */
+ public Builder setMinVersionCode(long minVersionCode) {
+ mMinVersionCode = minVersionCode;
+ return this;
+ }
+
+ /**
+ * Sets the maximum version code the override should apply to.
+ *
+ * default value: {@code Long.MAX_VALUE}.
+ */
+ public Builder setMaxVersionCode(long maxVersionCode) {
+ mMaxVersionCode = maxVersionCode;
+ return this;
+ }
+
+ /**
+ * Sets whether the override should be enabled for the given version range.
+ *
+ * default value: {@code false}.
+ */
+ public Builder setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ return this;
+ }
+
+ /**
+ * Build the {@link PackageOverride}.
+ *
+ * @throws IllegalArgumentException if {@code minVersionCode} is larger than
+ * {@code maxVersionCode}.
+ */
+ public PackageOverride build() {
+ if (mMinVersionCode > mMaxVersionCode) {
+ throw new IllegalArgumentException("minVersionCode must not be larger than "
+ + "maxVersionCode");
+ }
+ return new PackageOverride(mMinVersionCode, mMaxVersionCode, mEnabled);
+ }
+ };
+}
diff --git a/core/java/android/app/people/IConversationListener.aidl b/core/java/android/app/people/IConversationListener.aidl
new file mode 100644
index 000000000000..7cbd66dd6617
--- /dev/null
+++ b/core/java/android/app/people/IConversationListener.aidl
@@ -0,0 +1,33 @@
+/**
+ * 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.people;
+
+import android.app.people.ConversationChannel;
+import android.content.pm.ParceledListSlice;
+import android.os.UserHandle;
+
+import java.util.List;
+
+/**
+ * Interface for PeopleManager#ConversationListener.
+ *
+ * @hide
+ */
+oneway interface IConversationListener
+{
+ void onConversationUpdate(in ConversationChannel conversation);
+} \ No newline at end of file
diff --git a/core/java/android/app/people/IPeopleManager.aidl b/core/java/android/app/people/IPeopleManager.aidl
index ebe9f60dc150..496ca82bb61f 100644
--- a/core/java/android/app/people/IPeopleManager.aidl
+++ b/core/java/android/app/people/IPeopleManager.aidl
@@ -18,6 +18,7 @@ package android.app.people;
import android.app.people.ConversationStatus;
import android.app.people.ConversationChannel;
+import android.app.people.IConversationListener;
import android.content.pm.ParceledListSlice;
import android.net.Uri;
import android.os.IBinder;
@@ -49,6 +50,9 @@ interface IPeopleManager {
/** Removes all the recent conversations and uncaches their cached shortcuts. */
void removeAllRecentConversations();
+ /** Returns whether the shortcutId is backed by a Conversation in People Service. */
+ boolean isConversation(in String packageName, int userId, in String shortcutId);
+
/**
* Returns the last interaction with the specified conversation. If the
* conversation can't be found or no interactions have been recorded, returns 0L.
@@ -59,4 +63,6 @@ interface IPeopleManager {
void clearStatus(in String packageName, int userId, in String conversationId, in String statusId);
void clearStatuses(in String packageName, int userId, in String conversationId);
ParceledListSlice getStatuses(in String packageName, int userId, in String conversationId);
+ void registerConversationListener(in String packageName, int userId, in String shortcutId, in IConversationListener callback);
+ void unregisterConversationListener(in IConversationListener callback);
}
diff --git a/core/java/android/app/people/PeopleManager.java b/core/java/android/app/people/PeopleManager.java
index de7ba622ae88..108437eb0649 100644
--- a/core/java/android/app/people/PeopleManager.java
+++ b/core/java/android/app/people/PeopleManager.java
@@ -16,19 +16,29 @@
package android.app.people;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.Pair;
+import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* This class allows interaction with conversation and people data.
@@ -38,11 +48,18 @@ public final class PeopleManager {
private static final String LOG_TAG = PeopleManager.class.getSimpleName();
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public Map<ConversationListener, Pair<Executor, IConversationListener>>
+ mConversationListeners = new HashMap<>();
+
@NonNull
- private final Context mContext;
+ private Context mContext;
@NonNull
- private final IPeopleManager mService;
+ private IPeopleManager mService;
/**
* @hide
@@ -53,6 +70,41 @@ public final class PeopleManager {
Context.PEOPLE_SERVICE));
}
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public PeopleManager(@NonNull Context context, IPeopleManager service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Returns whether a shortcut has a conversation associated.
+ *
+ * <p>Requires android.permission.READ_PEOPLE_DATA permission.
+ *
+ * <p>This method may return different results for the same shortcut over time, as an app adopts
+ * conversation features or if a user hasn't communicated with the conversation associated to
+ * the shortcut in a while, so the result should not be stored and relied on indefinitely by
+ * clients.
+ *
+ * @param packageName name of the package the conversation is part of
+ * @param shortcutId the shortcut id backing the conversation
+ * @return whether the {@shortcutId} is backed by a Conversation.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PEOPLE_DATA)
+ public boolean isConversation(@NonNull String packageName, @NonNull String shortcutId) {
+ Preconditions.checkStringNotEmpty(packageName);
+ Preconditions.checkStringNotEmpty(shortcutId);
+ try {
+ return mService.isConversation(packageName, mContext.getUserId(), shortcutId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
/**
* Sets or updates a {@link ConversationStatus} for a conversation.
@@ -65,8 +117,7 @@ public final class PeopleManager {
*
* @param conversationId the {@link ShortcutInfo#getId() id} of the shortcut backing the
* conversation that has an active status
- * @param status the current status for the given conversation
- *
+ * @param status the current status for the given conversation
* @return whether the role is available in the system
*/
public void addOrUpdateStatus(@NonNull String conversationId,
@@ -86,8 +137,8 @@ public final class PeopleManager {
*
* @param conversationId the {@link ShortcutInfo#getId() id} of the shortcut backing the
* conversation that has an active status
- * @param statusId the {@link ConversationStatus#getId() id} of a published status for the given
- * conversation
+ * @param statusId the {@link ConversationStatus#getId() id} of a published status for the
+ * given conversation
*/
public void clearStatus(@NonNull String conversationId, @NonNull String statusId) {
Preconditions.checkStringNotEmpty(conversationId);
@@ -126,7 +177,7 @@ public final class PeopleManager {
try {
final ParceledListSlice<ConversationStatus> parceledList
= mService.getStatuses(
- mContext.getPackageName(), mContext.getUserId(), conversationId);
+ mContext.getPackageName(), mContext.getUserId(), conversationId);
if (parceledList != null) {
return parceledList.getList();
}
@@ -135,4 +186,103 @@ public final class PeopleManager {
}
return new ArrayList<>();
}
+
+ /**
+ * Listeners for conversation changes.
+ *
+ * @hide
+ */
+ public interface ConversationListener {
+ /**
+ * Triggers when the conversation registered for a listener has been updated.
+ *
+ * @param conversation The conversation with modified data
+ * @see IPeopleManager#registerConversationListener(String, int, String,
+ * android.app.people.ConversationListener)
+ *
+ * <p>Only system root and SysUI have access to register the listener.
+ */
+ default void onConversationUpdate(@NonNull ConversationChannel conversation) {
+ }
+ }
+
+ /**
+ * Register a listener to watch for changes to the conversation identified by {@code
+ * packageName}, {@code userId}, and {@code shortcutId}.
+ *
+ * @param packageName The package name to match and filter the conversation to send updates for.
+ * @param userId The user ID to match and filter the conversation to send updates for.
+ * @param shortcutId The shortcut ID to match and filter the conversation to send updates for.
+ * @param listener The listener to register to receive conversation updates.
+ * @param executor {@link Executor} to handle the listeners. To dispatch listeners to the
+ * main thread of your application, you can use
+ * {@link android.content.Context#getMainExecutor()}.
+ * @hide
+ */
+ public void registerConversationListener(String packageName, int userId, String shortcutId,
+ ConversationListener listener, Executor executor) {
+ requireNonNull(listener, "Listener cannot be null");
+ requireNonNull(packageName, "Package name cannot be null");
+ requireNonNull(shortcutId, "Shortcut ID cannot be null");
+ synchronized (mConversationListeners) {
+ IConversationListener proxy = (IConversationListener) new ConversationListenerProxy(
+ executor, listener);
+ try {
+ mService.registerConversationListener(
+ packageName, userId, shortcutId, proxy);
+ mConversationListeners.put(listener,
+ new Pair<>(executor, proxy));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Unregisters the listener previously registered to watch conversation changes.
+ *
+ * @param listener The listener to register to receive conversation updates.
+ * @hide
+ */
+ public void unregisterConversationListener(
+ ConversationListener listener) {
+ requireNonNull(listener, "Listener cannot be null");
+
+ synchronized (mConversationListeners) {
+ if (mConversationListeners.containsKey(listener)) {
+ IConversationListener proxy = mConversationListeners.remove(listener).second;
+ try {
+ mService.unregisterConversationListener(proxy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+
+ /**
+ * Listener proxy class for {@link ConversationListener}
+ *
+ * @hide
+ */
+ private static class ConversationListenerProxy extends
+ IConversationListener.Stub {
+ private final Executor mExecutor;
+ private final ConversationListener mListener;
+
+ ConversationListenerProxy(Executor executor, ConversationListener listener) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void onConversationUpdate(@NonNull ConversationChannel conversation) {
+ if (mListener == null || mExecutor == null) {
+ // Binder is dead.
+ Slog.e(LOG_TAG, "Binder is dead");
+ return;
+ }
+ mExecutor.execute(() -> mListener.onConversationUpdate(conversation));
+ }
+ }
}
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index d451599cc7b0..e6fdc006615a 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -60,7 +60,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
public void postExecute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
// TODO(lifecycler): Use interface callback instead of actual implementation.
- ActivityClient.getInstance().activityResumed(token);
+ ActivityClient.getInstance().activityResumed(token, client.isHandleSplashScreenExit(token));
}
@Override
diff --git a/core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java b/core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java
new file mode 100644
index 000000000000..5374984d31d0
--- /dev/null
+++ b/core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityThread;
+import android.app.ClientTransactionHandler;
+import android.os.Parcel;
+import android.window.SplashScreenView.SplashScreenViewParcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Transfer a splash screen view to an Activity.
+ * @hide
+ */
+public class TransferSplashScreenViewStateItem extends ActivityTransactionItem {
+
+ private SplashScreenViewParcelable mSplashScreenViewParcelable;
+ private @TransferRequest int mRequest;
+
+ @IntDef(value = {
+ ATTACH_TO,
+ HANDOVER_TO
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TransferRequest {}
+ // request client to attach the view on it.
+ public static final int ATTACH_TO = 0;
+ // tell client that you can handle the splash screen view.
+ public static final int HANDOVER_TO = 1;
+
+ @Override
+ public void execute(@NonNull ClientTransactionHandler client,
+ @NonNull ActivityThread.ActivityClientRecord r,
+ PendingTransactionActions pendingActions) {
+ switch (mRequest) {
+ case ATTACH_TO:
+ client.handleAttachSplashScreenView(r, mSplashScreenViewParcelable);
+ break;
+ case HANDOVER_TO:
+ client.handOverSplashScreenView(r);
+ break;
+ }
+ }
+
+ @Override
+ public void recycle() {
+ ObjectPool.recycle(this);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mRequest);
+ dest.writeTypedObject(mSplashScreenViewParcelable, flags);
+ }
+
+ private TransferSplashScreenViewStateItem() {}
+ private TransferSplashScreenViewStateItem(Parcel in) {
+ mRequest = in.readInt();
+ mSplashScreenViewParcelable = in.readTypedObject(SplashScreenViewParcelable.CREATOR);
+ }
+
+ /** Obtain an instance initialized with provided params. */
+ public static TransferSplashScreenViewStateItem obtain(@TransferRequest int state,
+ @Nullable SplashScreenViewParcelable parcelable) {
+ TransferSplashScreenViewStateItem instance =
+ ObjectPool.obtain(TransferSplashScreenViewStateItem.class);
+ if (instance == null) {
+ instance = new TransferSplashScreenViewStateItem();
+ }
+ instance.mRequest = state;
+ instance.mSplashScreenViewParcelable = parcelable;
+
+ return instance;
+ }
+
+ public static final @NonNull Creator<TransferSplashScreenViewStateItem> CREATOR =
+ new Creator<TransferSplashScreenViewStateItem>() {
+ public TransferSplashScreenViewStateItem createFromParcel(Parcel in) {
+ return new TransferSplashScreenViewStateItem(in);
+ }
+
+ public TransferSplashScreenViewStateItem[] newArray(int size) {
+ return new TransferSplashScreenViewStateItem[size];
+ }
+ };
+}
diff --git a/core/java/android/app/smartspace/SmartspaceAction.java b/core/java/android/app/smartspace/SmartspaceAction.java
index 439d851acea6..f17b044f7996 100644
--- a/core/java/android/app/smartspace/SmartspaceAction.java
+++ b/core/java/android/app/smartspace/SmartspaceAction.java
@@ -89,7 +89,7 @@ public final class SmartspaceAction implements Parcelable {
mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
mIntent = in.readTypedObject(Intent.CREATOR);
mUserHandle = in.readTypedObject(UserHandle.CREATOR);
- mExtras = in.readTypedObject(Bundle.CREATOR);
+ mExtras = in.readBundle();
}
private SmartspaceAction(
diff --git a/core/java/android/app/smartspace/SmartspaceTargetEvent.java b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
index 1e0653d67ace..920b9fe6a34f 100644
--- a/core/java/android/app/smartspace/SmartspaceTargetEvent.java
+++ b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
@@ -138,6 +138,15 @@ public final class SmartspaceTargetEvent implements Parcelable {
dest.writeInt(mEventType);
}
+ @Override
+ public String toString() {
+ return "SmartspaceTargetEvent{"
+ + "mSmartspaceTarget=" + mSmartspaceTarget
+ + ", mSmartspaceActionId='" + mSmartspaceActionId + '\''
+ + ", mEventType=" + mEventType
+ + '}';
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/app/timedetector/ExternalTimeSuggestion.aidl b/core/java/android/app/time/ExternalTimeSuggestion.aidl
index 14d57bf463fb..07a0fbbcbe8e 100644
--- a/core/java/android/app/timedetector/ExternalTimeSuggestion.aidl
+++ b/core/java/android/app/time/ExternalTimeSuggestion.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.app.timedetector;
+package android.app.time;
parcelable ExternalTimeSuggestion;
diff --git a/core/java/android/app/timedetector/ExternalTimeSuggestion.java b/core/java/android/app/time/ExternalTimeSuggestion.java
index 7ad303a9a1c6..b566eab9867d 100644
--- a/core/java/android/app/timedetector/ExternalTimeSuggestion.java
+++ b/core/java/android/app/time/ExternalTimeSuggestion.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.timedetector;
+package android.app.time;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl
index 462654323cb4..c4546be10601 100644
--- a/core/java/android/app/timedetector/ITimeDetectorService.aidl
+++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl
@@ -16,7 +16,7 @@
package android.app.timedetector;
-import android.app.timedetector.ExternalTimeSuggestion;
+import android.app.time.ExternalTimeSuggestion;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index df8d797e5b90..76f378590ae2 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -19,6 +19,7 @@ package android.app.timedetector;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.app.time.ExternalTimeSuggestion;
import android.content.Context;
import android.os.SystemClock;
import android.os.TimestampedValue;
diff --git a/core/java/android/app/timedetector/TimeDetectorImpl.java b/core/java/android/app/timedetector/TimeDetectorImpl.java
index f80869f0a4fc..ef818ef647d6 100644
--- a/core/java/android/app/timedetector/TimeDetectorImpl.java
+++ b/core/java/android/app/timedetector/TimeDetectorImpl.java
@@ -17,6 +17,7 @@
package android.app.timedetector;
import android.annotation.NonNull;
+import android.app.time.ExternalTimeSuggestion;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 2c1e951b6128..30ea5c476191 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -30,7 +30,7 @@ import java.util.Map;
interface IUsageStatsManager {
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
ParceledListSlice queryUsageStats(int bucketType, long beginTime, long endTime,
- String callingPackage);
+ String callingPackage, int userId);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime,
String callingPackage);
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index f74d16ee9238..31781ec79203 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -23,6 +23,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.UserHandleAware;
import android.app.Activity;
import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
@@ -437,11 +438,12 @@ public final class UsageStatsManager {
* @see #INTERVAL_YEARLY
* @see #INTERVAL_BEST
*/
+ @UserHandleAware
public List<UsageStats> queryUsageStats(int intervalType, long beginTime, long endTime) {
try {
@SuppressWarnings("unchecked")
ParceledListSlice<UsageStats> slice = mService.queryUsageStats(intervalType, beginTime,
- endTime, mContext.getOpPackageName());
+ endTime, mContext.getOpPackageName(), mContext.getUserId());
if (slice != null) {
return slice.getList();
}
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index 565e4cd7fea0..a72877e27943 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -37,7 +37,7 @@ import android.os.ServiceManager;
import android.util.DisplayMetrics;
import android.util.SparseArray;
import android.widget.RemoteViews;
-import android.widget.RemoteViews.OnClickHandler;
+import android.widget.RemoteViews.InteractionHandler;
import com.android.internal.R;
import com.android.internal.appwidget.IAppWidgetHost;
@@ -71,7 +71,7 @@ public class AppWidgetHost {
private final int mHostId;
private final Callbacks mCallbacks;
private final SparseArray<AppWidgetHostView> mViews = new SparseArray<>();
- private OnClickHandler mOnClickHandler;
+ private InteractionHandler mInteractionHandler;
static class Callbacks extends IAppWidgetHost.Stub {
private final WeakReference<Handler> mWeakHandler;
@@ -175,10 +175,10 @@ public class AppWidgetHost {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public AppWidgetHost(Context context, int hostId, OnClickHandler handler, Looper looper) {
+ public AppWidgetHost(Context context, int hostId, InteractionHandler handler, Looper looper) {
mContextOpPackageName = context.getOpPackageName();
mHostId = hostId;
- mOnClickHandler = handler;
+ mInteractionHandler = handler;
mHandler = new UpdateHandler(looper);
mCallbacks = new Callbacks(mHandler);
mDisplayMetrics = context.getResources().getDisplayMetrics();
@@ -401,7 +401,7 @@ public class AppWidgetHost {
return null;
}
AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget);
- view.setOnClickHandler(mOnClickHandler);
+ view.setInteractionHandler(mInteractionHandler);
view.setAppWidget(appWidgetId, appWidget);
synchronized (mViews) {
mViews.put(appWidgetId, view);
@@ -423,7 +423,7 @@ public class AppWidgetHost {
*/
protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
AppWidgetProviderInfo appWidget) {
- return new AppWidgetHostView(context, mOnClickHandler);
+ return new AppWidgetHostView(context, mInteractionHandler);
}
/**
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index a3c3a0e106a3..4277292e19a3 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -16,6 +16,7 @@
package android.appwidget;
+import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityOptions;
import android.compat.annotation.UnsupportedAppUsage;
@@ -29,6 +30,7 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
@@ -47,12 +49,13 @@ import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.RemoteViews;
-import android.widget.RemoteViews.OnClickHandler;
+import android.widget.RemoteViews.InteractionHandler;
import android.widget.RemoteViewsAdapter.RemoteAdapterConnectionCallback;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.Executor;
/**
@@ -87,8 +90,9 @@ public class AppWidgetHostView extends FrameLayout {
View mView;
int mViewMode = VIEW_MODE_NOINIT;
int mLayoutId = -1;
- private OnClickHandler mOnClickHandler;
+ private InteractionHandler mInteractionHandler;
private boolean mOnLightBackground;
+ PointF mCurrentSize = null;
private Executor mAsyncExecutor;
private CancellationSignal mLastExecutionSignal;
@@ -103,9 +107,9 @@ public class AppWidgetHostView extends FrameLayout {
/**
* @hide
*/
- public AppWidgetHostView(Context context, OnClickHandler handler) {
+ public AppWidgetHostView(Context context, InteractionHandler handler) {
this(context, android.R.anim.fade_in, android.R.anim.fade_out);
- mOnClickHandler = getHandler(handler);
+ mInteractionHandler = getHandler(handler);
}
/**
@@ -131,8 +135,8 @@ public class AppWidgetHostView extends FrameLayout {
* @param handler
* @hide
*/
- public void setOnClickHandler(OnClickHandler handler) {
- mOnClickHandler = getHandler(handler);
+ public void setInteractionHandler(InteractionHandler handler) {
+ mInteractionHandler = getHandler(handler);
}
/**
@@ -268,7 +272,8 @@ public class AppWidgetHostView extends FrameLayout {
* Provide guidance about the size of this widget to the AppWidgetManager. The widths and
* heights should correspond to the full area the AppWidgetHostView is given. Padding added by
* the framework will be accounted for automatically. This information gets embedded into the
- * AppWidget options and causes a callback to the AppWidgetProvider.
+ * AppWidget options and causes a callback to the AppWidgetProvider. In addition, the list of
+ * sizes is explicitly set to an empty list.
* @see AppWidgetProvider#onAppWidgetOptionsChanged(Context, AppWidgetManager, int, Bundle)
*
* @param newOptions The bundle of options, in addition to the size information,
@@ -277,14 +282,97 @@ public class AppWidgetHostView extends FrameLayout {
* @param minHeight The maximum height in dips that the widget will be displayed at.
* @param maxWidth The maximum width in dips that the widget will be displayed at.
* @param maxHeight The maximum height in dips that the widget will be displayed at.
- *
+ * @deprecated use {@link AppWidgetHostView#updateAppWidgetSize(Bundle, List)} instead.
*/
+ @Deprecated
public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth,
int maxHeight) {
updateAppWidgetSize(newOptions, minWidth, minHeight, maxWidth, maxHeight, false);
}
/**
+ * Provide guidance about the size of this widget to the AppWidgetManager. The sizes should
+ * correspond to the full area the AppWidgetHostView is given. Padding added by the framework
+ * will be accounted for automatically.
+ *
+ * This method will update the option bundle with the list of sizes and the min/max bounds for
+ * width and height.
+ *
+ * @see AppWidgetProvider#onAppWidgetOptionsChanged(Context, AppWidgetManager, int, Bundle)
+ *
+ * @param newOptions The bundle of options, in addition to the size information.
+ * @param sizes Sizes, in dips, the widget may be displayed at without calling the provider
+ * again. Typically, this will be size of the widget in landscape and portrait.
+ * On some foldables, this might include the size on the outer and inner screens.
+ */
+ public void updateAppWidgetSize(@NonNull Bundle newOptions, @NonNull List<PointF> sizes) {
+ AppWidgetManager widgetManager = AppWidgetManager.getInstance(mContext);
+
+ Rect padding = getDefaultPadding();
+ float density = getResources().getDisplayMetrics().density;
+
+ float xPaddingDips = (padding.left + padding.right) / density;
+ float yPaddingDips = (padding.top + padding.bottom) / density;
+
+ ArrayList<PointF> paddedSizes = new ArrayList<>(sizes.size());
+ float minWidth = Float.MAX_VALUE;
+ float maxWidth = 0;
+ float minHeight = Float.MAX_VALUE;
+ float maxHeight = 0;
+ for (int i = 0; i < sizes.size(); i++) {
+ PointF size = sizes.get(i);
+ PointF paddedPoint = new PointF(Math.max(0.f, size.x - xPaddingDips),
+ Math.max(0.f, size.y - yPaddingDips));
+ paddedSizes.add(paddedPoint);
+ minWidth = Math.min(minWidth, paddedPoint.x);
+ maxWidth = Math.max(maxWidth, paddedPoint.x);
+ minHeight = Math.min(minHeight, paddedPoint.y);
+ maxHeight = Math.max(maxHeight, paddedPoint.y);
+ }
+ if (paddedSizes.equals(
+ widgetManager.getAppWidgetOptions(mAppWidgetId).<PointF>getParcelableArrayList(
+ AppWidgetManager.OPTION_APPWIDGET_SIZES))) {
+ return;
+ }
+ Bundle options = newOptions.deepCopy();
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, (int) minWidth);
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, (int) minHeight);
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, (int) maxWidth);
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, (int) maxHeight);
+ options.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, paddedSizes);
+ updateAppWidgetOptions(options);
+ }
+
+ /**
+ * Set the current size of the widget. This should be the full area the AppWidgetHostView is
+ * given. Padding added by the framework will be accounted for automatically.
+ *
+ * This size will be used to choose the appropriate layout the next time the {@link RemoteViews}
+ * is re-inflated, if it was created with {@link RemoteViews#RemoteViews(Map)} .
+ */
+ public void setCurrentSize(@NonNull PointF size) {
+ Rect padding = getDefaultPadding();
+ float density = getResources().getDisplayMetrics().density;
+ float xPaddingDips = (padding.left + padding.right) / density;
+ float yPaddingDips = (padding.top + padding.bottom) / density;
+ PointF newSize = new PointF(size.x - xPaddingDips, size.y - yPaddingDips);
+ if (!newSize.equals(mCurrentSize)) {
+ mCurrentSize = newSize;
+ mLayoutId = -1; // Prevents recycling the view.
+ }
+ }
+
+ /**
+ * Clear the current size, indicating it is not currently known.
+ */
+ public void clearCurrentSize() {
+ if (mCurrentSize != null) {
+ mCurrentSize = null;
+ mLayoutId = -1;
+ }
+ }
+
+ /**
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -322,6 +410,8 @@ public class AppWidgetHostView extends FrameLayout {
newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, newMinHeight);
newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, newMaxWidth);
newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, newMaxHeight);
+ newOptions.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES,
+ new ArrayList<PointF>());
updateAppWidgetOptions(newOptions);
}
}
@@ -428,7 +518,7 @@ public class AppWidgetHostView extends FrameLayout {
// layout matches, try recycling it
if (content == null && layoutId == mLayoutId) {
try {
- remoteViews.reapply(mContext, mView, mOnClickHandler);
+ remoteViews.reapply(mContext, mView, mInteractionHandler);
content = mView;
recycled = true;
if (LOGD) Log.d(TAG, "was able to recycle existing layout");
@@ -440,7 +530,7 @@ public class AppWidgetHostView extends FrameLayout {
// Try normal RemoteView inflation
if (content == null) {
try {
- content = remoteViews.apply(mContext, this, mOnClickHandler);
+ content = remoteViews.apply(mContext, this, mInteractionHandler, mCurrentSize);
if (LOGD) Log.d(TAG, "had to inflate new layout");
} catch (RuntimeException e) {
exception = e;
@@ -492,7 +582,8 @@ public class AppWidgetHostView extends FrameLayout {
mView,
mAsyncExecutor,
new ViewApplyListener(remoteViews, layoutId, true),
- mOnClickHandler);
+ mInteractionHandler,
+ mCurrentSize);
} catch (Exception e) {
// Reapply failed. Try apply
}
@@ -502,7 +593,8 @@ public class AppWidgetHostView extends FrameLayout {
this,
mAsyncExecutor,
new ViewApplyListener(remoteViews, layoutId, false),
- mOnClickHandler);
+ mInteractionHandler,
+ mCurrentSize);
}
}
@@ -533,7 +625,8 @@ public class AppWidgetHostView extends FrameLayout {
AppWidgetHostView.this,
mAsyncExecutor,
new ViewApplyListener(mViews, mLayoutId, false),
- mOnClickHandler);
+ mInteractionHandler,
+ mCurrentSize);
} else {
applyContent(null, false, e);
}
@@ -715,11 +808,11 @@ public class AppWidgetHostView extends FrameLayout {
return null;
}
- private OnClickHandler getHandler(OnClickHandler handler) {
+ private InteractionHandler getHandler(InteractionHandler handler) {
return (view, pendingIntent, response) -> {
AppWidgetManager.getInstance(mContext).noteAppWidgetTapped(mAppWidgetId);
if (handler != null) {
- return handler.onClickHandler(view, pendingIntent, response);
+ return handler.onInteraction(view, pendingIntent, response);
} else {
return RemoteViews.startPendingIntent(view, pendingIntent,
response.getLaunchOptions(view));
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 37093a10b2f0..aac8710e8691 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -217,6 +217,12 @@ public class AppWidgetManager {
public static final String OPTION_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight";
/**
+ * A bundle extra ({@code List<PointF>}) that contains the list of possible sizes, in dips, a
+ * widget instance can take.
+ */
+ public static final String OPTION_APPWIDGET_SIZES = "appWidgetSizes";
+
+ /**
* A bundle extra that hints to the AppWidgetProvider the category of host that owns this
* this widget. Can have the value {@link
* AppWidgetProviderInfo#WIDGET_CATEGORY_HOME_SCREEN} or {@link
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index e96e22c4764a..d893a5e49aa9 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -179,6 +179,44 @@ public class AppWidgetProviderInfo implements Parcelable {
public int minResizeHeight;
/**
+ * Maximum width (in dp) which the widget can be resized to. This field has no effect if it is
+ * smaller than minWidth or if horizontal resizing isn't enabled (see {@link #resizeMode}).
+ *
+ * <p>This field corresponds to the <code>android:maxResizeWidth</code> attribute in the
+ * AppWidget meta-data file.
+ */
+ @SuppressLint("MutableBareField")
+ public int maxResizeWidth;
+
+ /**
+ * Maximum height (in dp) which the widget can be resized to. This field has no effect if it is
+ * smaller than minHeight or if vertical resizing isn't enabled (see {@link #resizeMode}).
+ *
+ * <p>This field corresponds to the <code>android:maxResizeHeight</code> attribute in the
+ * AppWidget meta-data file.
+ */
+ @SuppressLint("MutableBareField")
+ public int maxResizeHeight;
+
+ /**
+ * The default width of a widget when added to a host, in units of launcher grid cells.
+ *
+ * <p>This field corresponds to the <code>android:targetCellWidth</code> attribute in the
+ * AppWidget meta-data file.
+ */
+ @SuppressLint("MutableBareField")
+ public int targetCellWidth;
+
+ /**
+ * The default height of a widget when added to a host, in units of launcher grid cells.
+ *
+ * <p>This field corresponds to the <code>android:targetCellHeight</code> attribute in the
+ * AppWidget meta-data file.
+ */
+ @SuppressLint("MutableBareField")
+ public int targetCellHeight;
+
+ /**
* How often, in milliseconds, that this AppWidget wants to be updated.
* The AppWidget manager may place a limit on how often a AppWidget is updated.
*
@@ -247,12 +285,29 @@ public class AppWidgetProviderInfo implements Parcelable {
* A preview of what the AppWidget will look like after it's configured.
* If not supplied, the AppWidget's icon will be used.
*
- * <p>This field corresponds to the <code>android:previewImage</code> attribute in
- * the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
+ * <p>This field corresponds to the <code>android:previewImage</code> attribute in the AppWidget
+ * meta-data file.
*/
public int previewImage;
/**
+ * The layout resource id of a preview of what the AppWidget will look like after it's
+ * configured.
+ *
+ * <p>Unlike previewImage, previewLayout can better showcase AppWidget in different locales,
+ * system themes, display sizes & density etc.
+ *
+ * <p>If supplied, this will take precedence over the previewImage on supported widget hosts.
+ * Otherwise, previewImage will be used.
+ *
+ * <p>This field corresponds to the <code>android:previewLayout</code> attribute in the
+ * AppWidget meta-data file.
+ */
+ @SuppressLint("MutableBareField")
+ @IdRes
+ public int previewLayout;
+
+ /**
* The rules by which a widget can be resized. See {@link #RESIZE_NONE},
* {@link #RESIZE_NONE}, {@link #RESIZE_HORIZONTAL},
* {@link #RESIZE_VERTICAL}, {@link #RESIZE_BOTH}.
@@ -313,6 +368,10 @@ public class AppWidgetProviderInfo implements Parcelable {
this.minHeight = in.readInt();
this.minResizeWidth = in.readInt();
this.minResizeHeight = in.readInt();
+ this.maxResizeWidth = in.readInt();
+ this.maxResizeHeight = in.readInt();
+ this.targetCellWidth = in.readInt();
+ this.targetCellHeight = in.readInt();
this.updatePeriodMillis = in.readInt();
this.initialLayout = in.readInt();
this.initialKeyguardLayout = in.readInt();
@@ -320,6 +379,7 @@ public class AppWidgetProviderInfo implements Parcelable {
this.label = in.readString();
this.icon = in.readInt();
this.previewImage = in.readInt();
+ this.previewLayout = in.readInt();
this.autoAdvanceViewId = in.readInt();
this.resizeMode = in.readInt();
this.widgetCategory = in.readInt();
@@ -422,6 +482,10 @@ public class AppWidgetProviderInfo implements Parcelable {
out.writeInt(this.minHeight);
out.writeInt(this.minResizeWidth);
out.writeInt(this.minResizeHeight);
+ out.writeInt(this.maxResizeWidth);
+ out.writeInt(this.maxResizeHeight);
+ out.writeInt(this.targetCellWidth);
+ out.writeInt(this.targetCellHeight);
out.writeInt(this.updatePeriodMillis);
out.writeInt(this.initialLayout);
out.writeInt(this.initialKeyguardLayout);
@@ -429,6 +493,7 @@ public class AppWidgetProviderInfo implements Parcelable {
out.writeString(this.label);
out.writeInt(this.icon);
out.writeInt(this.previewImage);
+ out.writeInt(this.previewLayout);
out.writeInt(this.autoAdvanceViewId);
out.writeInt(this.resizeMode);
out.writeInt(this.widgetCategory);
@@ -444,8 +509,12 @@ public class AppWidgetProviderInfo implements Parcelable {
that.provider = this.provider == null ? null : this.provider.clone();
that.minWidth = this.minWidth;
that.minHeight = this.minHeight;
- that.minResizeWidth = this.minResizeHeight;
+ that.minResizeWidth = this.minResizeWidth;
that.minResizeHeight = this.minResizeHeight;
+ that.maxResizeWidth = this.maxResizeWidth;
+ that.maxResizeHeight = this.maxResizeHeight;
+ that.targetCellWidth = this.targetCellWidth;
+ that.targetCellHeight = this.targetCellHeight;
that.updatePeriodMillis = this.updatePeriodMillis;
that.initialLayout = this.initialLayout;
that.initialKeyguardLayout = this.initialKeyguardLayout;
@@ -453,6 +522,7 @@ public class AppWidgetProviderInfo implements Parcelable {
that.label = this.label;
that.icon = this.icon;
that.previewImage = this.previewImage;
+ that.previewLayout = this.previewLayout;
that.autoAdvanceViewId = this.autoAdvanceViewId;
that.resizeMode = this.resizeMode;
that.widgetCategory = this.widgetCategory;
@@ -492,6 +562,8 @@ public class AppWidgetProviderInfo implements Parcelable {
minHeight = TypedValue.complexToDimensionPixelSize(minHeight, displayMetrics);
minResizeWidth = TypedValue.complexToDimensionPixelSize(minResizeWidth, displayMetrics);
minResizeHeight = TypedValue.complexToDimensionPixelSize(minResizeHeight, displayMetrics);
+ maxResizeWidth = TypedValue.complexToDimensionPixelSize(maxResizeWidth, displayMetrics);
+ maxResizeHeight = TypedValue.complexToDimensionPixelSize(maxResizeHeight, displayMetrics);
}
/**
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 59944c6686f5..a3b10deb9e7f 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1664,7 +1664,7 @@ public final class BluetoothAdapter {
mContext = context;
}
- private String getOpPackageName() {
+ String getOpPackageName() {
// Workaround for legacy API for getting a BluetoothAdapter not
// passing a context
if (mContext != null) {
@@ -3418,6 +3418,61 @@ public final class BluetoothAdapter {
}
/**
+ * Register a callback to receive events whenever the bluetooth stack goes down and back up,
+ * e.g. in the event the bluetooth is turned off/on via settings.
+ *
+ * If the bluetooth stack is currently up, there will not be an initial callback call.
+ * You can use the return value as an indication of this being the case.
+ *
+ * Callbacks will be delivered on a binder thread.
+ *
+ * @return whether bluetooth is already up currently
+ *
+ * @hide
+ */
+ public boolean registerServiceLifecycleCallback(ServiceLifecycleCallback callback) {
+ return getBluetoothService(callback.mRemote) != null;
+ }
+
+ /**
+ * Unregister a callback registered via {@link #registerServiceLifecycleCallback}
+ *
+ * @hide
+ */
+ public void unregisterServiceLifecycleCallback(ServiceLifecycleCallback callback) {
+ removeServiceStateCallback(callback.mRemote);
+ }
+
+ /**
+ * A callback for {@link #registerServiceLifecycleCallback}
+ *
+ * @hide
+ */
+ public abstract static class ServiceLifecycleCallback {
+
+ /** Called when the bluetooth stack is up */
+ public abstract void onBluetoothServiceUp();
+
+ /** Called when the bluetooth stack is down */
+ public abstract void onBluetoothServiceDown();
+
+ IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() {
+ @Override
+ public void onBluetoothServiceUp(IBluetooth bluetoothService) {
+ ServiceLifecycleCallback.this.onBluetoothServiceUp();
+ }
+
+ @Override
+ public void onBluetoothServiceDown() {
+ ServiceLifecycleCallback.this.onBluetoothServiceDown();
+ }
+
+ @Override
+ public void onBrEdrDown() {}
+ };
+ }
+
+ /**
* Starts a scan for Bluetooth LE devices.
*
* <p>Results of the scan are reported using the
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 4e1f4cac744f..550f9e365948 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1385,7 +1385,8 @@ public final class BluetoothDevice implements Parcelable {
return false;
}
try {
- return service.createBond(this, transport, oobData);
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ return service.createBond(this, transport, oobData, adapter.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1559,6 +1560,31 @@ public final class BluetoothDevice implements Parcelable {
}
/**
+ * Checks whether this bluetooth device is associated with CDM and meets the criteria to skip
+ * the bluetooth pairing dialog because it has been already consented by the CDM prompt.
+ *
+ * @return true if we can bond without the dialog, false otherwise
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean canBondWithoutDialog() {
+ final IBluetooth service = sService;
+ if (service == null) {
+ Log.e(TAG, "BT not enabled. Cannot check if we can skip pairing dialog");
+ return false;
+ }
+ try {
+ if (DBG) Log.d(TAG, "canBondWithoutDialog, device: " + this);
+ return service.canBondWithoutDialog(this);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ return false;
+ }
+
+ /**
* Returns whether there is an open connection to this device.
*
* @return True if there is at least one open connection to this device.
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 6b79964832dc..9c0bcee93eeb 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -167,6 +167,11 @@ public final class BluetoothUuid {
/** @hide */
@NonNull
@SystemApi
+ public static final ParcelUuid VOLUME_CONTROL =
+ ParcelUuid.fromString("00001844-0000-1000-8000-00805F9B34FB");
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid BASE_UUID =
ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java
index 17bf11b3d4ef..9007d9d8bbcc 100644
--- a/core/java/android/companion/Association.java
+++ b/core/java/android/companion/Association.java
@@ -23,6 +23,7 @@ import android.os.Parcelable;
import com.android.internal.util.DataClass;
+import java.util.Date;
import java.util.Objects;
/**
@@ -38,16 +39,23 @@ public final class Association implements Parcelable {
private final @NonNull String mDeviceMacAddress;
private final @NonNull String mPackageName;
private final @Nullable String mDeviceProfile;
- private final boolean mKeepProfilePrivilegesWhenDeviceAway;
+ private final boolean mNotifyOnDeviceNearby;
+ private final long mTimeApprovedMs;
/** @hide */
public int getUserId() {
return mUserId;
}
+ private String timeApprovedMsToString() {
+ return new Date(mTimeApprovedMs).toString();
+ }
+
+
+
- // Code below generated by codegen v1.0.21.
+ // Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -71,7 +79,8 @@ public final class Association implements Parcelable {
@NonNull String deviceMacAddress,
@NonNull String packageName,
@Nullable String deviceProfile,
- boolean keepProfilePrivilegesWhenDeviceAway) {
+ boolean notifyOnDeviceNearby,
+ long timeApprovedMs) {
this.mUserId = userId;
com.android.internal.util.AnnotationValidations.validate(
UserIdInt.class, null, mUserId);
@@ -82,7 +91,8 @@ public final class Association implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPackageName);
this.mDeviceProfile = deviceProfile;
- this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway;
+ this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
+ this.mTimeApprovedMs = timeApprovedMs;
// onConstructed(); // You can define this method to get a callback
}
@@ -103,8 +113,13 @@ public final class Association implements Parcelable {
}
@DataClass.Generated.Member
- public boolean isKeepProfilePrivilegesWhenDeviceAway() {
- return mKeepProfilePrivilegesWhenDeviceAway;
+ public boolean isNotifyOnDeviceNearby() {
+ return mNotifyOnDeviceNearby;
+ }
+
+ @DataClass.Generated.Member
+ public long getTimeApprovedMs() {
+ return mTimeApprovedMs;
}
@Override
@@ -118,7 +133,8 @@ public final class Association implements Parcelable {
"deviceMacAddress = " + mDeviceMacAddress + ", " +
"packageName = " + mPackageName + ", " +
"deviceProfile = " + mDeviceProfile + ", " +
- "keepProfilePrivilegesWhenDeviceAway = " + mKeepProfilePrivilegesWhenDeviceAway +
+ "notifyOnDeviceNearby = " + mNotifyOnDeviceNearby + ", " +
+ "timeApprovedMs = " + timeApprovedMsToString() +
" }";
}
@@ -139,7 +155,8 @@ public final class Association implements Parcelable {
&& Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
&& Objects.equals(mPackageName, that.mPackageName)
&& Objects.equals(mDeviceProfile, that.mDeviceProfile)
- && mKeepProfilePrivilegesWhenDeviceAway == that.mKeepProfilePrivilegesWhenDeviceAway;
+ && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby
+ && mTimeApprovedMs == that.mTimeApprovedMs;
}
@Override
@@ -153,7 +170,8 @@ public final class Association implements Parcelable {
_hash = 31 * _hash + Objects.hashCode(mDeviceMacAddress);
_hash = 31 * _hash + Objects.hashCode(mPackageName);
_hash = 31 * _hash + Objects.hashCode(mDeviceProfile);
- _hash = 31 * _hash + Boolean.hashCode(mKeepProfilePrivilegesWhenDeviceAway);
+ _hash = 31 * _hash + Boolean.hashCode(mNotifyOnDeviceNearby);
+ _hash = 31 * _hash + Long.hashCode(mTimeApprovedMs);
return _hash;
}
@@ -164,13 +182,14 @@ public final class Association implements Parcelable {
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
- if (mKeepProfilePrivilegesWhenDeviceAway) flg |= 0x10;
+ if (mNotifyOnDeviceNearby) flg |= 0x10;
if (mDeviceProfile != null) flg |= 0x8;
dest.writeByte(flg);
dest.writeInt(mUserId);
dest.writeString(mDeviceMacAddress);
dest.writeString(mPackageName);
if (mDeviceProfile != null) dest.writeString(mDeviceProfile);
+ dest.writeLong(mTimeApprovedMs);
}
@Override
@@ -185,11 +204,12 @@ public final class Association implements Parcelable {
// static FieldType unparcelFieldName(Parcel in) { ... }
byte flg = in.readByte();
- boolean keepProfilePrivilegesWhenDeviceAway = (flg & 0x10) != 0;
+ boolean notifyOnDeviceNearby = (flg & 0x10) != 0;
int userId = in.readInt();
String deviceMacAddress = in.readString();
String packageName = in.readString();
String deviceProfile = (flg & 0x8) == 0 ? null : in.readString();
+ long timeApprovedMs = in.readLong();
this.mUserId = userId;
com.android.internal.util.AnnotationValidations.validate(
@@ -201,7 +221,8 @@ public final class Association implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPackageName);
this.mDeviceProfile = deviceProfile;
- this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway;
+ this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
+ this.mTimeApprovedMs = timeApprovedMs;
// onConstructed(); // You can define this method to get a callback
}
@@ -221,10 +242,10 @@ public final class Association implements Parcelable {
};
@DataClass.Generated(
- time = 1606940835778L,
- codegenVersion = "1.0.21",
+ time = 1612832377589L,
+ codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/companion/Association.java",
- inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final boolean mKeepProfilePrivilegesWhenDeviceAway\npublic int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
+ inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final boolean mNotifyOnDeviceNearby\nprivate final long mTimeApprovedMs\npublic int getUserId()\nprivate java.lang.String timeApprovedMsToString()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 527d8df94ea0..95d3515abb80 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -49,4 +49,6 @@ interface ICompanionDeviceManager {
void registerDevicePresenceListenerService(in String packageName, in String deviceAddress);
void unregisterDevicePresenceListenerService(in String packageName, in String deviceAddress);
+
+ boolean canPairWithoutPrompt(in String packageName, in String deviceMacAddress, int userId);
}
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 018863774184..f3ecbf6c08f3 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -740,6 +740,7 @@ public class ClipData implements Parcelable {
mIcon = null;
mItems = new ArrayList<Item>();
mItems.add(item);
+ mClipDescription.setIsStyledText(isStyledText());
}
/**
@@ -756,6 +757,7 @@ public class ClipData implements Parcelable {
mIcon = null;
mItems = new ArrayList<Item>();
mItems.add(item);
+ mClipDescription.setIsStyledText(isStyledText());
}
/**
@@ -914,6 +916,9 @@ public class ClipData implements Parcelable {
throw new NullPointerException("item is null");
}
mItems.add(item);
+ if (mItems.size() == 1) {
+ mClipDescription.setIsStyledText(isStyledText());
+ }
}
/**
@@ -1049,6 +1054,20 @@ public class ClipData implements Parcelable {
}
}
+ private boolean isStyledText() {
+ if (mItems.isEmpty()) {
+ return false;
+ }
+ final CharSequence text = mItems.get(0).getText();
+ if (text instanceof Spanned) {
+ Spanned spanned = (Spanned) text;
+ if (TextUtils.hasStyleSpan(spanned)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public String toString() {
StringBuilder b = new StringBuilder(128);
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index e3395e20947d..d48f83223d3e 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -120,6 +120,7 @@ public class ClipDescription implements Parcelable {
private final ArrayList<String> mMimeTypes;
private PersistableBundle mExtras;
private long mTimeStamp;
+ private boolean mIsStyledText;
/**
* Create a new clip.
@@ -325,6 +326,26 @@ public class ClipDescription implements Parcelable {
}
}
+ /**
+ * Returns true if the first item of the associated {@link ClipData} contains styled text, i.e.
+ * if it contains spans such as {@link android.text.style.CharacterStyle CharacterStyle}, {@link
+ * android.text.style.ParagraphStyle ParagraphStyle}, or {@link
+ * android.text.style.UpdateAppearance UpdateAppearance}. Returns false if it does not, or if
+ * there is no associated clip data.
+ */
+ public boolean isStyledText() {
+ return mIsStyledText;
+ }
+
+ /**
+ * Sets whether the associated {@link ClipData} contains styled text in its first item. This
+ * should be called when this description is associated with clip data or when the first item
+ * is added to the associated clip data.
+ */
+ void setIsStyledText(boolean isStyledText) {
+ mIsStyledText = isStyledText;
+ }
+
@Override
public String toString() {
StringBuilder b = new StringBuilder(128);
@@ -429,6 +450,7 @@ public class ClipDescription implements Parcelable {
dest.writeStringList(mMimeTypes);
dest.writePersistableBundle(mExtras);
dest.writeLong(mTimeStamp);
+ dest.writeBoolean(mIsStyledText);
}
ClipDescription(Parcel in) {
@@ -436,6 +458,7 @@ public class ClipDescription implements Parcelable {
mMimeTypes = in.createStringArrayList();
mExtras = in.readPersistableBundle();
mTimeStamp = in.readLong();
+ mIsStyledText = in.readBoolean();
}
public static final @android.annotation.NonNull Parcelable.Creator<ClipDescription> CREATOR =
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index af4a059dbb7f..61f7ee839538 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -63,7 +63,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.system.Int32Ref;
+import android.system.Int64Ref;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -4053,7 +4053,7 @@ public abstract class ContentResolver implements ContentInterface {
// Convert to Point, since that's what the API is defined as
final Bundle opts = new Bundle();
opts.putParcelable(EXTRA_SIZE, new Point(size.getWidth(), size.getHeight()));
- final Int32Ref orientation = new Int32Ref(0);
+ final Int64Ref orientation = new Int64Ref(0);
Bitmap bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
final AssetFileDescriptor afd = content.openTypedAssetFile(uri, "image/*", opts,
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5d28216756ae..4284dc2117c5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -93,6 +93,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
import java.util.concurrent.Executor;
/**
@@ -3479,6 +3480,7 @@ public abstract class Context {
STORAGE_STATS_SERVICE,
WALLPAPER_SERVICE,
TIME_ZONE_RULES_MANAGER_SERVICE,
+ VIBRATOR_MANAGER_SERVICE,
VIBRATOR_SERVICE,
//@hide: STATUS_BAR_SERVICE,
CONNECTIVITY_SERVICE,
@@ -3519,6 +3521,7 @@ public abstract class Context {
APPWIDGET_SERVICE,
//@hide: VOICE_INTERACTION_MANAGER_SERVICE,
//@hide: BACKUP_SERVICE,
+ REBOOT_READINESS_SERVICE,
ROLLBACK_SERVICE,
DROPBOX_SERVICE,
//@hide: DEVICE_IDLE_CONTROLLER,
@@ -3552,6 +3555,7 @@ public abstract class Context {
//@hide: NETWORK_SCORE_SERVICE,
USAGE_STATS_SERVICE,
MEDIA_SESSION_SERVICE,
+ MEDIA_COMMUNICATION_SERVICE,
BATTERY_SERVICE,
JOB_SCHEDULER_SERVICE,
//@hide: PERSISTENT_DATA_BLOCK_SERVICE,
@@ -3624,9 +3628,11 @@ public abstract class Context {
* (e.g., GPS) updates.
* <dt> {@link #SEARCH_SERVICE} ("search")
* <dd> A {@link android.app.SearchManager} for handling search.
+ * <dt> {@link #VIBRATOR_MANAGER_SERVICE} ("vibrator_manager")
+ * <dd> A {@link android.os.VibratorManager} for accessing the device vibrators, interacting
+ * with individual ones and playing synchronized effects on multiple vibrators.
* <dt> {@link #VIBRATOR_SERVICE} ("vibrator")
- * <dd> A {@link android.os.Vibrator} for interacting with the vibrator
- * hardware.
+ * <dd> A {@link android.os.Vibrator} for interacting with the vibrator hardware.
* <dt> {@link #CONNECTIVITY_SERVICE} ("connectivity")
* <dd> A {@link android.net.ConnectivityManager ConnectivityManager} for
* handling management of network connections.
@@ -3706,6 +3712,8 @@ public abstract class Context {
* @see android.hardware.SensorManager
* @see #STORAGE_SERVICE
* @see android.os.storage.StorageManager
+ * @see #VIBRATOR_MANAGER_SERVICE
+ * @see android.os.VibratorManager
* @see #VIBRATOR_SERVICE
* @see android.os.Vibrator
* @see #CONNECTIVITY_SERVICE
@@ -4033,8 +4041,19 @@ public abstract class Context {
public static final String WALLPAPER_SERVICE = "wallpaper";
/**
- * Use with {@link #getSystemService(String)} to retrieve a {@link
- * android.os.Vibrator} for interacting with the vibration hardware.
+ * Use with {@link #getSystemService(String)} to retrieve a {@link android.os.VibratorManager}
+ * for accessing the device vibrators, interacting with individual ones and playing synchronized
+ * effects on multiple vibrators.
+ *
+ * @see #getSystemService(String)
+ * @see android.os.VibratorManager
+ */
+ @SuppressLint("ServiceName")
+ public static final String VIBRATOR_MANAGER_SERVICE = "vibrator_manager";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a {@link android.os.Vibrator} for
+ * interacting with the vibration hardware.
*
* @see #getSystemService(String)
* @see android.os.Vibrator
@@ -4564,6 +4583,14 @@ public abstract class Context {
public static final String AUTOFILL_MANAGER_SERVICE = "autofill";
/**
+ * Official published name of the (internal) text to speech manager service.
+ *
+ * @hide
+ * @see #getSystemService(String)
+ */
+ public static final String TEXT_TO_SPEECH_MANAGER_SERVICE = "texttospeech";
+
+ /**
* Official published name of the content capture service.
*
* @hide
@@ -4725,6 +4752,17 @@ public abstract class Context {
public static final String ROLLBACK_SERVICE = "rollback";
/**
+ * Use with {@link #getSystemService(String)} to retrieve an
+ * {@link android.scheduling.RebootReadinessManagerService} for communicating
+ * with the reboot readiness detector.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ @SystemApi
+ public static final String REBOOT_READINESS_SERVICE = "reboot_readiness";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.DropBoxManager} instance for recording
* diagnostic logs.
@@ -5461,6 +5499,14 @@ public abstract class Context {
public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
/**
+ * Use with {@link #getSystemService(String)} to access
+ * {@link android.view.displayhash.DisplayHashManager} to handle display hashes.
+ *
+ * @see #getSystemService(String)
+ */
+ public static final String DISPLAY_HASH_SERVICE = "display_hash";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
@@ -5708,6 +5754,32 @@ public abstract class Context {
public abstract int checkUriPermission(Uri uri, int pid, int uid,
@Intent.AccessUriMode int modeFlags);
+ /**
+ * Determine whether a particular process and user ID has been granted
+ * permission to access a list of URIs. This only checks for permissions
+ * that have been explicitly granted -- if the given process/uid has
+ * more general access to the URI's content provider then this check will
+ * always fail.
+ *
+ * @param uris The list of URIs that is being checked.
+ * @param pid The process ID being checked against. Must be &gt; 0.
+ * @param uid The user ID being checked against. A uid of 0 is the root
+ * user, which will pass every permission check.
+ * @param modeFlags The access modes to check for the list of uris
+ *
+ * @return Array of permission grants corresponding to each entry in the list of uris.
+ * {@link PackageManager#PERMISSION_GRANTED} if the given pid/uid is allowed to access that uri,
+ * or {@link PackageManager#PERMISSION_DENIED} if it is not.
+ *
+ * @see #checkCallingUriPermission
+ */
+ @NonNull
+ @PackageManager.PermissionResult
+ public int[] checkUriPermissions(@NonNull List<Uri> uris, int pid, int uid,
+ @Intent.AccessUriMode int modeFlags) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
/** @hide */
@SuppressWarnings("HiddenAbstractMethod")
@PackageManager.PermissionResult
@@ -5738,6 +5810,32 @@ public abstract class Context {
public abstract int checkCallingUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags);
/**
+ * Determine whether the calling process and user ID has been
+ * granted permission to access a list of URIs. This is basically
+ * the same as calling {@link #checkUriPermissions(List, int, int, int)}
+ * with the pid and uid returned by {@link
+ * android.os.Binder#getCallingPid} and {@link
+ * android.os.Binder#getCallingUid}. One important difference is
+ * that if you are not currently processing an IPC, this function
+ * will always fail.
+ *
+ * @param uris The list of URIs that is being checked.
+ * @param modeFlags The access modes to check.
+ *
+ * @return Array of permission grants corresponding to each entry in the list of uris.
+ * {@link PackageManager#PERMISSION_GRANTED} if the given pid/uid is allowed to access that uri,
+ * or {@link PackageManager#PERMISSION_DENIED} if it is not.
+ *
+ * @see #checkUriPermission(Uri, int, int, int)
+ */
+ @NonNull
+ @PackageManager.PermissionResult
+ public int[] checkCallingUriPermissions(@NonNull List<Uri> uris,
+ @Intent.AccessUriMode int modeFlags) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* Determine whether the calling process of an IPC <em>or you</em> has been granted
* permission to access a specific URI. This is the same as
* {@link #checkCallingUriPermission}, except it grants your own permissions
@@ -5758,6 +5856,28 @@ public abstract class Context {
@Intent.AccessUriMode int modeFlags);
/**
+ * Determine whether the calling process of an IPC <em>or you</em> has been granted
+ * permission to access a list of URIs. This is the same as
+ * {@link #checkCallingUriPermission}, except it grants your own permissions
+ * if you are not currently processing an IPC. Use with care!
+ *
+ * @param uris The list of URIs that is being checked.
+ * @param modeFlags The access modes to check.
+ *
+ * @return Array of permission grants corresponding to each entry in the list of uris.
+ * {@link PackageManager#PERMISSION_GRANTED} if the given pid/uid is allowed to access that uri,
+ * or {@link PackageManager#PERMISSION_DENIED} if it is not.
+ *
+ * @see #checkCallingUriPermission
+ */
+ @NonNull
+ @PackageManager.PermissionResult
+ public int[] checkCallingOrSelfUriPermissions(@NonNull List<Uri> uris,
+ @Intent.AccessUriMode int modeFlags) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* Check both a Uri and normal permission. This allows you to perform
* both {@link #checkPermission} and {@link #checkUriPermission} in one
* call.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index c1c213eee81b..b71fb2712c24 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -53,6 +53,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
import java.util.concurrent.Executor;
/**
@@ -905,6 +906,13 @@ public class ContextWrapper extends Context {
return mBase.checkUriPermission(uri, pid, uid, modeFlags);
}
+ @NonNull
+ @Override
+ public int[] checkUriPermissions(@NonNull List<Uri> uris, int pid, int uid,
+ int modeFlags) {
+ return mBase.checkUriPermissions(uris, pid, uid, modeFlags);
+ }
+
/** @hide */
@Override
public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) {
@@ -916,11 +924,23 @@ public class ContextWrapper extends Context {
return mBase.checkCallingUriPermission(uri, modeFlags);
}
+ @NonNull
+ @Override
+ public int[] checkCallingUriPermissions(@NonNull List<Uri> uris, int modeFlags) {
+ return mBase.checkCallingUriPermissions(uris, modeFlags);
+ }
+
@Override
public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) {
return mBase.checkCallingOrSelfUriPermission(uri, modeFlags);
}
+ @NonNull
+ @Override
+ public int[] checkCallingOrSelfUriPermissions(@NonNull List<Uri> uris, int modeFlags) {
+ return mBase.checkCallingOrSelfUriPermissions(uris, modeFlags);
+ }
+
@Override
public int checkUriPermission(@Nullable Uri uri, @Nullable String readPermission,
@Nullable String writePermission, int pid, int uid, int modeFlags) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3f387d5fd72e..2765b3eaa585 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4766,6 +4766,32 @@ public class Intent implements Parcelable, Cloneable {
public static final String ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION =
"android.intent.action.PACKAGE_NEEDS_INTEGRITY_VERIFICATION";
+ /**
+ * Broadcast Action: Indicates that the device's reboot readiness has changed.
+ *
+ * <p>This broadcast will be sent with an extra that indicates whether or not the device is
+ * ready to reboot.
+ * <p>
+ * The receiver <em>must</em> have the {@link android.Manifest.permission#REBOOT} permission.
+ * <p class="note">
+ * This is a protected intent that can only be sent by the system.
+ *
+ * @see #EXTRA_IS_READY_TO_REBOOT
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_REBOOT_READY = "android.intent.action.REBOOT_READY";
+
+ /**
+ * A boolean extra used with {@link #ACTION_REBOOT_READY} which indicates if the
+ * device is ready to reboot.
+ * Will be {@code true} if ready to reboot, {@code false} otherwise.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_IS_READY_TO_REBOOT = "android.intent.extra.IS_READY_TO_REBOOT";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent categories (see addCategory()).
@@ -5730,7 +5756,7 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
/**
- * Used in the extra field in the remote intent. It's astring token passed with the
+ * Used in the extra field in the remote intent. It's a string token passed with the
* remote intent.
*/
public static final String EXTRA_REMOTE_INTENT_TOKEN =
@@ -6074,6 +6100,16 @@ public class Intent implements Parcelable, Cloneable {
*/
public static final String EXTRA_UNSTARTABLE_REASON = "android.intent.extra.UNSTARTABLE_REASON";
+ /**
+ * A boolean extra indicating whether an activity is bubbled. Set on the shortcut or
+ * pending intent provided for the bubble. If the extra is not present or false, then it is not
+ * bubbled.
+ *
+ * @see android.app.Notification.Builder#setBubbleMetadata(Notification.BubbleMetadata)
+ * @see android.app.Notification.BubbleMetadata.Builder#Builder(String)
+ */
+ public static final String EXTRA_IS_BUBBLED = "android.intent.extra.IS_BUBBLED";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
diff --git a/core/java/android/content/om/CriticalOverlayInfo.java b/core/java/android/content/om/CriticalOverlayInfo.java
new file mode 100644
index 000000000000..8fbc698f368a
--- /dev/null
+++ b/core/java/android/content/om/CriticalOverlayInfo.java
@@ -0,0 +1,65 @@
+/*
+ * 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.content.om;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+/**
+ * A subset of {@link OverlayInfo} fields that when changed cause the overlay's settings to be
+ * completely reinitialized.
+ *
+ * @hide
+ */
+public interface CriticalOverlayInfo {
+
+ /**
+ * @return the package name of the overlay.
+ */
+ @NonNull
+ String getPackageName();
+
+ /**
+ * @return the unique name of the overlay within its containing package.
+ */
+ @Nullable
+ String getOverlayName();
+
+ /**
+ * @return the target package name of the overlay.
+ */
+ @NonNull
+ String getTargetPackageName();
+
+ /**
+ * @return the name of the target overlayable declaration.
+ */
+ @Nullable
+ String getTargetOverlayableName();
+
+ /**
+ * @return an identifier representing the current overlay.
+ */
+ @NonNull
+ OverlayIdentifier getOverlayIdentifier();
+
+ /**
+ * Returns whether or not the overlay is a {@link FabricatedOverlay}.
+ */
+ boolean isFabricated();
+}
diff --git a/core/java/android/content/om/FabricatedOverlay.java b/core/java/android/content/om/FabricatedOverlay.java
new file mode 100644
index 000000000000..d62b47b34dcb
--- /dev/null
+++ b/core/java/android/content/om/FabricatedOverlay.java
@@ -0,0 +1,126 @@
+/*
+ * 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.content.om;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.FabricatedOverlayInternal;
+import android.os.FabricatedOverlayInternalEntry;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+
+/**
+ * Fabricated Runtime Resource Overlays (FRROs) are overlays generated ar runtime.
+ *
+ * Fabricated overlays are enabled, disabled, and reordered just like normal overlays. The
+ * overlayable policies a fabricated overlay fulfills are the same policies the creator of the
+ * overlay fulfill. For example, a fabricated overlay created by a platform signed package on the
+ * system partition would fulfil the {@code system} and {@code signature} policies.
+ *
+ * The owner of a fabricated overlay is the UID that created it. Overlays commit to the overlay
+ * manager persist across reboots. When the UID is uninstalled, its fabricated overlays are wiped.
+ *
+ * Processes with {@link Android.Manifest.permission.CHANGE_OVERLAY_PACKAGES} can manage normal
+ * overlays and fabricated overlays.
+ * @hide
+ */
+public class FabricatedOverlay {
+
+ /** Retrieves the identifier for this fabricated overlay. */
+ public OverlayIdentifier getIdentifier() {
+ return new OverlayIdentifier(
+ mOverlay.packageName, TextUtils.nullIfEmpty(mOverlay.overlayName));
+ }
+
+ public static class Builder {
+ private final String mOwningPackage;
+ private final String mName;
+ private final String mTargetPackage;
+ private String mTargetOverlayable = "";
+ private final ArrayList<FabricatedOverlayInternalEntry> mEntries = new ArrayList<>();
+
+ /**
+ * Constructs a build for a fabricated overlay.
+ *
+ * @param owningPackage the name of the package that owns the fabricated overlay (must
+ * be a package name of this UID).
+ * @param name a name used to uniquely identify the fabricated overlay owned by
+ * {@param owningPackageName}
+ * @param targetPackage the name of the package to overlay
+ */
+ public Builder(@NonNull String owningPackage, @NonNull String name,
+ @NonNull String targetPackage) {
+ Preconditions.checkStringNotEmpty(owningPackage,
+ "'owningPackage' must not be empty nor null");
+ Preconditions.checkStringNotEmpty(name,
+ "'name'' must not be empty nor null");
+ Preconditions.checkStringNotEmpty(targetPackage,
+ "'targetPackage' must not be empty nor null");
+
+ mOwningPackage = owningPackage;
+ mName = name;
+ mTargetPackage = targetPackage;
+ }
+
+ /**
+ * Sets the name of the overlayable resources to overlay (can be null).
+ */
+ public Builder setTargetOverlayable(@Nullable String targetOverlayable) {
+ mTargetOverlayable = TextUtils.emptyIfNull(targetOverlayable);
+ return this;
+ }
+
+ /**
+ * Sets the value of
+ *
+ * @param resourceName name of the target resource to overlay (in the form
+ * [package]:type/entry)
+ * @param dataType the data type of the new value
+ * @param value the unsigned 32 bit integer representing the new value
+ *
+ * @see android.util.TypedValue#type
+ */
+ public Builder setResourceValue(@NonNull String resourceName, int dataType, int value) {
+ final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
+ entry.resourceName = resourceName;
+ entry.dataType = dataType;
+ entry.data = value;
+ mEntries.add(entry);
+ return this;
+ }
+
+ /** Builds an immutable fabricated overlay. */
+ public FabricatedOverlay build() {
+ final FabricatedOverlayInternal overlay = new FabricatedOverlayInternal();
+ overlay.packageName = mOwningPackage;
+ overlay.overlayName = mName;
+ overlay.targetPackageName = mTargetPackage;
+ overlay.targetOverlayable = mTargetOverlayable;
+ overlay.entries = new ArrayList<>();
+ overlay.entries.addAll(mEntries);
+ return new FabricatedOverlay(overlay);
+ }
+ }
+
+ final FabricatedOverlayInternal mOverlay;
+ private FabricatedOverlay(FabricatedOverlayInternal overlay) {
+ mOverlay = overlay;
+ }
+}
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index 0b950b461285..e319d2c4655c 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -16,6 +16,7 @@
package android.content.om;
+import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.content.om.OverlayManagerTransaction;
@@ -66,6 +67,17 @@ interface IOverlayManager {
OverlayInfo getOverlayInfo(in String packageName, in int userId);
/**
+ * Returns information about the overlay with the given package name for the
+ * specified user.
+ *
+ * @param packageName The name of the overlay package.
+ * @param userId The user to get the OverlayInfo for.
+ * @return The OverlayInfo for the overlay package; or null if no such
+ * overlay package exists.
+ */
+ OverlayInfo getOverlayInfoByIdentifier(in OverlayIdentifier packageName, in int userId);
+
+ /**
* Request that an overlay package be enabled or disabled when possible to
* do so.
*
@@ -163,7 +175,7 @@ interface IOverlayManager {
* Invalidates and removes the idmap for an overlay,
* @param packageName The name of the overlay package whose idmap should be deleted.
*/
- void invalidateCachesForOverlay(in String packageName, in int userIs);
+ void invalidateCachesForOverlay(in String packageName, in int userId);
/**
* Perform a series of requests related to overlay packages. This is an
diff --git a/core/java/android/content/om/OverlayIdentifier.aidl b/core/java/android/content/om/OverlayIdentifier.aidl
new file mode 100644
index 000000000000..d1c7770f9168
--- /dev/null
+++ b/core/java/android/content/om/OverlayIdentifier.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.content.om;
+
+parcelable OverlayIdentifier;
diff --git a/core/java/android/content/om/OverlayIdentifier.java b/core/java/android/content/om/OverlayIdentifier.java
new file mode 100644
index 000000000000..454d0d120f2f
--- /dev/null
+++ b/core/java/android/content/om/OverlayIdentifier.java
@@ -0,0 +1,208 @@
+/*
+ * 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.content.om;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.util.Objects;
+
+/**
+ * A key used to uniquely identify a Runtime Resource Overlay (RRO).
+ *
+ * An overlay always belongs to a package and may optionally have a name associated with it.
+ * The name helps uniquely identify a particular overlay within a package.
+ * @hide
+ */
+/** @hide */
+@DataClass(genConstructor = false, genBuilder = false, genHiddenBuilder = false,
+ genEqualsHashCode = true, genToString = false)
+public class OverlayIdentifier implements Parcelable {
+ /**
+ * The package name containing or owning the overlay.
+ */
+ @Nullable
+ private final String mPackageName;
+
+ /**
+ * The unique name within the package of the overlay.
+ */
+ @Nullable
+ private final String mOverlayName;
+
+ /**
+ * Creates an identifier from a package and unique name within the package.
+ *
+ * @param packageName the package containing or owning the overlay
+ * @param overlayName the unique name of the overlay within the package
+ */
+ public OverlayIdentifier(@NonNull String packageName, @Nullable String overlayName) {
+ mPackageName = packageName;
+ mOverlayName = overlayName;
+ }
+
+ /**
+ * Creates an identifier for an overlay without a name.
+ *
+ * @param packageName the package containing or owning the overlay
+ */
+ public OverlayIdentifier(@NonNull String packageName) {
+ mPackageName = packageName;
+ mOverlayName = null;
+ }
+
+ @Override
+ public String toString() {
+ return mOverlayName == null ? mPackageName : mPackageName + ":" + mOverlayName;
+ }
+
+ /** @hide */
+ public static OverlayIdentifier fromString(@NonNull String text) {
+ final String[] parts = text.split(":", 2);
+ if (parts.length == 2) {
+ return new OverlayIdentifier(parts[0], parts[1]);
+ } else {
+ return new OverlayIdentifier(parts[0]);
+ }
+ }
+
+
+
+ // 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/content/om/OverlayIdentifier.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Retrieves the package name containing or owning the overlay.
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * Retrieves the unique name within the package of the overlay.
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getOverlayName() {
+ return mOverlayName;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(OverlayIdentifier other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ OverlayIdentifier that = (OverlayIdentifier) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && Objects.equals(mPackageName, that.mPackageName)
+ && Objects.equals(mOverlayName, that.mOverlayName);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + Objects.hashCode(mPackageName);
+ _hash = 31 * _hash + Objects.hashCode(mOverlayName);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mPackageName != null) flg |= 0x1;
+ if (mOverlayName != null) flg |= 0x2;
+ dest.writeByte(flg);
+ if (mPackageName != null) dest.writeString(mPackageName);
+ if (mOverlayName != null) dest.writeString(mOverlayName);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected OverlayIdentifier(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ String packageName = (flg & 0x1) == 0 ? null : in.readString();
+ String overlayName = (flg & 0x2) == 0 ? null : in.readString();
+
+ this.mPackageName = packageName;
+ this.mOverlayName = overlayName;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<OverlayIdentifier> CREATOR
+ = new Parcelable.Creator<OverlayIdentifier>() {
+ @Override
+ public OverlayIdentifier[] newArray(int size) {
+ return new OverlayIdentifier[size];
+ }
+
+ @Override
+ public OverlayIdentifier createFromParcel(@NonNull Parcel in) {
+ return new OverlayIdentifier(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1612482438728L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/content/om/OverlayIdentifier.java",
+ inputSignatures = "private final @android.annotation.Nullable java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mOverlayName\npublic @java.lang.Override java.lang.String toString()\npublic static android.content.om.OverlayIdentifier fromString(java.lang.String)\nclass OverlayIdentifier extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genHiddenBuilder=false, genEqualsHashCode=true, genToString=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index 517e4bda68d7..c66f49cab088 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -26,6 +26,8 @@ import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
@@ -37,7 +39,7 @@ import java.util.Objects;
* @hide
*/
@SystemApi
-public final class OverlayInfo implements Parcelable {
+public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
/** @hide */
@IntDef(prefix = "STATE_", value = {
@@ -143,6 +145,14 @@ public final class OverlayInfo implements Parcelable {
public final String packageName;
/**
+ * The unique name within the package of the overlay.
+ *
+ * @hide
+ */
+ @Nullable
+ public final String overlayName;
+
+ /**
* Package name of the target package
*
* @hide
@@ -201,6 +211,14 @@ public final class OverlayInfo implements Parcelable {
*/
public final boolean isMutable;
+ private OverlayIdentifier mIdentifierCached;
+
+ /**
+ *
+ * @hide
+ */
+ public final boolean isFabricated;
+
/**
* Create a new OverlayInfo based on source with an updated state.
*
@@ -210,17 +228,28 @@ public final class OverlayInfo implements Parcelable {
* @hide
*/
public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
- this(source.packageName, source.targetPackageName, source.targetOverlayableName,
- source.category, source.baseCodePath, state, source.userId, source.priority,
- source.isMutable);
+ this(source.packageName, source.overlayName, source.targetPackageName,
+ source.targetOverlayableName, source.category, source.baseCodePath, state,
+ source.userId, source.priority, source.isMutable, source.isFabricated);
}
/** @hide */
+ @VisibleForTesting
public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName,
@Nullable String targetOverlayableName, @Nullable String category,
- @NonNull String baseCodePath, int state, int userId,
- int priority, boolean isMutable) {
+ @NonNull String baseCodePath, int state, int userId, int priority, boolean isMutable) {
+ this(packageName, null /* overlayName */, targetPackageName, targetOverlayableName,
+ category, baseCodePath, state, userId, priority, isMutable,
+ false /* isFabricated */);
+ }
+
+ /** @hide */
+ public OverlayInfo(@NonNull String packageName, @Nullable String overlayName,
+ @NonNull String targetPackageName, @Nullable String targetOverlayableName,
+ @Nullable String category, @NonNull String baseCodePath, int state, int userId,
+ int priority, boolean isMutable, boolean isFabricated) {
this.packageName = packageName;
+ this.overlayName = overlayName;
this.targetPackageName = targetPackageName;
this.targetOverlayableName = targetOverlayableName;
this.category = category;
@@ -229,12 +258,14 @@ public final class OverlayInfo implements Parcelable {
this.userId = userId;
this.priority = priority;
this.isMutable = isMutable;
+ this.isFabricated = isFabricated;
ensureValidState();
}
/** @hide */
public OverlayInfo(Parcel source) {
packageName = source.readString();
+ overlayName = source.readString();
targetPackageName = source.readString();
targetOverlayableName = source.readString();
category = source.readString();
@@ -243,13 +274,15 @@ public final class OverlayInfo implements Parcelable {
userId = source.readInt();
priority = source.readInt();
isMutable = source.readBoolean();
+ isFabricated = source.readBoolean();
ensureValidState();
}
/**
- * Returns package name of the current overlay.
+ * {@inheritDoc}
* @hide
*/
+ @Override
@SystemApi
@NonNull
public String getPackageName() {
@@ -257,9 +290,20 @@ public final class OverlayInfo implements Parcelable {
}
/**
- * Returns the target package name of the current overlay.
+ * {@inheritDoc}
* @hide
*/
+ @Override
+ @Nullable
+ public String getOverlayName() {
+ return overlayName;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
@SystemApi
@NonNull
public String getTargetPackageName() {
@@ -268,7 +312,8 @@ public final class OverlayInfo implements Parcelable {
/**
* Returns the category of the current overlay.
- * @hide\
+ *
+ * @hide
*/
@SystemApi
@Nullable
@@ -278,6 +323,7 @@ public final class OverlayInfo implements Parcelable {
/**
* Returns user handle for which this overlay applies to.
+ *
* @hide
*/
@SystemApi
@@ -287,15 +333,47 @@ public final class OverlayInfo implements Parcelable {
}
/**
- * Returns name of the target overlayable declaration.
+ * {@inheritDoc}
* @hide
*/
+ @Override
@SystemApi
@Nullable
public String getTargetOverlayableName() {
return targetOverlayableName;
}
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public boolean isFabricated() {
+ return isFabricated;
+ }
+
+ /**
+ * Full path to the base APK or fabricated overlay for this overlay package.
+ *
+ * @hide
+ */
+ public String getBaseCodePath() {
+ return baseCodePath;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ @NonNull
+ public OverlayIdentifier getOverlayIdentifier() {
+ if (mIdentifierCached == null) {
+ mIdentifierCached = new OverlayIdentifier(packageName, overlayName);
+ }
+ return mIdentifierCached;
+ }
+
@SuppressWarnings("ConstantConditions")
private void ensureValidState() {
if (packageName == null) {
@@ -330,6 +408,7 @@ public final class OverlayInfo implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(packageName);
+ dest.writeString(overlayName);
dest.writeString(targetPackageName);
dest.writeString(targetOverlayableName);
dest.writeString(category);
@@ -338,6 +417,7 @@ public final class OverlayInfo implements Parcelable {
dest.writeInt(userId);
dest.writeInt(priority);
dest.writeBoolean(isMutable);
+ dest.writeBoolean(isFabricated);
}
public static final @android.annotation.NonNull Parcelable.Creator<OverlayInfo> CREATOR =
@@ -410,6 +490,7 @@ public final class OverlayInfo implements Parcelable {
result = prime * result + userId;
result = prime * result + state;
result = prime * result + ((packageName == null) ? 0 : packageName.hashCode());
+ result = prime * result + ((overlayName == null) ? 0 : overlayName.hashCode());
result = prime * result + ((targetPackageName == null) ? 0 : targetPackageName.hashCode());
result = prime * result + ((targetOverlayableName == null) ? 0
: targetOverlayableName.hashCode());
@@ -439,6 +520,9 @@ public final class OverlayInfo implements Parcelable {
if (!packageName.equals(other.packageName)) {
return false;
}
+ if (!Objects.equals(overlayName, other.overlayName)) {
+ return false;
+ }
if (!targetPackageName.equals(other.targetPackageName)) {
return false;
}
@@ -457,9 +541,13 @@ public final class OverlayInfo implements Parcelable {
@NonNull
@Override
public String toString() {
- return "OverlayInfo { overlay=" + packageName + ", targetPackage=" + targetPackageName
- + ((targetOverlayableName == null) ? ""
- : ", targetOverlayable=" + targetOverlayableName)
- + ", state=" + state + " (" + stateToString(state) + "), userId=" + userId + " }";
+ return "OverlayInfo {"
+ + "packageName=" + packageName
+ + ", overlayName=" + overlayName
+ + ", targetPackage=" + targetPackageName
+ + ", targetOverlayable=" + targetOverlayableName
+ + ", state=" + state + " (" + stateToString(state) + "),"
+ + ", userId=" + userId
+ + " }";
}
}
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index 7c14c2891d01..0f7e01b234c2 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -205,6 +205,25 @@ public class OverlayManager {
}
/**
+ * Returns information about the overlay represented by the identifier for the specified user.
+ *
+ * @param overlay the identifier representing the overlay
+ * @param userHandle the user of which to get overlay state info
+ * @return the overlay info or null if the overlay cannot be found
+ *
+ * @hide
+ */
+ @Nullable
+ public OverlayInfo getOverlayInfo(@NonNull final OverlayIdentifier overlay,
+ @NonNull final UserHandle userHandle) {
+ try {
+ return mService.getOverlayInfoByIdentifier(overlay, userHandle.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns information about all overlays for the given target package for
* the specified user. The returned list is ordered according to the
* overlay priority with the highest priority at the end of the list.
diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java
index 1fa8973c35b5..73be0ffbf467 100644
--- a/core/java/android/content/om/OverlayManagerTransaction.java
+++ b/core/java/android/content/om/OverlayManagerTransaction.java
@@ -20,6 +20,9 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
@@ -29,6 +32,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
/**
* Container for a batch of requests to the OverlayManagerService.
@@ -60,12 +64,13 @@ public class OverlayManagerTransaction
private OverlayManagerTransaction(@NonNull final Parcel source) {
final int size = source.readInt();
- mRequests = new ArrayList<Request>(size);
+ mRequests = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
final int request = source.readInt();
- final String packageName = source.readString();
+ final OverlayIdentifier overlay = source.readParcelable(null);
final int userId = source.readInt();
- mRequests.add(new Request(request, packageName, userId));
+ final Bundle extras = source.readBundle(null);
+ mRequests.add(new Request(request, overlay, userId, extras));
}
}
@@ -95,22 +100,36 @@ public class OverlayManagerTransaction
public static final int TYPE_SET_ENABLED = 0;
public static final int TYPE_SET_DISABLED = 1;
+ public static final int TYPE_REGISTER_FABRICATED = 2;
+ public static final int TYPE_UNREGISTER_FABRICATED = 3;
- @RequestType public final int type;
- public final String packageName;
+ public static final String BUNDLE_FABRICATED_OVERLAY = "fabricated_overlay";
+
+ @RequestType
+ public final int type;
+ @NonNull
+ public final OverlayIdentifier overlay;
public final int userId;
+ @Nullable
+ public final Bundle extras;
- public Request(@RequestType final int type, @NonNull final String packageName,
+ public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay,
final int userId) {
+ this(type, overlay, userId, null /* extras */);
+ }
+
+ public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay,
+ final int userId, @Nullable Bundle extras) {
this.type = type;
- this.packageName = packageName;
+ this.overlay = overlay;
this.userId = userId;
+ this.extras = extras;
}
@Override
public String toString() {
- return String.format("Request{type=0x%02x (%s), packageName=%s, userId=%d}",
- type, typeToString(), packageName, userId);
+ return String.format(Locale.US, "Request{type=0x%02x (%s), overlay=%s, userId=%d}",
+ type, typeToString(), overlay, userId);
}
/**
@@ -123,6 +142,8 @@ public class OverlayManagerTransaction
switch (type) {
case TYPE_SET_ENABLED: return "TYPE_SET_ENABLED";
case TYPE_SET_DISABLED: return "TYPE_SET_DISABLED";
+ case TYPE_REGISTER_FABRICATED: return "TYPE_REGISTER_FABRICATED";
+ case TYPE_UNREGISTER_FABRICATED: return "TYPE_UNREGISTER_FABRICATED";
default: return String.format("TYPE_UNKNOWN (0x%02x)", type);
}
}
@@ -152,22 +173,56 @@ public class OverlayManagerTransaction
* longer affect the resources of the target package. If the target is
* currently running, its outdated resources will be replaced by new ones.
*
- * @param packageName The name of the overlay package.
+ * @param overlay The name of the overlay package.
* @param enable true to enable the overlay, false to disable it.
* @return this Builder object, so you can chain additional requests
*/
- public Builder setEnabled(@NonNull String packageName, boolean enable) {
- return setEnabled(packageName, enable, UserHandle.myUserId());
+ public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable) {
+ return setEnabled(overlay, enable, UserHandle.myUserId());
}
/**
* @hide
*/
- public Builder setEnabled(@NonNull String packageName, boolean enable, int userId) {
- checkNotNull(packageName);
+ public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable, int userId) {
+ checkNotNull(overlay);
@Request.RequestType final int type =
enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED;
- mRequests.add(new Request(type, packageName, userId));
+ mRequests.add(new Request(type, overlay, userId));
+ return this;
+ }
+
+ /**
+ * Registers the fabricated overlay with the overlay manager so it can be enabled and
+ * disabled for any user.
+ *
+ * The fabricated overlay is initialized in a disabled state. If an overlay is re-registered
+ * the existing overlay will be replaced by the newly registered overlay and the enabled
+ * state of the overlay will be left unchanged if the target package and target overlayable
+ * have not changed.
+ *
+ * @param overlay the overlay to register with the overlay manager
+ *
+ * @hide
+ */
+ public Builder registerFabricatedOverlay(@NonNull FabricatedOverlay overlay) {
+ final Bundle extras = new Bundle();
+ extras.putParcelable(Request.BUNDLE_FABRICATED_OVERLAY, overlay.mOverlay);
+ mRequests.add(new Request(Request.TYPE_REGISTER_FABRICATED, overlay.getIdentifier(),
+ UserHandle.USER_ALL, extras));
+ return this;
+ }
+
+ /**
+ * Disables and removes the overlay from the overlay manager for all users.
+ *
+ * @param overlay the overlay to disable and remove
+ *
+ * @hide
+ */
+ public Builder unregisterFabricatedOverlay(@NonNull OverlayIdentifier overlay) {
+ mRequests.add(new Request(Request.TYPE_UNREGISTER_FABRICATED, overlay,
+ UserHandle.USER_ALL));
return this;
}
@@ -195,8 +250,9 @@ public class OverlayManagerTransaction
for (int i = 0; i < size; i++) {
final Request req = mRequests.get(i);
dest.writeInt(req.type);
- dest.writeString(req.packageName);
+ dest.writeParcelable(req.overlay, flags);
dest.writeInt(req.userId);
+ dest.writeBundle(req.extras);
}
}
diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java
index 85549d854c6d..ebe202b2a3fa 100644
--- a/core/java/android/content/pm/AppSearchShortcutInfo.java
+++ b/core/java/android/content/pm/AppSearchShortcutInfo.java
@@ -273,7 +273,7 @@ public class AppSearchShortcutInfo extends GenericDocument {
text, 0, null, disabledMessage, 0, null,
categoriesSet, intents, rank, extras,
getCreationTimestampMillis(), flags, iconResId, iconResName, bitmapPath, iconUri,
- disabledReason, persons, locusId);
+ disabledReason, persons, locusId, 0);
}
/** @hide */
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 92b801b8203e..404f0d0a2a4f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -938,6 +938,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public String[] resourceDirs;
/**
+ * Contains the contents of {@link #resourceDirs} and along with paths for overlays that may or
+ * may not be APK packages.
+ *
+ * {@hide}
+ */
+ public String[] overlayPaths;
+
+ /**
* String retrieved from the seinfo tag found in selinux policy. This value can be set through
* the mac_permissions.xml policy construct. This value is used for setting an SELinux security
* context on the process as well as its data directory.
@@ -1487,6 +1495,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
if (resourceDirs != null) {
pw.println(prefix + "resourceDirs=" + Arrays.toString(resourceDirs));
}
+ if (overlayPaths != null) {
+ pw.println(prefix + "overlayPaths=" + Arrays.toString(overlayPaths));
+ }
if ((dumpFlags & DUMP_FLAG_DETAILS) != 0 && seInfo != null) {
pw.println(prefix + "seinfo=" + seInfo);
pw.println(prefix + "seinfoUser=" + seInfoUser);
@@ -1583,6 +1594,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
proto.write(ApplicationInfoProto.RESOURCE_DIRS, dir);
}
}
+ if (overlayPaths != null) {
+ for (String dir : overlayPaths) {
+ proto.write(ApplicationInfoProto.OVERLAY_PATHS, dir);
+ }
+ }
proto.write(ApplicationInfoProto.DATA_DIR, dataDir);
proto.write(ApplicationInfoProto.CLASS_LOADER_NAME, classLoaderName);
if (!ArrayUtils.isEmpty(splitClassLoaderNames)) {
@@ -1734,6 +1750,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
primaryCpuAbi = orig.primaryCpuAbi;
secondaryCpuAbi = orig.secondaryCpuAbi;
resourceDirs = orig.resourceDirs;
+ overlayPaths = orig.overlayPaths;
seInfo = orig.seInfo;
seInfoUser = orig.seInfoUser;
sharedLibraryFiles = orig.sharedLibraryFiles;
@@ -1822,6 +1839,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeString8(primaryCpuAbi);
dest.writeString8(secondaryCpuAbi);
dest.writeString8Array(resourceDirs);
+ dest.writeString8Array(overlayPaths);
dest.writeString8(seInfo);
dest.writeString8(seInfoUser);
dest.writeString8Array(sharedLibraryFiles);
@@ -1907,6 +1925,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
primaryCpuAbi = source.readString8();
secondaryCpuAbi = source.readString8();
resourceDirs = source.createString8Array();
+ overlayPaths = source.createString8Array();
seInfo = source.readString8();
seInfoUser = source.readString8();
sharedLibraryFiles = source.createString8Array();
@@ -2303,7 +2322,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* @hide
*/
public String[] getAllApkPaths() {
- final String[][] inputLists = { splitSourceDirs, sharedLibraryFiles, resourceDirs };
+ final String[][] inputLists = {
+ splitSourceDirs, sharedLibraryFiles, resourceDirs, overlayPaths
+ };
final List<String> output = new ArrayList<>(10);
if (sourceDir != null) {
output.add(sourceDir);
diff --git a/core/java/android/content/pm/DataLoaderManager.java b/core/java/android/content/pm/DataLoaderManager.java
index e8fb2413bbbe..4d79936a8b22 100644
--- a/core/java/android/content/pm/DataLoaderManager.java
+++ b/core/java/android/content/pm/DataLoaderManager.java
@@ -41,6 +41,7 @@ public class DataLoaderManager {
* @param dataLoaderId ID for the new data loader binder service.
* @param params DataLoaderParamsParcel object that contains data loader params, including
* its package name, class name, and additional parameters.
+ * @param bindDelayMs introduce a delay before actual bind in case we want to avoid busylooping
* @param listener Callback for the data loader service to report status back to the
* caller.
* @return false if 1) target ID collides with a data loader that is already bound to data
@@ -48,9 +49,9 @@ public class DataLoaderManager {
* or 4) fails to bind to the specified data loader service, otherwise return true.
*/
public boolean bindToDataLoader(int dataLoaderId, @NonNull DataLoaderParamsParcel params,
- @NonNull IDataLoaderStatusListener listener) {
+ long bindDelayMs, @NonNull IDataLoaderStatusListener listener) {
try {
- return mService.bindToDataLoader(dataLoaderId, params, listener);
+ return mService.bindToDataLoader(dataLoaderId, params, bindDelayMs, listener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/IDataLoaderManager.aidl b/core/java/android/content/pm/IDataLoaderManager.aidl
index 93b3de7897c4..dda4d36d1217 100644
--- a/core/java/android/content/pm/IDataLoaderManager.aidl
+++ b/core/java/android/content/pm/IDataLoaderManager.aidl
@@ -23,7 +23,7 @@ import android.content.pm.IDataLoaderStatusListener;
/** @hide */
interface IDataLoaderManager {
- boolean bindToDataLoader(int id, in DataLoaderParamsParcel params,
+ boolean bindToDataLoader(int id, in DataLoaderParamsParcel params, long bindDelayMs,
IDataLoaderStatusListener listener);
IDataLoader getDataLoader(int dataLoaderId);
void unbindFromDataLoader(int dataLoaderId);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index a46876ec53c4..7fe2a41cc4b9 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -808,4 +808,6 @@ interface IPackageManager {
PackageManager.Property getProperty(String propertyName, String packageName, String className);
ParceledListSlice queryProperty(String propertyName, int componentType);
+
+ void setKeepUninstalledPackages(in List<String> packageList);
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 0c0e4020d04a..80fecc1a3195 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -167,10 +167,17 @@ public class LauncherApps {
*/
public static final int FLAG_CACHE_BUBBLE_SHORTCUTS = 1;
+ /**
+ * Cache shortcuts which are used in People Tile.
+ * @hide
+ */
+ public static final int FLAG_CACHE_PEOPLE_TILE_SHORTCUTS = 2;
+
/** @hide */
@IntDef(flag = false, prefix = { "FLAG_CACHE_" }, value = {
FLAG_CACHE_NOTIFICATION_SHORTCUTS,
FLAG_CACHE_BUBBLE_SHORTCUTS,
+ FLAG_CACHE_PEOPLE_TILE_SHORTCUTS
})
@Retention(RetentionPolicy.SOURCE)
public @interface ShortcutCacheFlags {}
@@ -1179,6 +1186,7 @@ public class LauncherApps {
* <ul>
* <li>{@link #FLAG_CACHE_NOTIFICATION_SHORTCUTS}
* <li>{@link #FLAG_CACHE_BUBBLE_SHORTCUTS}
+ * <li>{@link #FLAG_CACHE_PEOPLE_TILE_SHORTCUTS}
* </ul>
* @throws IllegalStateException when the user is locked, or when the {@code user} user
* is locked or not running.
@@ -1209,6 +1217,7 @@ public class LauncherApps {
* <ul>
* <li>{@link #FLAG_CACHE_NOTIFICATION_SHORTCUTS}
* <li>{@link #FLAG_CACHE_BUBBLE_SHORTCUTS}
+ * <li>{@link #FLAG_CACHE_PEOPLE_TILE_SHORTCUTS}
* </ul>
* @throws IllegalStateException when the user is locked, or when the {@code user} user
* is locked or not running.
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index f9980bc63d92..567501cba70b 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2065,6 +2065,13 @@ public class PackageInstaller {
this.forceQueryableOverride = true;
}
+ /**
+ * Sets the install scenario for this session, which describes the expected user journey.
+ */
+ public void setInstallScenario(@InstallScenario int installScenario) {
+ this.installScenario = installScenario;
+ }
+
/** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
@@ -2224,7 +2231,7 @@ public class PackageInstaller {
/** {@hide} */
public @InstallReason int installReason;
/** {@hide} */
- public @InstallReason int installScenario;
+ public @InstallScenario int installScenario;
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public long sizeBytes;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b95b991b095c..f9122b1f6c8b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -47,8 +47,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
-import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.dex.ArtManager;
+import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
@@ -1356,15 +1356,11 @@ public abstract class PackageManager {
/**
* A value to indicate the lack of CUJ information, disabling all installation scenario logic.
- *
- * @hide
*/
public static final int INSTALL_SCENARIO_DEFAULT = 0;
/**
* Installation scenario providing the fastest “install button to launch" experience possible.
- *
- * @hide
*/
public static final int INSTALL_SCENARIO_FAST = 1;
@@ -1381,8 +1377,6 @@ public abstract class PackageManager {
* less optimized applications. The device state (e.g. memory usage or battery status) should
* not be considered when making this decision as those factors are taken into account by the
* Package Manager when acting on the installation scenario.
- *
- * @hide
*/
public static final int INSTALL_SCENARIO_BULK = 2;
@@ -1393,8 +1387,6 @@ public abstract class PackageManager {
* operation that are marked BULK_SECONDARY, the faster the entire bulk operation will be.
*
* See the comments for INSTALL_SCENARIO_BULK for more information.
- *
- * @hide
*/
public static final int INSTALL_SCENARIO_BULK_SECONDARY = 3;
@@ -3592,9 +3584,12 @@ public abstract class PackageManager {
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
* the requisite kernel support to support incremental delivery aka Incremental FileSystem.
*
- * @see IncrementalManager#isEnabled
+ * @see IncrementalManager#isFeatureEnabled
* @hide
+ *
+ * @deprecated Use {@link #FEATURE_INCREMENTAL_DELIVERY_VERSION} instead.
*/
+ @Deprecated
@SystemApi
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_INCREMENTAL_DELIVERY =
@@ -3602,6 +3597,20 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * feature not present - IncFs is not present on the device.
+ * 1 - IncFs v1, core features, no PerUid support. Optional in R.
+ * 2 - IncFs v2, PerUid support, fs-verity support. Required in S.
+ *
+ * @see IncrementalManager#isFeatureEnabled and IncrementalManager#isV2()
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_INCREMENTAL_DELIVERY_VERSION =
+ "android.software.incremental_delivery_version";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
* The device has tuner hardware to support tuner operations.
*
* <p>This feature implies that the device has the tuner HAL implementation.
@@ -3660,6 +3669,15 @@ public abstract class PackageManager {
public static final String FEATURE_KEYSTORE_LIMITED_USE_KEY =
"android.hardware.keystore.limited_use_key";
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+ * a Keystore implementation that can create application-specific attestation keys.
+ * See {@link android.security.keystore.KeyGenParameterSpec.Builder#setAttestKeyAlias}.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_KEYSTORE_APP_ATTEST_KEY =
+ "android.hardware.keystore.app_attest_key";
+
/** @hide */
public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true;
@@ -4054,6 +4072,8 @@ public abstract class PackageManager {
/**
* Permission flag: This location permission is selected as the level of granularity of
* location accuracy.
+ * Example: If this flag is set for ACCESS_FINE_LOCATION, FINE location is the selected location
+ * accuracy for location permissions.
*
* @hide
*/
@@ -9282,4 +9302,20 @@ public abstract class PackageManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Set a list of apps to keep around as APKs even if no user has currently installed it.
+ * @param packageList List of package names to keep cached.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.KEEP_UNINSTALLED_PACKAGES)
+ @TestApi
+ public void setKeepUninstalledPackages(@NonNull List<String> packageList) {
+ try {
+ ActivityThread.getPackageManager().setKeepUninstalledPackages(packageList);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e6c0f6a4c2fa..bf8d1f6ab07b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -54,6 +54,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.overlay.OverlayPaths;
import android.content.pm.split.SplitAssetLoader;
import android.content.res.ApkAssets;
import android.content.res.AssetManager;
@@ -6150,6 +6151,56 @@ public class PackageParser {
}
/**
+ * Returns whether this instance is currently signed, or has ever been signed, with a
+ * signing certificate from the provided {@link Set} of {@code certDigests}.
+ *
+ * <p>The provided {@code certDigests} should contain the SHA-256 digest of the DER encoding
+ * of each trusted certificate with the digest characters in upper case. If this instance
+ * has multiple signers then all signers must be in the provided {@code Set}. If this
+ * instance has a signing lineage then this method will return true if any of the previous
+ * signers in the lineage match one of the entries in the {@code Set}.
+ */
+ public boolean hasAncestorOrSelfWithDigest(Set<String> certDigests) {
+ if (this == UNKNOWN || certDigests == null || certDigests.size() == 0) {
+ return false;
+ }
+ // If an app is signed by multiple signers then all of the signers must be in the Set.
+ if (signatures.length > 1) {
+ // If the Set has less elements than the number of signatures then immediately
+ // return false as there's no way to satisfy the requirement of all signatures being
+ // in the Set.
+ if (certDigests.size() < signatures.length) {
+ return false;
+ }
+ for (Signature signature : signatures) {
+ String signatureDigest = PackageUtils.computeSha256Digest(
+ signature.toByteArray());
+ if (!certDigests.contains(signatureDigest)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ String signatureDigest = PackageUtils.computeSha256Digest(signatures[0].toByteArray());
+ if (certDigests.contains(signatureDigest)) {
+ return true;
+ }
+ if (hasPastSigningCertificates()) {
+ // The last element in the pastSigningCertificates array is the current signer;
+ // since that was verified above just check all the signers in the lineage.
+ for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+ signatureDigest = PackageUtils.computeSha256Digest(
+ pastSigningCertificates[i].toByteArray());
+ if (certDigests.contains(signatureDigest)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Returns the SigningDetails with a descendant (or same) signer after verifying the
* descendant has the same, a superset, or a subset of the lineage of the ancestor.
*
@@ -7969,7 +8020,11 @@ public class PackageParser {
ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
}
ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
- ai.resourceDirs = state.getAllOverlayPaths();
+ final OverlayPaths overlayPaths = state.getAllOverlayPaths();
+ if (overlayPaths != null) {
+ ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
+ ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]);
+ }
ai.icon = (sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes;
}
@@ -8600,6 +8655,7 @@ public class PackageParser {
null,
null,
androidAppInfo.resourceDirs,
+ androidAppInfo.overlayPaths,
androidAppInfo.sharedLibraryFiles,
null,
null,
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 5cc74c0a1c8e..e115597865b3 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -31,6 +31,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
+import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.os.BaseBundle;
import android.os.Debug;
@@ -53,7 +54,6 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.Arrays;
-import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
@@ -85,9 +85,10 @@ public class PackageUserState {
public ArraySet<String> disabledComponents;
public ArraySet<String> enabledComponents;
- private String[] overlayPaths;
- private ArrayMap<String, String[]> sharedLibraryOverlayPaths; // Lib name to overlay paths
- private String[] cachedOverlayPaths;
+ private OverlayPaths overlayPaths;
+ // Maps library name to overlay paths.
+ private ArrayMap<String, OverlayPaths> sharedLibraryOverlayPaths;
+ private OverlayPaths cachedOverlayPaths;
@Nullable
private ArrayMap<ComponentName, Pair<String, Integer>> componentLabelIconOverrideMap;
@@ -121,8 +122,7 @@ public class PackageUserState {
uninstallReason = o.uninstallReason;
disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents);
enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
- overlayPaths =
- o.overlayPaths == null ? null : Arrays.copyOf(o.overlayPaths, o.overlayPaths.length);
+ overlayPaths = o.overlayPaths;
if (o.sharedLibraryOverlayPaths != null) {
sharedLibraryOverlayPaths = new ArrayMap<>(o.sharedLibraryOverlayPaths);
}
@@ -132,25 +132,55 @@ public class PackageUserState {
}
}
- public String[] getOverlayPaths() {
+ @Nullable
+ public OverlayPaths getOverlayPaths() {
return overlayPaths;
}
- public void setOverlayPaths(String[] paths) {
- overlayPaths = paths;
- cachedOverlayPaths = null;
+ @Nullable
+ public Map<String, OverlayPaths> getSharedLibraryOverlayPaths() {
+ return sharedLibraryOverlayPaths;
}
- public Map<String, String[]> getSharedLibraryOverlayPaths() {
- return sharedLibraryOverlayPaths;
+ /**
+ * Sets the path of overlays currently enabled for this package and user combination.
+ * @return true if the path contents differ than what they were previously
+ */
+ @Nullable
+ public boolean setOverlayPaths(@Nullable OverlayPaths paths) {
+ if (Objects.equals(paths, overlayPaths)) {
+ return false;
+ }
+ if ((overlayPaths == null && paths.isEmpty())
+ || (paths == null && overlayPaths.isEmpty())) {
+ return false;
+ }
+ overlayPaths = paths;
+ cachedOverlayPaths = null;
+ return true;
}
- public void setSharedLibraryOverlayPaths(String library, String[] paths) {
+ /**
+ * Sets the path of overlays currently enabled for a library that this package uses.
+ *
+ * @return true if the path contents for the library differ than what they were previously
+ */
+ public boolean setSharedLibraryOverlayPaths(@NonNull String library,
+ @Nullable OverlayPaths paths) {
if (sharedLibraryOverlayPaths == null) {
sharedLibraryOverlayPaths = new ArrayMap<>();
}
- sharedLibraryOverlayPaths.put(library, paths);
+ final OverlayPaths currentPaths = sharedLibraryOverlayPaths.get(library);
+ if (Objects.equals(paths, currentPaths)) {
+ return false;
+ }
cachedOverlayPaths = null;
+ if (paths == null || paths.isEmpty()) {
+ return sharedLibraryOverlayPaths.remove(library) != null;
+ } else {
+ sharedLibraryOverlayPaths.put(library, paths);
+ return true;
+ }
}
/**
@@ -332,35 +362,21 @@ public class PackageUserState {
return isComponentEnabled;
}
- public String[] getAllOverlayPaths() {
+ public OverlayPaths getAllOverlayPaths() {
if (overlayPaths == null && sharedLibraryOverlayPaths == null) {
return null;
}
-
if (cachedOverlayPaths != null) {
return cachedOverlayPaths;
}
-
- final LinkedHashSet<String> paths = new LinkedHashSet<>();
- if (overlayPaths != null) {
- final int N = overlayPaths.length;
- for (int i = 0; i < N; i++) {
- paths.add(overlayPaths[i]);
- }
- }
-
+ final OverlayPaths.Builder newPaths = new OverlayPaths.Builder();
+ newPaths.addAll(overlayPaths);
if (sharedLibraryOverlayPaths != null) {
- for (String[] libOverlayPaths : sharedLibraryOverlayPaths.values()) {
- if (libOverlayPaths != null) {
- final int N = libOverlayPaths.length;
- for (int i = 0; i < N; i++) {
- paths.add(libOverlayPaths[i]);
- }
- }
+ for (final OverlayPaths libOverlayPaths : sharedLibraryOverlayPaths.values()) {
+ newPaths.addAll(libOverlayPaths);
}
}
-
- cachedOverlayPaths = paths.toArray(new String[0]);
+ cachedOverlayPaths = newPaths.build();
return cachedOverlayPaths;
}
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 35f02a8d5dd2..0e70a3e4e600 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -28,8 +28,12 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForStringSet;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
/**
* Information you can retrieve about a particular security permission
@@ -278,6 +282,15 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
@SystemApi
public static final int PROTECTION_FLAG_ROLE = 0x4000000;
+ /**
+ * Additional flag for {@link #protectionLevel}, correspoinding to the {@code knownSigner} value
+ * of {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int PROTECTION_FLAG_KNOWN_SIGNER = 0x8000000;
+
/** @hide */
@IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
PROTECTION_FLAG_PRIVILEGED,
@@ -303,6 +316,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
PROTECTION_FLAG_RETAIL_DEMO,
PROTECTION_FLAG_RECENTS,
PROTECTION_FLAG_ROLE,
+ PROTECTION_FLAG_KNOWN_SIGNER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProtectionFlags {}
@@ -466,6 +480,17 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
*/
public @Nullable CharSequence nonLocalizedDescription;
+ private static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
+
+ /**
+ * A {@link Set} of trusted signing certificate digests. If this permission has the {@link
+ * #PROTECTION_FLAG_KNOWN_SIGNER} flag set the permission will be granted to a requesting app
+ * if the app is signed by any of these certificates.
+ *
+ * @hide
+ */
+ public @Nullable Set<String> knownCerts;
+
/** @hide */
public static int fixProtectionLevel(int level) {
if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
@@ -570,6 +595,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
if ((level & PermissionInfo.PROTECTION_FLAG_ROLE) != 0) {
protLevel.append("|role");
}
+ if ((level & PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER) != 0) {
+ protLevel.append("|knownSigner");
+ }
return protLevel.toString();
}
@@ -665,6 +693,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
dest.writeInt(descriptionRes);
dest.writeInt(requestRes);
TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
+ sForStringSet.parcel(knownCerts, dest, parcelableFlags);
}
/** @hide */
@@ -730,5 +759,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
descriptionRes = source.readInt();
requestRes = source.readInt();
nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ knownCerts = sForStringSet.unparcel(source);
}
}
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 522f4ca88519..5f80ba110773 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -129,6 +129,12 @@ public final class ShortcutInfo implements Parcelable {
/** @hide */
public static final int FLAG_HAS_ICON_URI = 1 << 15;
+ /**
+ * TODO(b/155135057): This is a quick and temporary fix for b/155135890. ShortcutService doesn't
+ * need to be aware of the outside world. Replace this with a more extensible solution.
+ * @hide
+ */
+ public static final int FLAG_CACHED_PEOPLE_TILE = 1 << 29;
/**
* TODO(b/155135057): This is a quick and temporary fix for b/155135890. ShortcutService doesn't
@@ -138,7 +144,8 @@ public final class ShortcutInfo implements Parcelable {
public static final int FLAG_CACHED_BUBBLES = 1 << 30;
/** @hide */
- public static final int FLAG_CACHED_ALL = FLAG_CACHED_NOTIFICATIONS | FLAG_CACHED_BUBBLES;
+ public static final int FLAG_CACHED_ALL =
+ FLAG_CACHED_NOTIFICATIONS | FLAG_CACHED_BUBBLES | FLAG_CACHED_PEOPLE_TILE;
/** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
@@ -159,6 +166,7 @@ public final class ShortcutInfo implements Parcelable {
FLAG_HAS_ICON_URI,
FLAG_CACHED_NOTIFICATIONS,
FLAG_CACHED_BUBBLES,
+ FLAG_CACHED_PEOPLE_TILE
})
@Retention(RetentionPolicy.SOURCE)
public @interface ShortcutFlags {}
@@ -434,6 +442,8 @@ public final class ShortcutInfo implements Parcelable {
private int mDisabledReason;
+ private int mStartingThemeResId;
+
private ShortcutInfo(Builder b) {
mUserId = b.mContext.getUserId();
@@ -462,6 +472,7 @@ public final class ShortcutInfo implements Parcelable {
mLocusId = b.mLocusId;
updateTimestamp();
+ mStartingThemeResId = b.mStartingThemeResId;
}
/**
@@ -608,6 +619,7 @@ public final class ShortcutInfo implements Parcelable {
// Set this bit.
mFlags |= FLAG_KEY_FIELDS_ONLY;
}
+ mStartingThemeResId = source.mStartingThemeResId;
}
/**
@@ -931,6 +943,9 @@ public final class ShortcutInfo implements Parcelable {
if (source.mLocusId != null) {
mLocusId = source.mLocusId;
}
+ if (source.mStartingThemeResId != 0) {
+ mStartingThemeResId = source.mStartingThemeResId;
+ }
}
/**
@@ -1000,6 +1015,8 @@ public final class ShortcutInfo implements Parcelable {
private LocusId mLocusId;
+ private int mStartingThemeResId;
+
/**
* Old style constructor.
* @hide
@@ -1102,6 +1119,15 @@ public final class ShortcutInfo implements Parcelable {
}
/**
+ * Sets a theme resource id for the splash screen.
+ */
+ @NonNull
+ public Builder setStartingTheme(int themeResId) {
+ mStartingThemeResId = themeResId;
+ return this;
+ }
+
+ /**
* @hide We don't support resource strings for dynamic shortcuts for now. (But unit tests
* use it.)
*/
@@ -1420,6 +1446,14 @@ public final class ShortcutInfo implements Parcelable {
return mIcon;
}
+ /**
+ * Returns the theme resource id used for the splash screen.
+ * @hide
+ */
+ public int getStartingThemeResId() {
+ return mStartingThemeResId;
+ }
+
/** @hide -- old signature, the internal code still uses it. */
@Nullable
@Deprecated
@@ -2138,6 +2172,7 @@ public final class ShortcutInfo implements Parcelable {
mPersons = source.readParcelableArray(cl, Person.class);
mLocusId = source.readParcelable(cl);
mIconUri = source.readString8();
+ mStartingThemeResId = source.readInt();
}
@Override
@@ -2189,6 +2224,7 @@ public final class ShortcutInfo implements Parcelable {
dest.writeParcelableArray(mPersons, flags);
dest.writeParcelable(mLocusId, flags);
dest.writeString8(mIconUri);
+ dest.writeInt(mStartingThemeResId);
}
public static final @NonNull Creator<ShortcutInfo> CREATOR =
@@ -2345,6 +2381,12 @@ public final class ShortcutInfo implements Parcelable {
sb.append("disabledReason=");
sb.append(getDisabledReasonDebugString(mDisabledReason));
+ if (mStartingThemeResId != 0) {
+ addIndentOrComma(sb, indent);
+ sb.append("SplashScreenThemeResId=");
+ sb.append(Integer.toHexString(mStartingThemeResId));
+ }
+
addIndentOrComma(sb, indent);
sb.append("categories=");
@@ -2430,7 +2472,7 @@ public final class ShortcutInfo implements Parcelable {
Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
long lastChangedTimestamp,
int flags, int iconResId, String iconResName, String bitmapPath, String iconUri,
- int disabledReason, Person[] persons, LocusId locusId) {
+ int disabledReason, Person[] persons, LocusId locusId, int startingThemeResId) {
mUserId = userId;
mId = id;
mPackageName = packageName;
@@ -2459,5 +2501,6 @@ public final class ShortcutInfo implements Parcelable {
mDisabledReason = disabledReason;
mPersons = persons;
mLocusId = locusId;
+ mStartingThemeResId = startingThemeResId;
}
}
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index c62767ee031b..233abf36131b 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -71,6 +71,13 @@ public abstract class ShortcutServiceInternal {
public abstract int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId);
+ /**
+ * Get the theme res ID of the starting window, it can be 0 if not specified.
+ */
+ public abstract int getShortcutStartingThemeResId(int launcherUserId,
+ @NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId,
+ int userId);
+
public abstract ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
@NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId);
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
index bf35c4d92d3a..bc5d14a533e2 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -22,17 +22,26 @@ import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
+import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.jar.StrictJarFile;
+import android.util.JsonReader;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.IOException;
+import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.zip.ZipEntry;
/**
* Helper class used to compute and validate the location of dex metadata files.
@@ -40,6 +49,12 @@ import java.util.Map;
* @hide
*/
public class DexMetadataHelper {
+ public static final String TAG = "DexMetadataHelper";
+ /** $> adb shell 'setprop log.tag.DexMetadataHelper VERBOSE' */
+ public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ /** $> adb shell 'setprop pm.dexopt.dm.require_manifest true' */
+ private static String PROPERTY_DM_JSON_MANIFEST_REQUIRED = "pm.dexopt.dm.require_manifest";
+
private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
private DexMetadataHelper() {}
@@ -147,14 +162,31 @@ public class DexMetadataHelper {
/**
* Validate that the given file is a dex metadata archive.
- * This is just a validation that the file is a zip archive.
+ * This is just a validation that the file is a zip archive that contains a manifest.json
+ * with the package name and version code.
*
* @throws PackageParserException if the file is not a .dm file.
*/
- public static void validateDexMetadataFile(String dmaPath) throws PackageParserException {
+ public static void validateDexMetadataFile(String dmaPath, String packageName, long versionCode)
+ throws PackageParserException {
+ validateDexMetadataFile(dmaPath, packageName, versionCode,
+ SystemProperties.getBoolean(PROPERTY_DM_JSON_MANIFEST_REQUIRED, false));
+ }
+
+ @VisibleForTesting
+ public static void validateDexMetadataFile(String dmaPath, String packageName, long versionCode,
+ boolean requireManifest) throws PackageParserException {
StrictJarFile jarFile = null;
+
+ if (DEBUG) {
+ Log.v(TAG, "validateDexMetadataFile: " + dmaPath + ", " + packageName +
+ ", " + versionCode);
+ }
+
try {
jarFile = new StrictJarFile(dmaPath, false, false);
+ validateDexMetadataManifest(dmaPath, jarFile, packageName, versionCode,
+ requireManifest);
} catch (IOException e) {
throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
"Error opening " + dmaPath, e);
@@ -168,6 +200,72 @@ public class DexMetadataHelper {
}
}
+ /** Ensure that packageName and versionCode match the manifest.json in the .dm file */
+ private static void validateDexMetadataManifest(String dmaPath, StrictJarFile jarFile,
+ String packageName, long versionCode, boolean requireManifest)
+ throws IOException, PackageParserException {
+ if (!requireManifest) {
+ if (DEBUG) {
+ Log.v(TAG, "validateDexMetadataManifest: " + dmaPath
+ + " manifest.json check skipped");
+ }
+ return;
+ }
+
+ ZipEntry zipEntry = jarFile.findEntry("manifest.json");
+ if (zipEntry == null) {
+ throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
+ "Missing manifest.json in " + dmaPath);
+ }
+ InputStream inputStream = jarFile.getInputStream(zipEntry);
+
+ JsonReader reader;
+ try {
+ reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
+ "Error opening manifest.json in " + dmaPath, e);
+ }
+ String jsonPackageName = null;
+ long jsonVersionCode = -1;
+
+ reader.beginObject();
+ while (reader.hasNext()) {
+ String name = reader.nextName();
+ if (name.equals("packageName")) {
+ jsonPackageName = reader.nextString();
+ } else if (name.equals("versionCode")) {
+ jsonVersionCode = reader.nextLong();
+ } else {
+ reader.skipValue();
+ }
+ }
+ reader.endObject();
+
+ if (jsonPackageName == null || jsonVersionCode == -1) {
+ throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
+ "manifest.json in " + dmaPath
+ + " is missing 'packageName' and/or 'versionCode'");
+ }
+
+ if (!jsonPackageName.equals(packageName)) {
+ throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
+ "manifest.json in " + dmaPath + " has invalid packageName: " + jsonPackageName
+ + ", expected: " + packageName);
+ }
+
+ if (versionCode != jsonVersionCode) {
+ throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
+ "manifest.json in " + dmaPath + " has invalid versionCode: " + jsonVersionCode
+ + ", expected: " + versionCode);
+ }
+
+ if (DEBUG) {
+ Log.v(TAG, "validateDexMetadataManifest: " + dmaPath + ", " + packageName +
+ ", " + versionCode + ": successful");
+ }
+ }
+
/**
* Validates that all dex metadata paths in the given list have a matching apk.
* (for any foo.dm there should be either a 'foo' of a 'foo.apk' file).
diff --git a/core/java/android/content/pm/overlay/OverlayPaths.java b/core/java/android/content/pm/overlay/OverlayPaths.java
new file mode 100644
index 000000000000..a4db733af013
--- /dev/null
+++ b/core/java/android/content/pm/overlay/OverlayPaths.java
@@ -0,0 +1,192 @@
+/*
+ * 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.content.pm.overlay;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.DataClass;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/** @hide */
+@DataClass(genConstructor = false, genBuilder = false, genHiddenBuilder = false,
+ genEqualsHashCode = true, genToString = true)
+public class OverlayPaths {
+ /**
+ * Represents {@link android.content.pm.ApplicationInfo#resourceDirs}.
+ * Only contains paths to APKs of overlays that can have their idmap resolved from their base
+ * APK path. Currently all overlay APKs can have their idmap path resolved from their idmap
+ * path.
+ */
+ @NonNull
+ private final List<String> mResourceDirs = new ArrayList<>();
+
+ /**
+ * Represents {@link android.content.pm.ApplicationInfo#overlayPaths}.
+ * Contains the contents of {@link #getResourceDirs()} and along with paths for overlays
+ * that are not APKs.
+ */
+ @NonNull
+ private final List<String> mOverlayPaths = new ArrayList<>();
+
+ public static class Builder {
+ final OverlayPaths mPaths = new OverlayPaths();
+
+ /**
+ * Adds a non-APK path to the contents of {@link OverlayPaths#getOverlayPaths()}.
+ */
+ public Builder addNonApkPath(@NonNull String idmapPath) {
+ mPaths.mOverlayPaths.add(idmapPath);
+ return this;
+ }
+
+ /**
+ * Adds a overlay APK path to the contents of {@link OverlayPaths#getResourceDirs()} and
+ * {@link OverlayPaths#getOverlayPaths()}.
+ */
+ public Builder addApkPath(@NonNull String overlayPath) {
+ addUniquePath(mPaths.mResourceDirs, overlayPath);
+ addUniquePath(mPaths.mOverlayPaths, overlayPath);
+ return this;
+ }
+
+ public Builder addAll(@Nullable OverlayPaths other) {
+ if (other != null) {
+ for (final String path : other.getResourceDirs()) {
+ addUniquePath(mPaths.mResourceDirs, path);
+ }
+ for (final String path : other.getOverlayPaths()) {
+ addUniquePath(mPaths.mOverlayPaths, path);
+ }
+ }
+ return this;
+ }
+
+ public OverlayPaths build() {
+ return mPaths;
+ }
+
+ private static void addUniquePath(@NonNull List<String> paths, @NonNull String path) {
+ if (!paths.contains(path)) {
+ paths.add(path);
+ }
+ }
+ }
+
+ /**
+ * Returns whether {@link #getOverlayPaths()} and {@link #getOverlayPaths} are empty.
+ */
+ public boolean isEmpty() {
+ return mResourceDirs.isEmpty() && mOverlayPaths.isEmpty();
+ }
+
+ private OverlayPaths() {
+ }
+
+
+
+ // 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/content/pm/overlay/OverlayPaths.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Represents {@link android.content.pm.ApplicationInfo#resourceDirs}.
+ * Only contains paths to APKs of overlays that can have their idmap resolved from their base
+ * APK path. Currently all overlay APKs can have their idmap path resolved from their idmap
+ * path.
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<String> getResourceDirs() {
+ return mResourceDirs;
+ }
+
+ /**
+ * Represents {@link android.content.pm.ApplicationInfo#overlayPaths}.
+ * Contains the contents of {@link #getResourceDirs()} and along with paths for overlays
+ * that are not APKs.
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<String> getOverlayPaths() {
+ return mOverlayPaths;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "OverlayPaths { " +
+ "resourceDirs = " + mResourceDirs + ", " +
+ "overlayPaths = " + mOverlayPaths +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(OverlayPaths other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ OverlayPaths that = (OverlayPaths) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && Objects.equals(mResourceDirs, that.mResourceDirs)
+ && Objects.equals(mOverlayPaths, that.mOverlayPaths);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + Objects.hashCode(mResourceDirs);
+ _hash = 31 * _hash + Objects.hashCode(mOverlayPaths);
+ return _hash;
+ }
+
+ @DataClass.Generated(
+ time = 1612307813586L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/content/pm/overlay/OverlayPaths.java",
+ inputSignatures = "private final @android.annotation.NonNull java.util.List<java.lang.String> mResourceDirs\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mOverlayPaths\npublic boolean isEmpty()\nclass OverlayPaths extends java.lang.Object implements []\nfinal android.content.pm.overlay.OverlayPaths mPaths\npublic android.content.pm.overlay.OverlayPaths.Builder addNonApkPath(java.lang.String)\npublic android.content.pm.overlay.OverlayPaths.Builder addApkPath(java.lang.String)\npublic android.content.pm.overlay.OverlayPaths.Builder addAll(android.content.pm.overlay.OverlayPaths)\npublic android.content.pm.overlay.OverlayPaths build()\nprivate static void addUniquePath(java.util.List<java.lang.String>,java.lang.String)\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genHiddenBuilder=false, genEqualsHashCode=true, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index b7365b3eaf61..9a84ded99c67 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -41,6 +41,7 @@ import android.content.pm.SELinuxUtil;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
import android.content.pm.SigningInfo;
+import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.component.ComponentParseUtils;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedAttribution;
@@ -412,7 +413,11 @@ public class PackageInfoWithoutStateUtils {
ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
}
ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
- ai.resourceDirs = state.getAllOverlayPaths();
+ final OverlayPaths overlayPaths = state.getAllOverlayPaths();
+ if (overlayPaths != null) {
+ ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
+ ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]);
+ }
return ai;
}
@@ -650,6 +655,7 @@ public class PackageInfoWithoutStateUtils {
pi.protectionLevel = p.getProtectionLevel();
pi.descriptionRes = p.getDescriptionRes();
pi.flags = p.getFlags();
+ pi.knownCerts = p.getKnownCerts();
if ((flags & PackageManager.GET_META_DATA) == 0) {
return pi;
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 66bdb9bb864a..b7aa30f00691 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -2807,7 +2807,7 @@ public class ParsingPackageUtils {
* limits length of the name to the {@link #MAX_FILE_NAME_SIZE}.
* @return Success if it's valid.
*/
- public static ParseResult validateName(ParseInput input, String name, boolean requireSeparator,
+ public static String validateName(String name, boolean requireSeparator,
boolean requireFilename) {
final int N = name.length();
boolean hasSep = false;
@@ -2828,18 +2828,28 @@ public class ParsingPackageUtils {
front = true;
continue;
}
- return input.error("bad character '" + c + "'");
+ return "bad character '" + c + "'";
}
if (requireFilename) {
if (!FileUtils.isValidExtFilename(name)) {
- return input.error("Invalid filename");
+ return "Invalid filename";
} else if (N > MAX_FILE_NAME_SIZE) {
- return input.error("the length of the name is greater than " + MAX_FILE_NAME_SIZE);
+ return "the length of the name is greater than " + MAX_FILE_NAME_SIZE;
}
}
- return hasSep || !requireSeparator
- ? input.success(null)
- : input.error("must have at least one '.' separator");
+ return hasSep || !requireSeparator ? null : "must have at least one '.' separator";
+ }
+
+ /**
+ * @see #validateName(String, boolean, boolean)
+ */
+ public static ParseResult validateName(ParseInput input, String name, boolean requireSeparator,
+ boolean requireFilename) {
+ final String errorMessage = validateName(name, requireSeparator, requireFilename);
+ if (errorMessage != null) {
+ return input.error(errorMessage);
+ }
+ return input.success(null);
}
/**
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermission.java b/core/java/android/content/pm/parsing/component/ParsedPermission.java
index f99a0b1dcadb..37e0e87c548c 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermission.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermission.java
@@ -21,14 +21,22 @@ import android.content.pm.PermissionInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+import com.android.internal.util.Parcelling.BuiltIn.ForStringSet;
+
+import java.util.Locale;
+import java.util.Set;
/** @hide */
public class ParsedPermission extends ParsedComponent {
+ private static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
+
@Nullable
String backgroundPermission;
@Nullable
@@ -39,6 +47,8 @@ public class ParsedPermission extends ParsedComponent {
boolean tree;
@Nullable
private ParsedPermissionGroup parsedPermissionGroup;
+ @Nullable
+ Set<String> knownCerts;
@VisibleForTesting
public ParsedPermission() {
@@ -81,6 +91,23 @@ public class ParsedPermission extends ParsedComponent {
return protectionLevel & ~PermissionInfo.PROTECTION_MASK_BASE;
}
+ public @Nullable Set<String> getKnownCerts() {
+ return knownCerts;
+ }
+
+ protected void setKnownCert(String knownCert) {
+ // Convert the provided digest to upper case for consistent Set membership
+ // checks when verifying the signing certificate digests of requesting apps.
+ this.knownCerts = Set.of(knownCert.toUpperCase(Locale.US));
+ }
+
+ protected void setKnownCerts(String[] knownCerts) {
+ this.knownCerts = new ArraySet<>();
+ for (String knownCert : knownCerts) {
+ this.knownCerts.add(knownCert.toUpperCase(Locale.US));
+ }
+ }
+
public int calculateFootprint() {
int size = getName().length();
if (getNonLocalizedLabel() != null) {
@@ -109,6 +136,7 @@ public class ParsedPermission extends ParsedComponent {
dest.writeInt(this.protectionLevel);
dest.writeBoolean(this.tree);
dest.writeParcelable(this.parsedPermissionGroup, flags);
+ sForStringSet.parcel(knownCerts, dest, flags);
}
protected ParsedPermission(Parcel in) {
@@ -121,6 +149,7 @@ public class ParsedPermission extends ParsedComponent {
this.protectionLevel = in.readInt();
this.tree = in.readBoolean();
this.parsedPermissionGroup = in.readParcelable(boot);
+ this.knownCerts = sForStringSet.unparcel(in);
}
public static final Parcelable.Creator<ParsedPermission> CREATOR =
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
index 9012b5ce2b1e..8afa70ec6364 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -90,6 +90,38 @@ public class ParsedPermissionUtils {
permission.flags = sa.getInt(
R.styleable.AndroidManifestPermission_permissionFlags, 0);
+ final int knownCertsResource = sa.getResourceId(
+ R.styleable.AndroidManifestPermission_knownCerts, 0);
+ if (knownCertsResource != 0) {
+ // The knownCerts attribute supports both a string array resource as well as a
+ // string resource for the case where the permission should only be granted to a
+ // single known signer.
+ final String resourceType = res.getResourceTypeName(knownCertsResource);
+ if (resourceType.equals("array")) {
+ final String[] knownCerts = res.getStringArray(knownCertsResource);
+ if (knownCerts != null) {
+ permission.setKnownCerts(knownCerts);
+ }
+ } else {
+ final String knownCert = res.getString(knownCertsResource);
+ if (knownCert != null) {
+ permission.setKnownCert(knownCert);
+ }
+ }
+ if (permission.knownCerts == null) {
+ Slog.w(TAG, packageName + " defines a knownSigner permission but"
+ + " the provided knownCerts resource is null");
+ }
+ } else {
+ // If the knownCerts resource ID is null check if the app specified a string
+ // value for the attribute representing a single trusted signer.
+ final String knownCert = sa.getString(
+ R.styleable.AndroidManifestPermission_knownCerts);
+ if (knownCert != null) {
+ permission.setKnownCert(knownCert);
+ }
+ }
+
// For now only platform runtime permissions can be restricted
if (!permission.isRuntime() || !"android".equals(permission.getPackageName())) {
permission.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED;
diff --git a/core/java/android/content/pm/permission/OWNERS b/core/java/android/content/pm/permission/OWNERS
index cde7b2ac1898..d302b0ae1ea8 100644
--- a/core/java/android/content/pm/permission/OWNERS
+++ b/core/java/android/content/pm/permission/OWNERS
@@ -3,7 +3,6 @@
toddke@android.com
toddke@google.com
patb@google.com
-moltmann@google.com
svetoslavganov@android.com
svetoslavganov@google.com
zhanghai@google.com
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
index af12536fff99..cbb3baaa6700 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
@@ -238,8 +238,8 @@ public interface DomainVerificationManager {
* permissions must be acquired and
* {@link Context#createPackageContextAsUser(String, int, UserHandle)} should be used.
*
- * This will be combined with the verification status and other system state to determine which
- * application is launched to handle an app link.
+ * Enabling an unverified domain will allow an application to open it, but this can only occur
+ * if no other app on the device is approved for the domain.
*
* @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}.
* @param domains The domains to toggle the state of.
@@ -290,13 +290,15 @@ public interface DomainVerificationManager {
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_UNKNOWN_DOMAIN,
+ REASON_UNABLE_TO_APPROVE
})
public @interface Reason {
}
@@ -313,6 +315,8 @@ public interface DomainVerificationManager {
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";
}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
index 8d16f75bf1b4..73346ef0273b 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
@@ -17,6 +17,7 @@
package android.content.pm.verify.domain;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Parcelable;
@@ -30,18 +31,18 @@ import java.util.Set;
import java.util.UUID;
/**
- * Contains the user selection state for a package. This means all web HTTP(S) domains
- * declared by a package in its manifest, whether or not they were marked for auto
- * verification.
+ * Contains the user selection state for a package. This means all web HTTP(S) domains declared by a
+ * package in its manifest, whether or not they were marked for auto verification.
* <p>
* By default, all apps are allowed to automatically open links with domains that they've
- * successfully verified against. This is reflected by {@link #isLinkHandlingAllowed()}.
- * The user can decide to disable this, disallowing the application from opening these
- * links.
+ * successfully verified against. This is reflected by {@link #isLinkHandlingAllowed()}. The user
+ * can decide to disable this, disallowing the application from opening all links. Note that the
+ * toggle affects <b>all</b> links and is not based on the verification state of the domains.
* <p>
- * Separately, independent of this toggle, the user can choose specific domains to allow
- * an app to open, which is reflected as part of {@link #getHostToUserSelectionMap()},
- * which maps the domain name to the true/false state of whether it was enabled by the user.
+ * Assuming the toggle is enabled, the user can also select additional unverified domains to grant
+ * to the application to open, which is reflected in {@link #getHostToUserSelectionMap()}. But only
+ * a single application can be approved for a domain unless the applications are both approved. If
+ * another application is approved, the user will not be allowed to enable the domain.
* <p>
* These values can be changed through the
* {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String,
@@ -105,7 +106,8 @@ public final class DomainVerificationUserSelection implements Parcelable {
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain
+ // /DomainVerificationUserSelection.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -216,7 +218,7 @@ public final class DomainVerificationUserSelection implements Parcelable {
@Override
@DataClass.Generated.Member
- public boolean equals(@android.annotation.Nullable Object o) {
+ public boolean equals(@Nullable Object o) {
// You can override field equality logic by defining either of the methods like:
// boolean fieldNameEquals(DomainVerificationUserSelection other) { ... }
// boolean fieldNameEquals(FieldType otherValue) { ... }
@@ -328,9 +330,9 @@ public final class DomainVerificationUserSelection implements Parcelable {
};
@DataClass.Generated(
- time = 1611799495498L,
+ time = 1612829797220L,
codegenVersion = "1.0.22",
- sourceFile = "frameworks/base/core/java/android/content/pm/domain/verify/DomainVerificationUserSelection.java",
+ sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.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 android.os.UserHandle mUser\nprivate final @android.annotation.NonNull boolean mLinkHandlingAllowed\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Boolean> mHostToUserSelectionMap\nclass DomainVerificationUserSelection extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 54c5ca476623..f49a7f660fc8 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -269,10 +269,11 @@ public class CompatibilityInfo implements Parcelable {
applicationInvertedScale = 1.0f / applicationScale;
compatFlags |= SCALING_REQUIRED;
} else if(density != 0) {
- applicationDensity = density;
- applicationScale = DisplayMetrics.DENSITY_DEVICE / (float) applicationDensity;
- applicationInvertedScale = 1.0f / applicationScale;
- compatFlags |= SCALING_REQUIRED;
+ applicationScale = overrideScale;
+ applicationInvertedScale = 1.0f / overrideScale;
+ applicationDensity = (int) ((DisplayMetrics.DENSITY_DEVICE_STABLE
+ * applicationInvertedScale) + .5f);
+ compatFlags |= HAS_OVERRIDE_SCALING;
} else {
applicationDensity = DisplayMetrics.DENSITY_DEVICE;
applicationScale = 1.0f;
@@ -534,10 +535,6 @@ public class CompatibilityInfo implements Parcelable {
if (isScalingRequired()) {
float invertedRatio = applicationInvertedScale;
inoutConfig.densityDpi = (int)((inoutConfig.densityDpi * invertedRatio) + .5f);
- inoutConfig.screenWidthDp = (int) ((inoutConfig.screenWidthDp * invertedRatio) + .5f);
- inoutConfig.screenHeightDp = (int) ((inoutConfig.screenHeightDp * invertedRatio) + .5f);
- inoutConfig.smallestScreenWidthDp =
- (int) ((inoutConfig.smallestScreenWidthDp * invertedRatio) + .5f);
inoutConfig.windowConfiguration.getMaxBounds().scale(invertedRatio);
inoutConfig.windowConfiguration.getBounds().scale(invertedRatio);
final Rect appBounds = inoutConfig.windowConfiguration.getAppBounds();
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
index 05769ddc5397..99b56a82173e 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -38,7 +38,7 @@ public final class ResourcesKey {
public final String[] mSplitResDirs;
@Nullable
- public final String[] mOverlayDirs;
+ public final String[] mOverlayPaths;
@Nullable
public final String[] mLibDirs;
@@ -67,7 +67,7 @@ public final class ResourcesKey {
public ResourcesKey(@Nullable String resDir,
@Nullable String[] splitResDirs,
- @Nullable String[] overlayDirs,
+ @Nullable String[] overlayPaths,
@Nullable String[] libDirs,
int overrideDisplayId,
@Nullable Configuration overrideConfig,
@@ -75,7 +75,7 @@ public final class ResourcesKey {
@Nullable ResourcesLoader[] loader) {
mResDir = resDir;
mSplitResDirs = splitResDirs;
- mOverlayDirs = overlayDirs;
+ mOverlayPaths = overlayPaths;
mLibDirs = libDirs;
mLoaders = (loader != null && loader.length == 0) ? null : loader;
mDisplayId = overrideDisplayId;
@@ -86,7 +86,7 @@ public final class ResourcesKey {
int hash = 17;
hash = 31 * hash + Objects.hashCode(mResDir);
hash = 31 * hash + Arrays.hashCode(mSplitResDirs);
- hash = 31 * hash + Arrays.hashCode(mOverlayDirs);
+ hash = 31 * hash + Arrays.hashCode(mOverlayPaths);
hash = 31 * hash + Arrays.hashCode(mLibDirs);
hash = 31 * hash + Objects.hashCode(mDisplayId);
hash = 31 * hash + Objects.hashCode(mOverrideConfiguration);
@@ -98,12 +98,12 @@ public final class ResourcesKey {
@UnsupportedAppUsage
public ResourcesKey(@Nullable String resDir,
@Nullable String[] splitResDirs,
- @Nullable String[] overlayDirs,
+ @Nullable String[] overlayPaths,
@Nullable String[] libDirs,
int displayId,
@Nullable Configuration overrideConfig,
@Nullable CompatibilityInfo compatInfo) {
- this(resDir, splitResDirs, overlayDirs, libDirs, displayId, overrideConfig, compatInfo,
+ this(resDir, splitResDirs, overlayPaths, libDirs, displayId, overrideConfig, compatInfo,
null);
}
@@ -115,7 +115,7 @@ public final class ResourcesKey {
if (mResDir != null && mResDir.startsWith(path)) {
return true;
} else {
- return anyStartsWith(mSplitResDirs, path) || anyStartsWith(mOverlayDirs, path)
+ return anyStartsWith(mSplitResDirs, path) || anyStartsWith(mOverlayPaths, path)
|| anyStartsWith(mLibDirs, path);
}
}
@@ -154,7 +154,7 @@ public final class ResourcesKey {
if (!Arrays.equals(mSplitResDirs, peer.mSplitResDirs)) {
return false;
}
- if (!Arrays.equals(mOverlayDirs, peer.mOverlayDirs)) {
+ if (!Arrays.equals(mOverlayPaths, peer.mOverlayPaths)) {
return false;
}
if (!Arrays.equals(mLibDirs, peer.mLibDirs)) {
@@ -186,8 +186,8 @@ public final class ResourcesKey {
}
builder.append("]");
builder.append(" mOverlayDirs=[");
- if (mOverlayDirs != null) {
- builder.append(TextUtils.join(",", mOverlayDirs));
+ if (mOverlayPaths != null) {
+ builder.append(TextUtils.join(",", mOverlayPaths));
}
builder.append("]");
builder.append(" mLibDirs=[");
diff --git a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
new file mode 100644
index 000000000000..25758e9d9a61
--- /dev/null
+++ b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
@@ -0,0 +1,264 @@
+/*
+ * 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.graphics.fonts;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Request for updating or adding a font family on the system.
+ *
+ * <p>You can update or add a font family with custom style parameters. The following example
+ * defines a font family called "roboto" using "Roboto-Regular" font file that is already available
+ * on the system by preloading or {@link FontManager#updateFontFile}.
+ * <pre>
+ * FontManager fm = getContext().getSystemService(FontManager.class);
+ * fm.updateFontFamily(new FontFamilyUpdateRequest.Builder()
+ * .addFontFamily(new FontFamilyUpdateRequest.FontFamily("roboto", Arrays.asList(
+ * new FontFamilyUpdateRequest.Font(
+ * "Roboto-Regular",
+ * new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ * Collections.emptyList()),
+ * new FontFamilyUpdateRequest.Font(
+ * "Roboto-Regular",
+ * new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_ITALIC),
+ * Collections.emptyList()))))
+ * .build(), fm.getFontConfig().getConfigVersion());
+ * </pre>
+ *
+ * <p>You can update or add font files in the same request by calling
+ * {@link FontFamilyUpdateRequest.Builder#addFontFileUpdateRequest(FontFileUpdateRequest)}.
+ * The following example adds "YourFont" font file and defines "your-font" font family in the same
+ * request. In this case, the font file represented by {@code yourFontFd} should be an OpenType
+ * compliant font file and have "YourFont" as PostScript name (ID=6) in 'name' table.
+ * <pre>
+ * FontManager fm = getContext().getSystemService(FontManager.class);
+ * fm.updateFontFamily(new FontFamilyUpdateRequest.Builder()
+ * .addFontFileUpdateRequest(new FontFileUpdateRequest(yourFontFd, signature))
+ * .addFontFamily(new FontFamilyUpdateRequest.FontFamily("your-font", Arrays.asList(
+ * new FontFamilyUpdateRequest.Font(
+ * "YourFont",
+ * new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ * Collections.emptyList()))))
+ * .build(), fm.getFontConfig().getConfigVersion());
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+public final class FontFamilyUpdateRequest {
+
+ /**
+ * A font family definition.
+ */
+ public static final class FontFamily {
+ @NonNull
+ private final String mName;
+ @NonNull
+ private final List<Font> mFonts;
+
+ /**
+ * Constructs a FontFamily.
+ *
+ * <p>A font family has a name to identify the font family. Apps can use
+ * {@link android.graphics.Typeface#create(String, int)} or XML resources to use a specific
+ * font family.
+ *
+ * <p>A font family consists of multiple fonts with different styles. The style information
+ * can be specified by {@link Font}.
+ *
+ * @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");
+ mName = name;
+ mFonts = fonts;
+ }
+
+ /**
+ * Returns the name of this family.
+ */
+ @NonNull
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns the fonts in this family.
+ */
+ @NonNull
+ public List<Font> getFonts() {
+ return mFonts;
+ }
+ }
+
+ /**
+ * A single entry in a font family representing a font.
+ */
+ public static final class Font {
+
+ @NonNull
+ private final String mPostScriptName;
+ @NonNull
+ private final FontStyle mStyle;
+ @NonNull
+ private final List<FontVariationAxis> mAxes;
+
+ /**
+ * Constructs a FontStyleVariation.
+ *
+ * <p>A font has a PostScript name to identify the font file to use, a {@link FontStyle}
+ * to specify the style, and a list of {@link FontVariationAxis} to specify axis tags and
+ * values for variable fonts. If the font file identified by {@code postScriptName} is not a
+ * variable font, {@code axes} must be empty.
+ *
+ * @param postScriptName The PostScript name of the font file to use. PostScript name is in
+ * Name ID 6 field in 'name' table, as specified by OpenType
+ * specification.
+ * @param style The style for this font.
+ * @param axes A list of {@link FontVariationAxis} to specify axis tags and values
+ * for variable fonts.
+ */
+ public Font(@NonNull String postScriptName, @NonNull FontStyle style,
+ @NonNull List<FontVariationAxis> axes) {
+ Objects.requireNonNull(postScriptName);
+ Preconditions.checkStringNotEmpty(postScriptName);
+ Objects.requireNonNull(style);
+ Objects.requireNonNull(axes);
+ Preconditions.checkCollectionElementsNotNull(axes, "axes");
+ mPostScriptName = postScriptName;
+ mStyle = style;
+ mAxes = axes;
+ }
+
+ /**
+ * Returns PostScript name of the font file to use.
+ */
+ @NonNull
+ public String getPostScriptName() {
+ return mPostScriptName;
+ }
+
+ /**
+ * Returns the style.
+ */
+ @NonNull
+ public FontStyle getStyle() {
+ return mStyle;
+ }
+
+ /**
+ * Returns the list of {@link FontVariationAxis}.
+ */
+ @NonNull
+ public List<FontVariationAxis> getAxes() {
+ return mAxes;
+ }
+ }
+
+ /**
+ * Builds a {@link FontFamilyUpdateRequest}.
+ */
+ public static final class Builder {
+ @NonNull
+ private final List<FontFileUpdateRequest> mFontFileUpdateRequests = new ArrayList<>();
+ @NonNull
+ private final List<FontFamily> mFontFamilies = new ArrayList<>();
+
+ /**
+ * Constructs a FontFamilyUpdateRequest.Builder.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Adds a {@link FontFileUpdateRequest} to execute as a part of the constructed
+ * {@link FontFamilyUpdateRequest}.
+ *
+ * @param request A font file update request.
+ * @return This builder object.
+ */
+ @NonNull
+ public Builder addFontFileUpdateRequest(@NonNull FontFileUpdateRequest request) {
+ Objects.requireNonNull(request);
+ mFontFileUpdateRequests.add(request);
+ return this;
+ }
+
+ /**
+ * Adds a font family to update an existing font family in the system font config or
+ * add as a new font family to the system font config.
+ *
+ * @param fontFamily An font family definition to add or update.
+ * @return This builder object.
+ */
+ @NonNull
+ public Builder addFontFamily(@NonNull FontFamily fontFamily) {
+ Objects.requireNonNull(fontFamily);
+ mFontFamilies.add(fontFamily);
+ return this;
+ }
+
+ /**
+ * Builds a {@link FontFamilyUpdateRequest}.
+ */
+ @NonNull
+ public FontFamilyUpdateRequest build() {
+ return new FontFamilyUpdateRequest(mFontFileUpdateRequests, mFontFamilies);
+ }
+ }
+
+ @NonNull
+ private final List<FontFileUpdateRequest> mFontFiles;
+
+ @NonNull
+ private final List<FontFamily> mFontFamilies;
+
+ private FontFamilyUpdateRequest(@NonNull List<FontFileUpdateRequest> fontFiles,
+ @NonNull List<FontFamily> fontFamilies) {
+ mFontFiles = fontFiles;
+ mFontFamilies = fontFamilies;
+ }
+
+ /**
+ * Returns the list of {@link FontFileUpdateRequest} that will be executed as a part of this
+ * request.
+ */
+ @NonNull
+ public List<FontFileUpdateRequest> getFontFileUpdateRequests() {
+ return mFontFiles;
+ }
+
+ /**
+ * Returns the list of {@link FontFamily} that will be updated in this request.
+ */
+ @NonNull
+ public List<FontFamily> getFontFamilies() {
+ return mFontFamilies;
+ }
+}
diff --git a/core/java/android/graphics/fonts/FontFileUpdateRequest.java b/core/java/android/graphics/fonts/FontFileUpdateRequest.java
new file mode 100644
index 000000000000..cf1dca965216
--- /dev/null
+++ b/core/java/android/graphics/fonts/FontFileUpdateRequest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.graphics.fonts;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.ParcelFileDescriptor;
+
+import java.util.Objects;
+
+/**
+ * Request for updating a font file on the system.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FontFileUpdateRequest {
+
+ private final ParcelFileDescriptor mParcelFileDescriptor;
+ private final byte[] mSignature;
+
+ /**
+ * Creates a FontFileUpdateRequest with the given file and signature.
+ *
+ * @param parcelFileDescriptor A file descriptor of the font file.
+ * @param signature A PKCS#7 detached signature for verifying the font file.
+ */
+ public FontFileUpdateRequest(@NonNull ParcelFileDescriptor parcelFileDescriptor,
+ @NonNull byte[] signature) {
+ Objects.requireNonNull(parcelFileDescriptor);
+ Objects.requireNonNull(signature);
+ mParcelFileDescriptor = parcelFileDescriptor;
+ mSignature = signature;
+ }
+
+ /**
+ * Returns the file descriptor of the font file.
+ */
+ @NonNull
+ public ParcelFileDescriptor getParcelFileDescriptor() {
+ return mParcelFileDescriptor;
+ }
+
+ /**
+ * Returns the PKCS#7 detached signature for verifying the font file.
+ */
+ @NonNull
+ public byte[] getSignature() {
+ return mSignature;
+ }
+}
diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java
index abb4f9fa7eef..e512cf1bbb1f 100644
--- a/core/java/android/graphics/fonts/FontManager.java
+++ b/core/java/android/graphics/fonts/FontManager.java
@@ -35,6 +35,8 @@ import com.android.internal.graphics.fonts.IFontManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -58,7 +60,7 @@ public class FontManager {
RESULT_ERROR_VERIFICATION_FAILURE, RESULT_ERROR_VERSION_MISMATCH,
RESULT_ERROR_INVALID_FONT_FILE, RESULT_ERROR_INVALID_FONT_NAME,
RESULT_ERROR_DOWNGRADING, RESULT_ERROR_FAILED_UPDATE_CONFIG,
- RESULT_ERROR_FONT_UPDATER_DISABLED, RESULT_ERROR_REMOTE_EXCEPTION })
+ RESULT_ERROR_FONT_UPDATER_DISABLED, RESULT_ERROR_FONT_NOT_FOUND })
@Retention(RetentionPolicy.SOURCE)
public @interface ResultCode {}
@@ -131,9 +133,10 @@ public class FontManager {
public static final int RESULT_ERROR_VERSION_MISMATCH = -8;
/**
- * Indicates a failure due to IPC communication.
+ * Indicates a failure occurred because a font with the specified PostScript name could not be
+ * found.
*/
- public static final int RESULT_ERROR_REMOTE_EXCEPTION = -9;
+ public static final int RESULT_ERROR_FONT_NOT_FOUND = -9;
/**
* Indicates a failure of opening font file.
@@ -205,42 +208,40 @@ public class FontManager {
}
/**
- * Update system installed font file.
+ * Update a system installed font file.
*
* <p>
- * To protect devices, system font updater relies on the Linux Kernel feature called fs-verity.
- * If the device is not ready for fs-verity, {@link #RESULT_ERROR_FONT_UPDATER_DISABLED} will be
+ * To protect devices, system font updater relies on a Linux Kernel feature called fs-verity.
+ * If the device does not support fs-verity, {@link #RESULT_ERROR_FONT_UPDATER_DISABLED} will be
* returned.
*
- * Android only accepts OpenType compliant font files. If other font files are provided,
+ * <p>Android only accepts OpenType compliant font files. If other font files are provided,
* {@link #RESULT_ERROR_INVALID_FONT_FILE} will be returned.
*
- * The font file to be updated is identified by PostScript name stored in name table. If the
- * font file doesn't have PostScript name entry, {@link #RESULT_ERROR_INVALID_FONT_NAME} will be
- * returned.
+ * <p>The font file to be updated is identified by PostScript name stored in the name table. If
+ * the font file doesn't have PostScript name entry, {@link #RESULT_ERROR_INVALID_FONT_NAME}
+ * will be returned.
*
- * The entire font file is verified with the given signature for the system installed
- * certificate. If the system cannot verify the font contents,
+ * <p>The entire font file is verified with the given signature using system installed
+ * certificates. If the system cannot verify the font file contents,
* {@link #RESULT_ERROR_VERIFICATION_FAILURE} will be returned.
*
- * The font file must have newer or equal revision number in the head table. In other words, the
- * downgrading font file is not allowed. If the older font file is provided,
+ * <p>The font file must have a newer revision number in the head table. In other words, it is
+ * not allowed to downgrade a font file. If an older font file is provided,
* {@link #RESULT_ERROR_DOWNGRADING} will be returned.
*
- * The caller must specify the base config version for keeping consist system configuration. If
- * the system configuration is updated for some reason between you get config with
- * {@link #getFontConfig()} and calling this method, {@link #RESULT_ERROR_VERSION_MISMATCH} will
- * be returned. Get the latest font configuration by calling {@link #getFontConfig()} again and
- * try with the latest config version again.
- *
- * @param pfd A file descriptor of the font file.
- * @param signature A PKCS#7 detached signature for verifying entire font files.
- * @param baseVersion A base config version to be updated. You can get latest config version by
- * {@link FontConfig#getConfigVersion()} via {@link #getFontConfig()}. If the
- * system has newer config version, the update will fail with
- * {@link #RESULT_ERROR_VERSION_MISMATCH}. Try to get the latest config and
- * try update again.
- * @return result code.
+ * <p>The caller must specify the base config version for keeping the font configuration
+ * consistent. If the font configuration is updated for some reason between the time you get
+ * a configuration with {@link #getFontConfig()} and the time when you call this method,
+ * {@link #RESULT_ERROR_VERSION_MISMATCH} will be returned. Get the latest font configuration by
+ * calling {@link #getFontConfig()} and call this method again with the latest config version.
+ *
+ * @param request A {@link FontFileUpdateRequest} to execute.
+ * @param baseVersion A base config version to be updated. You can get the latest config version
+ * by {@link FontConfig#getConfigVersion()} via {@link #getFontConfig()}. If
+ * the system has a newer config version, the update will fail with
+ * {@link #RESULT_ERROR_VERSION_MISMATCH}.
+ * @return A result code.
*
* @see FontConfig#getConfigVersion()
* @see #getFontConfig()
@@ -253,18 +254,88 @@ public class FontManager {
* @see #RESULT_ERROR_DOWNGRADING
* @see #RESULT_ERROR_FAILED_UPDATE_CONFIG
* @see #RESULT_ERROR_FONT_UPDATER_DISABLED
- * @see #RESULT_ERROR_REMOTE_EXCEPTION
*/
@RequiresPermission(Manifest.permission.UPDATE_FONTS) public @ResultCode int updateFontFile(
+ @NonNull FontFileUpdateRequest request, @IntRange(from = 0) int baseVersion) {
+ try {
+ return mIFontManager.updateFontFile(new FontUpdateRequest(
+ request.getParcelFileDescriptor(), request.getSignature()), baseVersion);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @deprecated Use {@link #updateFontFile(FontFileUpdateRequest, int)}
+ */
+ // TODO: Remove this API before Developer Preview 3.
+ @Deprecated
+ @RequiresPermission(Manifest.permission.UPDATE_FONTS) public @ResultCode int updateFontFile(
@NonNull ParcelFileDescriptor pfd,
@NonNull byte[] signature,
@IntRange(from = 0) int baseVersion
) {
+ return updateFontFile(new FontFileUpdateRequest(pfd, signature), baseVersion);
+ }
+
+
+ /**
+ * Update or add system wide font families.
+ *
+ * <p>This method will update existing font families or add new font families. The updated
+ * font family definitions will be used when creating {@link android.graphics.Typeface} objects
+ * with using {@link android.graphics.Typeface#create(String, int)} specifying the family name,
+ * or through XML resources. Note that system fallback fonts cannot be modified by this method.
+ * Apps must use {@link android.graphics.Typeface.CustomFallbackBuilder} to use custom fallback
+ * fonts.
+ *
+ * <p>Font files can be updated by including {@link FontFileUpdateRequest} to {@code request}
+ * via {@link FontFamilyUpdateRequest.Builder#addFontFileUpdateRequest(FontFileUpdateRequest)}.
+ * The same constraints as {@link #updateFontFile} will apply when updating font files.
+ *
+ * <p>The caller must specify the base config version for keeping the font configuration
+ * consistent. If the font configuration is updated for some reason between the time you get
+ * a configuration with {@link #getFontConfig()} and the time when you call this method,
+ * {@link #RESULT_ERROR_VERSION_MISMATCH} will be returned. Get the latest font configuration by
+ * calling {@link #getFontConfig()} and call this method again with the latest config version.
+ *
+ * @param request A {@link FontFamilyUpdateRequest} to execute.
+ * @param baseVersion A base config version to be updated. You can get the latest config version
+ * by {@link FontConfig#getConfigVersion()} via {@link #getFontConfig()}. If
+ * the system has a newer config version, the update will fail with
+ * {@link #RESULT_ERROR_VERSION_MISMATCH}.
+ * @return A result code.
+ * @see FontConfig#getConfigVersion()
+ * @see #getFontConfig()
+ * @see #RESULT_SUCCESS
+ * @see #RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE
+ * @see #RESULT_ERROR_VERIFICATION_FAILURE
+ * @see #RESULT_ERROR_VERSION_MISMATCH
+ * @see #RESULT_ERROR_INVALID_FONT_FILE
+ * @see #RESULT_ERROR_INVALID_FONT_NAME
+ * @see #RESULT_ERROR_DOWNGRADING
+ * @see #RESULT_ERROR_FAILED_UPDATE_CONFIG
+ * @see #RESULT_ERROR_FONT_UPDATER_DISABLED
+ * @see #RESULT_ERROR_FONT_NOT_FOUND
+ */
+ @RequiresPermission(Manifest.permission.UPDATE_FONTS) public @ResultCode int updateFontFamily(
+ @NonNull FontFamilyUpdateRequest request, @IntRange(from = 0) int baseVersion) {
+ List<FontUpdateRequest> requests = new ArrayList<>();
+ List<FontFileUpdateRequest> fontFileUpdateRequests = request.getFontFileUpdateRequests();
+ for (int i = 0; i < fontFileUpdateRequests.size(); i++) {
+ FontFileUpdateRequest fontFile = fontFileUpdateRequests.get(i);
+ requests.add(new FontUpdateRequest(fontFile.getParcelFileDescriptor(),
+ fontFile.getSignature()));
+ }
+ List<FontFamilyUpdateRequest.FontFamily> fontFamilies = request.getFontFamilies();
+ for (int i = 0; i < fontFamilies.size(); i++) {
+ FontFamilyUpdateRequest.FontFamily fontFamily = fontFamilies.get(i);
+ requests.add(new FontUpdateRequest(fontFamily.getName(), fontFamily.getFonts()));
+ }
try {
- return mIFontManager.updateFont(baseVersion, new FontUpdateRequest(pfd, signature));
+ return mIFontManager.updateFontFamily(requests, baseVersion);
} catch (RemoteException e) {
- Log.e(TAG, "Failed to call updateFont API", e);
- return RESULT_ERROR_REMOTE_EXCEPTION;
+ throw e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/graphics/fonts/FontUpdateRequest.java b/core/java/android/graphics/fonts/FontUpdateRequest.java
index db047f8ba696..b79c8f62d492 100644
--- a/core/java/android/graphics/fonts/FontUpdateRequest.java
+++ b/core/java/android/graphics/fonts/FontUpdateRequest.java
@@ -16,18 +16,37 @@
package android.graphics.fonts;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.LocaleList;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.text.FontConfig;
+
+import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
/**
* Represents a font update request. Currently only font install request is supported.
* @hide
*/
-// TODO: Support font config update.
public final class FontUpdateRequest implements Parcelable {
+ public static final int TYPE_UPDATE_FONT_FILE = 0;
+ public static final int TYPE_UPDATE_FONT_FAMILY = 1;
+
+ @IntDef(prefix = "TYPE_", value = {
+ TYPE_UPDATE_FONT_FILE,
+ TYPE_UPDATE_FONT_FAMILY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
public static final Creator<FontUpdateRequest> CREATOR = new Creator<FontUpdateRequest>() {
@Override
public FontUpdateRequest createFromParcel(Parcel in) {
@@ -40,39 +59,87 @@ public final class FontUpdateRequest implements Parcelable {
}
};
- @NonNull
+ private final @Type int mType;
+ // NonNull if mType == TYPE_UPDATE_FONT_FILE.
+ @Nullable
private final ParcelFileDescriptor mFd;
- @NonNull
+ // NonNull if mType == TYPE_UPDATE_FONT_FILE.
+ @Nullable
private final byte[] mSignature;
+ // NonNull if mType == TYPE_UPDATE_FONT_FAMILY.
+ @Nullable
+ private final FontConfig.FontFamily mFontFamily;
public FontUpdateRequest(@NonNull ParcelFileDescriptor fd, @NonNull byte[] signature) {
+ mType = TYPE_UPDATE_FONT_FILE;
mFd = fd;
mSignature = signature;
+ mFontFamily = null;
+ }
+
+ public FontUpdateRequest(@NonNull FontConfig.FontFamily fontFamily) {
+ mType = TYPE_UPDATE_FONT_FAMILY;
+ mFd = null;
+ mSignature = null;
+ mFontFamily = fontFamily;
+ }
+
+ public FontUpdateRequest(@NonNull String postScriptName,
+ @NonNull List<FontFamilyUpdateRequest.Font> variations) {
+ // TODO: Serialize the request directly instead of reusing FontConfig.FontFamily.
+ this(createFontFamily(postScriptName, variations));
}
- private FontUpdateRequest(Parcel in) {
+ private static FontConfig.FontFamily createFontFamily(@NonNull String postScriptName,
+ @NonNull List<FontFamilyUpdateRequest.Font> fonts) {
+ List<FontConfig.Font> configFonts = new ArrayList<>(fonts.size());
+ for (FontFamilyUpdateRequest.Font font : fonts) {
+ // TODO: Support .otf.
+ configFonts.add(new FontConfig.Font(new File(font.getPostScriptName() + ".ttf"), null,
+ font.getStyle(), 0 /* index */,
+ FontVariationAxis.toFontVariationSettings(font.getAxes()),
+ null /* fontFamilyName */));
+ }
+ return new FontConfig.FontFamily(configFonts, postScriptName,
+ LocaleList.getEmptyLocaleList(), FontConfig.FontFamily.VARIANT_DEFAULT);
+ }
+
+ protected FontUpdateRequest(Parcel in) {
+ mType = in.readInt();
mFd = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
mSignature = in.readBlob();
+ mFontFamily = in.readParcelable(FontConfig.FontFamily.class.getClassLoader());
}
- @NonNull
+ public @Type int getType() {
+ return mType;
+ }
+
+ @Nullable
public ParcelFileDescriptor getFd() {
return mFd;
}
- @NonNull
+ @Nullable
public byte[] getSignature() {
return mSignature;
}
+ @Nullable
+ public FontConfig.FontFamily getFontFamily() {
+ return mFontFamily;
+ }
+
@Override
public int describeContents() {
- return Parcelable.CONTENTS_FILE_DESCRIPTOR;
+ return mFd != null ? mFd.describeContents() : 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
dest.writeParcelable(mFd, flags);
dest.writeBlob(mSignature);
+ dest.writeParcelable(mFontFamily, flags);
}
}
diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS
index 3295042b0b35..2b4e4a106cec 100644
--- a/core/java/android/hardware/OWNERS
+++ b/core/java/android/hardware/OWNERS
@@ -3,3 +3,6 @@ per-file *Camera*=cychen@google.com,epeev@google.com,etalvala@google.com,shuzhen
# Sensor Privacy
per-file *SensorPrivacy* = file:platform/frameworks/native:/libs/sensorprivacy/OWNERS
+
+# Sensors framework
+per-file *Sensor*,*Trigger* = file:platform/frameworks/native:/services/sensorservice/OWNERS
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 3ee3a33dc2b4..673dc9932515 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -16,11 +16,18 @@
package android.hardware;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
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.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -54,6 +61,19 @@ public class SystemSensorManager extends SensorManager {
private static final boolean DEBUG_DYNAMIC_SENSOR = true;
private static final int MIN_DIRECT_CHANNEL_BUFFER_SIZE = 104;
private static final int MAX_LISTENER_COUNT = 128;
+ private static final int CAPPED_SAMPLING_PERIOD_US = 5000;
+ 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";
+ /**
+ * 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
+ * are faster than 200 Hz.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ static final long CHANGE_ID_SAMPLING_RATE_SENSORS_PERMISSION = 136069189L;
private static native void nativeClassInit();
private static native long nativeCreate(String opPackageName);
@@ -98,6 +118,8 @@ public class SystemSensorManager extends SensorManager {
// Looper associated with the context in which this instance was created.
private final Looper mMainLooper;
private final int mTargetSdkLevel;
+ private final boolean mIsPackageDebuggable;
+ private final boolean mHasHighSamplingRateSensorsPermission;
private final Context mContext;
private final long mNativeInstance;
@@ -111,9 +133,16 @@ public class SystemSensorManager extends SensorManager {
}
mMainLooper = mainLooper;
- mTargetSdkLevel = context.getApplicationInfo().targetSdkVersion;
+ ApplicationInfo appInfo = context.getApplicationInfo();
+ mTargetSdkLevel = appInfo.targetSdkVersion;
mContext = context;
mNativeInstance = nativeCreate(context.getOpPackageName());
+ mIsPackageDebuggable = (0 != (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE));
+ PackageManager packageManager = context.getPackageManager();
+ mHasHighSamplingRateSensorsPermission =
+ (PERMISSION_GRANTED == packageManager.checkPermission(
+ HIGH_SAMPLING_RATE_SENSORS_PERMISSION,
+ appInfo.packageName));
// initialize the sensor list
for (int index = 0;; ++index) {
@@ -544,10 +573,18 @@ public class SystemSensorManager extends SensorManager {
}
int sensorHandle = (sensor == null) ? -1 : sensor.getHandle();
+ if (Compatibility.isChangeEnabled(CHANGE_ID_SAMPLING_RATE_SENSORS_PERMISSION)
+ && rate > CAPPED_SAMPLING_RATE_LEVEL
+ && mIsPackageDebuggable
+ && !mHasHighSamplingRateSensorsPermission) {
+ Compatibility.reportChange(CHANGE_ID_SAMPLING_RATE_SENSORS_PERMISSION);
+ throw new SecurityException("To use the sampling rate level " + rate
+ + ", app needs to declare the normal permission"
+ + " HIGH_SAMPLING_RATE_SENSORS.");
+ }
int ret = nativeConfigDirectChannel(
mNativeInstance, channel.getNativeHandle(), sensorHandle, rate);
-
if (rate == SensorDirectChannel.RATE_STOP) {
return (ret == 0) ? 1 : 0;
} else {
@@ -747,6 +784,15 @@ public class SystemSensorManager extends SensorManager {
Sensor sensor, int rateUs, int maxBatchReportLatencyUs) {
if (mNativeSensorEventQueue == 0) throw new NullPointerException();
if (sensor == null) throw new NullPointerException();
+ if (Compatibility.isChangeEnabled(CHANGE_ID_SAMPLING_RATE_SENSORS_PERMISSION)
+ && rateUs < CAPPED_SAMPLING_PERIOD_US
+ && mManager.mIsPackageDebuggable
+ && !mManager.mHasHighSamplingRateSensorsPermission) {
+ Compatibility.reportChange(CHANGE_ID_SAMPLING_RATE_SENSORS_PERMISSION);
+ throw new SecurityException("To use the sampling rate of " + rateUs
+ + " microseconds, app needs to declare the normal permission"
+ + " HIGH_SAMPLING_RATE_SENSORS.");
+ }
return nativeEnableSensor(mNativeSensorEventQueue, sensor.getHandle(), rateUs,
maxBatchReportLatencyUs);
}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 08b1e245dc83..5b28e0035b09 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -236,8 +236,9 @@ public class BiometricManager {
@RequiresPermission(TEST_BIOMETRIC)
public BiometricTestSession createTestSession(int sensorId) {
try {
- return new BiometricTestSession(mContext,
- mService.createTestSession(sensorId, mContext.getOpPackageName()));
+ return new BiometricTestSession(mContext, sensorId,
+ (context, sensorId1, callback) -> mService
+ .createTestSession(sensorId1, callback, context.getOpPackageName()));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -390,7 +391,6 @@ public class BiometricManager {
* in Keystore land as SIDs, and are used during key generation.
* @hide
*/
- @RequiresPermission(USE_BIOMETRIC_INTERNAL)
public long[] getAuthenticatorIds() {
if (mService != null) {
try {
diff --git a/core/java/android/hardware/biometrics/BiometricTestSession.java b/core/java/android/hardware/biometrics/BiometricTestSession.java
index 2b689899af01..ff1a17e07c11 100644
--- a/core/java/android/hardware/biometrics/BiometricTestSession.java
+++ b/core/java/android/hardware/biometrics/BiometricTestSession.java
@@ -19,12 +19,17 @@ package android.hardware.biometrics;
import static android.Manifest.permission.TEST_BIOMETRIC;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.content.Context;
import android.hardware.fingerprint.FingerprintManager;
import android.os.RemoteException;
import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Common set of interfaces to test biometric-related APIs, including {@link BiometricPrompt} and
@@ -33,19 +38,60 @@ import android.util.ArraySet;
*/
@TestApi
public class BiometricTestSession implements AutoCloseable {
+ private static final String TAG = "BiometricTestSession";
+
+ /**
+ * @hide
+ */
+ public interface TestSessionProvider {
+ @NonNull
+ ITestSession createTestSession(@NonNull Context context, int sensorId,
+ @NonNull ITestSessionCallback callback) throws RemoteException;
+ }
+
private final Context mContext;
+ private final int mSensorId;
private final ITestSession mTestSession;
// Keep track of users that were tested, which need to be cleaned up when finishing.
- private final ArraySet<Integer> mTestedUsers;
+ @NonNull private final ArraySet<Integer> mTestedUsers;
+
+ // Track the users currently cleaning up, and provide a latch that gets notified when all
+ // users have finished cleaning up. This is an imperfect system, as there can technically be
+ // multiple cleanups per user. Theoretically we should track the cleanup's BaseClientMonitor's
+ // unique ID, but it's complicated to plumb it through. This should be fine for now.
+ @Nullable private CountDownLatch mCloseLatch;
+ @NonNull private final ArraySet<Integer> mUsersCleaningUp;
+
+ private final ITestSessionCallback mCallback = new ITestSessionCallback.Stub() {
+ @Override
+ public void onCleanupStarted(int userId) {
+ Log.d(TAG, "onCleanupStarted, sensor: " + mSensorId + ", userId: " + userId);
+ }
+
+ @Override
+ public void onCleanupFinished(int userId) {
+ Log.d(TAG, "onCleanupFinished, sensor: " + mSensorId
+ + ", userId: " + userId
+ + ", remaining users: " + mUsersCleaningUp.size());
+ mUsersCleaningUp.remove(userId);
+
+ if (mUsersCleaningUp.isEmpty() && mCloseLatch != null) {
+ mCloseLatch.countDown();
+ }
+ }
+ };
/**
* @hide
*/
- public BiometricTestSession(@NonNull Context context, @NonNull ITestSession testSession) {
+ public BiometricTestSession(@NonNull Context context, int sensorId,
+ @NonNull TestSessionProvider testSessionProvider) throws RemoteException {
mContext = context;
- mTestSession = testSession;
+ mSensorId = sensorId;
+ mTestSession = testSessionProvider.createTestSession(context, sensorId, mCallback);
mTestedUsers = new ArraySet<>();
+ mUsersCleaningUp = new ArraySet<>();
setTestHalEnabled(true);
}
@@ -61,6 +107,7 @@ public class BiometricTestSession implements AutoCloseable {
@RequiresPermission(TEST_BIOMETRIC)
private void setTestHalEnabled(boolean enabled) {
try {
+ Log.w(TAG, "setTestHalEnabled, sensor: " + mSensorId + " enabled: " + enabled);
mTestSession.setTestHalEnabled(enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -169,6 +216,11 @@ public class BiometricTestSession implements AutoCloseable {
@RequiresPermission(TEST_BIOMETRIC)
public void cleanupInternalState(int userId) {
try {
+ if (mUsersCleaningUp.contains(userId)) {
+ Log.w(TAG, "Cleanup already in progress for user: " + userId);
+ }
+
+ mUsersCleaningUp.add(userId);
mTestSession.cleanupInternalState(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -178,12 +230,24 @@ public class BiometricTestSession implements AutoCloseable {
@Override
@RequiresPermission(TEST_BIOMETRIC)
public void close() {
- // Disable the test HAL first, so that enumerate is run on the real HAL, which should have
- // no enrollments. Test-only framework enrollments will be deleted.
- setTestHalEnabled(false);
-
- for (int user : mTestedUsers) {
- cleanupInternalState(user);
+ // Cleanup can be performed using the test HAL, since it always responds to enumerate with
+ // zero enrollments.
+ if (!mTestedUsers.isEmpty()) {
+ mCloseLatch = new CountDownLatch(1);
+ for (int user : mTestedUsers) {
+ cleanupInternalState(user);
+ }
+
+ try {
+ Log.d(TAG, "Awaiting latch...");
+ mCloseLatch.await(10, TimeUnit.SECONDS);
+ Log.d(TAG, "Finished awaiting");
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Latch interrupted", e);
+ }
}
+
+ // Disable the test HAL after the sensor becomes idle.
+ setTestHalEnabled(false);
}
}
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index 0dfd5dbf300e..d8c9dbc849a9 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -20,6 +20,7 @@ import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
@@ -32,7 +33,7 @@ import android.hardware.biometrics.SensorPropertiesInternal;
*/
interface IAuthService {
// Creates a test session with the specified sensorId
- ITestSession createTestSession(int sensorId, String opPackageName);
+ ITestSession createTestSession(int sensorId, ITestSessionCallback callback, String opPackageName);
// Retrieve static sensor properties for all biometric sensors
List<SensorPropertiesInternal> getSensorProperties(String opPackageName);
diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
index c854ac9847d8..7639c5dd4d16 100644
--- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
@@ -20,6 +20,7 @@ import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
@@ -32,7 +33,7 @@ import android.hardware.face.Face;
interface IBiometricAuthenticator {
// Creates a test session
- ITestSession createTestSession(String opPackageName);
+ ITestSession createTestSession(ITestSessionCallback callback, String opPackageName);
// Retrieve static sensor properties
SensorPropertiesInternal getSensorProperties(String opPackageName);
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index a14a910a9e50..24331863a05f 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -21,6 +21,7 @@ import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
@@ -30,7 +31,7 @@ import android.hardware.biometrics.SensorPropertiesInternal;
*/
interface IBiometricService {
// Creates a test session with the specified sensorId
- ITestSession createTestSession(int sensorId, String opPackageName);
+ ITestSession createTestSession(int sensorId, ITestSessionCallback callback, String opPackageName);
// Retrieve static sensor properties for all biometric sensors
List<SensorPropertiesInternal> getSensorProperties(String opPackageName);
diff --git a/core/java/android/hardware/biometrics/ITestSession.aidl b/core/java/android/hardware/biometrics/ITestSession.aidl
index fa7a62c53531..f8395a119c0b 100644
--- a/core/java/android/hardware/biometrics/ITestSession.aidl
+++ b/core/java/android/hardware/biometrics/ITestSession.aidl
@@ -18,7 +18,7 @@ package android.hardware.biometrics;
import android.hardware.biometrics.SensorPropertiesInternal;
/**
- * A test service for FingerprintManager and BiometricPrompt.
+ * A test service for FingerprintManager and BiometricManager.
* @hide
*/
interface ITestSession {
diff --git a/tools/hiddenapi/Android.bp b/core/java/android/hardware/biometrics/ITestSessionCallback.aidl
index e0eb06cbea7f..3d9517f29548 100644
--- a/tools/hiddenapi/Android.bp
+++ b/core/java/android/hardware/biometrics/ITestSessionCallback.aidl
@@ -13,18 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.hardware.biometrics;
-python_binary_host {
- name: "merge_csv",
- main: "merge_csv.py",
- srcs: ["merge_csv.py"],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true
- },
- },
+/**
+ * ITestSession callback for FingerprintManager and BiometricManager.
+ * @hide
+ */
+interface ITestSessionCallback {
+ void onCleanupStarted(int userId);
+ void onCleanupFinished(int userId);
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 0f595b700013..16ab900dee06 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2908,6 +2908,29 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<int[]>("android.scaler.availableRotateAndCropModes", int[].class);
/**
+ * <p>Default YUV/PRIVATE size to use for requesting secure image buffers.</p>
+ * <p>This entry lists the default size supported in the secure camera mode. This entry is
+ * optional on devices support the SECURE_IMAGE_DATA capability. This entry will be null
+ * if the camera device does not list SECURE_IMAGE_DATA capability.</p>
+ * <p>When the key is present, only a PRIVATE/YUV output of the specified size is guaranteed
+ * to be supported by the camera HAL in the secure camera mode. Any other format or
+ * resolutions might not be supported. Use
+ * {@link CameraDevice#isSessionConfigurationSupported }
+ * API to query if a secure session configuration is supported if the device supports this
+ * API.</p>
+ * <p>If this key returns null on a device with SECURE_IMAGE_DATA capability, the application
+ * can assume all output sizes listed in the
+ * {@link android.hardware.camera2.params.StreamConfigurationMap }
+ * are supported.</p>
+ * <p><b>Units</b>: Pixels</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<android.util.Size> SCALER_DEFAULT_SECURE_IMAGE_SIZE =
+ new Key<android.util.Size>("android.scaler.defaultSecureImageSize", android.util.Size.class);
+
+ /**
* <p>The area of the image sensor which corresponds to active pixels after any geometric
* distortion correction has been applied.</p>
* <p>This is the rectangle representing the size of the active region of the sensor (i.e.
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 8fe71583912c..8451dedb6c37 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -1233,6 +1233,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
int sequenceId) {
synchronized (mInterfaceLock) {
if (mInternalRepeatingRequestEnabled) {
+ mRepeatingRequestImageReader.setOnImageAvailableListener(
+ new ImageLoopbackCallback(), mHandler);
resumeInternalRepeatingRequest(true);
}
}
@@ -1263,7 +1265,12 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
mRequestUpdatedNeeded = false;
resumeInternalRepeatingRequest(false);
} else if (mInternalRepeatingRequestEnabled) {
+ mRepeatingRequestImageReader.setOnImageAvailableListener(
+ new ImageLoopbackCallback(), mHandler);
resumeInternalRepeatingRequest(true);
+ } else {
+ mRepeatingRequestImageReader.setOnImageAvailableListener(
+ new ImageLoopbackCallback(), mHandler);
}
}
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index 8bb0b184ce02..10a814acd70b 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -1917,9 +1917,18 @@ public final class StreamConfigurationMap {
(3 << HAL_DATASPACE_TRANSFER_SHIFT) |
(1 << HAL_DATASPACE_RANGE_SHIFT);
- private static final int HAL_DATASPACE_DEPTH = 0x1000;
- private static final int HAL_DATASPACE_DYNAMIC_DEPTH = 0x1002;
- private static final int HAL_DATASPACE_HEIF = 0x1003;
+ /**
+ * @hide
+ */
+ public static final int HAL_DATASPACE_DEPTH = 0x1000;
+ /**
+ * @hide
+ */
+ public static final int HAL_DATASPACE_DYNAMIC_DEPTH = 0x1002;
+ /**
+ * @hide
+ */
+ public static final int HAL_DATASPACE_HEIF = 0x1003;
private static final long DURATION_20FPS_NS = 50000000L;
/**
* @see #getDurations(int, int)
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index f175e7b00b7e..2d4b2ccd7514 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -42,6 +42,12 @@ public final class DeviceStateManager {
*/
public static final int INVALID_DEVICE_STATE = -1;
+ /** The minimum allowed device state identifier. */
+ public static final int MINIMUM_DEVICE_STATE = 0;
+
+ /** The maximum allowed device state identifier. */
+ public static final int MAXIMUM_DEVICE_STATE = 255;
+
private final DeviceStateManagerGlobal mGlobal;
/** @hide */
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 9bae1ff4b906..bbf421da6b48 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -854,8 +854,8 @@ public final class DisplayManager {
*
* @hide Requires signature permission.
*/
- public void setTemporaryBrightness(float brightness) {
- mGlobal.setTemporaryBrightness(brightness);
+ public void setTemporaryBrightness(int displayId, float brightness) {
+ mGlobal.setTemporaryBrightness(displayId, brightness);
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 77ae9471dfb0..60fe5825d6a1 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -636,13 +636,13 @@ public final class DisplayManagerGlobal {
* Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
* </p>
*
- * @param brightness The brightness value from 0 to 255.
+ * @param brightness The brightness value from 0.0f to 1.0f.
*
* @hide Requires signature permission.
*/
- public void setTemporaryBrightness(float brightness) {
+ public void setTemporaryBrightness(int displayId, float brightness) {
try {
- mDm.setTemporaryBrightness(brightness);
+ mDm.setTemporaryBrightness(displayId, brightness);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index a9f78fa03a6d..ff8a7208a9f0 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -116,7 +116,7 @@ interface IDisplayManager {
boolean isMinimalPostProcessingRequested(int displayId);
// Temporarily sets the display brightness.
- void setTemporaryBrightness(float brightness);
+ void setTemporaryBrightness(int displayId, float brightness);
// Temporarily sets the auto brightness adjustment factor.
void setTemporaryAutoBrightnessAdjustment(float adjustment);
diff --git a/core/java/android/hardware/face/FaceAuthenticationFrame.aidl b/core/java/android/hardware/face/FaceAuthenticationFrame.aidl
new file mode 100644
index 000000000000..4dc41f149328
--- /dev/null
+++ b/core/java/android/hardware/face/FaceAuthenticationFrame.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.face;
+
+/**
+ * @hide
+ */
+parcelable FaceAuthenticationFrame;
diff --git a/core/java/android/hardware/face/FaceDataFrame.java b/core/java/android/hardware/face/FaceDataFrame.java
index 3a0e09b70b50..092359c99173 100644
--- a/core/java/android/hardware/face/FaceDataFrame.java
+++ b/core/java/android/hardware/face/FaceDataFrame.java
@@ -63,6 +63,22 @@ public final class FaceDataFrame implements Parcelable {
}
/**
+ * A container for data common to {@link FaceAuthenticationFrame} and {@link FaceEnrollFrame}.
+ *
+ * @param acquiredInfo An integer corresponding to a known acquired message.
+ * @param vendorCode An integer representing a custom vendor-specific message. Ignored unless
+ * {@code acquiredInfo} is {@code FACE_ACQUIRED_VENDOR}.
+ */
+ public FaceDataFrame(int acquiredInfo, int vendorCode) {
+ mAcquiredInfo = acquiredInfo;
+ mVendorCode = vendorCode;
+ mPan = 0f;
+ mTilt = 0f;
+ mDistance = 0f;
+ mIsCancellable = false;
+ }
+
+ /**
* @return An integer corresponding to a known acquired message.
*
* @see android.hardware.biometrics.BiometricFaceConstants
diff --git a/core/java/android/hardware/face/FaceEnrollFrame.aidl b/core/java/android/hardware/face/FaceEnrollFrame.aidl
new file mode 100644
index 000000000000..b8546812beeb
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollFrame.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.face;
+
+/**
+ * @hide
+ */
+parcelable FaceEnrollFrame;
diff --git a/core/java/android/hardware/face/FaceEnrollStage.java b/core/java/android/hardware/face/FaceEnrollStage.java
index 03dba551aae2..de717fbe46b4 100644
--- a/core/java/android/hardware/face/FaceEnrollStage.java
+++ b/core/java/android/hardware/face/FaceEnrollStage.java
@@ -28,6 +28,7 @@ import java.lang.annotation.RetentionPolicy;
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
+ FaceEnrollStage.UNKNOWN,
FaceEnrollStage.FIRST_FRAME_RECEIVED,
FaceEnrollStage.WAITING_FOR_CENTERING,
FaceEnrollStage.HOLD_STILL_IN_CENTER,
@@ -37,6 +38,11 @@ import java.lang.annotation.RetentionPolicy;
})
public @interface FaceEnrollStage {
/**
+ * The current enrollment stage is not known.
+ */
+ int UNKNOWN = -1;
+
+ /**
* Enrollment has just begun. No action is needed from the user yet.
*/
int FIRST_FRAME_RECEIVED = 0;
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 588bc01d7d42..a9bcdeff7e47 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -71,17 +71,19 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
private static final int MSG_FACE_DETECTED = 109;
private static final int MSG_CHALLENGE_INTERRUPTED = 110;
private static final int MSG_CHALLENGE_INTERRUPT_FINISHED = 111;
+ private static final int MSG_AUTHENTICATION_FRAME = 112;
+ private static final int MSG_ENROLLMENT_FRAME = 113;
private final IFaceService mService;
private final Context mContext;
private IBinder mToken = new Binder();
- private AuthenticationCallback mAuthenticationCallback;
- private FaceDetectionCallback mFaceDetectionCallback;
- private EnrollmentCallback mEnrollmentCallback;
- private RemovalCallback mRemovalCallback;
- private SetFeatureCallback mSetFeatureCallback;
- private GetFeatureCallback mGetFeatureCallback;
- private GenerateChallengeCallback mGenerateChallengeCallback;
+ @Nullable private AuthenticationCallback mAuthenticationCallback;
+ @Nullable private FaceDetectionCallback mFaceDetectionCallback;
+ @Nullable private EnrollmentCallback mEnrollmentCallback;
+ @Nullable private RemovalCallback mRemovalCallback;
+ @Nullable private SetFeatureCallback mSetFeatureCallback;
+ @Nullable private GetFeatureCallback mGetFeatureCallback;
+ @Nullable private GenerateChallengeCallback mGenerateChallengeCallback;
private CryptoObject mCryptoObject;
private Face mRemovalFace;
private Handler mHandler;
@@ -154,6 +156,16 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
public void onChallengeInterruptFinished(int sensorId) {
mHandler.obtainMessage(MSG_CHALLENGE_INTERRUPT_FINISHED, sensorId).sendToTarget();
}
+
+ @Override
+ public void onAuthenticationFrame(FaceAuthenticationFrame frame) {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_FRAME, frame).sendToTarget();
+ }
+
+ @Override
+ public void onEnrollmentFrame(FaceEnrollFrame frame) {
+ mHandler.obtainMessage(MSG_ENROLLMENT_FRAME, frame).sendToTarget();
+ }
};
/**
@@ -562,12 +574,23 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver,
mContext.getOpPackageName());
} catch (RemoteException e) {
- Slog.w(TAG, "Remote exception in remove: ", e);
- if (callback != null) {
- callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE,
- getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */));
- }
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Removes all face templates for the given user.
+ * @hide
+ */
+ @RequiresPermission(MANAGE_BIOMETRIC)
+ public void removeAll(int userId, @NonNull RemovalCallback callback) {
+ if (mService != null) {
+ try {
+ mRemovalCallback = callback;
+ mService.removeAll(mToken, userId, mServiceReceiver, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
}
@@ -821,67 +844,6 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
}
/**
- * @hide
- */
- public static String getAcquiredString(Context context, int acquireInfo, int vendorCode) {
- switch (acquireInfo) {
- case FACE_ACQUIRED_GOOD:
- return null;
- case FACE_ACQUIRED_INSUFFICIENT:
- return context.getString(R.string.face_acquired_insufficient);
- case FACE_ACQUIRED_TOO_BRIGHT:
- return context.getString(R.string.face_acquired_too_bright);
- case FACE_ACQUIRED_TOO_DARK:
- return context.getString(R.string.face_acquired_too_dark);
- case FACE_ACQUIRED_TOO_CLOSE:
- return context.getString(R.string.face_acquired_too_close);
- case FACE_ACQUIRED_TOO_FAR:
- return context.getString(R.string.face_acquired_too_far);
- case FACE_ACQUIRED_TOO_HIGH:
- return context.getString(R.string.face_acquired_too_high);
- case FACE_ACQUIRED_TOO_LOW:
- return context.getString(R.string.face_acquired_too_low);
- case FACE_ACQUIRED_TOO_RIGHT:
- return context.getString(R.string.face_acquired_too_right);
- case FACE_ACQUIRED_TOO_LEFT:
- return context.getString(R.string.face_acquired_too_left);
- case FACE_ACQUIRED_POOR_GAZE:
- return context.getString(R.string.face_acquired_poor_gaze);
- case FACE_ACQUIRED_NOT_DETECTED:
- return context.getString(R.string.face_acquired_not_detected);
- case FACE_ACQUIRED_TOO_MUCH_MOTION:
- return context.getString(R.string.face_acquired_too_much_motion);
- case FACE_ACQUIRED_RECALIBRATE:
- return context.getString(R.string.face_acquired_recalibrate);
- case FACE_ACQUIRED_TOO_DIFFERENT:
- return context.getString(R.string.face_acquired_too_different);
- case FACE_ACQUIRED_TOO_SIMILAR:
- return context.getString(R.string.face_acquired_too_similar);
- case FACE_ACQUIRED_PAN_TOO_EXTREME:
- return context.getString(R.string.face_acquired_pan_too_extreme);
- case FACE_ACQUIRED_TILT_TOO_EXTREME:
- return context.getString(R.string.face_acquired_tilt_too_extreme);
- case FACE_ACQUIRED_ROLL_TOO_EXTREME:
- return context.getString(R.string.face_acquired_roll_too_extreme);
- case FACE_ACQUIRED_FACE_OBSCURED:
- return context.getString(R.string.face_acquired_obscured);
- case FACE_ACQUIRED_START:
- return null;
- case FACE_ACQUIRED_SENSOR_DIRTY:
- return context.getString(R.string.face_acquired_sensor_dirty);
- case FACE_ACQUIRED_VENDOR: {
- String[] msgArray = context.getResources().getStringArray(
- R.array.face_acquired_vendor);
- if (vendorCode < msgArray.length) {
- return msgArray[vendorCode];
- }
- }
- }
- Slog.w(TAG, "Invalid acquired message: " + acquireInfo + ", " + vendorCode);
- return null;
- }
-
- /**
* Used so BiometricPrompt can map the face ones onto existing public constants.
* @hide
*/
@@ -1248,6 +1210,12 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
case MSG_CHALLENGE_INTERRUPT_FINISHED:
sendChallengeInterruptFinished((int) msg.obj /* sensorId */);
break;
+ case MSG_AUTHENTICATION_FRAME:
+ sendAuthenticationFrame((FaceAuthenticationFrame) msg.obj /* frame */);
+ break;
+ case MSG_ENROLLMENT_FRAME:
+ sendEnrollmentFrame((FaceEnrollFrame) msg.obj /* frame */);
+ break;
default:
Slog.w(TAG, "Unknown message: " + msg.what);
}
@@ -1349,15 +1317,172 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
private void sendAcquiredResult(int acquireInfo, int vendorCode) {
if (mAuthenticationCallback != null) {
+ final FaceAuthenticationFrame frame = new FaceAuthenticationFrame(
+ new FaceDataFrame(acquireInfo, vendorCode));
+ sendAuthenticationFrame(frame);
+ } else if (mEnrollmentCallback != null) {
+ final FaceEnrollFrame frame = new FaceEnrollFrame(
+ null /* cell */,
+ FaceEnrollStage.UNKNOWN,
+ new FaceDataFrame(acquireInfo, vendorCode));
+ sendEnrollmentFrame(frame);
+ }
+ }
+
+ private void sendAuthenticationFrame(@Nullable FaceAuthenticationFrame frame) {
+ if (frame == null) {
+ Slog.w(TAG, "Received null authentication frame");
+ } else if (mAuthenticationCallback != null) {
+ // TODO(b/178414967): Send additional frame data to callback
+ final int acquireInfo = frame.getData().getAcquiredInfo();
+ final int vendorCode = frame.getData().getVendorCode();
+ final int helpCode = getHelpCode(acquireInfo, vendorCode);
+ final String helpMessage = getAuthHelpMessage(mContext, acquireInfo, vendorCode);
mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
+
+ // Ensure that only non-null help messages are sent.
+ if (helpMessage != null) {
+ mAuthenticationCallback.onAuthenticationHelp(helpCode, helpMessage);
+ }
}
- final String msg = getAcquiredString(mContext, acquireInfo, vendorCode);
- final int clientInfo = acquireInfo == FACE_ACQUIRED_VENDOR
- ? (vendorCode + FACE_ACQUIRED_VENDOR_BASE) : acquireInfo;
- if (mEnrollmentCallback != null) {
- mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
- } else if (mAuthenticationCallback != null && msg != null) {
- mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
+ }
+
+ private void sendEnrollmentFrame(@Nullable FaceEnrollFrame frame) {
+ if (frame == null) {
+ Slog.w(TAG, "Received null enrollment frame");
+ } else if (mEnrollmentCallback != null) {
+ // TODO(b/178414967): Send additional frame data to callback
+ final int acquireInfo = frame.getData().getAcquiredInfo();
+ final int vendorCode = frame.getData().getVendorCode();
+ final int helpCode = getHelpCode(acquireInfo, vendorCode);
+ final String helpMessage = getEnrollHelpMessage(mContext, acquireInfo, vendorCode);
+ mEnrollmentCallback.onEnrollmentHelp(helpCode, helpMessage);
+ }
+ }
+
+ private static int getHelpCode(int acquireInfo, int vendorCode) {
+ return acquireInfo == FACE_ACQUIRED_VENDOR
+ ? vendorCode + FACE_ACQUIRED_VENDOR_BASE
+ : acquireInfo;
+ }
+
+ /**
+ * @hide
+ */
+ @Nullable
+ public static String getAuthHelpMessage(Context context, int acquireInfo, int vendorCode) {
+ switch (acquireInfo) {
+ // No help message is needed for a good capture.
+ case FACE_ACQUIRED_GOOD:
+ case FACE_ACQUIRED_START:
+ return null;
+
+ // Consolidate positional feedback to reduce noise during authentication.
+ case FACE_ACQUIRED_NOT_DETECTED:
+ case FACE_ACQUIRED_TOO_CLOSE:
+ case FACE_ACQUIRED_TOO_FAR:
+ case FACE_ACQUIRED_TOO_HIGH:
+ case FACE_ACQUIRED_TOO_LOW:
+ case FACE_ACQUIRED_TOO_RIGHT:
+ case FACE_ACQUIRED_TOO_LEFT:
+ case FACE_ACQUIRED_POOR_GAZE:
+ case FACE_ACQUIRED_PAN_TOO_EXTREME:
+ case FACE_ACQUIRED_TILT_TOO_EXTREME:
+ case FACE_ACQUIRED_ROLL_TOO_EXTREME:
+ return context.getString(R.string.face_acquired_not_detected);
+
+ // Provide more detailed feedback for other soft errors.
+ case FACE_ACQUIRED_INSUFFICIENT:
+ return context.getString(R.string.face_acquired_insufficient);
+ case FACE_ACQUIRED_TOO_BRIGHT:
+ return context.getString(R.string.face_acquired_too_bright);
+ case FACE_ACQUIRED_TOO_DARK:
+ return context.getString(R.string.face_acquired_too_dark);
+ case FACE_ACQUIRED_TOO_MUCH_MOTION:
+ return context.getString(R.string.face_acquired_too_much_motion);
+ case FACE_ACQUIRED_RECALIBRATE:
+ return context.getString(R.string.face_acquired_recalibrate);
+ case FACE_ACQUIRED_TOO_DIFFERENT:
+ return context.getString(R.string.face_acquired_too_different);
+ case FACE_ACQUIRED_TOO_SIMILAR:
+ return context.getString(R.string.face_acquired_too_similar);
+ case FACE_ACQUIRED_FACE_OBSCURED:
+ return context.getString(R.string.face_acquired_obscured);
+ case FACE_ACQUIRED_SENSOR_DIRTY:
+ return context.getString(R.string.face_acquired_sensor_dirty);
+
+ // Find and return the appropriate vendor-specific message.
+ case FACE_ACQUIRED_VENDOR: {
+ String[] msgArray = context.getResources().getStringArray(
+ R.array.face_acquired_vendor);
+ if (vendorCode < msgArray.length) {
+ return msgArray[vendorCode];
+ }
+ }
}
+
+ Slog.w(TAG, "Unknown authentication acquired message: " + acquireInfo + ", " + vendorCode);
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ @Nullable
+ public static String getEnrollHelpMessage(Context context, int acquireInfo, int vendorCode) {
+ switch (acquireInfo) {
+ case FACE_ACQUIRED_GOOD:
+ case FACE_ACQUIRED_START:
+ return null;
+ case FACE_ACQUIRED_INSUFFICIENT:
+ return context.getString(R.string.face_acquired_insufficient);
+ case FACE_ACQUIRED_TOO_BRIGHT:
+ return context.getString(R.string.face_acquired_too_bright);
+ case FACE_ACQUIRED_TOO_DARK:
+ return context.getString(R.string.face_acquired_too_dark);
+ case FACE_ACQUIRED_TOO_CLOSE:
+ return context.getString(R.string.face_acquired_too_close);
+ case FACE_ACQUIRED_TOO_FAR:
+ return context.getString(R.string.face_acquired_too_far);
+ case FACE_ACQUIRED_TOO_HIGH:
+ return context.getString(R.string.face_acquired_too_high);
+ case FACE_ACQUIRED_TOO_LOW:
+ return context.getString(R.string.face_acquired_too_low);
+ case FACE_ACQUIRED_TOO_RIGHT:
+ return context.getString(R.string.face_acquired_too_right);
+ case FACE_ACQUIRED_TOO_LEFT:
+ return context.getString(R.string.face_acquired_too_left);
+ case FACE_ACQUIRED_POOR_GAZE:
+ return context.getString(R.string.face_acquired_poor_gaze);
+ case FACE_ACQUIRED_NOT_DETECTED:
+ return context.getString(R.string.face_acquired_not_detected);
+ case FACE_ACQUIRED_TOO_MUCH_MOTION:
+ return context.getString(R.string.face_acquired_too_much_motion);
+ case FACE_ACQUIRED_RECALIBRATE:
+ return context.getString(R.string.face_acquired_recalibrate);
+ case FACE_ACQUIRED_TOO_DIFFERENT:
+ return context.getString(R.string.face_acquired_too_different);
+ case FACE_ACQUIRED_TOO_SIMILAR:
+ return context.getString(R.string.face_acquired_too_similar);
+ case FACE_ACQUIRED_PAN_TOO_EXTREME:
+ return context.getString(R.string.face_acquired_pan_too_extreme);
+ case FACE_ACQUIRED_TILT_TOO_EXTREME:
+ return context.getString(R.string.face_acquired_tilt_too_extreme);
+ case FACE_ACQUIRED_ROLL_TOO_EXTREME:
+ return context.getString(R.string.face_acquired_roll_too_extreme);
+ case FACE_ACQUIRED_FACE_OBSCURED:
+ return context.getString(R.string.face_acquired_obscured);
+ case FACE_ACQUIRED_SENSOR_DIRTY:
+ return context.getString(R.string.face_acquired_sensor_dirty);
+ case FACE_ACQUIRED_VENDOR: {
+ String[] msgArray = context.getResources().getStringArray(
+ R.array.face_acquired_vendor);
+ if (vendorCode < msgArray.length) {
+ return msgArray[vendorCode];
+ }
+ }
+ }
+ Slog.w(TAG, "Unknown enrollment acquired message: " + acquireInfo + ", " + vendorCode);
+ return null;
}
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index a3e7e2d2c5cb..6e7c701ef5ff 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -19,6 +19,7 @@ import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -32,7 +33,7 @@ import android.view.Surface;
interface IFaceService {
// Creates a test session with the specified sensorId
- ITestSession createTestSession(int sensorId, String opPackageName);
+ ITestSession createTestSession(int sensorId, ITestSessionCallback callback, String opPackageName);
// Requests a proto dump of the specified sensor
byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer);
@@ -83,10 +84,13 @@ interface IFaceService {
// Cancel enrollment in progress
void cancelEnrollment(IBinder token);
- // Any errors resulting from this call will be returned to the listener
+ // Removes the specified face enrollment for the specified userId.
void remove(IBinder token, int faceId, int userId, IFaceServiceReceiver receiver,
String opPackageName);
+ // Removes all face enrollments for the specified userId.
+ void removeAll(IBinder token, int userId, IFaceServiceReceiver receiver, String opPackageName);
+
// Get the enrolled face for user.
List<Face> getEnrolledFaces(int sensorId, int userId, String opPackageName);
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
index bd4d3a0b7017..2ef1430a2f99 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -16,6 +16,8 @@
package android.hardware.face;
import android.hardware.face.Face;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceEnrollFrame;
/**
* Communication channel from the FaceService back to FaceAuthenticationManager.
@@ -34,4 +36,6 @@ oneway interface IFaceServiceReceiver {
void onChallengeGenerated(int sensorId, long challenge);
void onChallengeInterrupted(int sensorId);
void onChallengeInterruptFinished(int sensorId);
+ void onAuthenticationFrame(in FaceAuthenticationFrame frame);
+ void onEnrollmentFrame(in FaceEnrollFrame frame);
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 188a2a47fca0..fc795d8a0488 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -114,6 +114,21 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
*/
public static final int SENSOR_ID_ANY = -1;
+ private static class RemoveTracker {
+ static final int REMOVE_SINGLE = 1;
+ static final int REMOVE_ALL = 2;
+ @IntDef({REMOVE_SINGLE, REMOVE_ALL})
+ @interface RemoveRequest {}
+
+ final @RemoveRequest int mRemoveRequest;
+ @Nullable final Fingerprint mSingleFingerprint;
+
+ RemoveTracker(@RemoveRequest int request, @Nullable Fingerprint fingerprint) {
+ mRemoveRequest = request;
+ mSingleFingerprint = fingerprint;
+ }
+ }
+
private IFingerprintService mService;
private Context mContext;
private IBinder mToken = new Binder();
@@ -123,10 +138,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
private RemovalCallback mRemovalCallback;
private GenerateChallengeCallback mGenerateChallengeCallback;
private CryptoObject mCryptoObject;
- private Fingerprint mRemovalFingerprint;
+ @Nullable private RemoveTracker mRemoveTracker;
private Handler mHandler;
-
/**
* Retrieves a list of properties for all fingerprint sensors on the device.
* @hide
@@ -153,8 +167,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
@RequiresPermission(TEST_BIOMETRIC)
public BiometricTestSession createTestSession(int sensorId) {
try {
- return new BiometricTestSession(mContext,
- mService.createTestSession(sensorId, mContext.getOpPackageName()));
+ return new BiometricTestSession(mContext, sensorId,
+ (context, sensorId1, callback) -> mService
+ .createTestSession(sensorId1, callback, context.getOpPackageName()));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -735,15 +750,27 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
public void remove(Fingerprint fp, int userId, RemovalCallback callback) {
if (mService != null) try {
mRemovalCallback = callback;
- mRemovalFingerprint = fp;
+ mRemoveTracker = new RemoveTracker(RemoveTracker.REMOVE_SINGLE, fp);
mService.remove(mToken, fp.getBiometricId(), userId, mServiceReceiver,
mContext.getOpPackageName());
} catch (RemoteException e) {
- Slog.w(TAG, "Remote exception in remove: ", e);
- if (callback != null) {
- callback.onRemovalError(fp, FINGERPRINT_ERROR_HW_UNAVAILABLE,
- getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */));
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes all face templates for the given user.
+ * @hide
+ */
+ @RequiresPermission(MANAGE_FINGERPRINT)
+ public void removeAll(int userId, @NonNull RemovalCallback callback) {
+ if (mService != null) {
+ try {
+ mRemovalCallback = callback;
+ mRemoveTracker = new RemoveTracker(RemoveTracker.REMOVE_ALL, null /* fp */);
+ mService.removeAll(mToken, userId, mServiceReceiver, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
}
@@ -1044,16 +1071,29 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
if (mRemovalCallback == null) {
return;
}
- if (fingerprint == null) {
- Slog.e(TAG, "Received MSG_REMOVED, but fingerprint is null");
+
+ if (mRemoveTracker == null) {
+ Slog.w(TAG, "Removal tracker is null");
return;
}
- int fingerId = fingerprint.getBiometricId();
- int reqFingerId = mRemovalFingerprint.getBiometricId();
- if (reqFingerId != 0 && fingerId != 0 && fingerId != reqFingerId) {
- Slog.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId);
- return;
+ if (mRemoveTracker.mRemoveRequest == RemoveTracker.REMOVE_SINGLE) {
+ if (fingerprint == null) {
+ Slog.e(TAG, "Received MSG_REMOVED, but fingerprint is null");
+ return;
+ }
+
+ if (mRemoveTracker.mSingleFingerprint == null) {
+ Slog.e(TAG, "Missing fingerprint");
+ return;
+ }
+
+ final int fingerId = fingerprint.getBiometricId();
+ int reqFingerId = mRemoveTracker.mSingleFingerprint.getBiometricId();
+ if (reqFingerId != 0 && fingerId != 0 && fingerId != reqFingerId) {
+ Slog.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId);
+ return;
+ }
}
mRemovalCallback.onRemovalSucceeded(fingerprint, remaining);
@@ -1110,7 +1150,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
getErrorString(mContext, errMsgId, vendorCode));
} else if (mRemovalCallback != null) {
- mRemovalCallback.onRemovalError(mRemovalFingerprint, clientErrMsgId,
+ final Fingerprint fp = mRemoveTracker != null
+ ? mRemoveTracker.mSingleFingerprint : null;
+ mRemovalCallback.onRemovalError(fp, clientErrMsgId,
getErrorString(mContext, errMsgId, vendorCode));
}
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 8888247e2823..054c0d0f6513 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -19,6 +19,7 @@ import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -33,7 +34,7 @@ import java.util.List;
interface IFingerprintService {
// Creates a test session with the specified sensorId
- ITestSession createTestSession(int sensorId, String opPackageName);
+ ITestSession createTestSession(int sensorId, ITestSessionCallback callback, String opPackageName);
// Requests a proto dump of the specified sensor
byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer);
@@ -87,6 +88,9 @@ interface IFingerprintService {
void remove(IBinder token, int fingerId, int userId, IFingerprintServiceReceiver receiver,
String opPackageName);
+ // Removes all face enrollments for the specified userId.
+ void removeAll(IBinder token, int userId, IFingerprintServiceReceiver receiver, String opPackageName);
+
// Rename the fingerprint specified by fingerId and userId to the given name
void rename(int fingerId, int userId, String name);
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index c69c47f80d01..eaa38f3e862c 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -26,6 +26,7 @@ import android.os.CombinedVibrationEffect;
import android.hardware.input.IInputSensorEventListener;
import android.hardware.input.InputSensorInfo;
import android.os.IBinder;
+import android.os.IVibratorStateListener;
import android.os.VibrationEffect;
import android.view.InputDevice;
import android.view.InputEvent;
@@ -92,6 +93,8 @@ interface IInputManager {
void cancelVibrate(int deviceId, IBinder token);
int[] getVibratorIds(int deviceId);
boolean isVibrating(int deviceId);
+ boolean registerVibratorStateListener(int deviceId, in IVibratorStateListener listener);
+ boolean unregisterVibratorStateListener(int deviceId, in IVibratorStateListener listener);
// Input device battery query.
int getBatteryStatus(int deviceId);
diff --git a/core/java/android/hardware/input/InputDeviceVibrator.java b/core/java/android/hardware/input/InputDeviceVibrator.java
index c60d6ce46fdb..a4817ae27fa5 100644
--- a/core/java/android/hardware/input/InputDeviceVibrator.java
+++ b/core/java/android/hardware/input/InputDeviceVibrator.java
@@ -18,10 +18,18 @@ package android.hardware.input;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
+import android.app.ActivityThread;
+import android.content.Context;
import android.os.Binder;
+import android.os.IVibratorStateListener;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
import java.util.concurrent.Executor;
@@ -29,12 +37,18 @@ import java.util.concurrent.Executor;
* Vibrator implementation that communicates with the input device vibrators.
*/
final class InputDeviceVibrator extends Vibrator {
+ private static final String TAG = "InputDeviceVibrator";
+
// mDeviceId represents InputDevice ID the vibrator belongs to
private final int mDeviceId;
private final int mVibratorId;
private final Binder mToken;
private final InputManager mInputManager;
+ @GuardedBy("mDelegates")
+ private final ArrayMap<OnVibratorStateChangedListener,
+ OnVibratorStateChangedListenerDelegate> mDelegates = new ArrayMap<>();
+
InputDeviceVibrator(InputManager inputManager, int deviceId, int vibratorId) {
mInputManager = inputManager;
mDeviceId = deviceId;
@@ -42,6 +56,28 @@ final class InputDeviceVibrator extends Vibrator {
mToken = new Binder();
}
+ private class OnVibratorStateChangedListenerDelegate extends
+ IVibratorStateListener.Stub {
+ private final Executor mExecutor;
+ private final OnVibratorStateChangedListener mListener;
+
+ OnVibratorStateChangedListenerDelegate(@NonNull OnVibratorStateChangedListener listener,
+ @NonNull Executor executor) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void onVibrating(boolean isVibrating) {
+ mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating));
+ }
+ }
+
+ @Override
+ public int getId() {
+ return mVibratorId;
+ }
+
@Override
public boolean hasVibrator() {
return true;
@@ -52,25 +88,73 @@ final class InputDeviceVibrator extends Vibrator {
return mInputManager.isVibrating(mDeviceId);
}
- /* TODO: b/161634264 Support Vibrator listener API in input devices */
+ /**
+ * Adds a listener for vibrator state changes. Callbacks will be executed on the main thread.
+ * If the listener was previously added and not removed, this call will be ignored.
+ *
+ * @param listener listener to be added
+ */
@Override
public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
- throw new UnsupportedOperationException(
- "addVibratorStateListener not supported in InputDeviceVibrator");
+ Preconditions.checkNotNull(listener);
+ Context context = ActivityThread.currentApplication();
+ addVibratorStateListener(context.getMainExecutor(), listener);
}
+ /**
+ * Adds a listener for vibrator state change. If the listener was previously added and not
+ * removed, this call will be ignored.
+ *
+ * @param listener Listener to be added.
+ * @param executor The {@link Executor} on which the listener's callbacks will be executed on.
+ */
@Override
public void addVibratorStateListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull OnVibratorStateChangedListener listener) {
- throw new UnsupportedOperationException(
- "addVibratorStateListener not supported in InputDeviceVibrator");
+ Preconditions.checkNotNull(listener);
+ Preconditions.checkNotNull(executor);
+
+ synchronized (mDelegates) {
+ // If listener is already registered, reject and return.
+ if (mDelegates.containsKey(listener)) {
+ Log.w(TAG, "Listener already registered.");
+ return;
+ }
+
+ final OnVibratorStateChangedListenerDelegate delegate =
+ new OnVibratorStateChangedListenerDelegate(listener, executor);
+ if (!mInputManager.registerVibratorStateListener(mDeviceId, delegate)) {
+ Log.w(TAG, "Failed to register vibrate state listener");
+ return;
+ }
+ mDelegates.put(listener, delegate);
+
+ }
}
+ /**
+ * Removes the listener for vibrator state changes. If the listener was not previously
+ * registered, this call will do nothing.
+ *
+ * @param listener Listener to be removed.
+ */
@Override
public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
- throw new UnsupportedOperationException(
- "removeVibratorStateListener not supported in InputDeviceVibrator");
+ Preconditions.checkNotNull(listener);
+
+ synchronized (mDelegates) {
+ // Check if the listener is registered, otherwise will return.
+ if (mDelegates.containsKey(listener)) {
+ final OnVibratorStateChangedListenerDelegate delegate = mDelegates.get(listener);
+
+ if (!mInputManager.unregisterVibratorStateListener(mDeviceId, delegate)) {
+ Log.w(TAG, "Failed to unregister vibrate state listener");
+ return;
+ }
+ mDelegates.remove(listener);
+ }
+ }
}
@Override
diff --git a/core/java/android/hardware/input/InputDeviceVibratorManager.java b/core/java/android/hardware/input/InputDeviceVibratorManager.java
index a381b02ab2a6..d843407c289d 100644
--- a/core/java/android/hardware/input/InputDeviceVibratorManager.java
+++ b/core/java/android/hardware/input/InputDeviceVibratorManager.java
@@ -16,9 +16,12 @@
package android.hardware.input;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Binder;
import android.os.CombinedVibrationEffect;
import android.os.NullVibrator;
+import android.os.VibrationAttributes;
import android.os.Vibrator;
import android.os.VibratorManager;
import android.util.SparseArray;
@@ -86,6 +89,7 @@ public class InputDeviceVibratorManager extends VibratorManager
}
}
+ @NonNull
@Override
public int[] getVibratorIds() {
synchronized (mVibrators) {
@@ -97,6 +101,7 @@ public class InputDeviceVibratorManager extends VibratorManager
}
}
+ @NonNull
@Override
public Vibrator getVibrator(int vibratorId) {
synchronized (mVibrators) {
@@ -107,6 +112,7 @@ public class InputDeviceVibratorManager extends VibratorManager
return NullVibrator.getInstance();
}
+ @NonNull
@Override
public Vibrator getDefaultVibrator() {
// Returns vibrator ID 0
@@ -119,7 +125,13 @@ public class InputDeviceVibratorManager extends VibratorManager
}
@Override
- public void vibrate(CombinedVibrationEffect effect) {
+ public void vibrate(int uid, String opPkg, @NonNull CombinedVibrationEffect effect,
+ String reason, @Nullable VibrationAttributes attributes) {
mInputManager.vibrate(mDeviceId, effect, mToken);
}
+
+ @Override
+ public void cancel() {
+ mInputManager.cancelVibrate(mDeviceId, mToken);
+ }
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 185c59d8ccfc..8a01c660ebd0 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -35,6 +35,7 @@ import android.os.Build;
import android.os.CombinedVibrationEffect;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IVibratorStateListener;
import android.os.InputEventInjectionSync;
import android.os.Looper;
import android.os.Message;
@@ -1484,6 +1485,32 @@ public final class InputManager {
}
/**
+ * Register input device vibrator state listener
+ *
+ * @hide
+ */
+ public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+ try {
+ return mIm.registerVibratorStateListener(deviceId, listener);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregister input device vibrator state listener
+ *
+ * @hide
+ */
+ public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+ try {
+ return mIm.unregisterVibratorStateListener(deviceId, listener);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets a sensor manager service associated with an input device, always create a new instance.
* @return The sensor manager, never null.
* @hide
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 43480ab9cc44..49beeb3a2e96 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -127,6 +127,20 @@ public class ContextHubClient implements Closeable {
* This function returns RESULT_SUCCESS if the message has reached the HAL, but
* does not guarantee delivery of the message to the target nanoapp.
*
+ * Before sending the first message to your nanoapp, it's recommended that the following
+ * operations should be performed:
+ * 1) Invoke {@link ContextHubManager#queryNanoApps(ContextHubInfo)} to verify the nanoapp is
+ * present.
+ * 2) Validate that you have the permissions to communicate with the nanoapp by looking at
+ * {@link NanoAppState#getNanoAppPermissions}.
+ * 3) If you don't have permissions, send an idempotent message to the nanoapp ensuring any
+ * work your app previously may have asked it to do is stopped. This is useful if your app
+ * restarts due to permission changes and no longer has the permissions when it is started
+ * again.
+ * 4) If you have valid permissions, send a message to your nanoapp to resubscribe so that it's
+ * aware you have restarted or so you can initially subscribe if this is the first time you
+ * have sent it a message.
+ *
* @param message the message object to send
*
* @return the result of sending the message defined as in ContextHubTransaction.Result
diff --git a/core/java/android/hardware/location/ContextHubClientCallback.java b/core/java/android/hardware/location/ContextHubClientCallback.java
index b31b85fdabf6..7e484dda283c 100644
--- a/core/java/android/hardware/location/ContextHubClientCallback.java
+++ b/core/java/android/hardware/location/ContextHubClientCallback.java
@@ -117,10 +117,11 @@ public class ContextHubClientCallback {
* 4) {@link ContextHubClient} performs any cleanup required with the nanoapp
* 5) Callback invoked with the nanoapp ID and {@link ContextHubManager#AUTHORIZATION_DENIED}.
* At this point, any further attempts of communication between the nanoapp and the
- * {@link ContextHubClient} will be dropped by the contexthub along with
- * {@link ContextHubManager#AUTHORIZATION_DENIED} being sent. The {@link ContextHubClient}
- * should assume no communciation can happen again until
- * {@link ContextHubManager#AUTHORIZATION_GRANTED} is received.
+ * {@link ContextHubClient} will be dropped by the contexthub and a return value of
+ * {@link ContextHubTransaction#RESULT_FAILED_PERMISSION_DENIED} will be used when calling
+ * {@link ContextHubClient#sendMessageToNanoApp}. The {@link ContextHubClient} should assume
+ * no communciation can happen again until {@link ContextHubManager#AUTHORIZATION_GRANTED} is
+ * received.
*
* @param client the client that is associated with this callback
* @param nanoAppId the ID of the nanoapp associated with the new
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index ebb3021bf083..a65f36b14f13 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -24,6 +24,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.app.ActivityThread;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -849,9 +850,18 @@ public final class ContextHubManager {
attributionTag = context.getAttributionTag();
}
+ // Workaround for old APIs not providing a context
+ String packageName;
+ if (context != null) {
+ packageName = context.getPackageName();
+ } else {
+ packageName = ActivityThread.currentPackageName();
+ }
+
IContextHubClient clientProxy;
try {
- clientProxy = mService.createClient(hubInfo.getId(), clientInterface, attributionTag);
+ clientProxy = mService.createClient(
+ hubInfo.getId(), clientInterface, attributionTag, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/location/ContextHubTransaction.java b/core/java/android/hardware/location/ContextHubTransaction.java
index d11e0a9b6081..86f77c0bf138 100644
--- a/core/java/android/hardware/location/ContextHubTransaction.java
+++ b/core/java/android/hardware/location/ContextHubTransaction.java
@@ -81,7 +81,8 @@ public class ContextHubTransaction<T> {
RESULT_FAILED_AT_HUB,
RESULT_FAILED_TIMEOUT,
RESULT_FAILED_SERVICE_INTERNAL_FAILURE,
- RESULT_FAILED_HAL_UNAVAILABLE
+ RESULT_FAILED_HAL_UNAVAILABLE,
+ RESULT_FAILED_PERMISSION_DENIED
})
public @interface Result {}
public static final int RESULT_SUCCESS = 0;
@@ -117,6 +118,11 @@ public class ContextHubTransaction<T> {
* Failure mode when the Context Hub HAL was not available.
*/
public static final int RESULT_FAILED_HAL_UNAVAILABLE = 8;
+ /**
+ * Failure mode when the user of the API doesn't have the required permissions to perform the
+ * operation.
+ */
+ public static final int RESULT_FAILED_PERMISSION_DENIED = 9;
/**
* A class describing the response for a ContextHubTransaction.
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index 4961195a3017..92882c4f93bb 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -60,7 +60,8 @@ interface IContextHubService {
// Creates a client to send and receive messages
IContextHubClient createClient(
- int contextHubId, in IContextHubClientCallback client, in String attributionTag);
+ int contextHubId, in IContextHubClientCallback client, in String attributionTag,
+ in String packageName);
// Creates a PendingIntent-based client to send and receive messages
IContextHubClient createPendingIntentClient(
diff --git a/core/java/android/hardware/location/OWNERS b/core/java/android/hardware/location/OWNERS
index 383321bc3d69..bd40409f71c6 100644
--- a/core/java/android/hardware/location/OWNERS
+++ b/core/java/android/hardware/location/OWNERS
@@ -4,3 +4,6 @@ mstogaitis@google.com
wyattriley@google.com
etn@google.com
weiwa@google.com
+
+# ContextHub team
+per-file *ContextHub*,*NanoApp* = file:platform/system/chre:/OWNERS
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index ca79901c2bbe..7f07af79ab69 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -135,6 +135,12 @@ interface IUsbManager
/* Resets the USB gadget. */
void resetUsbGadget();
+ /* Set USB data on or off */
+ boolean enableUsbDataSignal(boolean enable);
+
+ /* Gets the USB Hal Version. */
+ int getUsbHalVersion();
+
/* Get the functionfs control handle for the given function. Usb
* descriptors will already be written, and the handle will be
* ready to use.
diff --git a/core/java/android/hardware/usb/OWNERS b/core/java/android/hardware/usb/OWNERS
index 8f2b39da4f63..8f5c2a025672 100644
--- a/core/java/android/hardware/usb/OWNERS
+++ b/core/java/android/hardware/usb/OWNERS
@@ -1,4 +1,3 @@
# Bug component: 175220
-moltmann@google.com
badhri@google.com
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 5bac481fb23e..841fd2f49967 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -516,6 +516,46 @@ public class UsbManager {
public static final int USB_DATA_TRANSFER_RATE_40G = 40 * 1024;
/**
+ * The Value for USB hal is not presented.
+ *
+ * {@hide}
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int USB_HAL_NOT_SUPPORTED = -1;
+
+ /**
+ * Value for USB Hal Version v1.0.
+ *
+ * {@hide}
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int USB_HAL_V1_0 = 10;
+
+ /**
+ * Value for USB Hal Version v1.1.
+ *
+ * {@hide}
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int USB_HAL_V1_1 = 11;
+
+ /**
+ * Value for USB Hal Version v1.2.
+ *
+ * {@hide}
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int USB_HAL_V1_2 = 12;
+
+ /**
+ * Value for USB Hal Version v1.3.
+ *
+ * {@hide}
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int USB_HAL_V1_3 = 13;
+
+ /**
* Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
* {@hide}
*/
@@ -617,6 +657,16 @@ public class UsbManager {
})
public @interface UsbGadgetHalVersion {}
+ /** @hide */
+ @IntDef(prefix = { "USB_HAL_" }, value = {
+ USB_HAL_NOT_SUPPORTED,
+ USB_HAL_V1_0,
+ USB_HAL_V1_1,
+ USB_HAL_V1_2,
+ USB_HAL_V1_3,
+ })
+ public @interface UsbHalVersion {}
+
private final Context mContext;
private final IUsbManager mService;
@@ -1076,6 +1126,26 @@ public class UsbManager {
}
/**
+ * Get the Current USB Hal Version.
+ * <p>
+ * This function returns the current USB Hal Version.
+ * </p>
+ *
+ * @return a integer {@code USB_HAL_*} represent hal version.
+ *
+ * {@hide}
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public @UsbHalVersion int getUsbHalVersion() {
+ try {
+ return mService.getUsbHalVersion();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Resets the USB Gadget.
* <p>
* Performs USB data stack reset through USB Gadget HAL.
@@ -1095,6 +1165,28 @@ public class UsbManager {
}
/**
+ * Enable/Disable the USB data signaling.
+ * <p>
+ * Enables/Disables USB data path in all the USB ports.
+ * It will force to stop or restore USB data signaling.
+ * </p>
+ *
+ * @param enable enable or disable USB data signaling
+ * @return true enable or disable USB data successfully
+ * false if something wrong
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public boolean enableUsbDataSignal(boolean enable) {
+ try {
+ return mService.enableUsbDataSignal(enable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns a list of physical USB ports on the device.
* <p>
* This list is guaranteed to contain all dual-role USB Type C ports but it might
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityMetricsEvent.aidl b/core/java/android/net/ConnectivityMetricsEvent.aidl
index 1c541dc4c8cc..1c541dc4c8cc 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityMetricsEvent.aidl
+++ b/core/java/android/net/ConnectivityMetricsEvent.aidl
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index d6774d47b49e..933256a3b475 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -58,6 +58,9 @@ interface IIpSecService
in LinkAddress localAddr,
in String callingPackage);
+ void setNetworkForTunnelInterface(
+ int tunnelResourceId, in Network underlyingNetwork, in String callingPackage);
+
void deleteTunnelInterface(int resourceId, in String callingPackage);
IpSecTransformResponse createTransform(
diff --git a/core/java/android/net/IOnSetOemNetworkPreferenceListener.aidl b/core/java/android/net/IOnSetOemNetworkPreferenceListener.aidl
new file mode 100644
index 000000000000..7979afc54f90
--- /dev/null
+++ b/core/java/android/net/IOnSetOemNetworkPreferenceListener.aidl
@@ -0,0 +1,23 @@
+/**
+ *
+ * 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;
+
+/** @hide */
+oneway interface IOnSetOemNetworkPreferenceListener {
+ void onComplete();
+}
diff --git a/core/java/android/net/IVpnManager.aidl b/core/java/android/net/IVpnManager.aidl
new file mode 100644
index 000000000000..271efe41a9ef
--- /dev/null
+++ b/core/java/android/net/IVpnManager.aidl
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.Network;
+
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
+
+/**
+ * Interface that manages VPNs.
+ */
+/** {@hide} */
+interface IVpnManager {
+ /** VpnService APIs */
+ boolean prepareVpn(String oldPackage, String newPackage, int userId);
+ void setVpnPackageAuthorization(String packageName, int userId, int vpnType);
+ ParcelFileDescriptor establishVpn(in VpnConfig config);
+ boolean addVpnAddress(String address, int prefixLength);
+ boolean removeVpnAddress(String address, int prefixLength);
+ boolean setUnderlyingNetworksForVpn(in Network[] networks);
+
+ /** VpnManager APIs */
+ boolean provisionVpnProfile(in VpnProfile profile, String packageName);
+ void deleteVpnProfile(String packageName);
+ void startVpnProfile(String packageName);
+ void stopVpnProfile(String packageName);
+
+ /** Always-on VPN APIs */
+ boolean isAlwaysOnVpnPackageSupported(int userId, String packageName);
+ boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown,
+ in List<String> lockdownAllowlist);
+ String getAlwaysOnVpnPackage(int userId);
+ boolean isVpnLockdownEnabled(int userId);
+ List<String> getVpnLockdownAllowlist(int userId);
+ boolean isCallerCurrentAlwaysOnVpnApp();
+ boolean isCallerCurrentAlwaysOnVpnLockdownApp();
+
+ /** Legacy VPN APIs */
+ void startLegacyVpn(in VpnProfile profile);
+ LegacyVpnInfo getLegacyVpnInfo(int userId);
+ boolean updateLockdownVpn();
+
+ /** General system APIs */
+ VpnConfig getVpnConfig(int userId);
+ void factoryReset();
+}
diff --git a/packages/Connectivity/framework/src/android/net/InterfaceConfiguration.aidl b/core/java/android/net/InterfaceConfiguration.aidl
index 8aa5e3452853..8aa5e3452853 100644
--- a/packages/Connectivity/framework/src/android/net/InterfaceConfiguration.aidl
+++ b/core/java/android/net/InterfaceConfiguration.aidl
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 70bca3019818..98acd98cc465 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -782,6 +782,42 @@ public final class IpSecManager {
}
}
+ /**
+ * Update the underlying network for this IpSecTunnelInterface.
+ *
+ * <p>This new underlying network will be used for all transforms applied AFTER this call is
+ * complete. Before new {@link IpSecTransform}(s) with matching addresses are applied to
+ * this tunnel interface, traffic will still use the old SA, and be routed on the old
+ * underlying network.
+ *
+ * <p>To migrate IPsec tunnel mode traffic, a caller should:
+ *
+ * <ol>
+ * <li>Update the IpSecTunnelInterface’s underlying network.
+ * <li>Apply {@link IpSecTransform}(s) with matching addresses to this
+ * IpSecTunnelInterface.
+ * </ol>
+ *
+ * @param underlyingNetwork the new {@link Network} that will carry traffic for this tunnel.
+ * This network MUST never be the network exposing this IpSecTunnelInterface, otherwise
+ * this method will throw an {@link IllegalArgumentException}.
+ */
+ // TODO: b/169171001 Update the documentation when transform migration is supported.
+ // The purpose of making updating network and applying transforms separate is to leave open
+ // the possibility to support lossless migration procedures. To do that, Android platform
+ // will need to support multiple inbound tunnel mode transforms, just like it can support
+ // multiple transport mode transforms.
+ @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
+ @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
+ public void setUnderlyingNetwork(@NonNull Network underlyingNetwork) throws IOException {
+ try {
+ mService.setNetworkForTunnelInterface(
+ mResourceId, underlyingNetwork, mOpPackageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private IpSecTunnelInterface(@NonNull Context ctx, @NonNull IIpSecService service,
@NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress,
@NonNull Network underlyingNetwork)
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index e01e5aecc7e6..f7c1c4bb6d96 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -19,7 +19,6 @@ package android.net;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.system.ErrnoException;
-import android.system.Int32Ref;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructLinger;
@@ -65,14 +64,11 @@ class LocalSocketImpl
public int available() throws IOException {
FileDescriptor myFd = fd;
if (myFd == null) throw new IOException("socket closed");
-
- Int32Ref avail = new Int32Ref(0);
try {
- Os.ioctlInt(myFd, OsConstants.FIONREAD, avail);
+ return Os.ioctlInt(myFd, OsConstants.FIONREAD);
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
}
- return avail.value;
}
/** {@inheritDoc} */
@@ -134,7 +130,7 @@ class LocalSocketImpl
public void write (byte[] b) throws IOException {
write(b, 0, b.length);
}
-
+
/** {@inheritDoc} */
@Override
public void write (byte[] b, int off, int len) throws IOException {
@@ -255,7 +251,7 @@ class LocalSocketImpl
/** note timeout presently ignored */
protected void connect(LocalSocketAddress address, int timeout)
throws IOException
- {
+ {
if (fd == null) {
throw new IOException("socket not created");
}
@@ -339,7 +335,7 @@ class LocalSocketImpl
* @throws IOException if socket has been closed or cannot be created.
*/
protected OutputStream getOutputStream() throws IOException
- {
+ {
if (fd == null) {
throw new IOException("socket not created");
}
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 5d8122b6ce90..32b19a462218 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -32,7 +32,7 @@ import java.util.Objects;
/**
* Network definition that includes strong identity. Analogous to combining
- * {@link NetworkInfo} and an IMSI.
+ * {@link NetworkCapabilities} and an IMSI.
*
* @hide
*/
@@ -160,7 +160,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
*/
public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state,
boolean defaultNetwork, @NetworkType int subType) {
- final int type = state.networkInfo.getType();
+ final int legacyType = state.legacyNetworkType;
String subscriberId = null;
String networkId = null;
@@ -171,7 +171,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
subscriberId = state.subscriberId;
- if (type == TYPE_WIFI) {
+ if (legacyType == TYPE_WIFI) {
if (state.networkCapabilities.getSsid() != null) {
networkId = state.networkCapabilities.getSsid();
if (networkId == null) {
@@ -184,7 +184,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
}
}
- return new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered,
+ return new NetworkIdentity(legacyType, subType, subscriberId, networkId, roaming, metered,
defaultNetwork);
}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 3e6237d99011..6353a25e745f 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -22,6 +22,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -55,6 +56,7 @@ import java.util.concurrent.ConcurrentHashMap;
*
* @hide
*/
+@TestApi
@SystemService(Context.NETWORK_POLICY_SERVICE)
public class NetworkPolicyManager {
@@ -125,6 +127,7 @@ public class NetworkPolicyManager {
public static final int RULE_REJECT_ALL = 1 << 6;
/**
* Reject traffic on all networks for restricted networking mode.
+ * @hide
*/
public static final int RULE_REJECT_RESTRICTED_MODE = 1 << 10;
@@ -351,6 +354,7 @@ public class NetworkPolicyManager {
}
/** @hide */
+ @TestApi
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void setRestrictBackground(boolean restrictBackground) {
try {
@@ -361,6 +365,7 @@ public class NetworkPolicyManager {
}
/** @hide */
+ @TestApi
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean getRestrictBackground() {
try {
@@ -506,6 +511,8 @@ public class NetworkPolicyManager {
/**
* Get multipath preference for the given network.
+ *
+ * @hide
*/
public int getMultipathPreference(Network network) {
try {
@@ -624,7 +631,9 @@ public class NetworkPolicyManager {
}
/** @hide */
- public static String resolveNetworkId(WifiConfiguration config) {
+ @TestApi
+ @NonNull
+ public static String resolveNetworkId(@NonNull WifiConfiguration config) {
return WifiInfo.sanitizeSsid(config.isPasspoint()
? config.providerFriendlyName : config.SSID);
}
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index e1ef8b5ea5c9..813fde1c15f2 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -40,7 +41,7 @@ public class NetworkState implements Parcelable {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public final Network network;
public final String subscriberId;
- public final String networkId;
+ public final int legacyNetworkType;
private NetworkState() {
networkInfo = null;
@@ -48,18 +49,34 @@ public class NetworkState implements Parcelable {
networkCapabilities = null;
network = null;
subscriberId = null;
- networkId = null;
+ legacyNetworkType = 0;
}
+ public NetworkState(int legacyNetworkType, @NonNull LinkProperties linkProperties,
+ @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
+ @Nullable String subscriberId) {
+ this(legacyNetworkType, new NetworkInfo(legacyNetworkType, 0, null, null), linkProperties,
+ networkCapabilities, network, subscriberId);
+ }
+
+ // Constructor that used internally in ConnectivityService mainline module.
public NetworkState(@NonNull NetworkInfo networkInfo, @NonNull LinkProperties linkProperties,
@NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
- String subscriberId, String networkId) {
+ @Nullable String subscriberId) {
+ this(networkInfo.getType(), networkInfo, linkProperties,
+ networkCapabilities, network, subscriberId);
+ }
+
+ public NetworkState(int legacyNetworkType, @NonNull NetworkInfo networkInfo,
+ @NonNull LinkProperties linkProperties,
+ @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
+ @Nullable String subscriberId) {
this.networkInfo = networkInfo;
this.linkProperties = linkProperties;
this.networkCapabilities = networkCapabilities;
this.network = network;
this.subscriberId = subscriberId;
- this.networkId = networkId;
+ this.legacyNetworkType = legacyNetworkType;
// This object is an atomic view of a network, so the various components
// should always agree on roaming state.
@@ -79,7 +96,7 @@ public class NetworkState implements Parcelable {
networkCapabilities = in.readParcelable(null);
network = in.readParcelable(null);
subscriberId = in.readString();
- networkId = in.readString();
+ legacyNetworkType = in.readInt();
}
@Override
@@ -94,7 +111,7 @@ public class NetworkState implements Parcelable {
out.writeParcelable(networkCapabilities, flags);
out.writeParcelable(network, flags);
out.writeString(subscriberId);
- out.writeString(networkId);
+ out.writeInt(legacyNetworkType);
}
@UnsupportedAppUsage
diff --git a/core/java/android/net/OemNetworkPreferences.java b/core/java/android/net/OemNetworkPreferences.java
index 5e56164cc82c..b4034556f66e 100644
--- a/core/java/android/net/OemNetworkPreferences.java
+++ b/core/java/android/net/OemNetworkPreferences.java
@@ -18,6 +18,7 @@ package android.net;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcelable;
@@ -29,11 +30,12 @@ import java.util.Map;
import java.util.Objects;
/** @hide */
+@SystemApi
public final class OemNetworkPreferences implements Parcelable {
/**
- * Use default behavior requesting networks. Equivalent to not setting any preference at all.
+ * Default in case this value is not set. Using it will result in an error.
*/
- public static final int OEM_NETWORK_PREFERENCE_DEFAULT = 0;
+ public static final int OEM_NETWORK_PREFERENCE_UNINITIALIZED = 0;
/**
* If an unmetered network is available, use it.
@@ -45,17 +47,17 @@ public final class OemNetworkPreferences implements Parcelable {
/**
* If an unmetered network is available, use it.
* Otherwise, if a network with the OEM_PAID capability is available, use it.
- * Otherwise, the app doesn't get a network.
+ * Otherwise, the app doesn't get a default network.
*/
public static final int OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK = 2;
/**
- * Prefer only NET_CAPABILITY_OEM_PAID networks.
+ * Use only NET_CAPABILITY_OEM_PAID networks.
*/
public static final int OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY = 3;
/**
- * Prefer only NET_CAPABILITY_OEM_PRIVATE networks.
+ * Use only NET_CAPABILITY_OEM_PRIVATE networks.
*/
public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4;
@@ -95,8 +97,6 @@ public final class OemNetworkPreferences implements Parcelable {
/**
* Builder used to create {@link OemNetworkPreferences} objects. Specify the preferred Network
* to package name mappings.
- *
- * @hide
*/
public static final class Builder {
private final Bundle mNetworkMappings;
@@ -135,7 +135,7 @@ public final class OemNetworkPreferences implements Parcelable {
* @return The builder to facilitate chaining.
*/
@NonNull
- public Builder removeNetworkPreference(@NonNull final String packageName) {
+ public Builder clearNetworkPreference(@NonNull final String packageName) {
Objects.requireNonNull(packageName);
mNetworkMappings.remove(packageName);
return this;
@@ -160,7 +160,7 @@ public final class OemNetworkPreferences implements Parcelable {
/** @hide */
@IntDef(prefix = "OEM_NETWORK_PREFERENCE_", value = {
- OEM_NETWORK_PREFERENCE_DEFAULT,
+ OEM_NETWORK_PREFERENCE_UNINITIALIZED,
OEM_NETWORK_PREFERENCE_OEM_PAID,
OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK,
OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY,
@@ -174,12 +174,14 @@ public final class OemNetworkPreferences implements Parcelable {
*
* @param value int value of OemNetworkPreference
* @return string version of OemNetworkPreference
+ *
+ * @hide
*/
@NonNull
public static String oemNetworkPreferenceToString(@OemNetworkPreference int value) {
switch (value) {
- case OEM_NETWORK_PREFERENCE_DEFAULT:
- return "OEM_NETWORK_PREFERENCE_DEFAULT";
+ case OEM_NETWORK_PREFERENCE_UNINITIALIZED:
+ return "OEM_NETWORK_PREFERENCE_UNINITIALIZED";
case OEM_NETWORK_PREFERENCE_OEM_PAID:
return "OEM_NETWORK_PREFERENCE_OEM_PAID";
case OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK:
diff --git a/packages/Connectivity/framework/src/android/net/UidRange.aidl b/core/java/android/net/UidRange.aidl
index f70fc8e2fefd..f70fc8e2fefd 100644
--- a/packages/Connectivity/framework/src/android/net/UidRange.aidl
+++ b/core/java/android/net/UidRange.aidl
diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java
index 3bc0f9ca4e6a..b172ccc4e370 100644
--- a/core/java/android/net/UidRange.java
+++ b/core/java/android/net/UidRange.java
@@ -21,6 +21,7 @@ import static android.os.UserHandle.PER_USER_RANGE;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
import java.util.Collection;
@@ -45,6 +46,14 @@ public final class UidRange implements Parcelable {
return new UidRange(userId * PER_USER_RANGE, (userId + 1) * PER_USER_RANGE - 1);
}
+ /** Creates a UidRange for the specified user. */
+ public static UidRange createForUser(UserHandle user) {
+ final UserHandle nextUser = UserHandle.of(user.getIdentifier() + 1);
+ final int start = UserHandle.getUid(user, 0 /* appId */);
+ final int end = UserHandle.getUid(nextUser, 0) - 1;
+ return new UidRange(start, end);
+ }
+
/** Returns the smallest user Id which is contained in this UidRange */
public int getStartUser() {
return start / PER_USER_RANGE;
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
new file mode 100644
index 000000000000..f472ed4381d1
--- /dev/null
+++ b/core/java/android/net/VpnManager.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.RemoteException;
+
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.GeneralSecurityException;
+import java.util.List;
+
+/**
+ * This class provides an interface for apps to manage platform VPN profiles
+ *
+ * <p>Apps can use this API to provide profiles with which the platform can set up a VPN without
+ * further app intermediation. When a VPN profile is present and the app is selected as an always-on
+ * VPN, the platform will directly trigger the negotiation of the VPN without starting or waking the
+ * app (unlike VpnService).
+ *
+ * <p>VPN apps using supported protocols should preferentially use this API over the {@link
+ * VpnService} API for ease-of-development and reduced maintainance burden. This also give the user
+ * the guarantee that VPN network traffic is not subjected to on-device packet interception.
+ *
+ * @see Ikev2VpnProfile
+ */
+public class VpnManager {
+ /** Type representing a lack of VPN @hide */
+ public static final int TYPE_VPN_NONE = -1;
+
+ /**
+ * A VPN created by an app using the {@link VpnService} API.
+ * @hide
+ */
+ public static final int TYPE_VPN_SERVICE = 1;
+
+ /**
+ * A VPN created using a {@link VpnManager} API such as {@link #startProvisionedVpnProfile}.
+ * @hide
+ */
+ public static final int TYPE_VPN_PLATFORM = 2;
+
+ /**
+ * An IPsec VPN created by the built-in LegacyVpnRunner.
+ * @deprecated new Android devices should use VPN_TYPE_PLATFORM instead.
+ * @hide
+ */
+ @Deprecated
+ public static final int TYPE_VPN_LEGACY = 3;
+
+ /**
+ * Channel for VPN notifications.
+ * @hide
+ */
+ public static final String NOTIFICATION_CHANNEL_VPN = "VPN";
+
+ /** @hide */
+ @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VpnType {}
+
+ @NonNull private final Context mContext;
+ @NonNull private final IVpnManager mService;
+
+ private static Intent getIntentForConfirmation() {
+ final Intent intent = new Intent();
+ final ComponentName componentName = ComponentName.unflattenFromString(
+ Resources.getSystem().getString(
+ com.android.internal.R.string.config_platformVpnConfirmDialogComponent));
+ intent.setComponent(componentName);
+ return intent;
+ }
+
+ /**
+ * Create an instance of the VpnManager with the given context.
+ *
+ * <p>Internal only. Applications are expected to obtain an instance of the VpnManager via the
+ * {@link Context.getSystemService()} method call.
+ *
+ * @hide
+ */
+ public VpnManager(@NonNull Context ctx, @NonNull IVpnManager service) {
+ mContext = checkNotNull(ctx, "missing Context");
+ mService = checkNotNull(service, "missing IVpnManager");
+ }
+
+ /**
+ * Install a VpnProfile configuration keyed on the calling app's package name.
+ *
+ * <p>This method returns {@code null} if user consent has already been granted, or an {@link
+ * Intent} to a system activity. If an intent is returned, the application should launch the
+ * activity using {@link Activity#startActivityForResult} to request user consent. The activity
+ * may pop up a dialog to require user action, and the result will come back via its {@link
+ * Activity#onActivityResult}. If the result is {@link Activity#RESULT_OK}, the user has
+ * consented, and the VPN profile can be started.
+ *
+ * @param profile the VpnProfile provided by this package. Will override any previous VpnProfile
+ * stored for this package.
+ * @return an Intent requesting user consent to start the VPN, or null if consent is not
+ * required based on privileges or previous user consent.
+ */
+ @Nullable
+ public Intent provisionVpnProfile(@NonNull PlatformVpnProfile profile) {
+ final VpnProfile internalProfile;
+
+ try {
+ internalProfile = profile.toVpnProfile();
+ } catch (GeneralSecurityException | IOException e) {
+ // Conversion to VpnProfile failed; this is an invalid profile. Both of these exceptions
+ // indicate a failure to convert a PrivateKey or X509Certificate to a Base64 encoded
+ // string as required by the VpnProfile.
+ throw new IllegalArgumentException("Failed to serialize PlatformVpnProfile", e);
+ }
+
+ try {
+ // Profile can never be null; it either gets set, or an exception is thrown.
+ if (mService.provisionVpnProfile(internalProfile, mContext.getOpPackageName())) {
+ return null;
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return getIntentForConfirmation();
+ }
+
+ /**
+ * Delete the VPN profile configuration that was provisioned by the calling app
+ *
+ * @throws SecurityException if this would violate user settings
+ */
+ public void deleteProvisionedVpnProfile() {
+ try {
+ mService.deleteVpnProfile(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Request the startup of a previously provisioned VPN.
+ *
+ * @throws SecurityException exception if user or device settings prevent this VPN from being
+ * setup, or if user consent has not been granted
+ */
+ public void startProvisionedVpnProfile() {
+ try {
+ mService.startVpnProfile(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Tear down the VPN provided by the calling app (if any) */
+ public void stopProvisionedVpnProfile() {
+ try {
+ mService.stopVpnProfile(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return the VPN configuration for the given user ID.
+ * @hide
+ */
+ @Nullable
+ public VpnConfig getVpnConfig(@UserIdInt int userId) {
+ try {
+ return mService.getVpnConfig(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Resets all VPN settings back to factory defaults.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void factoryReset() {
+ try {
+ mService.factoryReset();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Prepare for a VPN application.
+ * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
+ * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ *
+ * @param oldPackage Package name of the application which currently controls VPN, which will
+ * be replaced. If there is no such application, this should should either be
+ * {@code null} or {@link VpnConfig.LEGACY_VPN}.
+ * @param newPackage Package name of the application which should gain control of VPN, or
+ * {@code null} to disable.
+ * @param userId User for whom to prepare the new VPN.
+ *
+ * @hide
+ */
+ public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
+ int userId) {
+ try {
+ return mService.prepareVpn(oldPackage, newPackage, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether the VPN package has the ability to launch VPNs without user intervention. This
+ * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
+ * class. If the caller is not {@code userId}, {@link
+ * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ *
+ * @param packageName The package for which authorization state should change.
+ * @param userId User for whom {@code packageName} is installed.
+ * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
+ * permissions should be granted. When unauthorizing an app, {@link
+ * VpnManager.TYPE_VPN_NONE} should be used.
+ * @hide
+ */
+ public void setVpnPackageAuthorization(
+ String packageName, int userId, @VpnManager.VpnType int vpnType) {
+ try {
+ mService.setVpnPackageAuthorization(packageName, userId, vpnType);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Checks if a VPN app supports always-on mode.
+ *
+ * In order to support the always-on feature, an app has to
+ * <ul>
+ * <li>target {@link VERSION_CODES#N API 24} or above, and
+ * <li>not opt out through the {@link VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}
+ * meta-data field.
+ * </ul>
+ *
+ * @param userId The identifier of the user for whom the VPN app is installed.
+ * @param vpnPackage The canonical package name of the VPN app.
+ * @return {@code true} if and only if the VPN app exists and supports always-on mode.
+ * @hide
+ */
+ public boolean isAlwaysOnVpnPackageSupportedForUser(int userId, @Nullable String vpnPackage) {
+ try {
+ return mService.isAlwaysOnVpnPackageSupported(userId, vpnPackage);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Configures an always-on VPN connection through a specific application.
+ * This connection is automatically granted and persisted after a reboot.
+ *
+ * <p>The designated package should declare a {@link VpnService} in its
+ * manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
+ * otherwise the call will fail.
+ *
+ * @param userId The identifier of the user to set an always-on VPN for.
+ * @param vpnPackage The package name for an installed VPN app on the device, or {@code null}
+ * to remove an existing always-on VPN configuration.
+ * @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
+ * {@code false} otherwise.
+ * @param lockdownAllowlist The list of packages that are allowed to access network directly
+ * when VPN is in lockdown mode but is not running. Non-existent packages are ignored so
+ * this method must be called when a package that should be allowed is installed or
+ * uninstalled.
+ * @return {@code true} if the package is set as always-on VPN controller;
+ * {@code false} otherwise.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
+ public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage,
+ boolean lockdownEnabled, @Nullable List<String> lockdownAllowlist) {
+ try {
+ return mService.setAlwaysOnVpnPackage(
+ userId, vpnPackage, lockdownEnabled, lockdownAllowlist);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the package name of the currently set always-on VPN application.
+ * If there is no always-on VPN set, or the VPN is provided by the system instead
+ * of by an app, {@code null} will be returned.
+ *
+ * @return Package name of VPN controller responsible for always-on VPN,
+ * or {@code null} if none is set.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
+ public String getAlwaysOnVpnPackageForUser(int userId) {
+ try {
+ return mService.getAlwaysOnVpnPackage(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @return whether always-on VPN is in lockdown mode.
+ *
+ * @hide
+ **/
+ @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
+ public boolean isVpnLockdownEnabled(int userId) {
+ try {
+ return mService.isVpnLockdownEnabled(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @return the list of packages that are allowed to access network when always-on VPN is in
+ * lockdown mode but not connected. Returns {@code null} when VPN lockdown is not active.
+ *
+ * @hide
+ **/
+ @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
+ public List<String> getVpnLockdownAllowlist(int userId) {
+ try {
+ return mService.getVpnLockdownAllowlist(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return the legacy VPN information for the specified user ID.
+ * @hide
+ */
+ public LegacyVpnInfo getLegacyVpnInfo(@UserIdInt int userId) {
+ try {
+ return mService.getLegacyVpnInfo(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Starts a legacy VPN.
+ * @hide
+ */
+ public void startLegacyVpn(VpnProfile profile) {
+ try {
+ mService.startLegacyVpn(profile);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Informs the service that legacy lockdown VPN state should be updated (e.g., if its keystore
+ * entry has been updated). If the LockdownVpn mechanism is enabled, updates the vpn
+ * with a reload of its profile.
+ *
+ * <p>This method can only be called by the system UID
+ * @return a boolean indicating success
+ *
+ * @hide
+ */
+ public boolean updateLockdownVpn() {
+ try {
+ return mService.updateLockdownVpn();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/Connectivity/framework/src/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 8e90a119fe21..e43b0b6fa635 100644
--- a/packages/Connectivity/framework/src/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -170,12 +170,11 @@ public class VpnService extends Service {
"android.net.VpnService.SUPPORTS_ALWAYS_ON";
/**
- * Use IConnectivityManager since those methods are hidden and not
- * available in ConnectivityManager.
+ * Use IVpnManager since those methods are hidden and not available in VpnManager.
*/
- private static IConnectivityManager getService() {
- return IConnectivityManager.Stub.asInterface(
- ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+ private static IVpnManager getService() {
+ return IVpnManager.Stub.asInterface(
+ ServiceManager.getService(Context.VPN_MANAGEMENT_SERVICE));
}
/**
@@ -226,15 +225,15 @@ public class VpnService extends Service {
@SystemApi
@RequiresPermission(android.Manifest.permission.CONTROL_VPN)
public static void prepareAndAuthorize(Context context) {
- IConnectivityManager cm = getService();
+ IVpnManager vm = getService();
String packageName = context.getPackageName();
try {
// Only prepare if we're not already prepared.
int userId = context.getUserId();
- if (!cm.prepareVpn(packageName, null, userId)) {
- cm.prepareVpn(null, packageName, userId);
+ if (!vm.prepareVpn(packageName, null, userId)) {
+ vm.prepareVpn(null, packageName, userId);
}
- cm.setVpnPackageAuthorization(packageName, userId, VpnManager.TYPE_VPN_SERVICE);
+ vm.setVpnPackageAuthorization(packageName, userId, VpnManager.TYPE_VPN_SERVICE);
} catch (RemoteException e) {
// ignore
}
diff --git a/core/java/android/net/vcn/IVcnManagementService.aidl b/core/java/android/net/vcn/IVcnManagementService.aidl
index 4f293eeb3c3b..6a3cb42ed75d 100644
--- a/core/java/android/net/vcn/IVcnManagementService.aidl
+++ b/core/java/android/net/vcn/IVcnManagementService.aidl
@@ -18,6 +18,7 @@ package android.net.vcn;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
+import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
@@ -33,4 +34,7 @@ interface IVcnManagementService {
void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy(in NetworkCapabilities nc, in LinkProperties lp);
+
+ void registerVcnStatusCallback(in ParcelUuid subscriptionGroup, in IVcnStatusCallback callback, in String opPkgName);
+ void unregisterVcnStatusCallback(in IVcnStatusCallback callback);
}
diff --git a/core/java/android/net/vcn/IVcnStatusCallback.aidl b/core/java/android/net/vcn/IVcnStatusCallback.aidl
new file mode 100644
index 000000000000..555e9b5883e8
--- /dev/null
+++ b/core/java/android/net/vcn/IVcnStatusCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.net.vcn;
+
+/** @hide */
+interface IVcnStatusCallback {
+ void onEnteredSafeMode();
+ void onGatewayConnectionError(
+ in int[] gatewayNetworkCapabilities,
+ int errorCode,
+ in String exceptionClass,
+ in String exceptionMessage);
+} \ No newline at end of file
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index 5eb4ba6a2f8e..52cc2182b094 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -20,6 +20,7 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.net.NetworkRequest;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
@@ -41,7 +42,6 @@ import java.util.Set;
* brought up on demand based on active {@link NetworkRequest}(s).
*
* @see VcnManager for more information on the Virtual Carrier Network feature
- * @hide
*/
public final class VcnConfig implements Parcelable {
@NonNull private static final String TAG = VcnConfig.class.getSimpleName();
@@ -56,7 +56,8 @@ public final class VcnConfig implements Parcelable {
@NonNull String packageName,
@NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs) {
mPackageName = packageName;
- mGatewayConnectionConfigs = Collections.unmodifiableSet(gatewayConnectionConfigs);
+ mGatewayConnectionConfigs =
+ Collections.unmodifiableSet(new ArraySet<>(gatewayConnectionConfigs));
validate();
}
@@ -96,11 +97,7 @@ public final class VcnConfig implements Parcelable {
return mPackageName;
}
- /**
- * Retrieves the set of configured tunnels.
- *
- * @hide
- */
+ /** Retrieves the set of configured GatewayConnection(s). */
@NonNull
public Set<VcnGatewayConnectionConfig> getGatewayConnectionConfigs() {
return Collections.unmodifiableSet(mGatewayConnectionConfigs);
@@ -168,11 +165,7 @@ public final class VcnConfig implements Parcelable {
}
};
- /**
- * This class is used to incrementally build {@link VcnConfig} objects.
- *
- * @hide
- */
+ /** This class is used to incrementally build {@link VcnConfig} objects. */
public static final class Builder {
@NonNull private final String mPackageName;
@@ -190,7 +183,6 @@ public final class VcnConfig implements Parcelable {
*
* @param gatewayConnectionConfig the configuration for an individual gateway connection
* @return this {@link Builder} instance, for chaining
- * @hide
*/
@NonNull
public Builder addGatewayConnectionConfig(
@@ -205,7 +197,6 @@ public final class VcnConfig implements Parcelable {
* Builds and validates the VcnConfig.
*
* @return an immutable VcnConfig instance
- * @hide
*/
@NonNull
public VcnConfig build() {
diff --git a/core/java/android/net/vcn/VcnControlPlaneConfig.java b/core/java/android/net/vcn/VcnControlPlaneConfig.java
new file mode 100644
index 000000000000..92f6c4440377
--- /dev/null
+++ b/core/java/android/net/vcn/VcnControlPlaneConfig.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.net.vcn;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.PersistableBundle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * This class represents a control plane configuration for a Virtual Carrier Network connection.
+ *
+ * <p>Each {@link VcnGatewayConnectionConfig} must have a {@link VcnControlPlaneConfig}, containing
+ * all connection, authentication and authorization parameters required to establish a Gateway
+ * Connection with a remote endpoint.
+ *
+ * <p>A {@link VcnControlPlaneConfig} object can be shared by multiple {@link
+ * VcnGatewayConnectionConfig}(s) if they will used for connecting with the same remote endpoint.
+ *
+ * @see VcnManager
+ * @see VcnGatewayConnectionConfig
+ */
+public abstract class VcnControlPlaneConfig {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({CONFIG_TYPE_IKE})
+ public @interface ConfigType {}
+
+ /** @hide */
+ public static final int CONFIG_TYPE_IKE = 1;
+
+ private static final String CONFIG_TYPE_KEY = "mConfigType";
+ @ConfigType private final int mConfigType;
+
+ /**
+ * Package private constructor.
+ *
+ * @hide
+ */
+ VcnControlPlaneConfig(int configType) {
+ mConfigType = configType;
+ }
+
+ /**
+ * Constructs a VcnControlPlaneConfig object by deserializing a PersistableBundle.
+ *
+ * @param in the {@link PersistableBundle} containing an {@link VcnControlPlaneConfig} object
+ * @hide
+ */
+ public static VcnControlPlaneConfig fromPersistableBundle(@NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ int configType = in.getInt(CONFIG_TYPE_KEY);
+ switch (configType) {
+ case CONFIG_TYPE_IKE:
+ return new VcnControlPlaneIkeConfig(in);
+ default:
+ throw new IllegalStateException("Unrecognized configType: " + configType);
+ }
+ }
+
+ /**
+ * Converts this VcnControlPlaneConfig to a PersistableBundle.
+ *
+ * @hide
+ */
+ @NonNull
+ public PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = new PersistableBundle();
+ result.putInt(CONFIG_TYPE_KEY, mConfigType);
+ return result;
+ }
+
+ /** @hide */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mConfigType);
+ }
+
+ /** @hide */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof VcnControlPlaneConfig)) {
+ return false;
+ }
+
+ return mConfigType == ((VcnControlPlaneConfig) o).mConfigType;
+ }
+
+ /**
+ * Returns a deep copy of this object.
+ *
+ * @hide
+ */
+ public abstract VcnControlPlaneConfig copy();
+}
diff --git a/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java b/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java
new file mode 100644
index 000000000000..de086f63b14d
--- /dev/null
+++ b/core/java/android/net/vcn/VcnControlPlaneIkeConfig.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.vcn;
+
+import static android.net.vcn.VcnControlPlaneConfig.CONFIG_TYPE_IKE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import android.os.PersistableBundle;
+import android.util.ArraySet;
+
+import java.util.Objects;
+
+/**
+ * This class is an IKEv2 control plane configuration for a Virtual Carrier Network connection.
+ *
+ * <p>This class is an extension of the {@link VcnControlPlaneConfig}, containing IKEv2-specific
+ * configuration, authentication and authorization parameters.
+ *
+ * @see VcnControlPlaneConfig
+ */
+public final class VcnControlPlaneIkeConfig extends VcnControlPlaneConfig {
+ private static final String TAG = VcnControlPlaneIkeConfig.class.getSimpleName();
+
+ // STOPSHIP: b/163604823 Make mIkeParams and mChildParams @NonNull when it is supported to
+ // construct mIkeParams and mChildParams from PersistableBundles.
+
+ private static final String IKE_PARAMS_KEY = "mIkeParams";
+ @Nullable private final IkeSessionParams mIkeParams;
+
+ private static final String CHILD_PARAMS_KEY = "mChildParams";
+ @Nullable private final TunnelModeChildSessionParams mChildParams;
+
+ private static final ArraySet<String> BUNDLE_KEY_SET = new ArraySet<>();
+
+ {
+ BUNDLE_KEY_SET.add(IKE_PARAMS_KEY);
+ BUNDLE_KEY_SET.add(CHILD_PARAMS_KEY);
+ }
+
+ /**
+ * Constructs a VcnControlPlaneIkeConfig object.
+ *
+ * @param ikeParams the IKE Session negotiation parameters
+ * @param childParams the tunnel mode Child Session negotiation parameters
+ */
+ public VcnControlPlaneIkeConfig(
+ @NonNull IkeSessionParams ikeParams,
+ @NonNull TunnelModeChildSessionParams childParams) {
+ super(CONFIG_TYPE_IKE);
+ mIkeParams = ikeParams;
+ mChildParams = childParams;
+ validate();
+ }
+
+ /**
+ * Constructs a VcnControlPlaneIkeConfig object by deserializing a PersistableBundle.
+ *
+ * @param in the {@link PersistableBundle} containing an {@link VcnControlPlaneIkeConfig} object
+ * @hide
+ */
+ public VcnControlPlaneIkeConfig(@NonNull PersistableBundle in) {
+ super(CONFIG_TYPE_IKE);
+ final PersistableBundle ikeParamsBundle = in.getPersistableBundle(IKE_PARAMS_KEY);
+ final PersistableBundle childParamsBundle = in.getPersistableBundle(CHILD_PARAMS_KEY);
+
+ // STOPSHIP: b/163604823 Support constructing mIkeParams and mChildParams from
+ // PersistableBundles.
+
+ mIkeParams = null;
+ mChildParams = null;
+ }
+
+ private void validate() {
+ Objects.requireNonNull(mIkeParams, "mIkeParams was null");
+ Objects.requireNonNull(mChildParams, "mChildParams was null");
+ }
+
+ /**
+ * Converts this VcnControlPlaneConfig to a PersistableBundle.
+ *
+ * @hide
+ */
+ @Override
+ @NonNull
+ public PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = super.toPersistableBundle();
+
+ // STOPSHIP: b/163604823 Support converting mIkeParams and mChildParams to
+ // PersistableBundles.
+ return result;
+ }
+
+ /** Retrieves the IKE Session configuration. */
+ @NonNull
+ public IkeSessionParams getIkeSessionParams() {
+ return mIkeParams;
+ }
+
+ /** Retrieves the tunnel mode Child Session configuration. */
+ @NonNull
+ public TunnelModeChildSessionParams getChildSessionParams() {
+ return mChildParams;
+ }
+
+ /** @hide */
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), mIkeParams, mChildParams);
+ }
+
+ /** @hide */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof VcnControlPlaneIkeConfig)) {
+ return false;
+ }
+
+ VcnControlPlaneIkeConfig other = (VcnControlPlaneIkeConfig) o;
+
+ // STOPSHIP: b/163604823 Also check mIkeParams and mChildParams when it is supported to
+ // construct mIkeParams and mChildParams from PersistableBundles. They are not checked
+ // now so that VcnGatewayConnectionConfigTest and VcnConfigTest can pass.
+ return super.equals(o);
+ }
+
+ /** @hide */
+ @Override
+ public VcnControlPlaneConfig copy() {
+ return new VcnControlPlaneIkeConfig(
+ new IkeSessionParams.Builder(mIkeParams).build(),
+ new TunnelModeChildSessionParams.Builder(mChildParams).build());
+ }
+}
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index cead2f1caad1..9f83b21f0d0c 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -21,6 +21,8 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.PersistableBundle;
import android.util.ArraySet;
@@ -55,28 +57,23 @@ import java.util.concurrent.TimeUnit;
* subscription group under which this configuration is registered (see {@link
* VcnManager#setVcnConfig}).
*
- * <p>Services that can be provided by a VCN network, or required for underlying networks are
- * limited to services provided by cellular networks:
+ * <p>As an abstraction of a cellular network, services that can be provided by a VCN network, or
+ * required for underlying networks are limited to services provided by cellular networks:
*
* <ul>
- * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_MMS}
- * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_SUPL}
- * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_DUN}
- * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_FOTA}
- * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_IMS}
- * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_CBS}
- * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_IA}
- * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_RCS}
- * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_XCAP}
- * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_EIMS}
- * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET}
- * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_MCX}
+ * <li>{@link NetworkCapabilities#NET_CAPABILITY_MMS}
+ * <li>{@link NetworkCapabilities#NET_CAPABILITY_SUPL}
+ * <li>{@link NetworkCapabilities#NET_CAPABILITY_DUN}
+ * <li>{@link NetworkCapabilities#NET_CAPABILITY_FOTA}
+ * <li>{@link NetworkCapabilities#NET_CAPABILITY_IMS}
+ * <li>{@link NetworkCapabilities#NET_CAPABILITY_CBS}
+ * <li>{@link NetworkCapabilities#NET_CAPABILITY_IA}
+ * <li>{@link NetworkCapabilities#NET_CAPABILITY_RCS}
+ * <li>{@link NetworkCapabilities#NET_CAPABILITY_XCAP}
+ * <li>{@link NetworkCapabilities#NET_CAPABILITY_EIMS}
+ * <li>{@link NetworkCapabilities#NET_CAPABILITY_INTERNET}
+ * <li>{@link NetworkCapabilities#NET_CAPABILITY_MCX}
* </ul>
- *
- * <p>The meteredness and roaming of the VCN {@link Network} will be determined by that of the
- * underlying Network(s).
- *
- * @hide
*/
public final class VcnGatewayConnectionConfig {
// TODO: Use MIN_MTU_V6 once it is public, @hide
@@ -153,14 +150,15 @@ public final class VcnGatewayConnectionConfig {
TimeUnit.MINUTES.toMillis(15)
};
+ private static final String CTRL_PLANE_CONFIG_KEY = "mCtrlPlaneConfig";
+ @NonNull private VcnControlPlaneConfig mCtrlPlaneConfig;
+
private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities";
@NonNull private final SortedSet<Integer> mExposedCapabilities;
private static final String UNDERLYING_CAPABILITIES_KEY = "mUnderlyingCapabilities";
@NonNull private final SortedSet<Integer> mUnderlyingCapabilities;
- // TODO: Add Ike/ChildSessionParams as a subclass - maybe VcnIkeGatewayConnectionConfig
-
private static final String MAX_MTU_KEY = "mMaxMtu";
private final int mMaxMtu;
@@ -169,10 +167,12 @@ public final class VcnGatewayConnectionConfig {
/** Builds a VcnGatewayConnectionConfig with the specified parameters. */
private VcnGatewayConnectionConfig(
+ @NonNull VcnControlPlaneConfig ctrlPlaneConfig,
@NonNull Set<Integer> exposedCapabilities,
@NonNull Set<Integer> underlyingCapabilities,
@NonNull long[] retryIntervalsMs,
@IntRange(from = MIN_MTU_V6) int maxMtu) {
+ mCtrlPlaneConfig = ctrlPlaneConfig;
mExposedCapabilities = new TreeSet(exposedCapabilities);
mUnderlyingCapabilities = new TreeSet(underlyingCapabilities);
mRetryIntervalsMs = retryIntervalsMs;
@@ -184,11 +184,16 @@ public final class VcnGatewayConnectionConfig {
/** @hide */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public VcnGatewayConnectionConfig(@NonNull PersistableBundle in) {
+ final PersistableBundle ctrlPlaneConfigBundle =
+ in.getPersistableBundle(CTRL_PLANE_CONFIG_KEY);
+ Objects.requireNonNull(ctrlPlaneConfigBundle, "ctrlPlaneConfigBundle was null");
+
final PersistableBundle exposedCapsBundle =
in.getPersistableBundle(EXPOSED_CAPABILITIES_KEY);
final PersistableBundle underlyingCapsBundle =
in.getPersistableBundle(UNDERLYING_CAPABILITIES_KEY);
+ mCtrlPlaneConfig = VcnControlPlaneConfig.fromPersistableBundle(ctrlPlaneConfigBundle);
mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
mUnderlyingCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
@@ -200,6 +205,8 @@ public final class VcnGatewayConnectionConfig {
}
private void validate() {
+ Objects.requireNonNull(mCtrlPlaneConfig, "control plane config was null");
+
Preconditions.checkArgument(
mExposedCapabilities != null && !mExposedCapabilities.isEmpty(),
"exposedCapsBundle was null or empty");
@@ -243,14 +250,23 @@ public final class VcnGatewayConnectionConfig {
}
/**
+ * Returns control plane configuration.
+ *
+ * @hide
+ */
+ @NonNull
+ public VcnControlPlaneConfig getControlPlaneConfig() {
+ return mCtrlPlaneConfig.copy();
+ }
+
+ /**
* Returns all exposed capabilities.
*
* <p>The returned integer-value capabilities will not contain duplicates, and will be sorted in
* ascending numerical order.
*
* @see Builder#addExposedCapability(int)
- * @see Builder#clearExposedCapability(int)
- * @hide
+ * @see Builder#removeExposedCapability(int)
*/
@NonNull
public int[] getExposedCapabilities() {
@@ -278,8 +294,7 @@ public final class VcnGatewayConnectionConfig {
* <p>The returned integer-value capabilities will be sorted in ascending numerical order.
*
* @see Builder#addRequiredUnderlyingCapability(int)
- * @see Builder#clearRequiredUnderlyingCapability(int)
- * @hide
+ * @see Builder#removeRequiredUnderlyingCapability(int)
*/
@NonNull
public int[] getRequiredUnderlyingCapabilities() {
@@ -305,7 +320,6 @@ public final class VcnGatewayConnectionConfig {
* Retrieves the configured retry intervals.
*
* @see Builder#setRetryInterval(long[])
- * @hide
*/
@NonNull
public long[] getRetryInterval() {
@@ -317,7 +331,7 @@ public final class VcnGatewayConnectionConfig {
*
* <p>Left to prevent the need to make major changes while changes are actively in flight.
*
- * @deprecated use getRequiredUnderlyingCapabilities() instead
+ * @deprecated use getRetryInterval() instead
* @hide
*/
@Deprecated
@@ -329,8 +343,7 @@ public final class VcnGatewayConnectionConfig {
/**
* Retrieves the maximum MTU allowed for this Gateway Connection.
*
- * @see Builder.setMaxMtu(int)
- * @hide
+ * @see Builder#setMaxMtu(int)
*/
@IntRange(from = MIN_MTU_V6)
public int getMaxMtu() {
@@ -347,6 +360,7 @@ public final class VcnGatewayConnectionConfig {
public PersistableBundle toPersistableBundle() {
final PersistableBundle result = new PersistableBundle();
+ final PersistableBundle ctrlPlaneConfigBundle = mCtrlPlaneConfig.toPersistableBundle();
final PersistableBundle exposedCapsBundle =
PersistableBundleUtils.fromList(
new ArrayList<>(mExposedCapabilities),
@@ -356,6 +370,7 @@ public final class VcnGatewayConnectionConfig {
new ArrayList<>(mUnderlyingCapabilities),
PersistableBundleUtils.INTEGER_SERIALIZER);
+ result.putPersistableBundle(CTRL_PLANE_CONFIG_KEY, ctrlPlaneConfigBundle);
result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle);
result.putPersistableBundle(UNDERLYING_CAPABILITIES_KEY, underlyingCapsBundle);
result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
@@ -388,10 +403,9 @@ public final class VcnGatewayConnectionConfig {
/**
* This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects.
- *
- * @hide
*/
public static final class Builder {
+ @NonNull private final VcnControlPlaneConfig mCtrlPlaneConfig;
@NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
@NonNull private final Set<Integer> mUnderlyingCapabilities = new ArraySet();
@NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
@@ -402,6 +416,18 @@ public final class VcnGatewayConnectionConfig {
// when on Cell.
/**
+ * Construct a Builder object.
+ *
+ * @param ctrlPlaneConfig the control plane configuration
+ * @see VcnControlPlaneConfig
+ */
+ public Builder(@NonNull VcnControlPlaneConfig ctrlPlaneConfig) {
+ Objects.requireNonNull(ctrlPlaneConfig, "ctrlPlaneConfig was null");
+
+ mCtrlPlaneConfig = ctrlPlaneConfig;
+ }
+
+ /**
* Add a capability that this VCN Gateway Connection will support.
*
* @param exposedCapability the app-facing capability to be exposed by this VCN Gateway
@@ -409,7 +435,6 @@ public final class VcnGatewayConnectionConfig {
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
* Connection
- * @hide
*/
@NonNull
public Builder addExposedCapability(@VcnSupportedCapability int exposedCapability) {
@@ -427,10 +452,10 @@ public final class VcnGatewayConnectionConfig {
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
* Connection
- * @hide
*/
@NonNull
- public Builder clearExposedCapability(@VcnSupportedCapability int exposedCapability) {
+ @SuppressLint("BuilderSetStyle") // For consistency with NetCaps.Builder add/removeCap
+ public Builder removeExposedCapability(@VcnSupportedCapability int exposedCapability) {
checkValidCapability(exposedCapability);
mExposedCapabilities.remove(exposedCapability);
@@ -445,7 +470,6 @@ public final class VcnGatewayConnectionConfig {
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
* networks
- * @hide
*/
@NonNull
public Builder addRequiredUnderlyingCapability(
@@ -468,10 +492,10 @@ public final class VcnGatewayConnectionConfig {
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
* networks
- * @hide
*/
@NonNull
- public Builder clearRequiredUnderlyingCapability(
+ @SuppressLint("BuilderSetStyle") // For consistency with NetCaps.Builder add/removeCap
+ public Builder removeRequiredUnderlyingCapability(
@VcnSupportedCapability int underlyingCapability) {
checkValidCapability(underlyingCapability);
@@ -501,7 +525,6 @@ public final class VcnGatewayConnectionConfig {
* 15m]}
* @return this {@link Builder} instance, for chaining
* @see VcnManager for additional discussion on fail-safe mode
- * @hide
*/
@NonNull
public Builder setRetryInterval(@NonNull long[] retryIntervalsMs) {
@@ -523,7 +546,6 @@ public final class VcnGatewayConnectionConfig {
* @param maxMtu the maximum MTU allowed for this Gateway Connection. Must be greater than
* the IPv6 minimum MTU of 1280. Defaults to 1500.
* @return this {@link Builder} instance, for chaining
- * @hide
*/
@NonNull
public Builder setMaxMtu(@IntRange(from = MIN_MTU_V6) int maxMtu) {
@@ -538,12 +560,15 @@ public final class VcnGatewayConnectionConfig {
* Builds and validates the VcnGatewayConnectionConfig.
*
* @return an immutable VcnGatewayConnectionConfig instance
- * @hide
*/
@NonNull
public VcnGatewayConnectionConfig build() {
return new VcnGatewayConnectionConfig(
- mExposedCapabilities, mUnderlyingCapabilities, mRetryIntervalsMs, mMaxMtu);
+ mCtrlPlaneConfig,
+ mExposedCapabilities,
+ mUnderlyingCapabilities,
+ mRetryIntervalsMs,
+ mMaxMtu);
}
}
}
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index fa090f59a8b9..aea0ea988f50 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -17,19 +17,26 @@ package android.net.vcn;
import static java.util.Objects.requireNonNull;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
+import android.os.Binder;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@@ -37,12 +44,12 @@ import java.util.concurrent.Executor;
/**
* VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks.
*
- * <p>A VCN creates a virtualization layer to allow MVNOs to aggregate heterogeneous physical
+ * <p>A VCN creates a virtualization layer to allow carriers to aggregate heterogeneous physical
* networks, unifying them as a single carrier network. This enables infrastructure flexibility on
- * the part of MVNOs without impacting user connectivity, abstracting the physical network
+ * the part of carriers without impacting user connectivity, abstracting the physical network
* technologies as an implementation detail of their public network.
*
- * <p>Each VCN virtualizes an Carrier's network by building tunnels to a carrier's core network over
+ * <p>Each VCN virtualizes a carrier's network by building tunnels to a carrier's core network over
* carrier-managed physical links and supports a IP mobility layer to ensure seamless transitions
* between the underlying networks. Each VCN is configured based on a Subscription Group (see {@link
* android.telephony.SubscriptionManager}) and aggregates all networks that are brought up based on
@@ -60,15 +67,12 @@ import java.util.concurrent.Executor;
* tasks. In Safe Mode, the system will allow underlying cellular networks to be used as default.
* Additionally, during Safe Mode, the VCN will continue to retry the connections, and will
* automatically exit Safe Mode if all active tunnels connect successfully.
- *
- * @hide
*/
@SystemService(Context.VCN_MANAGEMENT_SERVICE)
public class VcnManager {
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
- @VisibleForTesting
- public static final Map<
+ private static final Map<
VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
@@ -88,7 +92,18 @@ public class VcnManager {
mService = requireNonNull(service, "missing service");
}
- // TODO: Make setVcnConfig(), clearVcnConfig() Public API
+ /**
+ * Get all currently registered VcnUnderlyingNetworkPolicyListeners for testing purposes.
+ *
+ * @hide
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ public static Map<VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+ getAllPolicyListeners() {
+ return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS);
+ }
+
/**
* Sets the VCN configuration for a given subscription group.
*
@@ -100,11 +115,10 @@ public class VcnManager {
*
* @param subscriptionGroup the subscription group that the configuration should be applied to
* @param config the configuration parameters for the VCN
- * @throws SecurityException if the caller does not have carrier privileges, or is not running
- * as the primary user
- * @throws IOException if the configuration failed to be persisted. A caller encountering this
- * exception should attempt to retry (possibly after a delay).
- * @hide
+ * @throws SecurityException if the caller does not have carrier privileges for the provided
+ * subscriptionGroup, or is not running as the primary user
+ * @throws IOException if the configuration failed to be saved and persisted to disk. This may
+ * occur due to temporary disk errors, or more permanent conditions such as a full disk.
*/
@RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant
public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config)
@@ -121,7 +135,6 @@ public class VcnManager {
}
}
- // TODO: Make setVcnConfig(), clearVcnConfig() Public API
/**
* Clears the VCN configuration for a given subscription group.
*
@@ -132,9 +145,8 @@ public class VcnManager {
* @param subscriptionGroup the subscription group that the configuration should be applied to
* @throws SecurityException if the caller does not have carrier privileges, or is not running
* as the primary user
- * @throws IOException if the configuration failed to be cleared. A caller encountering this
- * exception should attempt to retry (possibly after a delay).
- * @hide
+ * @throws IOException if the configuration failed to be cleared from disk. This may occur due
+ * to temporary disk errors, or more permanent conditions such as a full disk.
*/
@RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant
public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) throws IOException {
@@ -254,6 +266,154 @@ public class VcnManager {
}
}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ VCN_ERROR_CODE_INTERNAL_ERROR,
+ VCN_ERROR_CODE_CONFIG_ERROR,
+ VCN_ERROR_CODE_NETWORK_ERROR
+ })
+ public @interface VcnErrorCode {}
+
+ /**
+ * Value indicating that an internal failure occurred in this Gateway Connection.
+ *
+ * @hide
+ */
+ public static final int VCN_ERROR_CODE_INTERNAL_ERROR = 0;
+
+ /**
+ * Value indicating that an error with this Gateway Connection's configuration occurred.
+ *
+ * <p>For example, this error code will be returned after authentication failures.
+ *
+ * @hide
+ */
+ public static final int VCN_ERROR_CODE_CONFIG_ERROR = 1;
+
+ /**
+ * Value indicating that a Network error occurred with this Gateway Connection.
+ *
+ * <p>For example, this error code will be returned if an underlying {@link android.net.Network}
+ * for this Gateway Connection is lost, or if an error occurs while resolving the connection
+ * endpoint address.
+ *
+ * @hide
+ */
+ public static final int VCN_ERROR_CODE_NETWORK_ERROR = 2;
+
+ // TODO: make VcnStatusCallback @SystemApi
+ /**
+ * VcnStatusCallback is the interface for Carrier apps to receive updates for their VCNs.
+ *
+ * <p>VcnStatusCallbacks may be registered before {@link VcnConfig}s are provided for a
+ * subscription group.
+ *
+ * @hide
+ */
+ public abstract static class VcnStatusCallback {
+ private VcnStatusCallbackBinder mCbBinder;
+
+ /**
+ * Invoked when the VCN for this Callback's subscription group enters safe mode.
+ *
+ * <p>A VCN will be put into safe mode if any of the gateway connections were unable to
+ * establish a connection within a system-determined timeout (while underlying networks were
+ * available).
+ *
+ * <p>A VCN-configuring app may opt to exit safe mode by (re)setting the VCN configuration
+ * via {@link #setVcnConfig(ParcelUuid, VcnConfig)}.
+ */
+ public abstract void onEnteredSafeMode();
+
+ /**
+ * Invoked when a VCN Gateway Connection corresponding to this callback's subscription
+ * encounters an error.
+ *
+ * @param networkCapabilities an array of underlying NetworkCapabilities for the Gateway
+ * Connection that encountered the error for identification purposes. These will be a
+ * sorted list with no duplicates, matching one of the {@link
+ * VcnGatewayConnectionConfig}s set in the {@link VcnConfig} for this subscription
+ * group.
+ * @param errorCode {@link VcnErrorCode} to indicate the error that occurred
+ * @param detail Throwable to provide additional information about the error, or {@code
+ * null} if none
+ */
+ public abstract void onGatewayConnectionError(
+ @NonNull int[] networkCapabilities,
+ @VcnErrorCode int errorCode,
+ @Nullable Throwable detail);
+ }
+
+ /**
+ * Registers the given callback to receive status updates for the specified subscription.
+ *
+ * <p>Callbacks can be registered for a subscription before {@link VcnConfig}s are set for it.
+ *
+ * <p>A {@link VcnStatusCallback} may only be registered for one subscription at a time. {@link
+ * VcnStatusCallback}s may be reused once unregistered.
+ *
+ * <p>A {@link VcnStatusCallback} will only be invoked if the registering package has carrier
+ * privileges for the specified subscription at the time of invocation.
+ *
+ * @param subscriptionGroup The subscription group to match for callbacks
+ * @param executor The {@link Executor} to be used for invoking callbacks
+ * @param callback The VcnStatusCallback to be registered
+ * @throws IllegalStateException if callback is currently registered with VcnManager
+ * @hide
+ */
+ public void registerVcnStatusCallback(
+ @NonNull ParcelUuid subscriptionGroup,
+ @NonNull Executor executor,
+ @NonNull VcnStatusCallback callback) {
+ requireNonNull(subscriptionGroup, "subscriptionGroup must not be null");
+ requireNonNull(executor, "executor must not be null");
+ requireNonNull(callback, "callback must not be null");
+
+ synchronized (callback) {
+ if (callback.mCbBinder != null) {
+ throw new IllegalStateException("callback is already registered with VcnManager");
+ }
+ callback.mCbBinder = new VcnStatusCallbackBinder(executor, callback);
+
+ try {
+ mService.registerVcnStatusCallback(
+ subscriptionGroup, callback.mCbBinder, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ callback.mCbBinder = null;
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Unregisters the given callback.
+ *
+ * <p>Once unregistered, the callback will stop receiving status updates for the subscription it
+ * was registered with.
+ *
+ * @param callback The callback to be unregistered
+ * @hide
+ */
+ public void unregisterVcnStatusCallback(@NonNull VcnStatusCallback callback) {
+ requireNonNull(callback, "callback must not be null");
+
+ synchronized (callback) {
+ if (callback.mCbBinder == null) {
+ // no Binder attached to this callback, so it's not currently registered
+ return;
+ }
+
+ try {
+ mService.unregisterVcnStatusCallback(callback.mCbBinder);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } finally {
+ callback.mCbBinder = null;
+ }
+ }
+ }
+
/**
* Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System
* Server.
@@ -273,7 +433,62 @@ public class VcnManager {
@Override
public void onPolicyChanged() {
- mExecutor.execute(() -> mListener.onPolicyChanged());
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> mListener.onPolicyChanged()));
+ }
+ }
+
+ /**
+ * Binder wrapper for VcnStatusCallbacks to receive signals from VcnManagementService.
+ *
+ * @hide
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class VcnStatusCallbackBinder extends IVcnStatusCallback.Stub {
+ @NonNull private final Executor mExecutor;
+ @NonNull private final VcnStatusCallback mCallback;
+
+ public VcnStatusCallbackBinder(
+ @NonNull Executor executor, @NonNull VcnStatusCallback callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onEnteredSafeMode() {
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> mCallback.onEnteredSafeMode()));
+ }
+
+ // TODO(b/180521637): use ServiceSpecificException for safer Exception 'parceling'
+ @Override
+ public void onGatewayConnectionError(
+ @NonNull int[] networkCapabilities,
+ @VcnErrorCode int errorCode,
+ @Nullable String exceptionClass,
+ @Nullable String exceptionMessage) {
+ final Throwable cause = createThrowableByClassName(exceptionClass, exceptionMessage);
+
+ Binder.withCleanCallingIdentity(
+ () ->
+ mExecutor.execute(
+ () ->
+ mCallback.onGatewayConnectionError(
+ networkCapabilities, errorCode, cause)));
+ }
+
+ private static Throwable createThrowableByClassName(
+ @Nullable String className, @Nullable String message) {
+ if (className == null) {
+ return null;
+ }
+
+ try {
+ Class<?> c = Class.forName(className);
+ return (Throwable) c.getConstructor(String.class).newInstance(message);
+ } catch (ReflectiveOperationException | ClassCastException e) {
+ return new RuntimeException(className + ": " + message);
+ }
}
}
}
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java b/core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java
new file mode 100644
index 000000000000..a97563724e50
--- /dev/null
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java
@@ -0,0 +1,125 @@
+/*
+ * 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.vcn;
+
+import android.annotation.NonNull;
+import android.net.NetworkSpecifier;
+import android.net.TelephonyNetworkSpecifier;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * NetworkSpecifier object for VCN underlying network requests.
+ *
+ * <p>This matches any underlying network with the appropriate subIds.
+ *
+ * @hide
+ */
+public final class VcnUnderlyingNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+ @NonNull private final int[] mSubIds;
+
+ /**
+ * Builds a new VcnUnderlyingNetworkSpecifier with the given list of subIds
+ *
+ * @hide
+ */
+ public VcnUnderlyingNetworkSpecifier(@NonNull int[] subIds) {
+ mSubIds = Objects.requireNonNull(subIds, "subIds were null");
+ }
+
+ /**
+ * Retrieves the list of subIds supported by this VcnUnderlyingNetworkSpecifier
+ *
+ * @hide
+ */
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public int[] getSubIds() {
+ return mSubIds;
+ }
+
+ public static final @NonNull Creator<VcnUnderlyingNetworkSpecifier> CREATOR =
+ new Creator<VcnUnderlyingNetworkSpecifier>() {
+ @Override
+ public VcnUnderlyingNetworkSpecifier createFromParcel(Parcel in) {
+ int[] subIds = in.createIntArray();
+ return new VcnUnderlyingNetworkSpecifier(subIds);
+ }
+
+ @Override
+ public VcnUnderlyingNetworkSpecifier[] newArray(int size) {
+ return new VcnUnderlyingNetworkSpecifier[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeIntArray(mSubIds);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(mSubIds);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof VcnUnderlyingNetworkSpecifier)) {
+ return false;
+ }
+
+ VcnUnderlyingNetworkSpecifier lhs = (VcnUnderlyingNetworkSpecifier) obj;
+ return Arrays.equals(mSubIds, lhs.mSubIds);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("VcnUnderlyingNetworkSpecifier [")
+ .append("mSubIds = ").append(Arrays.toString(mSubIds))
+ .append("]")
+ .toString();
+ }
+
+ /** @hide */
+ @Override
+ public boolean canBeSatisfiedBy(NetworkSpecifier other) {
+ if (other instanceof TelephonyNetworkSpecifier) {
+ return ArrayUtils.contains(
+ mSubIds, ((TelephonyNetworkSpecifier) other).getSubscriptionId());
+ }
+ // TODO(b/180140053): Allow matching against WifiNetworkAgentSpecifier
+
+ // MatchAllNetworkSpecifier matched in NetworkCapabilities.
+ return equals(other);
+ }
+}
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index f2b466d7fa5d..c6efaace76d7 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -47,6 +47,9 @@ public abstract class BatteryConsumer {
POWER_COMPONENT_SYSTEM_SERVICES,
POWER_COMPONENT_SENSORS,
POWER_COMPONENT_GNSS,
+ POWER_COMPONENT_WAKELOCK,
+ POWER_COMPONENT_SCREEN,
+ POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS,
})
@Retention(RetentionPolicy.SOURCE)
public static @interface PowerComponent {
@@ -63,8 +66,14 @@ public abstract class BatteryConsumer {
public static final int POWER_COMPONENT_MOBILE_RADIO = 8;
public static final int POWER_COMPONENT_SENSORS = 9;
public static final int POWER_COMPONENT_GNSS = 10;
+ public static final int POWER_COMPONENT_WAKELOCK = 12;
+ public static final int POWER_COMPONENT_SCREEN = 13;
+ // Power that is re-attributed to other battery consumers. For example, for System Server
+ // this represents the power attributed to apps requesting system services.
+ // The value should be negative or zero.
+ public static final int POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS = 14;
- public static final int POWER_COMPONENT_COUNT = 11;
+ public static final int POWER_COMPONENT_COUNT = 15;
public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000;
public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999;
@@ -85,6 +94,8 @@ public abstract class BatteryConsumer {
TIME_COMPONENT_MOBILE_RADIO,
TIME_COMPONENT_SENSORS,
TIME_COMPONENT_GNSS,
+ TIME_COMPONENT_WAKELOCK,
+ TIME_COMPONENT_SCREEN,
})
@Retention(RetentionPolicy.SOURCE)
public static @interface TimeComponent {
@@ -101,8 +112,10 @@ public abstract class BatteryConsumer {
public static final int TIME_COMPONENT_MOBILE_RADIO = 8;
public static final int TIME_COMPONENT_SENSORS = 9;
public static final int TIME_COMPONENT_GNSS = 10;
+ public static final int TIME_COMPONENT_WAKELOCK = 12;
+ public static final int TIME_COMPONENT_SCREEN = 13;
- public static final int TIME_COMPONENT_COUNT = 11;
+ public static final int TIME_COMPONENT_COUNT = 14;
public static final int FIRST_CUSTOM_TIME_COMPONENT_ID = 1000;
public static final int LAST_CUSTOM_TIME_COMPONENT_ID = 9999;
@@ -117,7 +130,7 @@ public abstract class BatteryConsumer {
* Total power consumed by this consumer, in mAh.
*/
public double getConsumedPower() {
- return mPowerComponents.getTotalPowerConsumed();
+ return mPowerComponents.getTotalConsumedPower();
}
/**
@@ -232,5 +245,13 @@ public abstract class BatteryConsumer {
componentUsageTimeMillis);
return (T) this;
}
+
+ /**
+ * Returns the total power accumulated by this builder so far. It may change
+ * by the time the {@code build()} method is called.
+ */
+ public double getTotalPower() {
+ return mPowerComponentsBuilder.getTotalPower();
+ }
}
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index cc86a604c194..cf9b534fc29f 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -994,6 +994,19 @@ public abstract class BatteryStats implements Parcelable {
*/
public abstract long getScreenOnEnergy();
+ /**
+ * Returns the energies 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}).
+ *
+ * @return energies (in microjoules) used since boot for each (custom) energy consumer of
+ * type OTHER, indexed by their ordinal. Returns null if no energy reporting is
+ * supported.
+ *
+ * {@hide}
+ */
+ public abstract @Nullable long[] getCustomMeasuredEnergiesMicroJoules();
+
public static abstract class Sensor {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -1658,7 +1671,7 @@ public abstract class BatteryStats implements Parcelable {
public char batteryVoltage;
// The charge of the battery in micro-Ampere-hours.
- public int batteryChargeUAh;
+ public int batteryChargeUah;
public double modemRailChargeMah;
public double wifiRailChargeMah;
@@ -1871,7 +1884,7 @@ public abstract class BatteryStats implements Parcelable {
bat = (((int)batteryTemperature)&0xffff)
| ((((int)batteryVoltage)<<16)&0xffff0000);
dest.writeInt(bat);
- dest.writeInt(batteryChargeUAh);
+ dest.writeInt(batteryChargeUah);
dest.writeDouble(modemRailChargeMah);
dest.writeDouble(wifiRailChargeMah);
dest.writeInt(states);
@@ -1903,7 +1916,7 @@ public abstract class BatteryStats implements Parcelable {
int bat2 = src.readInt();
batteryTemperature = (short)(bat2&0xffff);
batteryVoltage = (char)((bat2>>16)&0xffff);
- batteryChargeUAh = src.readInt();
+ batteryChargeUah = src.readInt();
modemRailChargeMah = src.readDouble();
wifiRailChargeMah = src.readDouble();
states = src.readInt();
@@ -1946,7 +1959,7 @@ public abstract class BatteryStats implements Parcelable {
batteryPlugType = 0;
batteryTemperature = 0;
batteryVoltage = 0;
- batteryChargeUAh = 0;
+ batteryChargeUah = 0;
modemRailChargeMah = 0;
wifiRailChargeMah = 0;
states = 0;
@@ -1978,7 +1991,7 @@ public abstract class BatteryStats implements Parcelable {
batteryPlugType = o.batteryPlugType;
batteryTemperature = o.batteryTemperature;
batteryVoltage = o.batteryVoltage;
- batteryChargeUAh = o.batteryChargeUAh;
+ batteryChargeUah = o.batteryChargeUah;
modemRailChargeMah = o.modemRailChargeMah;
wifiRailChargeMah = o.wifiRailChargeMah;
states = o.states;
@@ -2012,7 +2025,7 @@ public abstract class BatteryStats implements Parcelable {
&& batteryPlugType == o.batteryPlugType
&& batteryTemperature == o.batteryTemperature
&& batteryVoltage == o.batteryVoltage
- && batteryChargeUAh == o.batteryChargeUAh
+ && batteryChargeUah == o.batteryChargeUah
&& modemRailChargeMah == o.modemRailChargeMah
&& wifiRailChargeMah == o.wifiRailChargeMah
&& states == o.states
@@ -2511,6 +2524,19 @@ public abstract class BatteryStats implements Parcelable {
*/
public abstract long getScreenDozeEnergy();
+ /**
+ * Returns the energies used for each
+ * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
+ * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
+ *
+ * @return energies (in microjoules) used since boot for each (custom) energy consumer of
+ * type OTHER, indexed by their ordinal. Returns null if no energy reporting is
+ * supported.
+ *
+ * {@hide}
+ */
+ public abstract @Nullable long[] getCustomMeasuredEnergiesMicroJoules();
+
public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS = new BitDescription[] {
new BitDescription(HistoryItem.STATE_CPU_RUNNING_FLAG, "running", "r"),
new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock", "w"),
@@ -2833,6 +2859,11 @@ public abstract class BatteryStats implements Parcelable {
public abstract boolean getIsOnBattery();
/**
+ * Returns the timestamp of when battery stats collection started, in microseconds.
+ */
+ public abstract long getStatsStartRealtime();
+
+ /**
* Returns a SparseArray containing the statistics for each uid.
*/
@UnsupportedAppUsage
@@ -5268,6 +5299,15 @@ public abstract class BatteryStats implements Parcelable {
pw.print(" flash=");
printmAh(pw, bs.flashlightPowerMah);
}
+ if (bs.customMeasuredPowerMah != null) {
+ for (int idx = 0; idx < bs.customMeasuredPowerMah.length; idx++) {
+ final double customPowerMah = bs.customMeasuredPowerMah[idx];
+ if (customPowerMah != 0) {
+ pw.print(" custom[" + idx + "]=");
+ printmAh(pw, customPowerMah);
+ }
+ }
+ }
pw.print(" )");
}
@@ -6485,7 +6525,7 @@ public abstract class BatteryStats implements Parcelable {
item.append(checkin ? ",Bv=" : " volt=");
item.append(oldVolt);
}
- final int chargeMAh = rec.batteryChargeUAh / 1000;
+ final int chargeMAh = rec.batteryChargeUah / 1000;
if (oldChargeMAh != chargeMAh) {
oldChargeMAh = chargeMAh;
item.append(checkin ? ",Bcc=" : " charge=");
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index af8e8de85d9f..35a5a7c7c1d0 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -17,7 +17,7 @@
package android.os;
import android.annotation.NonNull;
-import android.annotation.SuppressLint;
+import android.util.Range;
import android.util.SparseArray;
import java.util.ArrayList;
@@ -31,22 +31,59 @@ import java.util.List;
public final class BatteryUsageStats implements Parcelable {
private final double mConsumedPower;
private final int mDischargePercentage;
+ private final long mStatsStartRealtimeMs;
+ private final double mDischargedPowerLowerBound;
+ private final double mDischargedPowerUpperBound;
private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers;
private final ArrayList<SystemBatteryConsumer> mSystemBatteryConsumers;
+ private final ArrayList<UserBatteryConsumer> mUserBatteryConsumers;
private BatteryUsageStats(@NonNull Builder builder) {
- mConsumedPower = builder.mConsumedPower;
+ mStatsStartRealtimeMs = builder.mStatsStartRealtimeMs;
mDischargePercentage = builder.mDischargePercentage;
- int uidBatteryConsumerCount = builder.mUidBatteryConsumerBuilders.size();
+ mDischargedPowerLowerBound = builder.mDischargedPowerLowerBoundMah;
+ mDischargedPowerUpperBound = builder.mDischargedPowerUpperBoundMah;
+
+ double totalPower = 0;
+
+ final int uidBatteryConsumerCount = builder.mUidBatteryConsumerBuilders.size();
mUidBatteryConsumers = new ArrayList<>(uidBatteryConsumerCount);
for (int i = 0; i < uidBatteryConsumerCount; i++) {
- mUidBatteryConsumers.add(builder.mUidBatteryConsumerBuilders.valueAt(i).build());
+ final UidBatteryConsumer.Builder uidBatteryConsumerBuilder =
+ builder.mUidBatteryConsumerBuilders.valueAt(i);
+ if (!uidBatteryConsumerBuilder.isExcludedFromBatteryUsageStats()) {
+ final UidBatteryConsumer consumer = uidBatteryConsumerBuilder.build();
+ totalPower += consumer.getConsumedPower();
+ mUidBatteryConsumers.add(consumer);
+ }
}
- int systemBatteryConsumerCount = builder.mSystemBatteryConsumerBuilders.size();
+
+ final int systemBatteryConsumerCount = builder.mSystemBatteryConsumerBuilders.size();
mSystemBatteryConsumers = new ArrayList<>(systemBatteryConsumerCount);
for (int i = 0; i < systemBatteryConsumerCount; i++) {
- mSystemBatteryConsumers.add(builder.mSystemBatteryConsumerBuilders.valueAt(i).build());
+ final SystemBatteryConsumer consumer =
+ builder.mSystemBatteryConsumerBuilders.valueAt(i).build();
+ totalPower += consumer.getConsumedPower();
+ mSystemBatteryConsumers.add(consumer);
+ }
+
+ final int userBatteryConsumerCount = builder.mUserBatteryConsumerBuilders.size();
+ mUserBatteryConsumers = new ArrayList<>(userBatteryConsumerCount);
+ for (int i = 0; i < userBatteryConsumerCount; i++) {
+ final UserBatteryConsumer consumer =
+ builder.mUserBatteryConsumerBuilders.valueAt(i).build();
+ totalPower += consumer.getConsumedPower();
+ mUserBatteryConsumers.add(consumer);
}
+
+ mConsumedPower = totalPower;
+ }
+
+ /**
+ * Timestamp of the latest battery stats reset, in milliseconds.
+ */
+ public long getStatsStartRealtime() {
+ return mStatsStartRealtimeMs;
}
/**
@@ -58,6 +95,14 @@ public final class BatteryUsageStats implements Parcelable {
}
/**
+ * Returns the discharged power since BatteryStats were last reset, in mAh as an estimated
+ * range.
+ */
+ public Range<Double> getDischargedPowerRange() {
+ return Range.create(mDischargedPowerLowerBound, mDischargedPowerUpperBound);
+ }
+
+ /**
* Total amount of battery charge drained since BatteryStats reset (e.g. due to being fully
* charged), in mAh
*/
@@ -75,26 +120,40 @@ public final class BatteryUsageStats implements Parcelable {
return mSystemBatteryConsumers;
}
+ @NonNull
+ public List<UserBatteryConsumer> getUserBatteryConsumers() {
+ return mUserBatteryConsumers;
+ }
+
@Override
public int describeContents() {
return 0;
}
private BatteryUsageStats(@NonNull Parcel source) {
+ mStatsStartRealtimeMs = source.readLong();
+ mConsumedPower = source.readDouble();
+ mDischargePercentage = source.readInt();
+ mDischargedPowerLowerBound = source.readDouble();
+ mDischargedPowerUpperBound = source.readDouble();
mUidBatteryConsumers = new ArrayList<>();
source.readParcelableList(mUidBatteryConsumers, getClass().getClassLoader());
mSystemBatteryConsumers = new ArrayList<>();
source.readParcelableList(mSystemBatteryConsumers, getClass().getClassLoader());
- mConsumedPower = source.readDouble();
- mDischargePercentage = source.readInt();
+ mUserBatteryConsumers = new ArrayList<>();
+ source.readParcelableList(mUserBatteryConsumers, getClass().getClassLoader());
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeParcelableList(mUidBatteryConsumers, flags);
- dest.writeParcelableList(mSystemBatteryConsumers, flags);
+ dest.writeLong(mStatsStartRealtimeMs);
dest.writeDouble(mConsumedPower);
dest.writeInt(mDischargePercentage);
+ dest.writeDouble(mDischargedPowerLowerBound);
+ dest.writeDouble(mDischargedPowerUpperBound);
+ dest.writeParcelableList(mUidBatteryConsumers, flags);
+ dest.writeParcelableList(mSystemBatteryConsumers, flags);
+ dest.writeParcelableList(mUserBatteryConsumers, flags);
}
@NonNull
@@ -114,12 +173,16 @@ public final class BatteryUsageStats implements Parcelable {
public static final class Builder {
private final int mCustomPowerComponentCount;
private final int mCustomTimeComponentCount;
- private double mConsumedPower;
+ private long mStatsStartRealtimeMs;
private int mDischargePercentage;
+ private double mDischargedPowerLowerBoundMah;
+ private double mDischargedPowerUpperBoundMah;
private final SparseArray<UidBatteryConsumer.Builder> mUidBatteryConsumerBuilders =
new SparseArray<>();
private final SparseArray<SystemBatteryConsumer.Builder> mSystemBatteryConsumerBuilders =
new SparseArray<>();
+ private final SparseArray<UserBatteryConsumer.Builder> mUserBatteryConsumerBuilders =
+ new SparseArray<>();
public Builder(int customPowerComponentCount, int customTimeComponentCount) {
mCustomPowerComponentCount = customPowerComponentCount;
@@ -135,11 +198,17 @@ public final class BatteryUsageStats implements Parcelable {
}
/**
+ * Sets the timestamp of the latest battery stats reset, in milliseconds.
+ */
+ public Builder setStatsStartRealtime(long statsStartRealtimeMs) {
+ mStatsStartRealtimeMs = statsStartRealtimeMs;
+ return this;
+ }
+
+ /**
* Sets the battery discharge amount since BatteryStats reset as percentage of the full
* charge.
- *
*/
- @SuppressLint("PercentageInt") // See b/174188159
@NonNull
public Builder setDischargePercentage(int dischargePercentage) {
mDischargePercentage = dischargePercentage;
@@ -147,11 +216,13 @@ public final class BatteryUsageStats implements Parcelable {
}
/**
- * Sets the battery discharge amount since BatteryStats reset, in mAh.
+ * Sets the estimated battery discharge range.
*/
@NonNull
- public Builder setConsumedPower(double consumedPower) {
- mConsumedPower = consumedPower;
+ public Builder setDischargedPowerRange(double dischargedPowerLowerBoundMah,
+ double dischargedPowerUpperBoundMah) {
+ mDischargedPowerLowerBoundMah = dischargedPowerLowerBoundMah;
+ mDischargedPowerUpperBoundMah = dischargedPowerUpperBoundMah;
return this;
}
@@ -173,8 +244,8 @@ public final class BatteryUsageStats implements Parcelable {
}
/**
- * Creates or returns a exiting UidBatteryConsumer, which represents battery attribution
- * data for an individual UID.
+ * Creates or returns a exiting SystemBatteryConsumer, which represents battery attribution
+ * data for a specific drain type.
*/
@NonNull
public SystemBatteryConsumer.Builder getOrCreateSystemBatteryConsumerBuilder(
@@ -188,6 +259,21 @@ public final class BatteryUsageStats implements Parcelable {
return builder;
}
+ /**
+ * Creates or returns a exiting UserBatteryConsumer, which represents battery attribution
+ * data for an individual {@link UserHandle}.
+ */
+ @NonNull
+ public UserBatteryConsumer.Builder getOrCreateUserBatteryConsumerBuilder(int userId) {
+ UserBatteryConsumer.Builder builder = mUserBatteryConsumerBuilders.get(userId);
+ if (builder == null) {
+ builder = new UserBatteryConsumer.Builder(mCustomPowerComponentCount,
+ mCustomTimeComponentCount, userId);
+ mUserBatteryConsumerBuilders.put(userId, builder);
+ }
+ return builder;
+ }
+
@NonNull
public SparseArray<UidBatteryConsumer.Builder> getUidBatteryConsumerBuilders() {
return mUidBatteryConsumerBuilders;
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index 48e7389e5ff2..17cb735eea49 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -18,6 +18,7 @@ package android.os;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.util.IntArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -53,9 +54,13 @@ public final class BatteryUsageStatsQuery implements Parcelable {
public static final int FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL = 1;
private final int mFlags;
+ @NonNull
+ private final int[] mUserIds;
private BatteryUsageStatsQuery(@NonNull Builder builder) {
mFlags = builder.mFlags;
+ mUserIds = builder.mUserIds != null ? builder.mUserIds.toArray()
+ : new int[]{UserHandle.USER_ALL};
}
@BatteryUsageStatsFlags
@@ -63,13 +68,36 @@ public final class BatteryUsageStatsQuery implements Parcelable {
return mFlags;
}
+ /**
+ * Returns an array of users for which the attribution is requested. It may
+ * contain {@link UserHandle#USER_ALL} to indicate that the attribution
+ * should be performed for all users. Battery consumed by users <b>not</b> included
+ * in this array will be returned in the aggregated form as {@link UserBatteryConsumer}'s.
+ */
+ @NonNull
+ public int[] getUserIds() {
+ return mUserIds;
+ }
+
+ /**
+ * Returns true if the power calculations must be based on the PowerProfile constants,
+ * even if measured energy data is available.
+ */
+ public boolean shouldForceUsePowerProfileModel() {
+ return (mFlags & FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL) != 0;
+ }
+
private BatteryUsageStatsQuery(Parcel in) {
mFlags = in.readInt();
+ mUserIds = new int[in.readInt()];
+ in.readIntArray(mUserIds);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mFlags);
+ dest.writeInt(mUserIds.length);
+ dest.writeIntArray(mUserIds);
}
@Override
@@ -96,6 +124,7 @@ public final class BatteryUsageStatsQuery implements Parcelable {
*/
public static final class Builder {
private int mFlags;
+ private IntArray mUserIds;
/**
* Builds a read-only BatteryUsageStatsQuery object.
@@ -105,6 +134,18 @@ public final class BatteryUsageStatsQuery implements Parcelable {
}
/**
+ * Add a user whose battery stats should be included in the battery usage stats.
+ * {@link UserHandle#USER_ALL} will be used by default if no users are added explicitly.
+ */
+ public Builder addUser(@NonNull UserHandle userHandle) {
+ if (mUserIds == null) {
+ mUserIds = new IntArray(1);
+ }
+ mUserIds.add(userHandle.getIdentifier());
+ return this;
+ }
+
+ /**
* Sets flags to modify the behavior of {@link BatteryStatsManager#getBatteryUsageStats}.
*/
public Builder setFlags(@BatteryUsageStatsFlags int flags) {
diff --git a/core/java/android/os/CombinedVibrationEffect.java b/core/java/android/os/CombinedVibrationEffect.java
index cb4e9cba0977..c8e682c86ea7 100644
--- a/core/java/android/os/CombinedVibrationEffect.java
+++ b/core/java/android/os/CombinedVibrationEffect.java
@@ -17,6 +17,7 @@
package android.os;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.util.SparseArray;
import com.android.internal.util.Preconditions;
@@ -86,7 +87,20 @@ public abstract class CombinedVibrationEffect implements Parcelable {
return 0;
}
- /** @hide */
+ /**
+ * Gets the estimated duration of the combined vibration in milliseconds.
+ *
+ * <p>For synced combinations this means the maximum duration of any individual {@link
+ * VibrationEffect}. For sequential combinations, this is a sum of each step and delays.
+ *
+ * <p>For combinations of effects without a defined end (e.g. a Waveform with a non-negative
+ * repeat index), this returns Long.MAX_VALUE. For effects with an unknown duration (e.g.
+ * Prebaked effects where the length is device and potentially run-time dependent), this returns
+ * -1.
+ *
+ * @hide
+ */
+ @TestApi
public abstract long getDuration();
/** @hide */
@@ -256,6 +270,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
*
* @hide
*/
+ @TestApi
public static final class Mono extends CombinedVibrationEffect {
private final VibrationEffect mEffect;
@@ -267,6 +282,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
mEffect = effect;
}
+ @NonNull
public VibrationEffect getEffect() {
return mEffect;
}
@@ -282,6 +298,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
mEffect.validate();
}
+ /** @hide */
@Override
public boolean hasVibrator(int vibratorId) {
return true;
@@ -307,7 +324,12 @@ public abstract class CombinedVibrationEffect implements Parcelable {
}
@Override
- public void writeToParcel(Parcel out, int flags) {
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(PARCEL_TOKEN_MONO);
mEffect.writeToParcel(out, flags);
}
@@ -335,6 +357,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
*
* @hide
*/
+ @TestApi
public static final class Stereo extends CombinedVibrationEffect {
/** Mapping vibrator ids to effects. */
@@ -357,6 +380,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
}
/** Effects to be performed in sync, where each key represents the vibrator id. */
+ @NonNull
public SparseArray<VibrationEffect> getEffects() {
return mEffects;
}
@@ -394,6 +418,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
}
}
+ /** @hide */
@Override
public boolean hasVibrator(int vibratorId) {
return mEffects.indexOfKey(vibratorId) >= 0;
@@ -418,7 +443,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(mEffects);
+ return mEffects.contentHashCode();
}
@Override
@@ -427,7 +452,12 @@ public abstract class CombinedVibrationEffect implements Parcelable {
}
@Override
- public void writeToParcel(Parcel out, int flags) {
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(PARCEL_TOKEN_STEREO);
out.writeInt(mEffects.size());
for (int i = 0; i < mEffects.size(); i++) {
@@ -459,6 +489,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
*
* @hide
*/
+ @TestApi
public static final class Sequential extends CombinedVibrationEffect {
private final List<CombinedVibrationEffect> mEffects;
private final List<Integer> mDelays;
@@ -480,11 +511,13 @@ public abstract class CombinedVibrationEffect implements Parcelable {
}
/** Effects to be performed in sequence. */
+ @NonNull
public List<CombinedVibrationEffect> getEffects() {
return mEffects;
}
/** Delay to be applied before each effect in {@link #getEffects()}. */
+ @NonNull
public List<Integer> getDelays() {
return mDelays;
}
@@ -542,6 +575,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
}
}
+ /** @hide */
@Override
public boolean hasVibrator(int vibratorId) {
final int effectCount = mEffects.size();
@@ -564,7 +598,7 @@ public abstract class CombinedVibrationEffect implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(mEffects);
+ return Objects.hash(mEffects, mDelays);
}
@Override
@@ -573,7 +607,12 @@ public abstract class CombinedVibrationEffect implements Parcelable {
}
@Override
- public void writeToParcel(Parcel out, int flags) {
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(PARCEL_TOKEN_SEQUENTIAL);
out.writeInt(mEffects.size());
for (int i = 0; i < mEffects.size(); i++) {
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 9584bc74d6f2..e3b13f4f9f17 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1902,7 +1902,8 @@ public final class Debug
* Retrieves the PSS memory used by the process as given by the smaps. Optionally supply a long
* array of up to 3 entries to also receive (up to 3 values in order): the Uss and SwapPss and
* Rss (only filled in as of {@link android.os.Build.VERSION_CODES#P}) of the process, and
- * another array to also retrieve the separate memtrack size.
+ * another array to also retrieve the separate memtrack sizes (up to 4 values in order): the
+ * total memtrack reported size, memtrack graphics, memtrack gl and memtrack other.
*
* @return The PSS memory usage, or 0 if failed to retrieve (i.e., given pid has gone).
* @hide
@@ -2557,6 +2558,22 @@ public final class Debug
public static native long getZramFreeKb();
/**
+ * Return total memory size in kilobytes for exported DMA-BUFs or -1 if
+ * the DMA-BUF sysfs stats at /sys/kernel/dmabuf/buffers could not be read.
+ *
+ * @hide
+ */
+ public static native long getDmabufTotalExportedKb();
+
+ /**
+ * Return total memory size in kilobytes for DMA-BUFs exported from the DMA-BUF
+ * heaps frameworks or -1 in the case of an error.
+ *
+ * @hide
+ */
+ public static native long getDmabufHeapTotalExportedKb();
+
+ /**
* Return memory size in kilobytes allocated for ION heaps or -1 if
* /sys/kernel/ion/total_heaps_kb could not be read.
*
@@ -2565,6 +2582,14 @@ public final class Debug
public static native long getIonHeapsSizeKb();
/**
+ * Return memory size in kilobytes allocated for DMA-BUF heap pools or -1 if
+ * /sys/kernel/dma_heap/total_pools_kb could not be read.
+ *
+ * @hide
+ */
+ public static native long getDmabufHeapPoolsSizeKb();
+
+ /**
* Return memory size in kilobytes allocated for ION pools or -1 if
* /sys/kernel/ion/total_pools_kb could not be read.
*
@@ -2573,13 +2598,20 @@ public final class Debug
public static native long getIonPoolsSizeKb();
/**
- * Return ION memory mapped by processes in kB.
+ * Return GPU DMA buffer usage in kB or -1 on error.
+ *
+ * @hide
+ */
+ public static native long getGpuDmaBufUsageKb();
+
+ /**
+ * Return DMA-BUF memory mapped by processes in kB.
* Notes:
* * Warning: Might impact performance as it reads /proc/<pid>/maps files for each process.
*
* @hide
*/
- public static native long getIonMappedSizeKb();
+ public static native long getDmabufMappedSizeKb();
/**
* Return memory size in kilobytes used by GPU.
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 518e29d51091..124c0b00b2b2 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -18,6 +18,7 @@ package android.os;
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.AppGlobals;
@@ -836,6 +837,21 @@ public class Environment {
public static String DIRECTORY_AUDIOBOOKS = "Audiobooks";
/**
+ * Standard directory in which to place any audio files which are
+ * recordings.
+ */
+ @NonNull
+ // The better way is that expose a static method getRecordingDirectories.
+ // But since it's an existing API surface and developers already
+ // used to DIRECTORY_* constants, we should keep using this pattern
+ // for consistency. We use SuppressLint here to avoid exposing a final
+ // field. A final field will prevent us from ever changing the value of
+ // DIRECTORY_RECORDINGS. Not that it's likely that we will ever need to
+ // change it, but it's better to have such option.
+ @SuppressLint({"MutableBareField", "AllUpper"})
+ public static String DIRECTORY_RECORDINGS = "Recordings";
+
+ /**
* List of standard storage directories.
* <p>
* Each of its values have its own constant:
@@ -851,6 +867,7 @@ public class Environment {
* <li>{@link #DIRECTORY_DCIM}
* <li>{@link #DIRECTORY_DOCUMENTS}
* <li>{@link #DIRECTORY_AUDIOBOOKS}
+ * <li>{@link #DIRECTORY_RECORDINGS}
* </ul>
* @hide
*/
@@ -866,6 +883,7 @@ public class Environment {
DIRECTORY_DCIM,
DIRECTORY_DOCUMENTS,
DIRECTORY_AUDIOBOOKS,
+ DIRECTORY_RECORDINGS,
};
/**
@@ -891,6 +909,7 @@ public class Environment {
/** {@hide} */ public static final int HAS_DCIM = 1 << 8;
/** {@hide} */ public static final int HAS_DOCUMENTS = 1 << 9;
/** {@hide} */ public static final int HAS_AUDIOBOOKS = 1 << 10;
+ /** {@hide} */ public static final int HAS_RECORDINGS = 1 << 11;
/** {@hide} */ public static final int HAS_ANDROID = 1 << 16;
/** {@hide} */ public static final int HAS_OTHER = 1 << 17;
@@ -921,6 +940,7 @@ public class Environment {
else if (DIRECTORY_DCIM.equals(name)) res |= HAS_DCIM;
else if (DIRECTORY_DOCUMENTS.equals(name)) res |= HAS_DOCUMENTS;
else if (DIRECTORY_AUDIOBOOKS.equals(name)) res |= HAS_AUDIOBOOKS;
+ else if (DIRECTORY_RECORDINGS.equals(name)) res |= HAS_RECORDINGS;
else if (DIRECTORY_ANDROID.equals(name)) res |= HAS_ANDROID;
else res |= HAS_OTHER;
}
@@ -1339,8 +1359,17 @@ public class Environment {
}
final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
- return appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE,
- uid, context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED;
+ final String opPackageName = context.getOpPackageName();
+
+ if (appOps.noteOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, uid,
+ opPackageName) == AppOpsManager.MODE_ALLOWED) {
+ return true;
+ }
+
+ // Legacy external storage access is granted to instrumentations invoked with
+ // "--no-isolated-storage" flag.
+ return appOps.noteOpNoThrow(AppOpsManager.OP_NO_ISOLATED_STORAGE, uid,
+ opPackageName) == AppOpsManager.MODE_ALLOWED;
}
private static boolean isScopedStorageEnforced(boolean defaultScopedStorage,
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 33deddae01b6..874add5cdbd8 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -24,7 +24,6 @@ import android.net.Network;
import android.net.NetworkStats;
import android.net.RouteInfo;
import android.net.UidRange;
-import android.os.INetworkActivityListener;
/**
* @hide
@@ -294,25 +293,6 @@ interface INetworkManagementService
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
boolean isBandwidthControlEnabled();
- /**
- * Sets idletimer for an interface.
- *
- * This either initializes a new idletimer or increases its
- * reference-counting if an idletimer already exists for given
- * {@code iface}.
- *
- * {@code type} is the type of the interface, such as TYPE_MOBILE.
- *
- * Every {@code addIdleTimer} should be paired with a
- * {@link removeIdleTimer} to cleanup when the network disconnects.
- */
- void addIdleTimer(String iface, int timeout, int type);
-
- /**
- * Removes idletimer for an interface.
- */
- void removeIdleTimer(String iface);
-
void setFirewallEnabled(boolean enabled);
boolean isFirewallEnabled();
void setFirewallInterfaceRule(String iface, boolean allow);
@@ -320,21 +300,6 @@ interface INetworkManagementService
void setFirewallUidRules(int chain, in int[] uids, in int[] rules);
void setFirewallChainEnabled(int chain, boolean enable);
- /**
- * Start listening for mobile activity state changes.
- */
- void registerNetworkActivityListener(INetworkActivityListener listener);
-
- /**
- * Stop listening for mobile activity state changes.
- */
- void unregisterNetworkActivityListener(INetworkActivityListener listener);
-
- /**
- * Check whether the mobile radio is currently active.
- */
- boolean isNetworkActive();
-
void addLegacyRouteForNetId(int netId, in RouteInfo routeInfo, int uid);
/**
diff --git a/core/java/android/os/ISystemConfig.aidl b/core/java/android/os/ISystemConfig.aidl
index 52f0ce1f054f..4d160da22ff8 100644
--- a/core/java/android/os/ISystemConfig.aidl
+++ b/core/java/android/os/ISystemConfig.aidl
@@ -35,4 +35,9 @@ interface ISystemConfig {
* @see SystemConfigManager#getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries
*/
Map getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries();
+
+ /**
+ * @see SystemConfigManager#getSystemPermissionUids
+ */
+ int[] getSystemPermissionUids(String permissionName);
}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index b39c182fcab6..7437e037fa3e 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -113,6 +113,7 @@ interface IUserManager {
boolean hasBadge(int userId);
boolean isUserUnlocked(int userId);
boolean isUserRunning(int userId);
+ boolean isUserForeground();
boolean isUserNameSet(int userId);
boolean hasRestrictedProfiles();
boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target, int flags);
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
deleted file mode 100644
index 1cd48dcf797b..000000000000
--- a/core/java/android/os/IVibratorService.aidl
+++ /dev/null
@@ -1,37 +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.os;
-
-import android.os.VibrationEffect;
-import android.os.VibrationAttributes;
-import android.os.VibratorInfo;
-import android.os.IVibratorStateListener;
-
-/** {@hide} */
-interface IVibratorService
-{
- boolean hasVibrator();
- boolean isVibrating();
- VibratorInfo getVibratorInfo();
- boolean registerVibratorStateListener(in IVibratorStateListener listener);
- boolean unregisterVibratorStateListener(in IVibratorStateListener listener);
- boolean hasAmplitudeControl();
- void vibrate(int uid, String opPkg, in VibrationEffect effect,
- in VibrationAttributes attributes, String reason, IBinder token);
- void cancelVibrate(IBinder token);
-}
-
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 2559a33c1ab2..a04047df4af1 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -1,15 +1,18 @@
# Haptics
+per-file CombinedVibrationEffect.aidl = michaelwr@google.com
+per-file CombinedVibrationEffect.java = michaelwr@google.com
per-file ExternalVibration.aidl = michaelwr@google.com
per-file ExternalVibration.java = michaelwr@google.com
per-file IExternalVibrationController.aidl = michaelwr@google.com
per-file IExternalVibratorService.aidl = michaelwr@google.com
per-file IVibratorManagerService.aidl = michaelwr@google.com
-per-file IVibratorService.aidl = michaelwr@google.com
per-file NullVibrator.java = michaelwr@google.com
per-file SystemVibrator.java = michaelwr@google.com
+per-file SystemVibratorManager.java = michaelwr@google.com
per-file VibrationEffect.aidl = michaelwr@google.com
per-file VibrationEffect.java = michaelwr@google.com
per-file Vibrator.java = michaelwr@google.com
+per-file VibratorManager.java = michaelwr@google.com
# PowerManager
per-file IPowerManager.aidl = michaelwr@google.com, santoscordon@google.com
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index 1337d558e439..d239b23b6ff6 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -29,42 +29,38 @@ class PowerComponents {
public static final int CUSTOM_TIME_COMPONENT_OFFSET = BatteryConsumer.TIME_COMPONENT_COUNT
- BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID;
- private final double mTotalPowerConsumed;
- private final double[] mPowerComponents;
- private final long[] mTimeComponents;
+ private final double mTotalConsumedPowerMah;
+ private final double[] mPowerComponentsMah;
+ private final long[] mTimeComponentsMs;
private final int mCustomPowerComponentCount;
PowerComponents(@NonNull Builder builder) {
mCustomPowerComponentCount = builder.mCustomPowerComponentCount;
- mPowerComponents = builder.mPowerComponents;
- mTimeComponents = builder.mTimeComponents;
- double totalPower = 0;
- for (int i = mPowerComponents.length - 1; i >= 0; i--) {
- totalPower += mPowerComponents[i];
- }
- mTotalPowerConsumed = totalPower;
+ mPowerComponentsMah = builder.mPowerComponentsMah;
+ mTimeComponentsMs = builder.mTimeComponentsMs;
+ mTotalConsumedPowerMah = builder.getTotalPower();
}
PowerComponents(@NonNull Parcel source) {
- mTotalPowerConsumed = source.readDouble();
+ mTotalConsumedPowerMah = source.readDouble();
mCustomPowerComponentCount = source.readInt();
- mPowerComponents = source.createDoubleArray();
- mTimeComponents = source.createLongArray();
+ mPowerComponentsMah = source.createDoubleArray();
+ mTimeComponentsMs = source.createLongArray();
}
/** Writes contents to Parcel */
void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeDouble(mTotalPowerConsumed);
+ dest.writeDouble(mTotalConsumedPowerMah);
dest.writeInt(mCustomPowerComponentCount);
- dest.writeDoubleArray(mPowerComponents);
- dest.writeLongArray(mTimeComponents);
+ dest.writeDoubleArray(mPowerComponentsMah);
+ dest.writeLongArray(mTimeComponentsMs);
}
/**
* Total power consumed by this consumer, in mAh.
*/
- public double getTotalPowerConsumed() {
- return mTotalPowerConsumed;
+ public double getTotalConsumedPower() {
+ return mTotalConsumedPowerMah;
}
/**
@@ -80,7 +76,7 @@ class PowerComponents {
"Unsupported power component ID: " + componentId);
}
try {
- return mPowerComponents[componentId];
+ return mPowerComponentsMah[componentId];
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException("Unsupported power component ID: " + componentId);
}
@@ -96,7 +92,7 @@ class PowerComponents {
if (componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
&& componentId < BatteryConsumer.LAST_CUSTOM_POWER_COMPONENT_ID) {
try {
- return mPowerComponents[CUSTOM_POWER_COMPONENT_OFFSET + componentId];
+ return mPowerComponentsMah[CUSTOM_POWER_COMPONENT_OFFSET + componentId];
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException(
"Unsupported custom power component ID: " + componentId);
@@ -120,7 +116,7 @@ class PowerComponents {
"Unsupported time component ID: " + componentId);
}
try {
- return mTimeComponents[componentId];
+ return mTimeComponentsMs[componentId];
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException("Unsupported power component ID: " + componentId);
}
@@ -138,7 +134,7 @@ class PowerComponents {
"Unsupported custom time component ID: " + componentId);
}
try {
- return mTimeComponents[CUSTOM_TIME_COMPONENT_OFFSET + componentId];
+ return mTimeComponentsMs[CUSTOM_TIME_COMPONENT_OFFSET + componentId];
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException(
"Unsupported custom time component ID: " + componentId);
@@ -149,16 +145,16 @@ class PowerComponents {
* Builder for PowerComponents.
*/
static final class Builder {
- private final double[] mPowerComponents;
+ private final double[] mPowerComponentsMah;
private final int mCustomPowerComponentCount;
- private final long[] mTimeComponents;
+ private final long[] mTimeComponentsMs;
Builder(int customPowerComponentCount, int customTimeComponentCount) {
mCustomPowerComponentCount = customPowerComponentCount;
int powerComponentCount =
BatteryConsumer.POWER_COMPONENT_COUNT + customPowerComponentCount;
- mPowerComponents = new double[powerComponentCount];
- mTimeComponents =
+ mPowerComponentsMah = new double[powerComponentCount];
+ mTimeComponentsMs =
new long[BatteryConsumer.TIME_COMPONENT_COUNT + customTimeComponentCount];
}
@@ -177,7 +173,7 @@ class PowerComponents {
"Unsupported power component ID: " + componentId);
}
try {
- mPowerComponents[componentId] = componentPower;
+ mPowerComponentsMah[componentId] = componentPower;
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException(
"Unsupported power component ID: " + componentId);
@@ -196,7 +192,8 @@ class PowerComponents {
if (componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
&& componentId < BatteryConsumer.LAST_CUSTOM_POWER_COMPONENT_ID) {
try {
- mPowerComponents[CUSTOM_POWER_COMPONENT_OFFSET + componentId] = componentPower;
+ mPowerComponentsMah[CUSTOM_POWER_COMPONENT_OFFSET + componentId] =
+ componentPower;
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException(
"Unsupported custom power component ID: " + componentId);
@@ -223,7 +220,7 @@ class PowerComponents {
"Unsupported time component ID: " + componentId);
}
try {
- mTimeComponents[componentId] = componentUsageDurationMillis;
+ mTimeComponentsMs[componentId] = componentUsageDurationMillis;
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException(
"Unsupported time component ID: " + componentId);
@@ -245,7 +242,7 @@ class PowerComponents {
"Unsupported custom time component ID: " + componentId);
}
try {
- mTimeComponents[CUSTOM_TIME_COMPONENT_OFFSET + componentId] =
+ mTimeComponentsMs[CUSTOM_TIME_COMPONENT_OFFSET + componentId] =
componentUsageDurationMillis;
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException(
@@ -255,12 +252,24 @@ class PowerComponents {
}
public void addPowerAndDuration(Builder other) {
- for (int i = 0; i < mPowerComponents.length; i++) {
- mPowerComponents[i] += other.mPowerComponents[i];
+ for (int i = 0; i < mPowerComponentsMah.length; i++) {
+ mPowerComponentsMah[i] += other.mPowerComponentsMah[i];
+ }
+ for (int i = 0; i < mTimeComponentsMs.length; i++) {
+ mTimeComponentsMs[i] += other.mTimeComponentsMs[i];
}
- for (int i = 0; i < mTimeComponents.length; i++) {
- mTimeComponents[i] += other.mTimeComponents[i];
+ }
+
+ /**
+ * Returns the total power accumulated by this builder so far. It may change
+ * by the time the {@code build()} method is called.
+ */
+ public double getTotalPower() {
+ double totalPowerMah = 0;
+ for (int i = mPowerComponentsMah.length - 1; i >= 0; i--) {
+ totalPowerMah += mPowerComponentsMah[i];
}
+ return totalPowerMah;
}
/**
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 059e932a9da9..ff9b4f41ef3b 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1629,6 +1629,12 @@ public final class PowerManager {
* <p>
* Requires the {@link android.Manifest.permission#REBOOT} permission.
* </p>
+ * <p>
+ * If the {@code reason} string contains ",quiescent", then the screen stays off during reboot
+ * and is not turned on again until the user triggers the device to wake up (for example,
+ * by pressing the power key).
+ * This behavior applies to Android TV devices launched on Android 11 (API level 30) or higher.
+ * </p>
*
* @param reason code to pass to the kernel (e.g., "recovery") to
* request special boot modes, or null.
@@ -1898,7 +1904,8 @@ public final class PowerManager {
* These estimates will be displayed on system UI surfaces in place of the system computed
* value.
*
- * Calling this requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+ * Calling this requires either the {@link android.Manifest.permission#DEVICE_POWER} or the
+ * {@link android.Manifest.permission#BATTERY_PREDICTION} permissions.
*
* @param timeRemaining The time remaining as a {@link Duration}.
* @param isPersonalized true if personalized based on device usage history, false otherwise.
@@ -1906,7 +1913,10 @@ public final class PowerManager {
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.BATTERY_PREDICTION,
+ android.Manifest.permission.DEVICE_POWER
+ })
public void setBatteryDischargePrediction(@NonNull Duration timeRemaining,
boolean isPersonalized) {
if (timeRemaining == null) {
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index fde43af9a091..378813053a7d 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -995,6 +995,16 @@ public class Process {
throws IllegalArgumentException, SecurityException;
/**
+ *
+ * Create a new process group in the cgroup uid/pid hierarchy
+ *
+ * @return <0 in case of error
+ *
+ * @hide
+ */
+ public static final native int createProcessGroup(int uid, int pid);
+
+ /**
* On some devices, the foreground process may have one or more CPU
* cores exclusively reserved for it. This method can be used to
* retrieve which cores that are (if any), so the calling process
@@ -1467,7 +1477,7 @@ public class Process {
*
* @hide
*/
- public static boolean hasFileLocks(int pid) throws IOException {
+ public static boolean hasFileLocks(int pid) throws Exception {
BufferedReader br = null;
try {
@@ -1479,8 +1489,13 @@ public class Process {
for (int i = 0; i < 5 && st.hasMoreTokens(); i++) {
String str = st.nextToken();
- if (i == 4 && Integer.parseInt(str) == pid) {
- return true;
+ try {
+ if (i == 4 && Integer.parseInt(str) == pid) {
+ return true;
+ }
+ } catch (NumberFormatException nfe) {
+ throw new Exception("Exception parsing /proc/locks at \" "
+ + line + " \", token #" + i);
}
}
}
diff --git a/core/java/android/os/SystemBatteryConsumer.java b/core/java/android/os/SystemBatteryConsumer.java
index fc4aa93b97c7..06cff904a215 100644
--- a/core/java/android/os/SystemBatteryConsumer.java
+++ b/core/java/android/os/SystemBatteryConsumer.java
@@ -45,12 +45,13 @@ public class SystemBatteryConsumer extends BatteryConsumer implements Parcelable
DRAIN_TYPE_FLASHLIGHT,
DRAIN_TYPE_IDLE,
DRAIN_TYPE_MEMORY,
- DRAIN_TYPE_OVERCOUNTED,
+ // Reserved: OVERCOUNTED,
DRAIN_TYPE_PHONE,
DRAIN_TYPE_SCREEN,
- DRAIN_TYPE_UNACCOUNTED,
+ // Reserved: UNACCOUNTED,
// Reserved: USER,
DRAIN_TYPE_WIFI,
+ DRAIN_TYPE_CUSTOM,
})
@Retention(RetentionPolicy.SOURCE)
public static @interface DrainType {
@@ -63,11 +64,10 @@ public class SystemBatteryConsumer extends BatteryConsumer implements Parcelable
public static final int DRAIN_TYPE_FLASHLIGHT = 5;
public static final int DRAIN_TYPE_IDLE = 6;
public static final int DRAIN_TYPE_MEMORY = 7;
- public static final int DRAIN_TYPE_OVERCOUNTED = 8;
public static final int DRAIN_TYPE_PHONE = 9;
public static final int DRAIN_TYPE_SCREEN = 10;
- public static final int DRAIN_TYPE_UNACCOUNTED = 11;
public static final int DRAIN_TYPE_WIFI = 13;
+ public static final int DRAIN_TYPE_CUSTOM = 14;
@DrainType
private final int mDrainType;
diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java
index 3f0632be90d1..9bfa8adc8571 100644
--- a/core/java/android/os/SystemConfigManager.java
+++ b/core/java/android/os/SystemConfigManager.java
@@ -111,4 +111,22 @@ public class SystemConfigManager {
return Collections.emptyMap();
}
}
+
+ /**
+ * Get uids which have been granted given permission in system configuration.
+ *
+ * The uids and assigning permissions are defined on data/etc/platform.xml
+ *
+ * @param permissionName The target permission.
+ * @return The uids have been granted given permission in system configuration.
+ */
+ @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
+ @NonNull
+ public int[] getSystemPermissionUids(@NonNull String permissionName) {
+ try {
+ return mInterface.getSystemPermissionUids(permissionName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 30afe38be397..b42a495ece56 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -17,19 +17,18 @@
package android.os;
import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.media.AudioAttributes;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -41,234 +40,178 @@ import java.util.concurrent.Executor;
public class SystemVibrator extends Vibrator {
private static final String TAG = "Vibrator";
- private static final int VIBRATOR_PRESENT_UNKNOWN = 0;
- private static final int VIBRATOR_PRESENT_YES = 1;
- private static final int VIBRATOR_PRESENT_NO = 2;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({VIBRATOR_PRESENT_UNKNOWN, VIBRATOR_PRESENT_YES, VIBRATOR_PRESENT_NO})
- private @interface VibratorPresent {}
-
- private final IVibratorService mService;
- private final IVibratorManagerService mManagerService;
- private final Object mLock = new Object();
- private final Binder mToken = new Binder();
+ private final VibratorManager mVibratorManager;
private final Context mContext;
- @GuardedBy("mLock")
- private VibratorInfo mVibratorInfo;
- @GuardedBy("mLock")
- @VibratorPresent
- private int mVibratorPresent;
- @GuardedBy("mDelegates")
- private final ArrayMap<OnVibratorStateChangedListener,
- OnVibratorStateChangedListenerDelegate> mDelegates = new ArrayMap<>();
+ @GuardedBy("mBrokenListeners")
+ private final ArrayList<AllVibratorsStateListener> mBrokenListeners = new ArrayList<>();
- @UnsupportedAppUsage
- public SystemVibrator() {
- mContext = null;
- mService = IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));
- mManagerService = IVibratorManagerService.Stub.asInterface(
- ServiceManager.getService("vibrator_manager"));
- }
+ @GuardedBy("mRegisteredListeners")
+ private final ArrayMap<OnVibratorStateChangedListener, AllVibratorsStateListener>
+ mRegisteredListeners = new ArrayMap<>();
@UnsupportedAppUsage
public SystemVibrator(Context context) {
super(context);
mContext = context;
- mService = IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));
- mManagerService = IVibratorManagerService.Stub.asInterface(
- ServiceManager.getService("vibrator_manager"));
+ mVibratorManager = mContext.getSystemService(VibratorManager.class);
}
@Override
public boolean hasVibrator() {
- try {
- synchronized (mLock) {
- if (mVibratorPresent == VIBRATOR_PRESENT_UNKNOWN && mService != null) {
- mVibratorPresent =
- mService.hasVibrator() ? VIBRATOR_PRESENT_YES : VIBRATOR_PRESENT_NO;
- }
- return mVibratorPresent == VIBRATOR_PRESENT_YES;
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to query vibrator presence", e);
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to check if vibrator exists; no vibrator manager.");
return false;
}
+ return mVibratorManager.getVibratorIds().length > 0;
}
- /**
- * Check whether the vibrator is vibrating.
- *
- * @return True if the hardware is vibrating, otherwise false.
- */
@Override
public boolean isVibrating() {
- if (mService == null) {
- Log.w(TAG, "Failed to vibrate; no vibrator service.");
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to vibrate; no vibrator manager.");
return false;
}
- try {
- return mService.isVibrating();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ for (int vibratorId : mVibratorManager.getVibratorIds()) {
+ if (mVibratorManager.getVibrator(vibratorId).isVibrating()) {
+ return true;
+ }
}
return false;
}
- private class OnVibratorStateChangedListenerDelegate extends
- IVibratorStateListener.Stub {
- private final Executor mExecutor;
- private final OnVibratorStateChangedListener mListener;
-
- OnVibratorStateChangedListenerDelegate(@NonNull OnVibratorStateChangedListener listener,
- @NonNull Executor executor) {
- mExecutor = executor;
- mListener = listener;
- }
-
- @Override
- public void onVibrating(boolean isVibrating) {
- mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating));
+ @Override
+ public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+ Objects.requireNonNull(listener);
+ if (mContext == null) {
+ Log.w(TAG, "Failed to add vibrate state listener; no vibrator context.");
+ return;
}
+ addVibratorStateListener(mContext.getMainExecutor(), listener);
}
- /**
- * Adds a listener for vibrator state change. If the listener was previously added and not
- * removed, this call will be ignored.
- *
- * @param listener Listener to be added.
- * @param executor The {@link Executor} on which the listener's callbacks will be executed on.
- */
@Override
public void addVibratorStateListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull OnVibratorStateChangedListener listener) {
Objects.requireNonNull(listener);
Objects.requireNonNull(executor);
- if (mService == null) {
- Log.w(TAG, "Failed to add vibrate state listener; no vibrator service.");
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to add vibrate state listener; no vibrator manager.");
return;
}
-
- synchronized (mDelegates) {
- // If listener is already registered, reject and return.
- if (mDelegates.containsKey(listener)) {
- Log.w(TAG, "Listener already registered.");
- return;
- }
- try {
- final OnVibratorStateChangedListenerDelegate delegate =
- new OnVibratorStateChangedListenerDelegate(listener, executor);
- if (!mService.registerVibratorStateListener(delegate)) {
- Log.w(TAG, "Failed to register vibrate state listener");
+ AllVibratorsStateListener delegate = null;
+ try {
+ synchronized (mRegisteredListeners) {
+ // If listener is already registered, reject and return.
+ if (mRegisteredListeners.containsKey(listener)) {
+ Log.w(TAG, "Listener already registered.");
return;
}
- mDelegates.put(listener, delegate);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ delegate = new AllVibratorsStateListener(executor, listener);
+ delegate.register(mVibratorManager);
+ mRegisteredListeners.put(listener, delegate);
+ delegate = null;
}
+ } finally {
+ if (delegate != null && delegate.hasRegisteredListeners()) {
+ // The delegate listener was left in a partial state with listeners registered to
+ // some but not all vibrators. Keep track of this to try to unregister them later.
+ synchronized (mBrokenListeners) {
+ mBrokenListeners.add(delegate);
+ }
+ }
+ tryUnregisterBrokenListeners();
}
}
- /**
- * Adds a listener for vibrator state changes. Callbacks will be executed on the main thread.
- * If the listener was previously added and not removed, this call will be ignored.
- *
- * @param listener listener to be added
- */
- @Override
- public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
- Objects.requireNonNull(listener);
- if (mContext == null) {
- Log.w(TAG, "Failed to add vibrate state listener; no vibrator context.");
- return;
- }
- addVibratorStateListener(mContext.getMainExecutor(), listener);
- }
-
- /**
- * Removes the listener for vibrator state changes. If the listener was not previously
- * registered, this call will do nothing.
- *
- * @param listener Listener to be removed.
- */
@Override
public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
Objects.requireNonNull(listener);
- if (mService == null) {
- Log.w(TAG, "Failed to remove vibrate state listener; no vibrator service.");
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to remove vibrate state listener; no vibrator manager.");
return;
}
- synchronized (mDelegates) {
- // Check if the listener is registered, otherwise will return.
- if (mDelegates.containsKey(listener)) {
- final OnVibratorStateChangedListenerDelegate delegate = mDelegates.get(listener);
- try {
- if (!mService.unregisterVibratorStateListener(delegate)) {
- Log.w(TAG, "Failed to unregister vibrate state listener");
- return;
- }
- mDelegates.remove(listener);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ synchronized (mRegisteredListeners) {
+ if (mRegisteredListeners.containsKey(listener)) {
+ AllVibratorsStateListener delegate = mRegisteredListeners.get(listener);
+ delegate.unregister(mVibratorManager);
+ mRegisteredListeners.remove(listener);
}
}
+ tryUnregisterBrokenListeners();
}
@Override
public boolean hasAmplitudeControl() {
- if (mService == null) {
- Log.w(TAG, "Failed to check amplitude control; no vibrator service.");
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to check vibrator has amplitude control; no vibrator manager.");
return false;
}
- try {
- return mService.hasAmplitudeControl();
- } catch (RemoteException e) {
+ int[] vibratorIds = mVibratorManager.getVibratorIds();
+ if (vibratorIds.length == 0) {
+ return false;
}
- return false;
+ for (int vibratorId : vibratorIds) {
+ if (!mVibratorManager.getVibrator(vibratorId).hasAmplitudeControl()) {
+ return false;
+ }
+ }
+ return true;
}
@Override
public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, VibrationEffect effect,
AudioAttributes attributes) {
- if (mManagerService == null) {
- Log.w(TAG, "Failed to set always-on effect; no vibrator service.");
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to set always-on effect; no vibrator manager.");
return false;
}
- try {
- VibrationAttributes atr = new VibrationAttributes.Builder(attributes, effect).build();
- CombinedVibrationEffect combinedEffect = CombinedVibrationEffect.createSynced(effect);
- return mManagerService.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combinedEffect, atr);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to set always-on effect.", e);
- }
- return false;
+ VibrationAttributes attr = new VibrationAttributes.Builder(attributes, effect).build();
+ CombinedVibrationEffect combinedEffect = CombinedVibrationEffect.createSynced(effect);
+ return mVibratorManager.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combinedEffect, attr);
}
@Override
public void vibrate(int uid, String opPkg, @NonNull VibrationEffect effect,
String reason, @NonNull VibrationAttributes attributes) {
- if (mService == null) {
- Log.w(TAG, "Failed to vibrate; no vibrator service.");
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to vibrate; no vibrator manager.");
return;
}
- try {
- mService.vibrate(uid, opPkg, effect, attributes, reason, mToken);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to vibrate.", e);
- }
+ CombinedVibrationEffect combinedEffect = CombinedVibrationEffect.createSynced(effect);
+ mVibratorManager.vibrate(uid, opPkg, combinedEffect, reason, attributes);
}
@Override
public int[] areEffectsSupported(@VibrationEffect.EffectType int... effectIds) {
- VibratorInfo vibratorInfo = getVibratorInfo();
int[] supported = new int[effectIds.length];
- for (int i = 0; i < effectIds.length; i++) {
- supported[i] = vibratorInfo == null
- ? Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN
- : vibratorInfo.isEffectSupported(effectIds[i]);
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to check supported effects; no vibrator manager.");
+ Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_NO);
+ return supported;
+ }
+ int[] vibratorIds = mVibratorManager.getVibratorIds();
+ if (vibratorIds.length == 0) {
+ Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_NO);
+ return supported;
+ }
+ int[][] vibratorSupportMap = new int[vibratorIds.length][effectIds.length];
+ for (int i = 0; i < vibratorIds.length; i++) {
+ vibratorSupportMap[i] = mVibratorManager.getVibrator(
+ vibratorIds[i]).areEffectsSupported(effectIds);
+ }
+ Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_YES);
+ for (int effectIdx = 0; effectIdx < effectIds.length; effectIdx++) {
+ for (int vibratorIdx = 0; vibratorIdx < vibratorIds.length; vibratorIdx++) {
+ int effectSupported = vibratorSupportMap[vibratorIdx][effectIdx];
+ if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_NO) {
+ supported[effectIdx] = Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
+ break;
+ } else if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN) {
+ supported[effectIdx] = Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN;
+ }
+ }
}
return supported;
}
@@ -276,42 +219,169 @@ public class SystemVibrator extends Vibrator {
@Override
public boolean[] arePrimitivesSupported(
@NonNull @VibrationEffect.Composition.Primitive int... primitiveIds) {
- VibratorInfo vibratorInfo = getVibratorInfo();
boolean[] supported = new boolean[primitiveIds.length];
- for (int i = 0; i < primitiveIds.length; i++) {
- supported[i] = vibratorInfo == null
- ? false : vibratorInfo.isPrimitiveSupported(primitiveIds[i]);
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to check supported primitives; no vibrator manager.");
+ Arrays.fill(supported, false);
+ return supported;
+ }
+ int[] vibratorIds = mVibratorManager.getVibratorIds();
+ if (vibratorIds.length == 0) {
+ Arrays.fill(supported, false);
+ return supported;
+ }
+ boolean[][] vibratorSupportMap = new boolean[vibratorIds.length][primitiveIds.length];
+ for (int i = 0; i < vibratorIds.length; i++) {
+ vibratorSupportMap[i] = mVibratorManager.getVibrator(
+ vibratorIds[i]).arePrimitivesSupported(primitiveIds);
+ }
+ Arrays.fill(supported, true);
+ for (int primitiveIdx = 0; primitiveIdx < primitiveIds.length; primitiveIdx++) {
+ for (int vibratorIdx = 0; vibratorIdx < vibratorIds.length; vibratorIdx++) {
+ if (!vibratorSupportMap[vibratorIdx][primitiveIdx]) {
+ supported[primitiveIdx] = false;
+ break;
+ }
+ }
}
return supported;
}
@Override
public void cancel() {
- if (mService == null) {
+ if (mVibratorManager == null) {
+ Log.w(TAG, "Failed to cancel vibrate; no vibrator manager.");
return;
}
- try {
- mService.cancelVibrate(mToken);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to cancel vibration.", e);
+ mVibratorManager.cancel();
+ }
+
+ /**
+ * Tries to unregister individual {@link android.os.Vibrator.OnVibratorStateChangedListener}
+ * that were left registered to vibrators after failures to register them to all vibrators.
+ *
+ * <p>This might happen if {@link AllVibratorsStateListener} fails to register to any vibrator
+ * and also fails to unregister any previously registered single listeners to other vibrators.
+ *
+ * <p>This method never throws {@link RuntimeException} if it fails to unregister again, it will
+ * fail silently and attempt to unregister the same broken listener later.
+ */
+ private void tryUnregisterBrokenListeners() {
+ synchronized (mBrokenListeners) {
+ try {
+ for (int i = mBrokenListeners.size(); --i >= 0; ) {
+ mBrokenListeners.get(i).unregister(mVibratorManager);
+ mBrokenListeners.remove(i);
+ }
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Failed to unregister broken listener", e);
+ }
}
}
- @Nullable
- private VibratorInfo getVibratorInfo() {
- try {
+ /** Listener for a single vibrator state change. */
+ private static class SingleVibratorStateListener implements OnVibratorStateChangedListener {
+ private final AllVibratorsStateListener mAllVibratorsListener;
+ private final int mVibratorIdx;
+
+ SingleVibratorStateListener(AllVibratorsStateListener listener, int vibratorIdx) {
+ mAllVibratorsListener = listener;
+ mVibratorIdx = vibratorIdx;
+ }
+
+ @Override
+ public void onVibratorStateChanged(boolean isVibrating) {
+ mAllVibratorsListener.onVibrating(mVibratorIdx, isVibrating);
+ }
+ }
+
+ /** Listener for all vibrators state change. */
+ private static class AllVibratorsStateListener {
+ private final Object mLock = new Object();
+ private final Executor mExecutor;
+ private final OnVibratorStateChangedListener mDelegate;
+
+ @GuardedBy("mLock")
+ private final SparseArray<SingleVibratorStateListener> mVibratorListeners =
+ new SparseArray<>();
+
+ @GuardedBy("mLock")
+ private int mInitializedMask;
+ @GuardedBy("mLock")
+ private int mVibratingMask;
+
+ AllVibratorsStateListener(@NonNull Executor executor,
+ @NonNull OnVibratorStateChangedListener listener) {
+ mExecutor = executor;
+ mDelegate = listener;
+ }
+
+ boolean hasRegisteredListeners() {
+ synchronized (mLock) {
+ return mVibratorListeners.size() > 0;
+ }
+ }
+
+ void register(VibratorManager vibratorManager) {
+ int[] vibratorIds = vibratorManager.getVibratorIds();
synchronized (mLock) {
- if (mVibratorInfo != null) {
- return mVibratorInfo;
+ for (int i = 0; i < vibratorIds.length; i++) {
+ int vibratorId = vibratorIds[i];
+ SingleVibratorStateListener listener = new SingleVibratorStateListener(this, i);
+ try {
+ vibratorManager.getVibrator(vibratorId).addVibratorStateListener(mExecutor,
+ listener);
+ mVibratorListeners.put(vibratorId, listener);
+ } catch (RuntimeException e) {
+ try {
+ unregister(vibratorManager);
+ } catch (RuntimeException e1) {
+ Log.w(TAG,
+ "Failed to unregister listener while recovering from a failed "
+ + "register call", e1);
+ }
+ throw e;
+ }
}
- if (mService == null) {
- return null;
+ }
+ }
+
+ void unregister(VibratorManager vibratorManager) {
+ synchronized (mLock) {
+ for (int i = mVibratorListeners.size(); --i >= 0; ) {
+ int vibratorId = mVibratorListeners.keyAt(i);
+ SingleVibratorStateListener listener = mVibratorListeners.valueAt(i);
+ vibratorManager.getVibrator(vibratorId).removeVibratorStateListener(listener);
+ mVibratorListeners.removeAt(i);
}
- return mVibratorInfo = mService.getVibratorInfo();
}
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to query vibrator info");
- throw e.rethrowFromSystemServer();
+ }
+
+ void onVibrating(int vibratorIdx, boolean vibrating) {
+ mExecutor.execute(() -> {
+ boolean anyVibrating;
+ synchronized (mLock) {
+ int allInitializedMask = 1 << mVibratorListeners.size() - 1;
+ int vibratorMask = 1 << vibratorIdx;
+ if ((mInitializedMask & vibratorMask) == 0) {
+ // First state report for this vibrator, set vibrating initial value.
+ mInitializedMask |= vibratorMask;
+ mVibratingMask |= vibrating ? vibratorMask : 0;
+ } else {
+ // Flip vibrating value, if changed.
+ boolean prevVibrating = (mVibratingMask & vibratorMask) != 0;
+ if (prevVibrating != vibrating) {
+ mVibratingMask ^= vibratorMask;
+ }
+ }
+ if (mInitializedMask != allInitializedMask) {
+ // Wait for all vibrators initial state to be reported before delegating.
+ return;
+ }
+ anyVibrating = mVibratingMask != 0;
+ }
+ mDelegate.onVibratorStateChanged(anyVibrating);
+ });
}
}
}
diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java
new file mode 100644
index 000000000000..b528eb157e36
--- /dev/null
+++ b/core/java/android/os/SystemVibratorManager.java
@@ -0,0 +1,360 @@
+/*
+ * 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 android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * VibratorManager implementation that controls the system vibrators.
+ *
+ * @hide
+ */
+public class SystemVibratorManager extends VibratorManager {
+ private static final String TAG = "VibratorManager";
+
+ private final IVibratorManagerService mService;
+ private final Context mContext;
+ private final Binder mToken = new Binder();
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private int[] mVibratorIds;
+ @GuardedBy("mLock")
+ private final SparseArray<Vibrator> mVibrators = new SparseArray<>();
+
+ @GuardedBy("mLock")
+ private final ArrayMap<Vibrator.OnVibratorStateChangedListener,
+ OnVibratorStateChangedListenerDelegate> mListeners = new ArrayMap<>();
+
+ /**
+ * @hide to prevent subclassing from outside of the framework
+ */
+ public SystemVibratorManager(Context context) {
+ super(context);
+ mContext = context;
+ mService = IVibratorManagerService.Stub.asInterface(
+ ServiceManager.getService(Context.VIBRATOR_MANAGER_SERVICE));
+ }
+
+ @NonNull
+ @Override
+ public int[] getVibratorIds() {
+ synchronized (mLock) {
+ if (mVibratorIds != null) {
+ return mVibratorIds;
+ }
+ try {
+ if (mService == null) {
+ Log.w(TAG, "Failed to retrieve vibrator ids; no vibrator manager service.");
+ } else {
+ return mVibratorIds = mService.getVibratorIds();
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return new int[0];
+ }
+ }
+
+ @NonNull
+ @Override
+ public Vibrator getVibrator(int vibratorId) {
+ synchronized (mLock) {
+ Vibrator vibrator = mVibrators.get(vibratorId);
+ if (vibrator != null) {
+ return vibrator;
+ }
+ VibratorInfo info = null;
+ try {
+ if (mService == null) {
+ Log.w(TAG, "Failed to retrieve vibrator; no vibrator manager service.");
+ } else {
+ info = mService.getVibratorInfo(vibratorId);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ if (info != null) {
+ vibrator = new SingleVibrator(info);
+ mVibrators.put(vibratorId, vibrator);
+ } else {
+ vibrator = NullVibrator.getInstance();
+ }
+ return vibrator;
+ }
+ }
+
+ @NonNull
+ @Override
+ public Vibrator getDefaultVibrator() {
+ return mContext.getSystemService(Vibrator.class);
+ }
+
+ @Override
+ public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
+ @Nullable CombinedVibrationEffect effect, @Nullable VibrationAttributes attributes) {
+ if (mService == null) {
+ Log.w(TAG, "Failed to set always-on effect; no vibrator manager service.");
+ return false;
+ }
+ try {
+ return mService.setAlwaysOnEffect(uid, opPkg, alwaysOnId, effect, attributes);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to set always-on effect.", e);
+ }
+ return false;
+ }
+
+ @Override
+ public void vibrate(int uid, String opPkg, @NonNull CombinedVibrationEffect effect,
+ String reason, @Nullable VibrationAttributes attributes) {
+ if (mService == null) {
+ Log.w(TAG, "Failed to vibrate; no vibrator manager service.");
+ return;
+ }
+ try {
+ mService.vibrate(uid, opPkg, effect, attributes, reason, mToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to vibrate.", e);
+ }
+ }
+
+ @Override
+ public void cancel() {
+ if (mService == null) {
+ Log.w(TAG, "Failed to cancel vibration; no vibrator manager service.");
+ return;
+ }
+ try {
+ mService.cancelVibrate(mToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to cancel vibration.", e);
+ }
+ }
+
+ /** Listener for vibrations on a single vibrator. */
+ private static class OnVibratorStateChangedListenerDelegate extends
+ IVibratorStateListener.Stub {
+ private final Executor mExecutor;
+ private final Vibrator.OnVibratorStateChangedListener mListener;
+
+ OnVibratorStateChangedListenerDelegate(
+ @NonNull Vibrator.OnVibratorStateChangedListener listener,
+ @NonNull Executor executor) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void onVibrating(boolean isVibrating) {
+ mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating));
+ }
+ }
+
+ /** Controls vibrations on a single vibrator. */
+ private final class SingleVibrator extends Vibrator {
+ private final VibratorInfo mVibratorInfo;
+
+ SingleVibrator(@NonNull VibratorInfo vibratorInfo) {
+ mVibratorInfo = vibratorInfo;
+ }
+
+ @Override
+ public int getId() {
+ return mVibratorInfo.getId();
+ }
+
+ @Override
+ public boolean hasVibrator() {
+ return true;
+ }
+
+ @Override
+ public boolean hasAmplitudeControl() {
+ return mVibratorInfo.hasAmplitudeControl();
+ }
+
+ @NonNull
+ @Override
+ public int[] areEffectsSupported(@NonNull int... effectIds) {
+ int[] supported = new int[effectIds.length];
+ for (int i = 0; i < effectIds.length; i++) {
+ supported[i] = mVibratorInfo.isEffectSupported(effectIds[i]);
+ }
+ return supported;
+ }
+
+ @Override
+ public boolean[] arePrimitivesSupported(
+ @NonNull @VibrationEffect.Composition.Primitive int... primitiveIds) {
+ boolean[] supported = new boolean[primitiveIds.length];
+ for (int i = 0; i < primitiveIds.length; i++) {
+ supported[i] = mVibratorInfo.isPrimitiveSupported(primitiveIds[i]);
+ }
+ return supported;
+ }
+
+ @Override
+ public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
+ @Nullable VibrationEffect effect, @Nullable AudioAttributes attributes) {
+ if (mService == null) {
+ Log.w(TAG, "Failed to set always-on effect on vibrator " + mVibratorInfo.getId()
+ + "; no vibrator manager service.");
+ return false;
+ }
+ try {
+ VibrationAttributes attr = new VibrationAttributes.Builder(
+ attributes, effect).build();
+ CombinedVibrationEffect combined = CombinedVibrationEffect.startSynced()
+ .addVibrator(mVibratorInfo.getId(), effect)
+ .combine();
+ return mService.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combined, attr);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to set always-on effect on vibrator " + mVibratorInfo.getId());
+ }
+ return false;
+ }
+
+ @Override
+ public void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, String reason,
+ @NonNull VibrationAttributes attributes) {
+ if (mService == null) {
+ Log.w(TAG, "Failed to vibrate on vibrator " + mVibratorInfo.getId()
+ + "; no vibrator manager service.");
+ return;
+ }
+ try {
+ CombinedVibrationEffect combined = CombinedVibrationEffect.startSynced()
+ .addVibrator(mVibratorInfo.getId(), vibe)
+ .combine();
+ mService.vibrate(uid, opPkg, combined, attributes, reason, mToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to vibrate.", e);
+ }
+ }
+
+ @Override
+ public void cancel() {
+ if (mService == null) {
+ Log.w(TAG, "Failed to cancel vibration on vibrator " + mVibratorInfo.getId()
+ + "; no vibrator manager service.");
+ return;
+ }
+ try {
+ mService.cancelVibrate(mToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to cancel vibration on vibrator " + mVibratorInfo.getId(), e);
+ }
+ }
+
+ @Override
+ public boolean isVibrating() {
+ if (mService == null) {
+ Log.w(TAG, "Failed to check status of vibrator " + mVibratorInfo.getId()
+ + "; no vibrator service.");
+ return false;
+ }
+ try {
+ return mService.isVibrating(mVibratorInfo.getId());
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
+ @Override
+ public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+ Objects.requireNonNull(listener);
+ if (mContext == null) {
+ Log.w(TAG, "Failed to add vibrate state listener; no vibrator context.");
+ return;
+ }
+ addVibratorStateListener(mContext.getMainExecutor(), listener);
+ }
+
+ @Override
+ public void addVibratorStateListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnVibratorStateChangedListener listener) {
+ Objects.requireNonNull(listener);
+ Objects.requireNonNull(executor);
+ if (mService == null) {
+ Log.w(TAG,
+ "Failed to add vibrate state listener to vibrator " + mVibratorInfo.getId()
+ + "; no vibrator service.");
+ return;
+ }
+ synchronized (mLock) {
+ // If listener is already registered, reject and return.
+ if (mListeners.containsKey(listener)) {
+ Log.w(TAG, "Listener already registered.");
+ return;
+ }
+ try {
+ OnVibratorStateChangedListenerDelegate delegate =
+ new OnVibratorStateChangedListenerDelegate(listener, executor);
+ if (!mService.registerVibratorStateListener(mVibratorInfo.getId(), delegate)) {
+ Log.w(TAG, "Failed to add vibrate state listener to vibrator "
+ + mVibratorInfo.getId());
+ return;
+ }
+ mListeners.put(listener, delegate);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ @Override
+ public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
+ Objects.requireNonNull(listener);
+ if (mService == null) {
+ Log.w(TAG, "Failed to remove vibrate state listener from vibrator "
+ + mVibratorInfo.getId() + "; no vibrator service.");
+ return;
+ }
+ synchronized (mLock) {
+ // Check if the listener is registered, otherwise will return.
+ if (mListeners.containsKey(listener)) {
+ OnVibratorStateChangedListenerDelegate delegate = mListeners.get(listener);
+ try {
+ if (!mService.unregisterVibratorStateListener(mVibratorInfo.getId(),
+ delegate)) {
+ Log.w(TAG, "Failed to remove vibrate state listener from vibrator "
+ + mVibratorInfo.getId());
+ return;
+ }
+ mListeners.remove(listener);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 9c9e4995d673..c8cbc517b226 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -168,8 +168,10 @@ public final class Trace {
}
/**
- * Set whether application tracing is allowed for this process. This is intended to be set
- * once at application start-up time based on whether the application is debuggable.
+ * From Android S, this is no-op.
+ *
+ * Before, set whether application tracing is allowed for this process. This is intended to be
+ * set once at application start-up time based on whether the application is debuggable.
*
* @hide
*/
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index 3ffaa9e4b1f0..bb40d905d481 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -29,21 +29,11 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela
private final int mUid;
@Nullable
private final String mPackageWithHighestDrain;
- private boolean mSystemComponent;
public int getUid() {
return mUid;
}
- /**
- * Returns true if this battery consumer is considered to be a part of the operating
- * system itself. For example, the UidBatteryConsumer with the UID {@link Process#BLUETOOTH_UID}
- * is a system component.
- */
- public boolean isSystemComponent() {
- return mSystemComponent;
- }
-
@Nullable
public String getPackageWithHighestDrain() {
return mPackageWithHighestDrain;
@@ -52,7 +42,6 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela
private UidBatteryConsumer(@NonNull Builder builder) {
super(builder.mPowerComponentsBuilder.build());
mUid = builder.mUid;
- mSystemComponent = builder.mSystemComponent;
mPackageWithHighestDrain = builder.mPackageWithHighestDrain;
}
@@ -95,15 +84,16 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela
private final BatteryStats.Uid mBatteryStatsUid;
private final int mUid;
private String mPackageWithHighestDrain;
- private boolean mSystemComponent;
+ private boolean mExcludeFromBatteryUsageStats;
public Builder(int customPowerComponentCount, int customTimeComponentCount,
- BatteryStats.Uid batteryStatsUid) {
+ @NonNull BatteryStats.Uid batteryStatsUid) {
super(customPowerComponentCount, customTimeComponentCount);
mBatteryStatsUid = batteryStatsUid;
mUid = batteryStatsUid.getUid();
}
+ @NonNull
public BatteryStats.Uid getBatteryStatsUid() {
return mBatteryStatsUid;
}
@@ -113,14 +103,6 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela
}
/**
- * Creates a read-only object out of the Builder values.
- */
- @NonNull
- public UidBatteryConsumer build() {
- return new UidBatteryConsumer(this);
- }
-
- /**
* Sets the name of the package owned by this UID that consumed the highest amount
* of power since BatteryStats reset.
*/
@@ -131,12 +113,27 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela
}
/**
- * Marks the UidBatteryConsumer as part of the system. For example,
- * the UidBatteryConsumer with the UID {@link Process#BLUETOOTH_UID} is considered
- * as a system component.
+ * Marks the UidBatteryConsumer for exclusion from the result set.
*/
- public void setSystemComponent(boolean systemComponent) {
- mSystemComponent = systemComponent;
+ public Builder excludeFromBatteryUsageStats() {
+ mExcludeFromBatteryUsageStats = true;
+ return this;
+ }
+
+ /**
+ * Returns true if this UidBatteryConsumer must be excluded from the
+ * BatteryUsageStats.
+ */
+ public boolean isExcludedFromBatteryUsageStats() {
+ return mExcludeFromBatteryUsageStats;
+ }
+
+ /**
+ * Creates a read-only object out of the Builder values.
+ */
+ @NonNull
+ public UidBatteryConsumer build() {
+ return new UidBatteryConsumer(this);
}
}
}
diff --git a/core/java/android/os/UserBatteryConsumer.java b/core/java/android/os/UserBatteryConsumer.java
new file mode 100644
index 000000000000..94e567f368b0
--- /dev/null
+++ b/core/java/android/os/UserBatteryConsumer.java
@@ -0,0 +1,115 @@
+/*
+ * 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 android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains power consumption data attributed to a {@link UserHandle}.
+ *
+ * {@hide}
+ */
+public class UserBatteryConsumer extends BatteryConsumer implements Parcelable {
+ private final int mUserId;
+
+ public int getUserId() {
+ return mUserId;
+ }
+
+ private UserBatteryConsumer(@NonNull UserBatteryConsumer.Builder builder) {
+ super(builder.mPowerComponentsBuilder.build());
+ mUserId = builder.mUserId;
+ }
+
+ private UserBatteryConsumer(Parcel in) {
+ super(new PowerComponents(in));
+ mUserId = in.readInt();
+ }
+
+ /**
+ * Writes the contents into a Parcel.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mUserId);
+ }
+
+ public static final Creator<UserBatteryConsumer> CREATOR =
+ new Creator<UserBatteryConsumer>() {
+ @Override
+ public UserBatteryConsumer createFromParcel(Parcel in) {
+ return new UserBatteryConsumer(in);
+ }
+
+ @Override
+ public UserBatteryConsumer[] newArray(int size) {
+ return new UserBatteryConsumer[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Builder for UserBatteryConsumer.
+ */
+ public static final class Builder extends BaseBuilder<Builder> {
+ private final int mUserId;
+ private List<UidBatteryConsumer.Builder> mUidBatteryConsumers;
+
+ Builder(int customPowerComponentCount, int customTimeComponentCount, int userId) {
+ super(customPowerComponentCount, customTimeComponentCount);
+ mUserId = userId;
+ }
+
+ /**
+ * Add a UidBatteryConsumer to this UserBatteryConsumer.
+ * <p>
+ * Calculated power and duration components of the added UID battery consumers
+ * are aggregated at the time the UserBatteryConsumer is built by the {@link #build()}
+ * method.
+ * </p>
+ */
+ public void addUidBatteryConsumer(UidBatteryConsumer.Builder uidBatteryConsumerBuilder) {
+ if (mUidBatteryConsumers == null) {
+ mUidBatteryConsumers = new ArrayList<>();
+ }
+ mUidBatteryConsumers.add(uidBatteryConsumerBuilder);
+ }
+
+ /**
+ * Creates a read-only object out of the Builder values.
+ */
+ @NonNull
+ public UserBatteryConsumer build() {
+ if (mUidBatteryConsumers != null) {
+ for (int i = mUidBatteryConsumers.size() - 1; i >= 0; i--) {
+ UidBatteryConsumer.Builder uidBatteryConsumer = mUidBatteryConsumers.get(i);
+ mPowerComponentsBuilder.addPowerAndDuration(
+ uidBatteryConsumer.mPowerComponentsBuilder);
+ }
+ }
+ return new UserBatteryConsumer(this);
+ }
+ }
+}
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 7e50ebc419dd..2119e7b951cd 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -326,6 +326,19 @@ public final class UserHandle implements Parcelable {
}
/**
+ * Returns the uid that is composed from the userHandle and the appId.
+ *
+ * @param userHandle the UserHandle to compose the uid
+ * @param appId the AppId to compose the uid
+ * @return the uid that is composed from the userHandle and the appId
+ * @hide
+ */
+ @SystemApi
+ public static int getUid(@NonNull UserHandle userHandle, @AppIdInt int appId) {
+ return getUid(userHandle.getIdentifier(), appId);
+ }
+
+ /**
* Returns the app id (or base uid) for a given uid, stripping out the user id from it.
* @hide
*/
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index ea1ce3728365..8bdfd3d3d627 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2300,6 +2300,19 @@ public class UserManager {
}
/**
+ * Checks if the calling user is running on foreground.
+ *
+ * @return whether the calling user is running on foreground.
+ */
+ public boolean isUserForeground() {
+ try {
+ return mService.isUserForeground();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Return whether the calling user is running in an "unlocked" state.
* <p>
* On devices with direct boot, a user is unlocked only after they've
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 7d85d13094a1..d6fa733927fb 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -184,6 +184,15 @@ public abstract class Vibrator {
}
/**
+ * Return the ID of this vibrator.
+ *
+ * @return The id of the vibrator controlled by this service.
+ */
+ public int getId() {
+ return -1;
+ }
+
+ /**
* Check whether the hardware has a vibrator.
*
* @return True if the hardware has a vibrator, else false.
diff --git a/core/java/android/os/VibratorManager.java b/core/java/android/os/VibratorManager.java
index 1d5a58745279..5dd38b6cbd86 100644
--- a/core/java/android/os/VibratorManager.java
+++ b/core/java/android/os/VibratorManager.java
@@ -17,45 +17,123 @@
package android.os;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.app.ActivityThread;
+import android.content.Context;
+import android.util.Log;
/**
- * VibratorManager provides access to multiple vibrators, as well as the ability to run them in
- * a synchronized fashion.
+ * Class that provides access to all vibrators from the device, as well as the ability to run them
+ * in a synchronized fashion.
+ * <p>
+ * If your process exits, any vibration you started will stop.
+ * </p>
*/
+@SystemService(Context.VIBRATOR_MANAGER_SERVICE)
public abstract class VibratorManager {
- /** @hide */
- protected static final String TAG = "VibratorManager";
+ private static final String TAG = "VibratorManager";
+
+ private final String mPackageName;
/**
- * {@hide}
+ * @hide to prevent subclassing from outside of the framework
*/
public VibratorManager() {
+ mPackageName = ActivityThread.currentPackageName();
+ }
+
+ /**
+ * @hide to prevent subclassing from outside of the framework
+ */
+ protected VibratorManager(Context context) {
+ mPackageName = context.getOpPackageName();
}
/**
- * This method lists all available actuator ids, returning a possible empty list.
- * If the device has only a single actuator, this should return a single entry with a
- * default id.
+ * List all available vibrator ids, returning a possible empty list.
+ *
+ * @return An array containing the ids of the vibrators available on the device.
*/
@NonNull
public abstract int[] getVibratorIds();
/**
- * Returns a Vibrator service for given id.
- * This allows users to perform a vibration effect on a single actuator.
- */
+ * Retrieve a single vibrator by id.
+ *
+ * @param vibratorId The id of the vibrator to be retrieved.
+ * @return The vibrator with given {@code vibratorId}, never null.
+ */
@NonNull
public abstract Vibrator getVibrator(int vibratorId);
/**
- * Returns the system default Vibrator service.
- */
+ * Returns the system default Vibrator service.
+ */
@NonNull
public abstract Vibrator getDefaultVibrator();
/**
- * Vibrates all actuators by passing each VibrationEffect within CombinedVibrationEffect
- * to the respective actuator, in sync.
+ * Configure an always-on haptics effect.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)
+ public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
+ @Nullable CombinedVibrationEffect effect, @Nullable VibrationAttributes attributes) {
+ Log.w(TAG, "Always-on effects aren't supported");
+ return false;
+ }
+
+ /**
+ * Vibrate with a given combination of effects.
+ *
+ * <p>
+ * Pass in a {@link CombinedVibrationEffect} representing a combination of {@link
+ * VibrationEffect} to be played on one or more vibrators.
+ * </p>
+ *
+ * @param effect an array of longs of times for which to turn the vibrator on or off.
+ */
+ @RequiresPermission(android.Manifest.permission.VIBRATE)
+ public final void vibrate(@NonNull CombinedVibrationEffect effect) {
+ vibrate(effect, null);
+ }
+
+ /**
+ * Vibrate with a given combination of effects.
+ *
+ * <p>
+ * Pass in a {@link CombinedVibrationEffect} representing a combination of {@link
+ * VibrationEffect} to be played on one or more vibrators.
+ * </p>
+ *
+ * @param effect an array of longs of times for which to turn the vibrator on or off.
+ * @param attributes {@link VibrationAttributes} corresponding to the vibration. For example,
+ * specify {@link VibrationAttributes#USAGE_ALARM} for alarm vibrations or
+ * {@link VibrationAttributes#USAGE_RINGTONE} for vibrations associated with
+ * incoming calls.
+ */
+ @RequiresPermission(android.Manifest.permission.VIBRATE)
+ public final void vibrate(@NonNull CombinedVibrationEffect effect,
+ @Nullable VibrationAttributes attributes) {
+ vibrate(Process.myUid(), mPackageName, effect, null, attributes);
+ }
+
+ /**
+ * Like {@link #vibrate(CombinedVibrationEffect, VibrationAttributes)}, but allows the
+ * caller to specify the vibration is owned by someone else and set reason for vibration.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.VIBRATE)
+ public abstract void vibrate(int uid, String opPkg, @NonNull CombinedVibrationEffect effect,
+ String reason, @Nullable VibrationAttributes attributes);
+
+ /**
+ * Turn all the vibrators off.
*/
- public abstract void vibrate(@NonNull CombinedVibrationEffect effect);
+ @RequiresPermission(android.Manifest.permission.VIBRATE)
+ public abstract void cancel();
}
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index f2fe71913bb1..a078e0434867 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -36,6 +36,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.DataLoaderParams;
import android.content.pm.IDataLoaderStatusListener;
+import android.content.pm.IPackageLoadingProgressCallback;
import android.content.pm.InstallationFileParcel;
import java.io.File;
@@ -71,7 +72,8 @@ public final class IncrementalFileStorages {
@Nullable StorageHealthCheckParams healthCheckParams,
@Nullable IStorageHealthListener healthListener,
@NonNull List<InstallationFileParcel> addedFiles,
- @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException {
+ @NonNull PerUidReadTimeouts[] perUidReadTimeouts,
+ IPackageLoadingProgressCallback progressCallback) throws IOException {
// TODO(b/136132412): validity check if session should not be incremental
IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
Context.INCREMENTAL_SERVICE);
@@ -95,6 +97,11 @@ public final class IncrementalFileStorages {
throw new IOException("Unknown file location: " + file.location);
}
}
+ // Register progress loading callback after files have been added
+ if (progressCallback != null) {
+ incrementalManager.registerLoadingProgressCallback(stageDir.getAbsolutePath(),
+ progressCallback);
+ }
result.startLoading(dataLoaderParams, statusListener, healthCheckParams, healthListener,
perUidReadTimeouts);
@@ -205,6 +212,7 @@ public final class IncrementalFileStorages {
try {
mDefaultStorage.unBind(mStageDir.getAbsolutePath());
+ mDefaultStorage.unregisterLoadingProgressListener();
} catch (IOException ignored) {
}
mDefaultStorage = null;
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 0ff68fc582d8..05899947c3df 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -241,6 +241,13 @@ public final class IncrementalManager {
}
/**
+ * Checks if device supports V2 calls (e.g. PerUid).
+ */
+ public static boolean isV2Available() {
+ return nativeIsV2Available();
+ }
+
+ /**
* Checks if Incremental installations are allowed.
* A developer can disable Incremental installations by setting the property.
*/
@@ -439,6 +446,7 @@ public final class IncrementalManager {
/* Native methods */
private static native boolean nativeIsEnabled();
+ private static native boolean nativeIsV2Available();
private static native boolean nativeIsIncrementalPath(@NonNull String path);
private static native byte[] nativeUnsafeGetFileSignature(@NonNull String path);
}
diff --git a/core/java/android/permission/AdminPermissionControlParams.aidl b/core/java/android/permission/AdminPermissionControlParams.aidl
new file mode 100644
index 000000000000..35e63d4851fc
--- /dev/null
+++ b/core/java/android/permission/AdminPermissionControlParams.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.permission;
+
+parcelable AdminPermissionControlParams;
diff --git a/core/java/android/permission/AdminPermissionControlParams.java b/core/java/android/permission/AdminPermissionControlParams.java
new file mode 100644
index 000000000000..49507220e7b0
--- /dev/null
+++ b/core/java/android/permission/AdminPermissionControlParams.java
@@ -0,0 +1,132 @@
+/*
+ * 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.permission;
+
+import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
+import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED;
+import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.admin.DevicePolicyManager;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A data object representing an admin's request to control a certain permission
+ * for a certain app.
+ * This class is processed by the Permission Controller's
+ * setRuntimePermissionGrantStateByDeviceAdmin method.
+ *
+ * @hide
+ */
+@SystemApi
+public final class AdminPermissionControlParams implements Parcelable {
+ // The package to grant/deny the permission to.
+ private final @NonNull String mGranteePackageName;
+ // The permission to grant/deny.
+ private final @NonNull String mPermission;
+ // The grant state (granted/denied/default).
+ private final @DevicePolicyManager.PermissionGrantState int mGrantState;
+ // Whether the admin can grant sensors-related permissions.
+ private final boolean mCanAdminGrantSensorsPermissions;
+
+ /**
+ * @hide
+ * A new instance is only created by the framework, so the constructor need not be visible
+ * as system API.
+ */
+ public AdminPermissionControlParams(@NonNull String granteePackageName,
+ @NonNull String permission,
+ int grantState, boolean canAdminGrantSensorsPermissions) {
+ Preconditions.checkStringNotEmpty(granteePackageName, "Package name must not be empty.");
+ Preconditions.checkStringNotEmpty(permission, "Permission must not be empty.");
+ checkArgument(grantState == PERMISSION_GRANT_STATE_GRANTED
+ || grantState == PERMISSION_GRANT_STATE_DENIED
+ || grantState == PERMISSION_GRANT_STATE_DEFAULT);
+
+ mGranteePackageName = granteePackageName;
+ mPermission = permission;
+ mGrantState = grantState;
+ mCanAdminGrantSensorsPermissions = canAdminGrantSensorsPermissions;
+ }
+
+ public static final @NonNull Creator<AdminPermissionControlParams> CREATOR =
+ new Creator<AdminPermissionControlParams>() {
+ @Override
+ public AdminPermissionControlParams createFromParcel(Parcel in) {
+ String granteePackageName = in.readString();
+ String permission = in.readString();
+ int grantState = in.readInt();
+ boolean mayAdminGrantSensorPermissions = in.readBoolean();
+
+ return new AdminPermissionControlParams(granteePackageName, permission,
+ grantState, mayAdminGrantSensorPermissions);
+ }
+
+ @Override
+ public AdminPermissionControlParams[] newArray(int size) {
+ return new AdminPermissionControlParams[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mGranteePackageName);
+ dest.writeString(mPermission);
+ dest.writeInt(mGrantState);
+ dest.writeBoolean(mCanAdminGrantSensorsPermissions);
+ }
+
+ /** Returns the name of the package the permission applies to */
+ public @NonNull String getGranteePackageName() {
+ return mGranteePackageName;
+ }
+
+ /** Returns the permission name */
+ public @NonNull String getPermission() {
+ return mPermission;
+ }
+
+ /** Returns the grant state */
+ public int getGrantState() {
+ return mGrantState;
+ }
+
+ /**
+ * return true if the admin may control grants of permissions related to sensors.
+ */
+ public boolean canAdminGrantSensorsPermissions() {
+ return mCanAdminGrantSensorsPermissions;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "Grantee %s Permission %s state: %d admin grant of sensors permissions: %b",
+ mGranteePackageName, mPermission, mGrantState, mCanAdminGrantSensorsPermissions);
+ }
+}
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 084cc2ff4aa4..6d677f35b563 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -20,6 +20,7 @@ import android.os.RemoteCallback;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
+import android.permission.AdminPermissionControlParams;
import com.android.internal.infra.AndroidFuture;
/**
@@ -39,8 +40,8 @@ oneway interface IPermissionController {
void countPermissionApps(in List<String> permissionNames, int flags,
in AndroidFuture callback);
void getPermissionUsages(boolean countSystem, long numMillis, in AndroidFuture callback);
- void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName, String packageName,
- String permission, int grantState, in AndroidFuture callback);
+ void setRuntimePermissionGrantStateByDeviceAdminFromParams(String callerPackageName,
+ in AdminPermissionControlParams params, in AndroidFuture callback);
void grantOrUpgradeDefaultRuntimePermissions(in AndroidFuture callback);
void notifyOneTimePermissionSessionTimeout(String packageName);
void updateUserSensitiveForApp(int uid, in AndroidFuture callback);
diff --git a/core/java/android/permission/OWNERS b/core/java/android/permission/OWNERS
index d09f351bdfd1..b32346848a69 100644
--- a/core/java/android/permission/OWNERS
+++ b/core/java/android/permission/OWNERS
@@ -1,6 +1,5 @@
# Bug component: 137825
-moltmann@google.com
evanseverson@google.com
ntmyren@google.com
zhanghai@google.com
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index f306805ac3a4..084b18eb2999 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -16,13 +16,9 @@
package android.permission;
-import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
-import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED;
-import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED;
import static android.permission.PermissionControllerService.SERVICE_INTERFACE;
import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
-import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
import static com.android.internal.util.Preconditions.checkFlagsArgument;
@@ -39,7 +35,6 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.ActivityThread;
-import android.app.admin.DevicePolicyManager.PermissionGrantState;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -70,6 +65,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@@ -323,11 +319,11 @@ public final class PermissionControllerManager {
/**
* Set the runtime permission state from a device admin.
+ * This variant takes into account whether the admin may or may not grant sensors-related
+ * permissions.
*
* @param callerPackageName The package name of the admin requesting the change
- * @param packageName Package the permission belongs to
- * @param permission Permission to change
- * @param grantState State to set the permission into
+ * @param params Information about the permission being granted.
* @param executor Executor to run the {@code callback} on
* @param callback The callback
*
@@ -338,30 +334,27 @@ public final class PermissionControllerManager {
Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY},
conditional = true)
public void setRuntimePermissionGrantStateByDeviceAdmin(@NonNull String callerPackageName,
- @NonNull String packageName, @NonNull String permission,
- @PermissionGrantState int grantState, @NonNull @CallbackExecutor Executor executor,
+ @NonNull AdminPermissionControlParams params,
+ @NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<Boolean> callback) {
checkStringNotEmpty(callerPackageName);
- checkStringNotEmpty(packageName);
- checkStringNotEmpty(permission);
- checkArgument(grantState == PERMISSION_GRANT_STATE_GRANTED
- || grantState == PERMISSION_GRANT_STATE_DENIED
- || grantState == PERMISSION_GRANT_STATE_DEFAULT);
- checkNotNull(executor);
- checkNotNull(callback);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ Objects.requireNonNull(params, "Admin control params must not be null.");
mRemoteService.postAsync(service -> {
AndroidFuture<Boolean> setRuntimePermissionGrantStateResult = new AndroidFuture<>();
- service.setRuntimePermissionGrantStateByDeviceAdmin(
- callerPackageName, packageName, permission, grantState,
+ service.setRuntimePermissionGrantStateByDeviceAdminFromParams(
+ callerPackageName, params,
setRuntimePermissionGrantStateResult);
return setRuntimePermissionGrantStateResult;
}).whenCompleteAsync((setRuntimePermissionGrantStateResult, err) -> {
final long token = Binder.clearCallingIdentity();
try {
if (err != null) {
- Log.e(TAG, "Error setting permissions state for device admin " + packageName,
- err);
+ Log.e(TAG,
+ "Error setting permissions state for device admin "
+ + callerPackageName, err);
callback.accept(false);
} else {
callback.accept(Boolean.TRUE.equals(setRuntimePermissionGrantStateResult));
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 8105b6517015..ad9e8b3d6dd4 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -16,9 +16,7 @@
package android.permission;
-import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED;
-import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED;
import static android.permission.PermissionControllerManager.COUNT_ONLY_WHEN_GRANTED;
import static android.permission.PermissionControllerManager.COUNT_WHEN_SYSTEM;
@@ -259,6 +257,8 @@ public abstract class PermissionControllerService extends Service {
}
/**
+ * @deprecated See {@link #onSetRuntimePermissionGrantStateByDeviceAdmin(String,
+ * AdminPermissionControlParams, Consumer)}.
* Set the runtime permission state from a device admin.
*
* @param callerPackageName The package name of the admin requesting the change
@@ -267,6 +267,7 @@ public abstract class PermissionControllerService extends Service {
* @param grantState State to set the permission into
* @param callback Callback waiting for whether the state could be set or not
*/
+ @Deprecated
@BinderThread
public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(
@NonNull String callerPackageName, @NonNull String packageName,
@@ -274,6 +275,20 @@ public abstract class PermissionControllerService extends Service {
@NonNull Consumer<Boolean> callback);
/**
+ * Set the runtime permission state from a device admin.
+ *
+ * @param callerPackageName The package name of the admin requesting the change
+ * @param params Parameters of admin request.
+ * @param callback Callback waiting for whether the state could be set or not
+ */
+ @BinderThread
+ public void onSetRuntimePermissionGrantStateByDeviceAdmin(
+ @NonNull String callerPackageName, @NonNull AdminPermissionControlParams params,
+ @NonNull Consumer<Boolean> callback) {
+ throw new AbstractMethodError("Must be overridden in implementing class");
+ }
+
+ /**
* Called when a package is considered inactive based on the criteria given by
* {@link PermissionManager#startOneTimePermissionSession(String, long, int, int)}.
* This method is called at the end of a one-time permission session
@@ -468,32 +483,26 @@ public abstract class PermissionControllerService extends Service {
}
@Override
- public void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName,
- String packageName, String permission, int grantState,
+ public void setRuntimePermissionGrantStateByDeviceAdminFromParams(
+ String callerPackageName, AdminPermissionControlParams params,
AndroidFuture callback) {
checkStringNotEmpty(callerPackageName);
- checkStringNotEmpty(packageName);
- checkStringNotEmpty(permission);
- checkArgument(grantState == PERMISSION_GRANT_STATE_GRANTED
- || grantState == PERMISSION_GRANT_STATE_DENIED
- || grantState == PERMISSION_GRANT_STATE_DEFAULT);
- checkNotNull(callback);
-
- if (grantState == PERMISSION_GRANT_STATE_DENIED) {
+ if (params.getGrantState() == PERMISSION_GRANT_STATE_DENIED) {
enforceSomePermissionsGrantedToCaller(
Manifest.permission.GRANT_RUNTIME_PERMISSIONS);
}
- if (grantState == PERMISSION_GRANT_STATE_DENIED) {
+ if (params.getGrantState() == PERMISSION_GRANT_STATE_DENIED) {
enforceSomePermissionsGrantedToCaller(
Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
}
enforceSomePermissionsGrantedToCaller(
Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY);
+ checkNotNull(callback);
onSetRuntimePermissionGrantStateByDeviceAdmin(callerPackageName,
- packageName, permission, grantState, callback::complete);
+ params, callback::complete);
}
@Override
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 85e9fdb9a9d1..0e35ef98f1b7 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -693,13 +693,14 @@ public class PermissionUsageHelper {
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);
- }
-
- // If this attribution is a proxy, remove it
- if (toRemoveProxies.contains(usage.toPackageAttr())) {
continue;
}
diff --git a/core/java/android/permissionpresenterservice/OWNERS b/core/java/android/permissionpresenterservice/OWNERS
index d09f351bdfd1..b32346848a69 100644
--- a/core/java/android/permissionpresenterservice/OWNERS
+++ b/core/java/android/permissionpresenterservice/OWNERS
@@ -1,6 +1,5 @@
# Bug component: 137825
-moltmann@google.com
evanseverson@google.com
ntmyren@google.com
zhanghai@google.com
diff --git a/core/java/android/print/OWNERS b/core/java/android/print/OWNERS
index 72f09832becf..28a242037f6a 100644
--- a/core/java/android/print/OWNERS
+++ b/core/java/android/print/OWNERS
@@ -1,5 +1,4 @@
# Bug component: 47273
-moltmann@google.com
svetoslavganov@android.com
svetoslavganov@google.com
diff --git a/core/java/android/print/pdf/OWNERS b/core/java/android/print/pdf/OWNERS
index 72f09832becf..28a242037f6a 100644
--- a/core/java/android/print/pdf/OWNERS
+++ b/core/java/android/print/pdf/OWNERS
@@ -1,5 +1,4 @@
# Bug component: 47273
-moltmann@google.com
svetoslavganov@android.com
svetoslavganov@google.com
diff --git a/core/java/android/printservice/OWNERS b/core/java/android/printservice/OWNERS
index 72f09832becf..28a242037f6a 100644
--- a/core/java/android/printservice/OWNERS
+++ b/core/java/android/printservice/OWNERS
@@ -1,5 +1,4 @@
# Bug component: 47273
-moltmann@google.com
svetoslavganov@android.com
svetoslavganov@google.com
diff --git a/core/java/android/printservice/recommendation/OWNERS b/core/java/android/printservice/recommendation/OWNERS
index 72f09832becf..28a242037f6a 100644
--- a/core/java/android/printservice/recommendation/OWNERS
+++ b/core/java/android/printservice/recommendation/OWNERS
@@ -1,5 +1,4 @@
# Bug component: 47273
-moltmann@google.com
svetoslavganov@android.com
svetoslavganov@google.com
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 91e091c50532..e134c29520b2 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -525,6 +525,13 @@ public final class DeviceConfig {
*/
public static final String NAMESPACE_INTERACTION_JANK_MONITOR = "interaction_jank_monitor";
+ /**
+ * Namespace for game overlay related features.
+ *
+ * @hide
+ */
+ public static final String NAMESPACE_GAME_OVERLAY = "game_overlay";
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index faa90d93719f..86b20c424869 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -350,6 +350,9 @@ public class FontsContract {
return cachedTypeface;
}
+ Log.w(TAG, "Platform version of downloadable fonts is deprecated. Please use"
+ + " androidx version instead.");
+
synchronized (sLock) {
// It is possible that Font is loaded during the thread sleep time
// re-check the cache to avoid re-loading the font
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index dd08ba33e30c..4c19ff386a48 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -44,6 +44,7 @@ import android.content.Context;
import android.content.IContentProvider;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
@@ -89,8 +90,11 @@ import com.android.internal.util.Preconditions;
import com.android.internal.widget.ILockSettings;
import java.io.IOException;
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -99,7 +103,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-
/**
* The Settings provider contains global system-level device preferences.
*/
@@ -1899,6 +1902,18 @@ public final class Settings {
public static final String EXTRA_CONVERSATION_ID = "android.provider.extra.CONVERSATION_ID";
/**
+ * Activity Extra: An {@code Arraylist<String>} of {@link NotificationChannel} field names to
+ * show on the Settings UI.
+ *
+ * <p>
+ * This is an optional extra field to the {@link #ACTION_CHANNEL_NOTIFICATION_SETTINGS}. If
+ * included the system will filter out any Settings that doesn't appear in this list that
+ * otherwise would display.
+ */
+ public static final String EXTRA_CHANNEL_FILTER_LIST
+ = "android.provider.extra.CHANNEL_FILTER_LIST";
+
+ /**
* Activity Action: Show notification redaction settings.
*
* @hide
@@ -2490,7 +2505,7 @@ public final class Settings {
public static final String EXTRA_NUMBER_OF_CERTIFICATES =
"android.settings.extra.number_of_certificates";
- private static final String JID_RESOURCE_PREFIX = "android";
+ private static final String SYSTEM_PACKAGE_NAME = "android";
public static final String AUTHORITY = "settings";
@@ -2659,22 +2674,30 @@ public final class Settings {
private final String mCallListCommand;
private final String mCallSetAllCommand;
+ private final ArraySet<String> mReadableFields;
+ private final ArraySet<String> mAllFields;
+
@GuardedBy("this")
private GenerationTracker mGenerationTracker;
- public NameValueCache(Uri uri, String getCommand, String setCommand,
- ContentProviderHolder providerHolder) {
- this(uri, getCommand, setCommand, null, null, providerHolder);
+ <T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
+ String setCommand, ContentProviderHolder providerHolder, Class<T> callerClass) {
+ this(uri, getCommand, setCommand, null, null, providerHolder,
+ callerClass);
}
- NameValueCache(Uri uri, String getCommand, String setCommand, String listCommand,
- String setAllCommand, ContentProviderHolder providerHolder) {
+ private <T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
+ String setCommand, String listCommand, String setAllCommand,
+ ContentProviderHolder providerHolder, Class<T> callerClass) {
mUri = uri;
mCallGetCommand = getCommand;
mCallSetCommand = setCommand;
mCallListCommand = listCommand;
mCallSetAllCommand = setAllCommand;
mProviderHolder = providerHolder;
+ mReadableFields = new ArraySet<>();
+ mAllFields = new ArraySet<>();
+ getPublicSettingsForClass(callerClass, mAllFields, mReadableFields);
}
public boolean putStringForUser(ContentResolver cr, String name, String value,
@@ -2726,6 +2749,19 @@ public final class Settings {
@UnsupportedAppUsage
public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
+ // Check if the target settings key is readable. Reject if the caller is not system and
+ // is trying to access a settings key defined in the Settings.Secure, Settings.System or
+ // Settings.Global and is not annotated as @Readable.
+ // Notice that a key string that is not defined in any of the Settings.* classes will
+ // still be regarded as readable.
+ // TODO(b/175024829): provide a register method.
+ if (!Settings.isInSystemServer() && !isSystemOrPrivilegedApp()
+ && mAllFields.contains(name) && !mReadableFields.contains(name)) {
+ throw new SecurityException(
+ "Settings key: <" + name + "> is not readable. From S+, new public "
+ + "settings keys need to be annotated with @Readable unless they are "
+ + "annotated with @hide.");
+ }
final boolean isSelf = (userHandle == UserHandle.myUserId());
int currentGeneration = -1;
if (isSelf) {
@@ -2900,6 +2936,19 @@ public final class Settings {
}
}
+ private static boolean isSystemOrPrivilegedApp() {
+ if (UserHandle.getAppId(Binder.getCallingUid()) < Process.FIRST_APPLICATION_UID) {
+ return true;
+ }
+ final Application application = ActivityThread.currentApplication();
+ if (application == null || application.getApplicationInfo() == null) {
+ return false;
+ }
+ final ApplicationInfo applicationInfo = application.getApplicationInfo();
+ return applicationInfo.isSystemApp() || applicationInfo.isPrivilegedApp()
+ || applicationInfo.isSignedWithPlatformKey();
+ }
+
public ArrayMap<String, String> getStringsForPrefix(ContentResolver cr, String prefix,
List<String> names) {
String namespace = prefix.substring(0, prefix.length() - 1);
@@ -3067,6 +3116,39 @@ public final class Settings {
}
/**
+ * This annotation indicates that the value of a setting is allowed to be read
+ * with the get* methods. The following settings should be readable:
+ * 1) all the public settings
+ * 2) all the hidden settings added before S
+ */
+ @Target({ ElementType.FIELD })
+ @Retention(RetentionPolicy.RUNTIME)
+ private @interface Readable {
+ }
+
+ private static <T extends NameValueTable> void getPublicSettingsForClass(
+ Class<T> callerClass, Set<String> allKeys, Set<String> readableKeys) {
+ final Field[] allFields = callerClass.getDeclaredFields();
+ try {
+ for (int i = 0; i < allFields.length; i++) {
+ final Field field = allFields[i];
+ if (!field.getType().equals(String.class)) {
+ continue;
+ }
+ final Object value = field.get(callerClass);
+ if (!value.getClass().equals(String.class)) {
+ continue;
+ }
+ allKeys.add((String) value);
+ if (field.getAnnotation(Readable.class) != null) {
+ readableKeys.add((String) value);
+ }
+ }
+ } catch (IllegalAccessException ignored) {
+ }
+ }
+
+ /**
* System settings, containing miscellaneous system preferences. This
* table holds simple name/value pairs. There are convenience
* functions for accessing individual settings entries.
@@ -3093,7 +3175,8 @@ public final class Settings {
CONTENT_URI,
CALL_METHOD_GET_SYSTEM,
CALL_METHOD_PUT_SYSTEM,
- sProviderHolder);
+ sProviderHolder,
+ System.class);
@UnsupportedAppUsage
private static final HashSet<String> MOVED_TO_SECURE;
@@ -3147,8 +3230,11 @@ public final class Settings {
MOVED_TO_SECURE_THEN_GLOBAL.add(Global.BLUETOOTH_ON);
MOVED_TO_SECURE_THEN_GLOBAL.add(Global.DATA_ROAMING);
MOVED_TO_SECURE_THEN_GLOBAL.add(Global.DEVICE_PROVISIONED);
- MOVED_TO_SECURE_THEN_GLOBAL.add(Global.USB_MASS_STORAGE_ENABLED);
MOVED_TO_SECURE_THEN_GLOBAL.add(Global.HTTP_PROXY);
+ MOVED_TO_SECURE_THEN_GLOBAL.add(Global.NETWORK_PREFERENCE);
+ MOVED_TO_SECURE_THEN_GLOBAL.add(Global.USB_MASS_STORAGE_ENABLED);
+ MOVED_TO_SECURE_THEN_GLOBAL.add(Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS);
+ MOVED_TO_SECURE_THEN_GLOBAL.add(Global.WIFI_MAX_DHCP_RETRY_COUNT);
// these are moving directly from system to global
MOVED_TO_GLOBAL.add(Settings.Global.AIRPLANE_MODE_ON);
@@ -3186,6 +3272,12 @@ public final class Settings {
MOVED_TO_GLOBAL.add(Settings.Global.SMS_SHORT_CODES_UPDATE_METADATA_URL);
MOVED_TO_GLOBAL.add(Settings.Global.CERT_PIN_UPDATE_CONTENT_URL);
MOVED_TO_GLOBAL.add(Settings.Global.CERT_PIN_UPDATE_METADATA_URL);
+ MOVED_TO_GLOBAL.add(Settings.Global.RADIO_NFC);
+ MOVED_TO_GLOBAL.add(Settings.Global.RADIO_CELL);
+ MOVED_TO_GLOBAL.add(Settings.Global.RADIO_WIFI);
+ MOVED_TO_GLOBAL.add(Settings.Global.RADIO_BLUETOOTH);
+ MOVED_TO_GLOBAL.add(Settings.Global.RADIO_WIMAX);
+ MOVED_TO_GLOBAL.add(Settings.Global.SHOW_PROCESSES);
}
/** @hide */
@@ -3210,6 +3302,11 @@ public final class Settings {
sNameValueCache.clearGenerationTrackerForTest();
}
+ /** @hide */
+ public static void getPublicSettings(Set<String> allKeys, Set<String> readableKeys) {
+ getPublicSettingsForClass(System.class, allKeys, readableKeys);
+ }
+
/**
* Look up a name in the database.
* @param resolver to access the database with
@@ -3235,6 +3332,7 @@ public final class Settings {
+ " to android.provider.Settings.Global, returning read-only value.");
return Global.getStringForUser(resolver, name, userHandle);
}
+
return sNameValueCache.getStringForUser(resolver, name, userHandle);
}
@@ -3713,6 +3811,7 @@ public final class Settings {
* 3 - The end button goes to the home screen. If the user is already on the
* home screen, it puts the device to sleep.
*/
+ @Readable
public static final String END_BUTTON_BEHAVIOR = "end_button_behavior";
/**
@@ -3737,6 +3836,7 @@ public final class Settings {
* Is advanced settings mode turned on. 0 == no, 1 == yes
* @hide
*/
+ @Readable
public static final String ADVANCED_SETTINGS = "advanced_settings";
/**
@@ -3837,6 +3937,7 @@ public final class Settings {
* @deprecated Use {@link WifiManager} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_USE_STATIC_IP = "wifi_use_static_ip";
/**
@@ -3847,6 +3948,7 @@ public final class Settings {
* @deprecated Use {@link WifiManager} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_STATIC_IP = "wifi_static_ip";
/**
@@ -3857,6 +3959,7 @@ public final class Settings {
* @deprecated Use {@link WifiManager} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_STATIC_GATEWAY = "wifi_static_gateway";
/**
@@ -3867,6 +3970,7 @@ public final class Settings {
* @deprecated Use {@link WifiManager} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_STATIC_NETMASK = "wifi_static_netmask";
/**
@@ -3877,6 +3981,7 @@ public final class Settings {
* @deprecated Use {@link WifiManager} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_STATIC_DNS1 = "wifi_static_dns1";
/**
@@ -3887,6 +3992,7 @@ public final class Settings {
* @deprecated Use {@link WifiManager} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_STATIC_DNS2 = "wifi_static_dns2";
/**
@@ -3897,6 +4003,7 @@ public final class Settings {
* 1 -- connectable but not discoverable
* 0 -- neither connectable nor discoverable
*/
+ @Readable
public static final String BLUETOOTH_DISCOVERABILITY =
"bluetooth_discoverability";
@@ -3905,6 +4012,7 @@ public final class Settings {
* Bluetooth becomes discoverable for a certain number of seconds,
* after which is becomes simply connectable. The value is in seconds.
*/
+ @Readable
public static final String BLUETOOTH_DISCOVERABILITY_TIMEOUT =
"bluetooth_discoverability_timeout";
@@ -3938,11 +4046,13 @@ public final class Settings {
* @deprecated Use {@link android.app.AlarmManager#getNextAlarmClock()}.
*/
@Deprecated
+ @Readable
public static final String NEXT_ALARM_FORMATTED = "next_alarm_formatted";
/**
* Scaling factor for fonts, float.
*/
+ @Readable
public static final String FONT_SCALE = "font_scale";
/**
@@ -3954,6 +4064,7 @@ public final class Settings {
* instead.
* @hide
*/
+ @Readable
public static final String SYSTEM_LOCALES = "system_locales";
@@ -3979,12 +4090,14 @@ public final class Settings {
* @deprecated This setting is no longer used.
*/
@Deprecated
+ @Readable
public static final String DIM_SCREEN = "dim_screen";
/**
* The display color mode.
* @hide
*/
+ @Readable
public static final String DISPLAY_COLOR_MODE = "display_color_mode";
/**
@@ -3992,6 +4105,7 @@ 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";
@@ -4008,6 +4122,7 @@ public final class Settings {
* If this isn't set, 0 will be used.
* @hide
*/
+ @Readable
public static final String MIN_REFRESH_RATE = "min_refresh_rate";
/**
@@ -4016,6 +4131,7 @@ public final class Settings {
* If this isn't set, the system falls back to a device specific default.
* @hide
*/
+ @Readable
public static final String PEAK_REFRESH_RATE = "peak_refresh_rate";
/**
@@ -4028,23 +4144,27 @@ public final class Settings {
* This value is bounded by maximum timeout set by
* {@link android.app.admin.DevicePolicyManager#setMaximumTimeToLock(ComponentName, long)}.
*/
+ @Readable
public static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout";
/**
* The screen backlight brightness between 0 and 255.
*/
+ @Readable
public static final String SCREEN_BRIGHTNESS = "screen_brightness";
/**
* The screen backlight brightness between 0 and 255.
* @hide
*/
+ @Readable
public static final String SCREEN_BRIGHTNESS_FOR_VR = "screen_brightness_for_vr";
/**
* The screen backlight brightness between 0.0f and 1.0f.
* @hide
*/
+ @Readable
public static final String SCREEN_BRIGHTNESS_FOR_VR_FLOAT =
"screen_brightness_for_vr_float";
@@ -4052,11 +4172,13 @@ public final class Settings {
* The screen backlight brightness between 0.0f and 1.0f.
* @hide
*/
+ @Readable
public static final String SCREEN_BRIGHTNESS_FLOAT = "screen_brightness_float";
/**
* Control whether to enable automatic brightness mode.
*/
+ @Readable
public static final String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode";
/**
@@ -4065,6 +4187,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String SCREEN_AUTO_BRIGHTNESS_ADJ = "screen_auto_brightness_adj";
/**
@@ -4082,6 +4205,8 @@ public final class Settings {
* @deprecated Use {@link android.provider.Settings.Secure#ADAPTIVE_SLEEP} instead.
* @hide
*/
+ @Deprecated
+ @Readable
public static final String ADAPTIVE_SLEEP = "adaptive_sleep";
/**
@@ -4108,6 +4233,7 @@ public final class Settings {
* stream type's bit should be set to 1 if it should be muted when going
* into an inaudible ringer mode.
*/
+ @Readable
public static final String MODE_RINGER_STREAMS_AFFECTED = "mode_ringer_streams_affected";
/**
@@ -4115,12 +4241,14 @@ public final class Settings {
* stream type's bit should be set to 1 if it should be muted when a mute request
* is received.
*/
+ @Readable
public static final String MUTE_STREAMS_AFFECTED = "mute_streams_affected";
/**
* Whether vibrate is on for different events. This is used internally,
* changing this value will not change the vibrate. See AudioManager.
*/
+ @Readable
public static final String VIBRATE_ON = "vibrate_on";
/**
@@ -4135,6 +4263,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String VIBRATE_INPUT_DEVICES = "vibrate_input_devices";
/**
@@ -4151,6 +4280,7 @@ public final class Settings {
* 3 - Strong vibrations
* @hide
*/
+ @Readable
public static final String NOTIFICATION_VIBRATION_INTENSITY =
"notification_vibration_intensity";
/**
@@ -4167,6 +4297,7 @@ public final class Settings {
* 3 - Strong vibrations
* @hide
*/
+ @Readable
public static final String RING_VIBRATION_INTENSITY =
"ring_vibration_intensity";
@@ -4184,6 +4315,7 @@ public final class Settings {
* 3 - Strong vibrations
* @hide
*/
+ @Readable
public static final String HAPTIC_FEEDBACK_INTENSITY =
"haptic_feedback_intensity";
@@ -4193,6 +4325,7 @@ public final class Settings {
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String VOLUME_RING = "volume_ring";
/**
@@ -4201,6 +4334,7 @@ public final class Settings {
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String VOLUME_SYSTEM = "volume_system";
/**
@@ -4209,6 +4343,7 @@ public final class Settings {
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String VOLUME_VOICE = "volume_voice";
/**
@@ -4217,6 +4352,7 @@ public final class Settings {
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String VOLUME_MUSIC = "volume_music";
/**
@@ -4225,6 +4361,7 @@ public final class Settings {
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String VOLUME_ALARM = "volume_alarm";
/**
@@ -4233,6 +4370,7 @@ public final class Settings {
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String VOLUME_NOTIFICATION = "volume_notification";
/**
@@ -4241,6 +4379,7 @@ public final class Settings {
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String VOLUME_BLUETOOTH_SCO = "volume_bluetooth_sco";
/**
@@ -4248,12 +4387,14 @@ public final class Settings {
* Acessibility volume. This is used internally, changing this
* value will not change the volume.
*/
+ @Readable
public static final String VOLUME_ACCESSIBILITY = "volume_a11y";
/**
* @hide
* Volume index for virtual assistant.
*/
+ @Readable
public static final String VOLUME_ASSISTANT = "volume_assistant";
/**
@@ -4261,6 +4402,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String VOLUME_MASTER = "volume_master";
/**
@@ -4269,6 +4411,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String MASTER_MONO = "master_mono";
/**
@@ -4276,6 +4419,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String MASTER_BALANCE = "master_balance";
/**
@@ -4293,6 +4437,7 @@ public final class Settings {
* @deprecated
*/
@Deprecated
+ @Readable
public static final String NOTIFICATIONS_USE_RING_VOLUME =
"notifications_use_ring_volume";
@@ -4309,6 +4454,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String VIBRATE_IN_SILENT = "vibrate_in_silent";
/**
@@ -4344,6 +4490,7 @@ public final class Settings {
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String APPEND_FOR_LAST_AUDIBLE = "_last_audible";
/**
@@ -4355,6 +4502,7 @@ public final class Settings {
*
* @see #DEFAULT_RINGTONE_URI
*/
+ @Readable
public static final String RINGTONE = "ringtone";
/**
@@ -4368,6 +4516,7 @@ public final class Settings {
public static final Uri DEFAULT_RINGTONE_URI = getUriFor(RINGTONE);
/** {@hide} */
+ @Readable
public static final String RINGTONE_CACHE = "ringtone_cache";
/** {@hide} */
public static final Uri RINGTONE_CACHE_URI = getUriFor(RINGTONE_CACHE);
@@ -4378,6 +4527,7 @@ public final class Settings {
* @see #RINGTONE
* @see #DEFAULT_NOTIFICATION_URI
*/
+ @Readable
public static final String NOTIFICATION_SOUND = "notification_sound";
/**
@@ -4389,6 +4539,7 @@ public final class Settings {
public static final Uri DEFAULT_NOTIFICATION_URI = getUriFor(NOTIFICATION_SOUND);
/** {@hide} */
+ @Readable
public static final String NOTIFICATION_SOUND_CACHE = "notification_sound_cache";
/** {@hide} */
public static final Uri NOTIFICATION_SOUND_CACHE_URI = getUriFor(NOTIFICATION_SOUND_CACHE);
@@ -4399,6 +4550,7 @@ public final class Settings {
* @see #RINGTONE
* @see #DEFAULT_ALARM_ALERT_URI
*/
+ @Readable
public static final String ALARM_ALERT = "alarm_alert";
/**
@@ -4410,6 +4562,7 @@ public final class Settings {
public static final Uri DEFAULT_ALARM_ALERT_URI = getUriFor(ALARM_ALERT);
/** {@hide} */
+ @Readable
public static final String ALARM_ALERT_CACHE = "alarm_alert_cache";
/** {@hide} */
public static final Uri ALARM_ALERT_CACHE_URI = getUriFor(ALARM_ALERT_CACHE);
@@ -4419,29 +4572,35 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";
/**
* Setting to enable Auto Replace (AutoText) in text editors. 1 = On, 0 = Off
*/
+ @Readable
public static final String TEXT_AUTO_REPLACE = "auto_replace";
/**
* Setting to enable Auto Caps in text editors. 1 = On, 0 = Off
*/
+ @Readable
public static final String TEXT_AUTO_CAPS = "auto_caps";
/**
* Setting to enable Auto Punctuate in text editors. 1 = On, 0 = Off. This
* feature converts two spaces to a "." and space.
*/
+ @Readable
public static final String TEXT_AUTO_PUNCTUATE = "auto_punctuate";
/**
* Setting to showing password characters in text editors. 1 = On, 0 = Off
*/
+ @Readable
public static final String TEXT_SHOW_PASSWORD = "show_password";
+ @Readable
public static final String SHOW_GTALK_SERVICE_STATUS =
"SHOW_GTALK_SERVICE_STATUS";
@@ -4451,6 +4610,7 @@ public final class Settings {
* @deprecated Use {@link WallpaperManager} instead.
*/
@Deprecated
+ @Readable
public static final String WALLPAPER_ACTIVITY = "wallpaper_activity";
/**
@@ -4472,6 +4632,7 @@ public final class Settings {
* 12
* 24
*/
+ @Readable
public static final String TIME_12_24 = "time_12_24";
/**
@@ -4480,6 +4641,7 @@ public final class Settings {
* dd/mm/yyyy
* yyyy/mm/dd
*/
+ @Readable
public static final String DATE_FORMAT = "date_format";
/**
@@ -4489,6 +4651,7 @@ public final class Settings {
* nonzero = it has been run in the past
* 0 = it has not been run in the past
*/
+ @Readable
public static final String SETUP_WIZARD_HAS_RUN = "setup_wizard_has_run";
/**
@@ -4525,6 +4688,7 @@ public final class Settings {
* by the application; if 1, it will be used by default unless explicitly
* disabled by the application.
*/
+ @Readable
public static final String ACCELEROMETER_ROTATION = "accelerometer_rotation";
/**
@@ -4535,6 +4699,7 @@ public final class Settings {
*
* @see Display#getRotation
*/
+ @Readable
public static final String USER_ROTATION = "user_rotation";
/**
@@ -4549,6 +4714,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY =
"hide_rotation_lock_toggle_for_accessibility";
@@ -4562,6 +4728,7 @@ public final class Settings {
* relied on the setting, while this is purely about the vibration setting for incoming
* calls.
*/
+ @Readable
public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
/**
@@ -4569,6 +4736,7 @@ public final class Settings {
* {@code 0}, enhanced call blocking functionality is disabled.
* @hide
*/
+ @Readable
public static final String DEBUG_ENABLE_ENHANCED_CALL_BLOCKING =
"debug.enable_enhanced_calling";
@@ -4576,6 +4744,7 @@ public final class Settings {
* Whether the audible DTMF tones are played by the dialer when dialing. The value is
* boolean (1 or 0).
*/
+ @Readable
public static final String DTMF_TONE_WHEN_DIALING = "dtmf_tone";
/**
@@ -4584,6 +4753,7 @@ public final class Settings {
* 0 = Normal
* 1 = Long
*/
+ @Readable
public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type";
/**
@@ -4592,6 +4762,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String HEARING_AID = "hearing_aid";
/**
@@ -4604,18 +4775,21 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String TTY_MODE = "tty_mode";
/**
* Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
* boolean (1 or 0).
*/
+ @Readable
public static final String SOUND_EFFECTS_ENABLED = "sound_effects_enabled";
/**
* Whether haptic feedback (Vibrate on tap) is enabled. The value is
* boolean (1 or 0).
*/
+ @Readable
public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled";
/**
@@ -4623,6 +4797,7 @@ public final class Settings {
* setting for this.
*/
@Deprecated
+ @Readable
public static final String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
/**
@@ -4631,6 +4806,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse";
/**
@@ -4640,6 +4816,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String POINTER_LOCATION = "pointer_location";
/**
@@ -4649,6 +4826,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String SHOW_TOUCHES = "show_touches";
/**
@@ -4659,6 +4837,7 @@ public final class Settings {
* 1 = yes
* @hide
*/
+ @Readable
public static final String WINDOW_ORIENTATION_LISTENER_LOG =
"window_orientation_listener_log";
@@ -4684,12 +4863,14 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String LOCKSCREEN_SOUNDS_ENABLED = "lockscreen_sounds_enabled";
/**
* Whether the lockscreen should be completely disabled.
* @hide
*/
+ @Readable
public static final String LOCKSCREEN_DISABLED = "lockscreen.disabled";
/**
@@ -4760,6 +4941,7 @@ public final class Settings {
* 1 = yes
* @hide
*/
+ @Readable
public static final String SIP_RECEIVE_CALLS = "sip_receive_calls";
/**
@@ -4768,18 +4950,21 @@ public final class Settings {
* "SIP_ADDRESS_ONLY" : Only if destination is a SIP address
* @hide
*/
+ @Readable
public static final String SIP_CALL_OPTIONS = "sip_call_options";
/**
* One of the sip call options: Always use SIP with network access.
* @hide
*/
+ @Readable
public static final String SIP_ALWAYS = "SIP_ALWAYS";
/**
* One of the sip call options: Only if destination is a SIP address.
* @hide
*/
+ @Readable
public static final String SIP_ADDRESS_ONLY = "SIP_ADDRESS_ONLY";
/**
@@ -4790,6 +4975,7 @@ public final class Settings {
* @hide
*/
@Deprecated
+ @Readable
public static final String SIP_ASK_ME_EACH_TIME = "SIP_ASK_ME_EACH_TIME";
/**
@@ -4801,12 +4987,14 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String POINTER_SPEED = "pointer_speed";
/**
* Whether lock-to-app will be triggered by long-press on recents.
* @hide
*/
+ @Readable
public static final String LOCK_TO_APP_ENABLED = "lock_to_app_enabled";
/**
@@ -4816,6 +5004,7 @@ public final class Settings {
* Backward-compatible with <code>PrefGetPreference(prefAllowEasterEggs)</code>.
* @hide
*/
+ @Readable
public static final String EGG_MODE = "egg_mode";
/**
@@ -4824,6 +5013,7 @@ public final class Settings {
* 1 - Show percentage
* @hide
*/
+ @Readable
public static final String SHOW_BATTERY_PERCENT = "status_bar_show_battery_percent";
/**
@@ -4832,6 +5022,7 @@ public final class Settings {
* for instance pausing media apps when another starts.
* @hide
*/
+ @Readable
public static final String MULTI_AUDIO_FOCUS_ENABLED = "multi_audio_focus_enabled";
/**
@@ -5027,6 +5218,7 @@ public final class Settings {
* @see android.telephony.TelephonyManager.WifiCallingChoices
* @hide
*/
+ @Readable
public static final String WHEN_TO_MAKE_WIFI_CALLS = "when_to_make_wifi_calls";
// Settings moved to Settings.Secure
@@ -5183,6 +5375,7 @@ public final class Settings {
* instead
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE =
Secure.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE;
@@ -5197,6 +5390,7 @@ public final class Settings {
* {@link android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS =
Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS;
@@ -5205,6 +5399,7 @@ public final class Settings {
* {@link android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED =
Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED;
@@ -5214,6 +5409,7 @@ public final class Settings {
* instead
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS =
Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS;
@@ -5222,6 +5418,7 @@ public final class Settings {
* {@link android.provider.Settings.Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT =
Secure.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT;
@@ -5256,6 +5453,7 @@ public final class Settings {
* instead
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS =
Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS;
@@ -5305,7 +5503,8 @@ public final class Settings {
CONTENT_URI,
CALL_METHOD_GET_SECURE,
CALL_METHOD_PUT_SECURE,
- sProviderHolder);
+ sProviderHolder,
+ Secure.class);
private static ILockSettings sLockSettings = null;
@@ -5444,6 +5643,11 @@ public final class Settings {
sNameValueCache.clearGenerationTrackerForTest();
}
+ /** @hide */
+ public static void getPublicSettings(Set<String> allKeys, Set<String> readableKeys) {
+ getPublicSettingsForClass(Secure.class, allKeys, readableKeys);
+ }
+
/**
* Look up a name in the database.
* @param resolver to access the database with
@@ -5939,6 +6143,7 @@ public final class Settings {
* Control whether to enable adaptive sleep mode.
* @hide
*/
+ @Readable
public static final String ADAPTIVE_SLEEP = "adaptive_sleep";
/**
@@ -5946,6 +6151,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CAMERA_AUTOROTATE = "camera_autorotate";
/**
@@ -5963,6 +6169,7 @@ public final class Settings {
* @hide
*/
@Deprecated
+ @Readable
public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu";
/**
@@ -5980,6 +6187,7 @@ public final class Settings {
* @deprecated This settings is not used anymore.
*/
@Deprecated
+ @Readable
public static final String ALLOW_MOCK_LOCATION = "mock_location";
/**
@@ -5988,6 +6196,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String ODI_CAPTIONS_ENABLED = "odi_captions_enabled";
/**
@@ -6027,6 +6236,7 @@ public final class Settings {
* to the Instant App, it is generated when the Instant App is first installed and reset if
* the user clears the Instant App.
*/
+ @Readable
public static final String ANDROID_ID = "android_id";
/**
@@ -6045,12 +6255,14 @@ public final class Settings {
* Setting to record the input method used by default, holding the ID
* of the desired method.
*/
+ @Readable
public static final String DEFAULT_INPUT_METHOD = "default_input_method";
/**
* Setting to record the input method subtype used by default, holding the ID
* of the desired method.
*/
+ @Readable
public static final String SELECTED_INPUT_METHOD_SUBTYPE =
"selected_input_method_subtype";
@@ -6059,12 +6271,14 @@ public final class Settings {
* and its last used subtype.
* @hide
*/
+ @Readable
public static final String INPUT_METHODS_SUBTYPE_HISTORY =
"input_methods_subtype_history";
/**
* Setting to record the visibility of input method selector
*/
+ @Readable
public static final String INPUT_METHOD_SELECTOR_VISIBILITY =
"input_method_selector_visibility";
@@ -6073,6 +6287,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
/**
@@ -6080,6 +6295,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String AUTOFILL_SERVICE = "autofill_service";
/**
@@ -6090,6 +6306,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION =
"autofill_field_classification";
@@ -6098,6 +6315,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String DARK_MODE_DIALOG_SEEN =
"dark_mode_dialog_seen";
@@ -6106,6 +6324,7 @@ public final class Settings {
* Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
* @hide
*/
+ @Readable
public static final String DARK_THEME_CUSTOM_START_TIME =
"dark_theme_custom_start_time";
@@ -6114,6 +6333,7 @@ public final class Settings {
* Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
* @hide
*/
+ @Readable
public static final String DARK_THEME_CUSTOM_END_TIME =
"dark_theme_custom_end_time";
@@ -6123,6 +6343,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE =
"autofill_user_data_max_user_data_size";
@@ -6133,6 +6354,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE =
"autofill_user_data_max_field_classification_size";
@@ -6143,6 +6365,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT =
"autofill_user_data_max_category_count";
@@ -6152,6 +6375,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH =
"autofill_user_data_max_value_length";
@@ -6161,6 +6385,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH =
"autofill_user_data_min_value_length";
@@ -6173,6 +6398,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String CONTENT_CAPTURE_ENABLED = "content_capture_enabled";
/**
@@ -6190,6 +6416,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String MANAGED_PROVISIONING_DPC_DOWNLOADED =
"managed_provisioning_dpc_downloaded";
@@ -6200,6 +6427,7 @@ public final class Settings {
* <p>
* Type: int (0 for false, 1 for true)
*/
+ @Readable
public static final String SECURE_FRP_MODE = "secure_frp_mode";
/**
@@ -6210,6 +6438,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String USER_SETUP_COMPLETE = "user_setup_complete";
/**
@@ -6265,6 +6494,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String USER_SETUP_PERSONALIZATION_STATE =
"user_setup_personalization_state";
@@ -6275,6 +6505,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String TV_USER_SETUP_COMPLETE = "tv_user_setup_complete";
/**
@@ -6286,6 +6517,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
/**
@@ -6296,6 +6528,7 @@ public final class Settings {
* Format like "ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0"
* where imeId is ComponentName and subtype is int32.
*/
+ @Readable
public static final String ENABLED_INPUT_METHODS = "enabled_input_methods";
/**
@@ -6304,6 +6537,7 @@ public final class Settings {
* by ':'.
* @hide
*/
+ @Readable
public static final String DISABLED_SYSTEM_INPUT_METHODS = "disabled_system_input_methods";
/**
@@ -6312,6 +6546,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
@SuppressLint("NoSettingsProvider")
public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
@@ -6329,6 +6564,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ALWAYS_ON_VPN_APP = "always_on_vpn_app";
/**
@@ -6337,6 +6573,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ALWAYS_ON_VPN_LOCKDOWN = "always_on_vpn_lockdown";
/**
@@ -6346,6 +6583,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ALWAYS_ON_VPN_LOCKDOWN_WHITELIST =
"always_on_vpn_lockdown_whitelist";
@@ -6359,6 +6597,8 @@ public final class Settings {
* {@link PackageManager#canRequestPackageInstalls()}
* @see PackageManager#canRequestPackageInstalls()
*/
+ @Deprecated
+ @Readable
public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
/**
@@ -6370,6 +6610,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String UNKNOWN_SOURCES_DEFAULT_REVERSED =
"unknown_sources_default_reversed";
@@ -6383,6 +6624,7 @@ public final class Settings {
* instead.
*/
@Deprecated
+ @Readable
public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
/**
@@ -6394,12 +6636,14 @@ public final class Settings {
* {@link LocationManager#MODE_CHANGED_ACTION}.
*/
@Deprecated
+ @Readable
public static final String LOCATION_MODE = "location_mode";
/**
* The App or module that changes the location mode.
* @hide
*/
+ @Readable
public static final String LOCATION_CHANGER = "location_changer";
/**
@@ -6468,6 +6712,7 @@ 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";
@@ -6477,6 +6722,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String LOCATION_COARSE_ACCURACY_M = "locationCoarseAccuracy";
/**
@@ -6484,6 +6730,7 @@ public final class Settings {
* @hide
*/
@Deprecated
+ @Readable
public static final String LOCK_BIOMETRIC_WEAK_FLAGS =
"lock_biometric_weak_flags";
@@ -6491,6 +6738,7 @@ public final class Settings {
* Whether lock-to-app will lock the keyguard when exiting.
* @hide
*/
+ @Readable
public static final String LOCK_TO_APP_EXIT_LOCKED = "lock_to_app_exit_locked";
/**
@@ -6501,6 +6749,7 @@ public final class Settings {
* {@link VERSION_CODES#M} or later throws a {@code SecurityException}.
*/
@Deprecated
+ @Readable
public static final String LOCK_PATTERN_ENABLED = "lock_pattern_autolock";
/**
@@ -6510,6 +6759,7 @@ public final class Settings {
* {@link VERSION_CODES#M} or later throws a {@code SecurityException}.
*/
@Deprecated
+ @Readable
public static final String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
/**
@@ -6523,6 +6773,7 @@ public final class Settings {
* {@link VERSION_CODES#M} or later throws a {@code SecurityException}.
*/
@Deprecated
+ @Readable
public static final String
LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
@@ -6532,6 +6783,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String LOCK_SCREEN_LOCK_AFTER_TIMEOUT = "lock_screen_lock_after_timeout";
@@ -6541,6 +6793,7 @@ public final class Settings {
* @deprecated
*/
@Deprecated
+ @Readable
public static final String LOCK_SCREEN_OWNER_INFO = "lock_screen_owner_info";
/**
@@ -6548,6 +6801,7 @@ public final class Settings {
* @hide
*/
@Deprecated
+ @Readable
public static final String LOCK_SCREEN_APPWIDGET_IDS =
"lock_screen_appwidget_ids";
@@ -6556,6 +6810,7 @@ public final class Settings {
* @hide
*/
@Deprecated
+ @Readable
public static final String LOCK_SCREEN_FALLBACK_APPWIDGET_ID =
"lock_screen_fallback_appwidget_id";
@@ -6564,6 +6819,7 @@ public final class Settings {
* @hide
*/
@Deprecated
+ @Readable
public static final String LOCK_SCREEN_STICKY_APPWIDGET =
"lock_screen_sticky_appwidget";
@@ -6574,6 +6830,7 @@ public final class Settings {
*/
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
"lock_screen_owner_info_enabled";
@@ -6586,6 +6843,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS =
"lock_screen_allow_private_notifications";
@@ -6594,6 +6852,7 @@ public final class Settings {
* without having to unlock
* @hide
*/
+ @Readable
public static final String LOCK_SCREEN_ALLOW_REMOTE_INPUT =
"lock_screen_allow_remote_input";
@@ -6603,12 +6862,14 @@ public final class Settings {
* {"clock": id, "_applied_timestamp": timestamp}
* @hide
*/
+ @Readable
public static final String LOCK_SCREEN_CUSTOM_CLOCK_FACE = "lock_screen_custom_clock_face";
/**
* Indicates which clock face to show on lock screen and AOD while docked.
* @hide
*/
+ @Readable
public static final String DOCKED_CLOCK_FACE = "docked_clock_face";
/**
@@ -6616,6 +6877,7 @@ public final class Settings {
* the lockscreen notification policy.
* @hide
*/
+ @Readable
public static final String SHOW_NOTE_ABOUT_NOTIFICATION_HIDING =
"show_note_about_notification_hiding";
@@ -6623,6 +6885,7 @@ public final class Settings {
* Set to 1 by the system after trust agents have been initialized.
* @hide
*/
+ @Readable
public static final String TRUST_AGENTS_INITIALIZED =
"trust_agents_initialized";
@@ -6633,6 +6896,7 @@ public final class Settings {
* many collisions. It should not be used.
*/
@Deprecated
+ @Readable
public static final String LOGGING_ID = "logging_id";
/**
@@ -6644,16 +6908,19 @@ public final class Settings {
/**
* No longer supported.
*/
+ @Readable
public static final String PARENTAL_CONTROL_ENABLED = "parental_control_enabled";
/**
* No longer supported.
*/
+ @Readable
public static final String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update";
/**
* No longer supported.
*/
+ @Readable
public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
/**
@@ -6662,6 +6929,7 @@ public final class Settings {
* and new Settings apps.
*/
// TODO: 881807
+ @Readable
public static final String SETTINGS_CLASSNAME = "settings_classname";
/**
@@ -6679,12 +6947,14 @@ public final class Settings {
/**
* If accessibility is enabled.
*/
+ @Readable
public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled";
/**
* Setting specifying if the accessibility shortcut is enabled.
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN =
"accessibility_shortcut_on_lock_screen";
@@ -6692,6 +6962,7 @@ public final class Settings {
* Setting specifying if the accessibility shortcut dialog has been shown to this user.
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN =
"accessibility_shortcut_dialog_shown";
@@ -6706,6 +6977,7 @@ public final class Settings {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @Readable
public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE =
"accessibility_shortcut_target_service";
@@ -6716,6 +6988,7 @@ public final class Settings {
* accessibility feature.
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_BUTTON_TARGET_COMPONENT =
"accessibility_button_target_component";
@@ -6728,6 +7001,7 @@ public final class Settings {
* accessibility feature.
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_BUTTON_TARGETS = "accessibility_button_targets";
/**
@@ -6736,17 +7010,20 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER =
"com.android.server.accessibility.MagnificationController";
/**
* If touch exploration is enabled.
*/
+ @Readable
public static final String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
/**
* List of the enabled accessibility providers.
*/
+ @Readable
public static final String ENABLED_ACCESSIBILITY_SERVICES =
"enabled_accessibility_services";
@@ -6756,6 +7033,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES =
"touch_exploration_granted_accessibility_services";
@@ -6763,12 +7041,14 @@ public final class Settings {
* Whether the Global Actions Panel is enabled.
* @hide
*/
+ @Readable
public static final String GLOBAL_ACTIONS_PANEL_ENABLED = "global_actions_panel_enabled";
/**
* Whether the Global Actions Panel can be toggled on or off in Settings.
* @hide
*/
+ @Readable
public static final String GLOBAL_ACTIONS_PANEL_AVAILABLE =
"global_actions_panel_available";
@@ -6776,6 +7056,7 @@ public final class Settings {
* Enables debug mode for the Global Actions Panel.
* @hide
*/
+ @Readable
public static final String GLOBAL_ACTIONS_PANEL_DEBUG_ENABLED =
"global_actions_panel_debug_enabled";
@@ -6784,24 +7065,28 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String HUSH_GESTURE_USED = "hush_gesture_used";
/**
* Number of times the user has manually clicked the ringer toggle
* @hide
*/
+ @Readable
public static final String MANUAL_RINGER_TOGGLE_COUNT = "manual_ringer_toggle_count";
/**
* Whether to play a sound for charging events.
* @hide
*/
+ @Readable
public static final String CHARGING_SOUNDS_ENABLED = "charging_sounds_enabled";
/**
* Whether to vibrate for charging events.
* @hide
*/
+ @Readable
public static final String CHARGING_VIBRATION_ENABLED = "charging_vibration_enabled";
/**
@@ -6811,6 +7096,7 @@ public final class Settings {
* user to specify a duration.
* @hide
*/
+ @Readable
public static final String ZEN_DURATION = "zen_duration";
/** @hide */ public static final int ZEN_DURATION_PROMPT = -1;
@@ -6820,24 +7106,28 @@ public final class Settings {
* If nonzero, will show the zen upgrade notification when the user toggles DND on/off.
* @hide
*/
+ @Readable
public static final String SHOW_ZEN_UPGRADE_NOTIFICATION = "show_zen_upgrade_notification";
/**
* If nonzero, will show the zen update settings suggestion.
* @hide
*/
+ @Readable
public static final String SHOW_ZEN_SETTINGS_SUGGESTION = "show_zen_settings_suggestion";
/**
* If nonzero, zen has not been updated to reflect new changes.
* @hide
*/
+ @Readable
public static final String ZEN_SETTINGS_UPDATED = "zen_settings_updated";
/**
* If nonzero, zen setting suggestion has been viewed by user
* @hide
*/
+ @Readable
public static final String ZEN_SETTINGS_SUGGESTION_VIEWED =
"zen_settings_suggestion_viewed";
@@ -6846,6 +7136,7 @@ public final class Settings {
* boolean (1 or 0).
* @hide
*/
+ @Readable
public static final String IN_CALL_NOTIFICATION_ENABLED = "in_call_notification_enabled";
/**
@@ -6854,6 +7145,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String KEYGUARD_SLICE_URI = "keyguard_slice_uri";
/**
@@ -6866,6 +7158,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String FONT_WEIGHT_ADJUSTMENT = "font_weight_adjustment";
/**
@@ -6876,6 +7169,7 @@ public final class Settings {
* at all times, which was the behavior when this value was {@code true}.
*/
@Deprecated
+ @Readable
public static final String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
/**
@@ -6883,6 +7177,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED =
"high_text_contrast_enabled";
@@ -6896,6 +7191,7 @@ public final class Settings {
*/
@UnsupportedAppUsage
@TestApi
+ @Readable
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED =
"accessibility_display_magnification_enabled";
@@ -6911,6 +7207,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED =
"accessibility_display_magnification_navbar_enabled";
@@ -6924,6 +7221,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE =
"accessibility_display_magnification_scale";
@@ -6934,6 +7232,7 @@ public final class Settings {
* @deprecated
*/
@Deprecated
+ @Readable
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE =
"accessibility_display_magnification_auto_update";
@@ -6943,6 +7242,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_SOFT_KEYBOARD_MODE =
"accessibility_soft_keyboard_mode";
@@ -6976,6 +7276,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_ENABLED =
"accessibility_captioning_enabled";
@@ -6986,6 +7287,7 @@ public final class Settings {
* @see java.util.Locale#toString
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_LOCALE =
"accessibility_captioning_locale";
@@ -7000,6 +7302,7 @@ public final class Settings {
* @see java.util.Locale#toString
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_PRESET =
"accessibility_captioning_preset";
@@ -7010,6 +7313,7 @@ public final class Settings {
* @see android.graphics.Color#argb
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR =
"accessibility_captioning_background_color";
@@ -7020,6 +7324,7 @@ public final class Settings {
* @see android.graphics.Color#argb
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR =
"accessibility_captioning_foreground_color";
@@ -7034,6 +7339,7 @@ public final class Settings {
* @see #ACCESSIBILITY_CAPTIONING_EDGE_COLOR
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_EDGE_TYPE =
"accessibility_captioning_edge_type";
@@ -7045,6 +7351,7 @@ public final class Settings {
* @see android.graphics.Color#argb
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_EDGE_COLOR =
"accessibility_captioning_edge_color";
@@ -7055,6 +7362,7 @@ public final class Settings {
* @see android.graphics.Color#argb
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_WINDOW_COLOR =
"accessibility_captioning_window_color";
@@ -7071,6 +7379,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_TYPEFACE =
"accessibility_captioning_typeface";
@@ -7079,12 +7388,14 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_FONT_SCALE =
"accessibility_captioning_font_scale";
/**
* Setting that specifies whether display color inversion is enabled.
*/
+ @Readable
public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED =
"accessibility_display_inversion_enabled";
@@ -7095,6 +7406,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED =
"accessibility_display_daltonizer_enabled";
@@ -7111,6 +7423,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String ACCESSIBILITY_DISPLAY_DALTONIZER =
"accessibility_display_daltonizer";
@@ -7121,6 +7434,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String ACCESSIBILITY_AUTOCLICK_ENABLED =
"accessibility_autoclick_enabled";
@@ -7131,6 +7445,7 @@ public final class Settings {
* @see #ACCESSIBILITY_AUTOCLICK_ENABLED
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_AUTOCLICK_DELAY =
"accessibility_autoclick_delay";
@@ -7141,6 +7456,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String ACCESSIBILITY_LARGE_POINTER_ICON =
"accessibility_large_pointer_icon";
@@ -7149,6 +7465,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String LONG_PRESS_TIMEOUT = "long_press_timeout";
/**
@@ -7156,6 +7473,7 @@ public final class Settings {
* down event for an interaction to be considered part of the same multi-press.
* @hide
*/
+ @Readable
public static final String MULTI_PRESS_TIMEOUT = "multi_press_timeout";
/**
@@ -7164,6 +7482,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS =
"accessibility_non_interactive_ui_timeout_ms";
@@ -7173,6 +7492,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS =
"accessibility_interactive_ui_timeout_ms";
@@ -7183,6 +7503,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String REDUCE_BRIGHT_COLORS_ACTIVATED =
"reduce_bright_colors_activated";
@@ -7192,6 +7513,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String REDUCE_BRIGHT_COLORS_LEVEL =
"reduce_bright_colors_level";
@@ -7200,6 +7522,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS =
"reduce_bright_colors_persist_across_reboots";
@@ -7212,6 +7535,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String ENABLED_PRINT_SERVICES =
"enabled_print_services";
@@ -7221,6 +7545,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String DISABLED_PRINT_SERVICES =
"disabled_print_services";
@@ -7231,6 +7556,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String DISPLAY_DENSITY_FORCED = "display_density_forced";
/**
@@ -7243,21 +7569,25 @@ public final class Settings {
* the framework text to speech APIs as of the Ice Cream Sandwich release.
*/
@Deprecated
+ @Readable
public static final String TTS_USE_DEFAULTS = "tts_use_defaults";
/**
* Default text-to-speech engine speech rate. 100 = 1x
*/
+ @Readable
public static final String TTS_DEFAULT_RATE = "tts_default_rate";
/**
* Default text-to-speech engine pitch. 100 = 1x
*/
+ @Readable
public static final String TTS_DEFAULT_PITCH = "tts_default_pitch";
/**
* Default text-to-speech engine.
*/
+ @Readable
public static final String TTS_DEFAULT_SYNTH = "tts_default_synth";
/**
@@ -7269,6 +7599,7 @@ public final class Settings {
* locale. {@link TextToSpeech#getLanguage()}.
*/
@Deprecated
+ @Readable
public static final String TTS_DEFAULT_LANG = "tts_default_lang";
/**
@@ -7280,6 +7611,7 @@ public final class Settings {
* locale. {@link TextToSpeech#getLanguage()}.
*/
@Deprecated
+ @Readable
public static final String TTS_DEFAULT_COUNTRY = "tts_default_country";
/**
@@ -7291,6 +7623,7 @@ public final class Settings {
* locale that is in use {@link TextToSpeech#getLanguage()}.
*/
@Deprecated
+ @Readable
public static final String TTS_DEFAULT_VARIANT = "tts_default_variant";
/**
@@ -7305,11 +7638,13 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String TTS_DEFAULT_LOCALE = "tts_default_locale";
/**
* Space delimited list of plugin packages that are enabled.
*/
+ @Readable
public static final String TTS_ENABLED_PLUGINS = "tts_enabled_plugins";
/**
@@ -7349,6 +7684,7 @@ public final class Settings {
* @deprecated This setting is not used.
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE =
"wifi_watchdog_acceptable_packet_loss_percentage";
@@ -7358,6 +7694,7 @@ public final class Settings {
* @deprecated This setting is not used.
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_AP_COUNT = "wifi_watchdog_ap_count";
/**
@@ -7365,6 +7702,7 @@ public final class Settings {
* @deprecated This setting is not used.
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS =
"wifi_watchdog_background_check_delay_ms";
@@ -7374,6 +7712,7 @@ public final class Settings {
* @deprecated This setting is not used.
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED =
"wifi_watchdog_background_check_enabled";
@@ -7382,6 +7721,7 @@ public final class Settings {
* @deprecated This setting is not used.
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS =
"wifi_watchdog_background_check_timeout_ms";
@@ -7393,6 +7733,7 @@ public final class Settings {
* @deprecated This setting is not used.
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT =
"wifi_watchdog_initial_ignored_ping_count";
@@ -7404,12 +7745,14 @@ public final class Settings {
* @deprecated This setting is not used.
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_MAX_AP_CHECKS = "wifi_watchdog_max_ap_checks";
/**
* @deprecated Use {@link android.provider.Settings.Global#WIFI_WATCHDOG_ON} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_ON = "wifi_watchdog_on";
/**
@@ -7417,6 +7760,7 @@ public final class Settings {
* @deprecated This setting is not used.
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_WATCH_LIST = "wifi_watchdog_watch_list";
/**
@@ -7424,6 +7768,7 @@ public final class Settings {
* @deprecated This setting is not used.
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_PING_COUNT = "wifi_watchdog_ping_count";
/**
@@ -7431,6 +7776,7 @@ public final class Settings {
* @deprecated This setting is not used.
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_PING_DELAY_MS = "wifi_watchdog_ping_delay_ms";
/**
@@ -7438,6 +7784,7 @@ public final class Settings {
* @deprecated This setting is not used.
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS = "wifi_watchdog_ping_timeout_ms";
/**
@@ -7462,6 +7809,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS =
"connectivity_release_pending_intent_delay_ms";
@@ -7475,12 +7823,14 @@ public final class Settings {
* now appear disconnected.
*/
@Deprecated
+ @Readable
public static final String BACKGROUND_DATA = "background_data";
/**
* Origins for which browsers should allow geolocation by default.
* The value is a space-separated list of origins.
*/
+ @Readable
public static final String ALLOWED_GEOLOCATION_ORIGINS
= "allowed_geolocation_origins";
@@ -7491,6 +7841,7 @@ public final class Settings {
* 3 = TTY VCO
* @hide
*/
+ @Readable
public static final String PREFERRED_TTY_MODE =
"preferred_tty_mode";
@@ -7500,6 +7851,7 @@ public final class Settings {
* 1 = enhanced voice privacy
* @hide
*/
+ @Readable
public static final String ENHANCED_VOICE_PRIVACY_ENABLED = "enhanced_voice_privacy_enabled";
/**
@@ -7508,6 +7860,7 @@ public final class Settings {
* 1 = enabled
* @hide
*/
+ @Readable
public static final String TTY_MODE_ENABLED = "tty_mode_enabled";
/**
@@ -7516,6 +7869,7 @@ public final class Settings {
* 0 = OFF
* 1 = ON
*/
+ @Readable
public static final String RTT_CALLING_MODE = "rtt_calling_mode";
/**
@@ -7525,6 +7879,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String BACKUP_ENABLED = "backup_enabled";
/**
@@ -7534,6 +7889,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String BACKUP_AUTO_RESTORE = "backup_auto_restore";
/**
@@ -7542,6 +7898,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String BACKUP_PROVISIONED = "backup_provisioned";
/**
@@ -7549,6 +7906,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String BACKUP_TRANSPORT = "backup_transport";
/**
@@ -7558,6 +7916,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String LAST_SETUP_SHOWN = "last_setup_shown";
/**
@@ -7580,6 +7939,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String SEARCH_GLOBAL_SEARCH_ACTIVITY =
"search_global_search_activity";
@@ -7587,21 +7947,25 @@ public final class Settings {
* The number of promoted sources in GlobalSearch.
* @hide
*/
+ @Readable
public static final String SEARCH_NUM_PROMOTED_SOURCES = "search_num_promoted_sources";
/**
* The maximum number of suggestions returned by GlobalSearch.
* @hide
*/
+ @Readable
public static final String SEARCH_MAX_RESULTS_TO_DISPLAY = "search_max_results_to_display";
/**
* The number of suggestions GlobalSearch will ask each non-web search source for.
* @hide
*/
+ @Readable
public static final String SEARCH_MAX_RESULTS_PER_SOURCE = "search_max_results_per_source";
/**
* The number of suggestions the GlobalSearch will ask the web search source for.
* @hide
*/
+ @Readable
public static final String SEARCH_WEB_RESULTS_OVERRIDE_LIMIT =
"search_web_results_override_limit";
/**
@@ -7609,69 +7973,81 @@ public final class Settings {
* promoted sources before continuing with all other sources.
* @hide
*/
+ @Readable
public static final String SEARCH_PROMOTED_SOURCE_DEADLINE_MILLIS =
"search_promoted_source_deadline_millis";
/**
* The number of milliseconds before GlobalSearch aborts search suggesiton queries.
* @hide
*/
+ @Readable
public static final String SEARCH_SOURCE_TIMEOUT_MILLIS = "search_source_timeout_millis";
/**
* The maximum number of milliseconds that GlobalSearch shows the previous results
* after receiving a new query.
* @hide
*/
+ @Readable
public static final String SEARCH_PREFILL_MILLIS = "search_prefill_millis";
/**
* The maximum age of log data used for shortcuts in GlobalSearch.
* @hide
*/
+ @Readable
public static final String SEARCH_MAX_STAT_AGE_MILLIS = "search_max_stat_age_millis";
/**
* The maximum age of log data used for source ranking in GlobalSearch.
* @hide
*/
+ @Readable
public static final String SEARCH_MAX_SOURCE_EVENT_AGE_MILLIS =
"search_max_source_event_age_millis";
/**
* The minimum number of impressions needed to rank a source in GlobalSearch.
* @hide
*/
+ @Readable
public static final String SEARCH_MIN_IMPRESSIONS_FOR_SOURCE_RANKING =
"search_min_impressions_for_source_ranking";
/**
* The minimum number of clicks needed to rank a source in GlobalSearch.
* @hide
*/
+ @Readable
public static final String SEARCH_MIN_CLICKS_FOR_SOURCE_RANKING =
"search_min_clicks_for_source_ranking";
/**
* The maximum number of shortcuts shown by GlobalSearch.
* @hide
*/
+ @Readable
public static final String SEARCH_MAX_SHORTCUTS_RETURNED = "search_max_shortcuts_returned";
/**
* The size of the core thread pool for suggestion queries in GlobalSearch.
* @hide
*/
+ @Readable
public static final String SEARCH_QUERY_THREAD_CORE_POOL_SIZE =
"search_query_thread_core_pool_size";
/**
* The maximum size of the thread pool for suggestion queries in GlobalSearch.
* @hide
*/
+ @Readable
public static final String SEARCH_QUERY_THREAD_MAX_POOL_SIZE =
"search_query_thread_max_pool_size";
/**
* The size of the core thread pool for shortcut refreshing in GlobalSearch.
* @hide
*/
+ @Readable
public static final String SEARCH_SHORTCUT_REFRESH_CORE_POOL_SIZE =
"search_shortcut_refresh_core_pool_size";
/**
* The maximum size of the thread pool for shortcut refreshing in GlobalSearch.
* @hide
*/
+ @Readable
public static final String SEARCH_SHORTCUT_REFRESH_MAX_POOL_SIZE =
"search_shortcut_refresh_max_pool_size";
/**
@@ -7679,12 +8055,14 @@ public final class Settings {
* wait before terminating.
* @hide
*/
+ @Readable
public static final String SEARCH_THREAD_KEEPALIVE_SECONDS =
"search_thread_keepalive_seconds";
/**
* The maximum number of concurrent suggestion queries to each source.
* @hide
*/
+ @Readable
public static final String SEARCH_PER_SOURCE_CONCURRENT_QUERY_LIMIT =
"search_per_source_concurrent_query_limit";
@@ -7693,24 +8071,28 @@ public final class Settings {
* (0 = false, 1 = true)
* @hide
*/
+ @Readable
public static final String MOUNT_PLAY_NOTIFICATION_SND = "mount_play_not_snd";
/**
* Whether or not UMS auto-starts on UMS host detection. (0 = false, 1 = true)
* @hide
*/
+ @Readable
public static final String MOUNT_UMS_AUTOSTART = "mount_ums_autostart";
/**
* Whether or not a notification is displayed on UMS host detection. (0 = false, 1 = true)
* @hide
*/
+ @Readable
public static final String MOUNT_UMS_PROMPT = "mount_ums_prompt";
/**
* Whether or not a notification is displayed while UMS is enabled. (0 = false, 1 = true)
* @hide
*/
+ @Readable
public static final String MOUNT_UMS_NOTIFY_ENABLED = "mount_ums_notify_enabled";
/**
@@ -7722,6 +8104,7 @@ public final class Settings {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @Readable
@SuppressLint("NoSettingsProvider")
public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
@@ -7731,6 +8114,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
@SuppressLint("NoSettingsProvider")
public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION =
"show_first_crash_dialog_dev_option";
@@ -7742,6 +8126,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String VOICE_RECOGNITION_SERVICE = "voice_recognition_service";
/**
@@ -7752,6 +8137,7 @@ public final class Settings {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @Readable
@SuppressLint("NoSettingsProvider")
public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker";
@@ -7764,6 +8150,7 @@ public final class Settings {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @Readable
@SuppressLint("NoSettingsProvider")
public static final String SELECTED_SPELL_CHECKER_SUBTYPE =
"selected_spell_checker_subtype";
@@ -7773,6 +8160,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String SPELL_CHECKER_ENABLED = "spell_checker_enabled";
/**
@@ -7785,6 +8173,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String INCALL_POWER_BUTTON_BEHAVIOR = "incall_power_button_behavior";
/**
@@ -7799,6 +8188,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String MINIMAL_POST_PROCESSING_ALLOWED =
"minimal_post_processing_allowed";
@@ -7843,6 +8233,7 @@ public final class Settings {
* @see #MATCH_CONTENT_FRAMERATE_ALWAYS
* @hide
*/
+ @Readable
public static final String MATCH_CONTENT_FRAME_RATE =
"match_content_frame_rate";
@@ -7874,6 +8265,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String INCALL_BACK_BUTTON_BEHAVIOR = "incall_back_button_behavior";
/**
@@ -7899,6 +8291,7 @@ public final class Settings {
* Whether the device should wake when the wake gesture sensor detects motion.
* @hide
*/
+ @Readable
public static final String WAKE_GESTURE_ENABLED = "wake_gesture_enabled";
/**
@@ -7906,6 +8299,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String DOZE_ENABLED = "doze_enabled";
/**
@@ -7916,36 +8310,42 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String DOZE_ALWAYS_ON = "doze_always_on";
/**
* Whether the device should pulse on pick up gesture.
* @hide
*/
+ @Readable
public static final String DOZE_PICK_UP_GESTURE = "doze_pulse_on_pick_up";
/**
* Whether the device should pulse on long press gesture.
* @hide
*/
+ @Readable
public static final String DOZE_PULSE_ON_LONG_PRESS = "doze_pulse_on_long_press";
/**
* Whether the device should pulse on double tap gesture.
* @hide
*/
+ @Readable
public static final String DOZE_DOUBLE_TAP_GESTURE = "doze_pulse_on_double_tap";
/**
* Whether the device should respond to the SLPI tap gesture.
* @hide
*/
+ @Readable
public static final String DOZE_TAP_SCREEN_GESTURE = "doze_tap_gesture";
/**
* Gesture that wakes up the display, showing some version of the lock screen.
* @hide
*/
+ @Readable
public static final String DOZE_WAKE_LOCK_SCREEN_GESTURE = "doze_wake_screen_gesture";
/**
@@ -7953,84 +8353,98 @@ public final class Settings {
* {@link Display.STATE_DOZE}.
* @hide
*/
+ @Readable
public static final String DOZE_WAKE_DISPLAY_GESTURE = "doze_wake_display_gesture";
/**
* Whether the device should suppress the current doze configuration and disable dozing.
* @hide
*/
+ @Readable
public static final String SUPPRESS_DOZE = "suppress_doze";
/**
* Gesture that skips media.
* @hide
*/
+ @Readable
public static final String SKIP_GESTURE = "skip_gesture";
/**
* Count of successful gestures.
* @hide
*/
+ @Readable
public static final String SKIP_GESTURE_COUNT = "skip_gesture_count";
/**
* Count of non-gesture interaction.
* @hide
*/
+ @Readable
public static final String SKIP_TOUCH_COUNT = "skip_touch_count";
/**
* Direction to advance media for skip gesture
* @hide
*/
+ @Readable
public static final String SKIP_DIRECTION = "skip_gesture_direction";
/**
* Gesture that silences sound (alarms, notification, calls).
* @hide
*/
+ @Readable
public static final String SILENCE_GESTURE = "silence_gesture";
/**
* Count of successful silence alarms gestures.
* @hide
*/
+ @Readable
public static final String SILENCE_ALARMS_GESTURE_COUNT = "silence_alarms_gesture_count";
/**
* Count of successful silence timer gestures.
* @hide
*/
+ @Readable
public static final String SILENCE_TIMER_GESTURE_COUNT = "silence_timer_gesture_count";
/**
* Count of successful silence call gestures.
* @hide
*/
+ @Readable
public static final String SILENCE_CALL_GESTURE_COUNT = "silence_call_gesture_count";
/**
* Count of non-gesture interaction.
* @hide
*/
+ @Readable
public static final String SILENCE_ALARMS_TOUCH_COUNT = "silence_alarms_touch_count";
/**
* Count of non-gesture interaction.
* @hide
*/
+ @Readable
public static final String SILENCE_TIMER_TOUCH_COUNT = "silence_timer_touch_count";
/**
* Count of non-gesture interaction.
* @hide
*/
+ @Readable
public static final String SILENCE_CALL_TOUCH_COUNT = "silence_call_touch_count";
/**
* Number of successful "Motion Sense" tap gestures to pause media.
* @hide
*/
+ @Readable
public static final String AWARE_TAP_PAUSE_GESTURE_COUNT = "aware_tap_pause_gesture_count";
/**
@@ -8038,12 +8452,14 @@ public final class Settings {
* have been used.
* @hide
*/
+ @Readable
public static final String AWARE_TAP_PAUSE_TOUCH_COUNT = "aware_tap_pause_touch_count";
/**
* 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";
@@ -8051,24 +8467,28 @@ 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";
@@ -8078,6 +8498,7 @@ public final class Settings {
* UiModeManager.
* @hide
*/
+ @Readable
public static final String UI_NIGHT_MODE = "ui_night_mode";
/**
@@ -8086,12 +8507,14 @@ public final class Settings {
* UiModeManager.
* @hide
*/
+ @Readable
public static final String UI_NIGHT_MODE_OVERRIDE_ON = "ui_night_mode_override_on";
/**
* 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";
/**
@@ -8100,12 +8523,14 @@ public final class Settings {
* UiModeManager.
* @hide
*/
+ @Readable
public static final String UI_NIGHT_MODE_OVERRIDE_OFF = "ui_night_mode_override_off";
/**
* Whether screensavers are enabled.
* @hide
*/
+ @Readable
public static final String SCREENSAVER_ENABLED = "screensaver_enabled";
/**
@@ -8115,6 +8540,7 @@ public final class Settings {
* battery, or upon dock insertion (if SCREENSAVER_ACTIVATE_ON_DOCK is set to 1).
* @hide
*/
+ @Readable
public static final String SCREENSAVER_COMPONENTS = "screensaver_components";
/**
@@ -8122,6 +8548,7 @@ public final class Settings {
* when the device is inserted into a (desk) dock.
* @hide
*/
+ @Readable
public static final String SCREENSAVER_ACTIVATE_ON_DOCK = "screensaver_activate_on_dock";
/**
@@ -8129,12 +8556,14 @@ public final class Settings {
* when the screen times out when not on battery.
* @hide
*/
+ @Readable
public static final String SCREENSAVER_ACTIVATE_ON_SLEEP = "screensaver_activate_on_sleep";
/**
* If screensavers are enabled, the default screensaver component.
* @hide
*/
+ @Readable
public static final String SCREENSAVER_DEFAULT_COMPONENT = "screensaver_default_component";
/**
@@ -8143,12 +8572,14 @@ public final class Settings {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @Readable
public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
/**
* Whether NFC payment is handled by the foreground application or a default.
* @hide
*/
+ @Readable
public static final String NFC_PAYMENT_FOREGROUND = "nfc_payment_foreground";
/**
@@ -8156,6 +8587,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
/**
@@ -8163,6 +8595,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String DIALER_DEFAULT_APPLICATION = "dialer_default_application";
/**
@@ -8170,6 +8603,7 @@ public final class Settings {
* application
* @hide
*/
+ @Readable
public static final String CALL_SCREENING_DEFAULT_COMPONENT =
"call_screening_default_component";
@@ -8180,6 +8614,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String EMERGENCY_ASSISTANCE_APPLICATION = "emergency_assistance_application";
/**
@@ -8188,6 +8623,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ASSIST_STRUCTURE_ENABLED = "assist_structure_enabled";
/**
@@ -8196,6 +8632,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ASSIST_SCREENSHOT_ENABLED = "assist_screenshot_enabled";
/**
@@ -8207,6 +8644,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ASSIST_DISCLOSURE_ENABLED = "assist_disclosure_enabled";
/**
@@ -8218,7 +8656,7 @@ public final class Settings {
*
* @hide
*/
-
+ @Readable
public static final String SHOW_ROTATION_SUGGESTIONS = "show_rotation_suggestions";
/**
@@ -8245,6 +8683,7 @@ public final class Settings {
* introduced to rotation suggestions.
* @hide
*/
+ @Readable
public static final String NUM_ROTATION_SUGGESTIONS_ACCEPTED =
"num_rotation_suggestions_accepted";
@@ -8257,6 +8696,7 @@ public final class Settings {
* @hide
*/
@Deprecated
+ @Readable
public static final String ENABLED_NOTIFICATION_ASSISTANT =
"enabled_notification_assistant";
@@ -8270,6 +8710,7 @@ public final class Settings {
*/
@Deprecated
@UnsupportedAppUsage
+ @Readable
public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
/**
@@ -8281,6 +8722,7 @@ public final class Settings {
*/
@Deprecated
@TestApi
+ @Readable
public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES =
"enabled_notification_policy_access_packages";
@@ -8294,6 +8736,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
@@ -8302,6 +8745,7 @@ public final class Settings {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @Readable
public static final String IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations";
/**
@@ -8309,6 +8753,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String PRINT_SERVICE_SEARCH_URI = "print_service_search_uri";
/**
@@ -8316,6 +8761,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String PAYMENT_SERVICE_SEARCH_URI = "payment_service_search_uri";
/**
@@ -8323,6 +8769,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String AUTOFILL_SERVICE_SEARCH_URI = "autofill_service_search_uri";
/**
@@ -8331,6 +8778,7 @@ public final class Settings {
* <p>
* Type : int (0 to show hints, 1 to skip showing hints)
*/
+ @Readable
public static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
/**
@@ -8338,6 +8786,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String UNSAFE_VOLUME_MUSIC_ACTIVE_MS = "unsafe_volume_music_active_ms";
/**
@@ -8348,6 +8797,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS =
"lock_screen_show_notifications";
@@ -8358,6 +8808,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS =
"lock_screen_show_silent_notifications";
@@ -8368,6 +8819,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String SHOW_NOTIFICATION_SNOOZE = "show_notification_snooze";
/**
@@ -8376,6 +8828,7 @@ public final class Settings {
* {@link android.net.Uri#encode(String)} and separated by ':'.
* @hide
*/
+ @Readable
public static final String TV_INPUT_HIDDEN_INPUTS = "tv_input_hidden_inputs";
/**
@@ -8384,6 +8837,7 @@ public final class Settings {
* and separated by ','. Each pair is separated by ':'.
* @hide
*/
+ @Readable
public static final String TV_INPUT_CUSTOM_LABELS = "tv_input_custom_labels";
/**
@@ -8401,6 +8855,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String TV_APP_USES_NON_SYSTEM_INPUTS = "tv_app_uses_non_system_inputs";
/**
@@ -8410,6 +8865,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String USB_AUDIO_AUTOMATIC_ROUTING_DISABLED =
"usb_audio_automatic_routing_disabled";
@@ -8425,6 +8881,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String SLEEP_TIMEOUT = "sleep_timeout";
/**
@@ -8439,12 +8896,14 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ATTENTIVE_TIMEOUT = "attentive_timeout";
/**
* Controls whether double tap to wake is enabled.
* @hide
*/
+ @Readable
public static final String DOUBLE_TAP_TO_WAKE = "double_tap_to_wake";
/**
@@ -8458,6 +8917,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String ASSISTANT = "assistant";
/**
@@ -8465,6 +8925,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled";
/**
@@ -8472,6 +8933,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String EMERGENCY_GESTURE_ENABLED = "emergency_gesture_enabled";
/**
@@ -8479,6 +8941,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String EMERGENCY_GESTURE_SOUND_ENABLED =
"emergency_gesture_sound_enabled";
@@ -8488,6 +8951,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED =
"camera_double_tap_power_gesture_disabled";
@@ -8497,6 +8961,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED =
"camera_double_twist_to_flip_enabled";
@@ -8506,6 +8971,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CAMERA_LIFT_TRIGGER_ENABLED = "camera_lift_trigger_enabled";
/**
@@ -8521,6 +8987,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String FLASHLIGHT_AVAILABLE = "flashlight_available";
/**
@@ -8528,18 +8995,21 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String FLASHLIGHT_ENABLED = "flashlight_enabled";
/**
* Whether or not face unlock is allowed on Keyguard.
* @hide
*/
+ @Readable
public static final String FACE_UNLOCK_KEYGUARD_ENABLED = "face_unlock_keyguard_enabled";
/**
* Whether or not face unlock dismisses the keyguard.
* @hide
*/
+ @Readable
public static final String FACE_UNLOCK_DISMISSES_KEYGUARD =
"face_unlock_dismisses_keyguard";
@@ -8547,6 +9017,7 @@ public final class Settings {
* Whether or not media is shown automatically when bypassing as a heads up.
* @hide
*/
+ @Readable
public static final String SHOW_MEDIA_WHEN_BYPASSING =
"show_media_when_bypassing";
@@ -8555,6 +9026,7 @@ public final class Settings {
* truth is obtained through the HAL.
* @hide
*/
+ @Readable
public static final String FACE_UNLOCK_ATTENTION_REQUIRED =
"face_unlock_attention_required";
@@ -8563,6 +9035,7 @@ public final class Settings {
* cached value, the source of truth is obtained through the HAL.
* @hide
*/
+ @Readable
public static final String FACE_UNLOCK_DIVERSITY_REQUIRED =
"face_unlock_diversity_required";
@@ -8571,6 +9044,7 @@ public final class Settings {
* Whether or not face unlock is allowed for apps (through BiometricPrompt).
* @hide
*/
+ @Readable
public static final String FACE_UNLOCK_APP_ENABLED = "face_unlock_app_enabled";
/**
@@ -8580,6 +9054,7 @@ public final class Settings {
* setConfirmationRequired API.
* @hide
*/
+ @Readable
public static final String FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION =
"face_unlock_always_require_confirmation";
@@ -8594,12 +9069,14 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String FACE_UNLOCK_RE_ENROLL = "face_unlock_re_enroll";
/**
* Whether or not debugging is enabled.
* @hide
*/
+ @Readable
public static final String BIOMETRIC_DEBUG_ENABLED =
"biometric_debug_enabled";
@@ -8608,6 +9085,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ASSIST_GESTURE_ENABLED = "assist_gesture_enabled";
/**
@@ -8615,6 +9093,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ASSIST_GESTURE_SENSITIVITY = "assist_gesture_sensitivity";
/**
@@ -8622,6 +9101,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ASSIST_GESTURE_SILENCE_ALERTS_ENABLED =
"assist_gesture_silence_alerts_enabled";
@@ -8630,6 +9110,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ASSIST_GESTURE_WAKE_ENABLED =
"assist_gesture_wake_enabled";
@@ -8641,36 +9122,42 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
/**
* Control whether Trust Agents are in active unlock or extend unlock mode.
* @hide
*/
+ @Readable
public static final String TRUST_AGENTS_EXTEND_UNLOCK = "trust_agents_extend_unlock";
/**
* Control whether the screen locks when trust is lost.
* @hide
*/
+ @Readable
public static final String LOCK_SCREEN_WHEN_TRUST_LOST = "lock_screen_when_trust_lost";
/**
* Control whether Night display is currently activated.
* @hide
*/
+ @Readable
public static final String NIGHT_DISPLAY_ACTIVATED = "night_display_activated";
/**
* Control whether Night display will automatically activate/deactivate.
* @hide
*/
+ @Readable
public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
/**
* Control the color temperature of Night Display, represented in Kelvin.
* @hide
*/
+ @Readable
public static final String NIGHT_DISPLAY_COLOR_TEMPERATURE =
"night_display_color_temperature";
@@ -8679,6 +9166,7 @@ public final class Settings {
* Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
* @hide
*/
+ @Readable
public static final String NIGHT_DISPLAY_CUSTOM_START_TIME =
"night_display_custom_start_time";
@@ -8687,6 +9175,7 @@ public final class Settings {
* Represented as milliseconds from midnight (e.g. 21600000 == 6am).
* @hide
*/
+ @Readable
public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time";
/**
@@ -8695,6 +9184,7 @@ public final class Settings {
* legacy cases, this is represented by the time in milliseconds (since epoch).
* @hide
*/
+ @Readable
public static final String NIGHT_DISPLAY_LAST_ACTIVATED_TIME =
"night_display_last_activated_time";
@@ -8702,6 +9192,7 @@ public final class Settings {
* Control whether display white balance is currently enabled.
* @hide
*/
+ @Readable
public static final String DISPLAY_WHITE_BALANCE_ENABLED = "display_white_balance_enabled";
/**
@@ -8712,6 +9203,7 @@ public final class Settings {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @Readable
public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
/**
@@ -8721,6 +9213,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String VR_DISPLAY_MODE = "vr_display_mode";
/**
@@ -8754,6 +9247,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
/**
@@ -8761,6 +9255,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String MANAGED_PROFILE_CONTACT_REMOTE_SEARCH =
"managed_profile_contact_remote_search";
@@ -8769,6 +9264,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CROSS_PROFILE_CALENDAR_ENABLED =
"cross_profile_calendar_enabled";
@@ -8777,6 +9273,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String AUTOMATIC_STORAGE_MANAGER_ENABLED =
"automatic_storage_manager_enabled";
@@ -8785,6 +9282,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN =
"automatic_storage_manager_days_to_retain";
@@ -8800,6 +9298,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED =
"automatic_storage_manager_bytes_cleared";
@@ -8808,6 +9307,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String AUTOMATIC_STORAGE_MANAGER_LAST_RUN =
"automatic_storage_manager_last_run";
/**
@@ -8817,6 +9317,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY =
"automatic_storage_manager_turned_off_by_policy";
@@ -8824,6 +9325,7 @@ public final class Settings {
* Whether SystemUI navigation keys is enabled.
* @hide
*/
+ @Readable
public static final String SYSTEM_NAVIGATION_KEYS_ENABLED =
"system_navigation_keys_enabled";
@@ -8832,6 +9334,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String QS_TILES = "sysui_qs_tiles";
/**
@@ -8842,6 +9345,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CONTROLS_ENABLED = "controls_enabled";
/**
@@ -8851,6 +9355,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String POWER_MENU_LOCKED_SHOW_CONTENT =
"power_menu_locked_show_content";
@@ -8860,18 +9365,21 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String INSTANT_APPS_ENABLED = "instant_apps_enabled";
/**
* Has this pairable device been paired or upgraded from a previously paired system.
* @hide
*/
+ @Readable
public static final String DEVICE_PAIRED = "device_paired";
/**
* Specifies additional package name for broadcasting the CMAS messages.
* @hide
*/
+ @Readable
public static final String CMAS_ADDITIONAL_BROADCAST_PKG = "cmas_additional_broadcast_pkg";
/**
@@ -8881,6 +9389,7 @@ public final class Settings {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @Readable
public static final String NOTIFICATION_BADGING = "notification_badging";
/**
@@ -8890,6 +9399,7 @@ public final class Settings {
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String NOTIFICATION_HISTORY_ENABLED = "notification_history_enabled";
/**
@@ -8898,6 +9408,7 @@ public final class Settings {
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String BUBBLE_IMPORTANT_CONVERSATIONS
= "bubble_important_conversations";
@@ -8907,18 +9418,21 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String NOTIFICATION_DISMISS_RTL = "notification_dismiss_rtl";
/**
* Comma separated list of QS tiles that have been auto-added already.
* @hide
*/
+ @Readable
public static final String QS_AUTO_ADDED_TILES = "qs_auto_tiles";
/**
* Whether the Lockdown button should be shown in the power menu.
* @hide
*/
+ @Readable
public static final String LOCKDOWN_IN_POWER_MENU = "lockdown_in_power_menu";
/**
@@ -8946,6 +9460,7 @@ public final class Settings {
* Type: string
* @hide
*/
+ @Readable
public static final String BACKUP_MANAGER_CONSTANTS = "backup_manager_constants";
@@ -8963,6 +9478,7 @@ public final class Settings {
* Type: string
* @hide
*/
+ @Readable
public static final String BACKUP_LOCAL_TRANSPORT_PARAMETERS =
"backup_local_transport_parameters";
@@ -8971,6 +9487,7 @@ public final class Settings {
* the user is driving.
* @hide
*/
+ @Readable
public static final String BLUETOOTH_ON_WHILE_DRIVING = "bluetooth_on_while_driving";
/**
@@ -8980,6 +9497,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String VOLUME_HUSH_GESTURE = "volume_hush_gesture";
/** @hide */
@@ -8996,6 +9514,7 @@ public final class Settings {
* The number of times (integer) the user has manually enabled battery saver.
* @hide
*/
+ @Readable
public static final String LOW_POWER_MANUAL_ACTIVATION_COUNT =
"low_power_manual_activation_count";
@@ -9005,6 +9524,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String LOW_POWER_WARNING_ACKNOWLEDGED =
"low_power_warning_acknowledged";
@@ -9013,6 +9533,7 @@ public final class Settings {
* suppressed.
* @hide
*/
+ @Readable
public static final String SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION =
"suppress_auto_battery_saver_suggestion";
@@ -9021,6 +9542,7 @@ public final class Settings {
* Type: string
* @hide
*/
+ @Readable
public static final String PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE =
"packages_to_clear_data_before_full_restore";
@@ -9029,6 +9551,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS =
"location_access_check_interval_millis";
@@ -9037,6 +9560,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS =
"location_access_check_delay_millis";
@@ -9046,6 +9570,7 @@ public final class Settings {
*/
@SystemApi
@Deprecated
+ @Readable
public static final String LOCATION_PERMISSIONS_UPGRADE_TO_Q_MODE =
"location_permissions_upgrade_to_q_mode";
@@ -9054,6 +9579,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String AUTO_REVOKE_DISABLED = "auto_revoke_disabled";
/**
@@ -9064,6 +9590,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String THEME_CUSTOMIZATION_OVERLAY_PACKAGES =
"theme_customization_overlay_packages";
@@ -9074,6 +9601,7 @@ public final class Settings {
* 2 = fully gestural
* @hide
*/
+ @Readable
public static final String NAVIGATION_MODE =
"navigation_mode";
@@ -9081,6 +9609,7 @@ public final class Settings {
* Scale factor for the back gesture inset size on the left side of the screen.
* @hide
*/
+ @Readable
public static final String BACK_GESTURE_INSET_SCALE_LEFT =
"back_gesture_inset_scale_left";
@@ -9088,6 +9617,7 @@ public final class Settings {
* Scale factor for the back gesture inset size on the right side of the screen.
* @hide
*/
+ @Readable
public static final String BACK_GESTURE_INSET_SCALE_RIGHT =
"back_gesture_inset_scale_right";
@@ -9097,30 +9627,35 @@ public final class Settings {
* No VALIDATOR as this setting will not be backed up.
* @hide
*/
+ @Readable
public static final String NEARBY_SHARING_COMPONENT = "nearby_sharing_component";
/**
* Controls whether aware is enabled.
* @hide
*/
+ @Readable
public static final String AWARE_ENABLED = "aware_enabled";
/**
* Controls whether aware_lock is enabled.
* @hide
*/
+ @Readable
public static final String AWARE_LOCK_ENABLED = "aware_lock_enabled";
/**
* Controls whether tap gesture is enabled.
* @hide
*/
+ @Readable
public static final String TAP_GESTURE = "tap_gesture";
/**
* Controls whether the people strip is enabled.
* @hide
*/
+ @Readable
public static final String PEOPLE_STRIP = "people_strip";
/**
@@ -9130,6 +9665,7 @@ public final class Settings {
* @see Settings.Global#SHOW_MEDIA_ON_QUICK_SETTINGS
* @hide
*/
+ @Readable
public static final String MEDIA_CONTROLS_RESUME = "qs_media_resumption";
/**
@@ -9138,6 +9674,7 @@ public final class Settings {
* @see Settings.Secure#MEDIA_CONTROLS_RESUME
* @hide
*/
+ @Readable
public static final String MEDIA_CONTROLS_RESUME_BLOCKED = "qs_media_resumption_blocked";
/**
@@ -9149,6 +9686,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String ACCESSIBILITY_MAGNIFICATION_MODE =
"accessibility_magnification_mode";
@@ -9184,6 +9722,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY =
"accessibility_magnification_capability";
@@ -9193,6 +9732,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT =
"accessibility_show_window_magnification_prompt";
@@ -9209,6 +9749,7 @@ public final class Settings {
* @see #ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_BUTTON_MODE =
"accessibility_button_mode";
@@ -9237,6 +9778,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_FLOATING_MENU_SIZE =
"accessibility_floating_menu_size";
@@ -9249,6 +9791,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_FLOATING_MENU_ICON_TYPE =
"accessibility_floating_menu_icon_type";
@@ -9257,6 +9800,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED =
"accessibility_floating_menu_fade_enabled";
@@ -9266,6 +9810,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_FLOATING_MENU_OPACITY =
"accessibility_floating_menu_opacity";
@@ -9274,6 +9819,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ADAPTIVE_CONNECTIVITY_ENABLED = "adaptive_connectivity_enabled";
/**
@@ -9285,6 +9831,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String[] LEGACY_RESTORE_SETTINGS = {
ENABLED_NOTIFICATION_LISTENERS,
ENABLED_NOTIFICATION_ASSISTANT,
@@ -9296,6 +9843,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS =
"reminder_exp_learning_time_elapsed";
@@ -9304,6 +9852,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ASSIST_HANDLES_LEARNING_EVENT_COUNT =
"reminder_exp_learning_event_count";
@@ -9417,6 +9966,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String NOTIFICATION_BUBBLES = "notification_bubbles";
/**
@@ -9425,6 +9975,7 @@ public final class Settings {
* Type: int
* @hide
*/
+ @Readable
public static final String ADD_USERS_WHEN_LOCKED = "add_users_when_locked";
/**
@@ -9432,6 +9983,7 @@ public final class Settings {
* <p>1 = apply ramping ringer
* <p>0 = do not apply ramping ringer
*/
+ @Readable
public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer";
/**
@@ -9443,12 +9995,14 @@ public final class Settings {
* No longer used. Should be removed once all dependencies have been updated.
*/
@UnsupportedAppUsage
+ @Readable
public static final String ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED =
"enable_accessibility_global_gesture_enabled";
/**
* Whether Airplane Mode is on.
*/
+ @Readable
public static final String AIRPLANE_MODE_ON = "airplane_mode_on";
/**
@@ -9456,30 +10010,36 @@ public final class Settings {
* {@hide}
*/
@SystemApi
+ @Readable
public static final String THEATER_MODE_ON = "theater_mode_on";
/**
* Constant for use in AIRPLANE_MODE_RADIOS to specify Bluetooth radio.
*/
+ @Readable
public static final String RADIO_BLUETOOTH = "bluetooth";
/**
* Constant for use in AIRPLANE_MODE_RADIOS to specify Wi-Fi radio.
*/
+ @Readable
public static final String RADIO_WIFI = "wifi";
/**
* {@hide}
*/
+ @Readable
public static final String RADIO_WIMAX = "wimax";
/**
* Constant for use in AIRPLANE_MODE_RADIOS to specify Cellular radio.
*/
+ @Readable
public static final String RADIO_CELL = "cell";
/**
* Constant for use in AIRPLANE_MODE_RADIOS to specify NFC radio.
*/
+ @Readable
public static final String RADIO_NFC = "nfc";
/**
@@ -9487,6 +10047,7 @@ public final class Settings {
* is on. This overrides WIFI_ON and BLUETOOTH_ON, if Wi-Fi and bluetooth are
* included in the comma separated list.
*/
+ @Readable
public static final String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";
/**
@@ -9498,6 +10059,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String AIRPLANE_MODE_TOGGLEABLE_RADIOS = "airplane_mode_toggleable_radios";
/**
@@ -9505,6 +10067,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String BLUETOOTH_CLASS_OF_DEVICE = "bluetooth_class_of_device";
/**
@@ -9512,6 +10075,7 @@ public final class Settings {
* See {@link android.bluetooth.BluetoothProfile}.
* {@hide}
*/
+ @Readable
public static final String BLUETOOTH_DISABLED_PROFILES = "bluetooth_disabled_profiles";
/**
@@ -9524,6 +10088,7 @@ public final class Settings {
* "00:11:22,0;01:02:03:04,2"
* @hide
*/
+ @Readable
public static final String BLUETOOTH_INTEROPERABILITY_LIST = "bluetooth_interoperability_list";
/**
@@ -9536,6 +10101,7 @@ public final class Settings {
* @deprecated This is no longer used or set by the platform.
*/
@Deprecated
+ @Readable
public static final String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
/**
@@ -9567,60 +10133,70 @@ public final class Settings {
* Value to specify if the user prefers the date, time and time zone
* to be automatically fetched from the network (NITZ). 1=yes, 0=no
*/
+ @Readable
public static final String AUTO_TIME = "auto_time";
/**
* Value to specify if the user prefers the time zone
* to be automatically fetched from the network (NITZ). 1=yes, 0=no
*/
+ @Readable
public static final String AUTO_TIME_ZONE = "auto_time_zone";
/**
* URI for the car dock "in" event sound.
* @hide
*/
+ @Readable
public static final String CAR_DOCK_SOUND = "car_dock_sound";
/**
* URI for the car dock "out" event sound.
* @hide
*/
+ @Readable
public static final String CAR_UNDOCK_SOUND = "car_undock_sound";
/**
* URI for the desk dock "in" event sound.
* @hide
*/
+ @Readable
public static final String DESK_DOCK_SOUND = "desk_dock_sound";
/**
* URI for the desk dock "out" event sound.
* @hide
*/
+ @Readable
public static final String DESK_UNDOCK_SOUND = "desk_undock_sound";
/**
* Whether to play a sound for dock events.
* @hide
*/
+ @Readable
public static final String DOCK_SOUNDS_ENABLED = "dock_sounds_enabled";
/**
* Whether to play a sound for dock events, only when an accessibility service is on.
* @hide
*/
+ @Readable
public static final String DOCK_SOUNDS_ENABLED_WHEN_ACCESSIBILITY = "dock_sounds_enabled_when_accessbility";
/**
* URI for the "device locked" (keyguard shown) sound.
* @hide
*/
+ @Readable
public static final String LOCK_SOUND = "lock_sound";
/**
* URI for the "device unlocked" sound.
* @hide
*/
+ @Readable
public static final String UNLOCK_SOUND = "unlock_sound";
/**
@@ -9628,24 +10204,28 @@ public final class Settings {
* state without unlocking.
* @hide
*/
+ @Readable
public static final String TRUSTED_SOUND = "trusted_sound";
/**
* URI for the low battery sound file.
* @hide
*/
+ @Readable
public static final String LOW_BATTERY_SOUND = "low_battery_sound";
/**
* Whether to play a sound for low-battery alerts.
* @hide
*/
+ @Readable
public static final String POWER_SOUNDS_ENABLED = "power_sounds_enabled";
/**
* URI for the "wireless charging started" sound.
* @hide
*/
+ @Readable
public static final String WIRELESS_CHARGING_STARTED_SOUND =
"wireless_charging_started_sound";
@@ -9653,6 +10233,7 @@ public final class Settings {
* URI for "wired charging started" sound.
* @hide
*/
+ @Readable
public static final String CHARGING_STARTED_SOUND = "charging_started_sound";
/**
@@ -9682,6 +10263,7 @@ public final class Settings {
* </ul>
* These values can be OR-ed together.
*/
+ @Readable
public static final String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
/**
@@ -9689,6 +10271,7 @@ public final class Settings {
* in the power menu.
* @hide
*/
+ @Readable
public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu";
/**
@@ -9697,6 +10280,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CUSTOM_BUGREPORT_HANDLER_APP = "custom_bugreport_handler_app";
/**
@@ -9705,29 +10289,34 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CUSTOM_BUGREPORT_HANDLER_USER = "custom_bugreport_handler_user";
/**
* Whether ADB over USB is enabled.
*/
+ @Readable
public static final String ADB_ENABLED = "adb_enabled";
/**
* Whether ADB over Wifi is enabled.
* @hide
*/
+ @Readable
public static final String ADB_WIFI_ENABLED = "adb_wifi_enabled";
/**
* Whether Views are allowed to save their attribute data.
* @hide
*/
+ @Readable
public static final String DEBUG_VIEW_ATTRIBUTES = "debug_view_attributes";
/**
* Which application package is allowed to save View attribute data.
* @hide
*/
+ @Readable
public static final String DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE =
"debug_view_attributes_application_package";
@@ -9735,12 +10324,14 @@ public final class Settings {
* Whether assisted GPS should be enabled or not.
* @hide
*/
+ @Readable
public static final String ASSISTED_GPS_ENABLED = "assisted_gps_enabled";
/**
* Whether bluetooth is enabled/disabled
* 0=disabled. 1=enabled.
*/
+ @Readable
public static final String BLUETOOTH_ON = "bluetooth_on";
/**
@@ -9749,6 +10340,7 @@ public final class Settings {
* 1 = CDMA Cell Broadcast SMS enabled
* @hide
*/
+ @Readable
public static final String CDMA_CELL_BROADCAST_SMS =
"cdma_cell_broadcast_sms";
@@ -9758,6 +10350,7 @@ public final class Settings {
* 2 = Roaming on any networks
* @hide
*/
+ @Readable
public static final String CDMA_ROAMING_MODE = "roaming_settings";
/**
@@ -9765,6 +10358,7 @@ public final class Settings {
* 1 = NV
* @hide
*/
+ @Readable
public static final String CDMA_SUBSCRIPTION_MODE = "subscription_mode";
/**
@@ -9774,44 +10368,49 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String DEFAULT_RESTRICT_BACKGROUND_DATA =
"default_restrict_background_data";
/** Inactivity timeout to track mobile data activity.
- *
- * If set to a positive integer, it indicates the inactivity timeout value in seconds to
- * infer the data activity of mobile network. After a period of no activity on mobile
- * networks with length specified by the timeout, an {@code ACTION_DATA_ACTIVITY_CHANGE}
- * intent is fired to indicate a transition of network status from "active" to "idle". Any
- * subsequent activity on mobile networks triggers the firing of {@code
- * ACTION_DATA_ACTIVITY_CHANGE} intent indicating transition from "idle" to "active".
- *
- * Network activity refers to transmitting or receiving data on the network interfaces.
- *
- * Tracking is disabled if set to zero or negative value.
- *
- * @hide
- */
- public static final String DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile";
+ *
+ * If set to a positive integer, it indicates the inactivity timeout value in seconds to
+ * infer the data activity of mobile network. After a period of no activity on mobile
+ * networks with length specified by the timeout, an {@code ACTION_DATA_ACTIVITY_CHANGE}
+ * intent is fired to indicate a transition of network status from "active" to "idle". Any
+ * subsequent activity on mobile networks triggers the firing of {@code
+ * ACTION_DATA_ACTIVITY_CHANGE} intent indicating transition from "idle" to "active".
+ *
+ * Network activity refers to transmitting or receiving data on the network interfaces.
+ *
+ * Tracking is disabled if set to zero or negative value.
+ *
+ * @hide
+ */
+ @Readable
+ public static final String DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile";
- /** Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE}
- * but for Wifi network.
- * @hide
- */
- public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi";
+ /** Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE}
+ * but for Wifi network.
+ * @hide
+ */
+ @Readable
+ public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi";
- /**
- * Whether or not data roaming is enabled. (0 = false, 1 = true)
- */
- public static final String DATA_ROAMING = "data_roaming";
+ /**
+ * Whether or not data roaming is enabled. (0 = false, 1 = true)
+ */
+ @Readable
+ public static final String DATA_ROAMING = "data_roaming";
- /**
- * The value passed to a Mobile DataConnection via bringUp which defines the
- * number of retries to preform when setting up the initial connection. The default
- * value defined in DataConnectionTrackerBase#DEFAULT_MDC_INITIAL_RETRY is currently 1.
- * @hide
- */
- public static final String MDC_INITIAL_MAX_RETRY = "mdc_initial_max_retry";
+ /**
+ * The value passed to a Mobile DataConnection via bringUp which defines the
+ * number of retries to perform when setting up the initial connection. The default
+ * value defined in DataConnectionTrackerBase#DEFAULT_MDC_INITIAL_RETRY is currently 1.
+ * @hide
+ */
+ @Readable
+ public static final String MDC_INITIAL_MAX_RETRY = "mdc_initial_max_retry";
/**
* Whether any package can be on external storage. When this is true, any
@@ -9819,6 +10418,7 @@ public final class Settings {
* or moving onto external storage. (0 = false, 1 = true)
* @hide
*/
+ @Readable
public static final String FORCE_ALLOW_ON_EXTERNAL = "force_allow_on_external";
/**
@@ -9833,6 +10433,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
/**
@@ -9844,6 +10445,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String EUICC_PROVISIONED = "euicc_provisioned";
/**
@@ -9859,6 +10461,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String EUICC_SUPPORTED_COUNTRIES = "euicc_supported_countries";
/**
@@ -9874,6 +10477,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String EUICC_UNSUPPORTED_COUNTRIES = "euicc_unsupported_countries";
/**
@@ -9882,6 +10486,7 @@ public final class Settings {
* (0 = false, 1 = true)
* @hide
*/
+ @Readable
public static final String DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES
= "force_resizable_activities";
@@ -9889,6 +10494,7 @@ public final class Settings {
* Whether to enable experimental freeform support for windows.
* @hide
*/
+ @Readable
public static final String DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT
= "enable_freeform_support";
@@ -9896,6 +10502,7 @@ public final class Settings {
* Whether to enable experimental desktop mode on secondary displays.
* @hide
*/
+ @Readable
public static final String DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS =
"force_desktop_mode_on_external_displays";
@@ -9907,6 +10514,7 @@ public final class Settings {
* @hide
*/
@Deprecated
+ @Readable
public static final String DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM =
"enable_sizecompat_freeform";
@@ -9917,6 +10525,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
@SuppressLint("NoSettingsProvider")
public static final String DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW =
"enable_non_resizable_multi_window";
@@ -9928,6 +10537,7 @@ public final class Settings {
* (0 = false, 1 = true)
* @hide
*/
+ @Readable
public static final String DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR =
"render_shadows_in_compositor";
@@ -9936,6 +10546,7 @@ public final class Settings {
* (0 = false, 1 = true)
* @hide
*/
+ @Readable
public static final String DEVELOPMENT_USE_BLAST_ADAPTER_VR =
"use_blast_adapter_vr";
@@ -9944,6 +10555,7 @@ public final class Settings {
* (0 = false, 1 = true)
* @hide
*/
+ @Readable
public static final String DEVELOPMENT_USE_BLAST_ADAPTER_SV =
"use_blast_adapter_sv";
@@ -9953,21 +10565,24 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH =
"wm_display_settings_path";
- /**
+ /**
* Whether user has enabled development settings.
*/
- public static final String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
+ @Readable
+ public static final String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
- /**
+ /**
* Whether the device has been provisioned (0 = false, 1 = true).
* <p>On a multiuser device with a separate system user, the screen may be locked
* as soon as this is set to true and further activities cannot be launched on the
* system user unless they are marked to show over keyguard.
*/
- public static final String DEVICE_PROVISIONED = "device_provisioned";
+ @Readable
+ public static final String DEVICE_PROVISIONED = "device_provisioned";
/**
* Indicates whether mobile data should be allowed while the device is being provisioned.
@@ -9980,52 +10595,58 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String DEVICE_PROVISIONING_MOBILE_DATA_ENABLED =
"device_provisioning_mobile_data";
- /**
+ /**
* The saved value for WindowManagerService.setForcedDisplaySize().
* Two integers separated by a comma. If unset, then use the real display size.
* @hide
*/
- public static final String DISPLAY_SIZE_FORCED = "display_size_forced";
+ @Readable
+ public static final String DISPLAY_SIZE_FORCED = "display_size_forced";
- /**
+ /**
* The saved value for WindowManagerService.setForcedDisplayScalingMode().
* 0 or unset if scaling is automatic, 1 if scaling is disabled.
* @hide
*/
- public static final String DISPLAY_SCALING_FORCE = "display_scaling_force";
+ @Readable
+ public static final String DISPLAY_SCALING_FORCE = "display_scaling_force";
- /**
+ /**
* The maximum size, in bytes, of a download that the download manager will transfer over
* a non-wifi connection.
* @hide
*/
- public static final String DOWNLOAD_MAX_BYTES_OVER_MOBILE =
+ @Readable
+ public static final String DOWNLOAD_MAX_BYTES_OVER_MOBILE =
"download_manager_max_bytes_over_mobile";
- /**
+ /**
* The recommended maximum size, in bytes, of a download that the download manager should
* transfer over a non-wifi connection. Over this size, the use will be warned, but will
* have the option to start the download over the mobile connection anyway.
* @hide
*/
- public static final String DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE =
+ @Readable
+ public static final String DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE =
"download_manager_recommended_max_bytes_over_mobile";
- /**
+ /**
* @deprecated Use {@link android.provider.Settings.Secure#INSTALL_NON_MARKET_APPS} instead
*/
- @Deprecated
- public static final String INSTALL_NON_MARKET_APPS = Secure.INSTALL_NON_MARKET_APPS;
+ @Deprecated
+ public static final String INSTALL_NON_MARKET_APPS = Secure.INSTALL_NON_MARKET_APPS;
- /**
+ /**
* Whether HDMI control shall be enabled. If disabled, no CEC/MHL command will be
* sent or processed. (0 = false, 1 = true)
* @hide
*/
- public static final String HDMI_CONTROL_ENABLED = "hdmi_control_enabled";
+ @Readable
+ public static final String HDMI_CONTROL_ENABLED = "hdmi_control_enabled";
/**
* Controls whether volume control commands via HDMI CEC are enabled. (0 = false, 1 =
@@ -10061,16 +10682,18 @@ public final class Settings {
* @hide
* @see android.hardware.hdmi.HdmiControlManager#setHdmiCecVolumeControlEnabled(boolean)
*/
+ @Readable
public static final String HDMI_CONTROL_VOLUME_CONTROL_ENABLED =
"hdmi_control_volume_control_enabled";
- /**
+ /**
* Whether HDMI System Audio Control feature is enabled. If enabled, TV will try to turn on
* system audio mode if there's a connected CEC-enabled AV Receiver. Then audio stream will
* be played on AVR instead of TV spaeker. If disabled, the system audio mode will never be
* activated.
* @hide
*/
+ @Readable
public static final String HDMI_SYSTEM_AUDIO_CONTROL_ENABLED =
"hdmi_system_audio_control_enabled";
@@ -10080,6 +10703,7 @@ public final class Settings {
* disabled, you can only switch the input via controls on this device.
* @hide
*/
+ @Readable
public static final String HDMI_CEC_SWITCH_ENABLED =
"hdmi_cec_switch_enabled";
@@ -10087,6 +10711,7 @@ public final class Settings {
* HDMI CEC version to use. Defaults to v1.4b.
* @hide
*/
+ @Readable
public static final String HDMI_CEC_VERSION =
"hdmi_cec_version";
@@ -10096,6 +10721,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String HDMI_CONTROL_AUTO_WAKEUP_ENABLED =
"hdmi_control_auto_wakeup_enabled";
@@ -10105,6 +10731,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED =
"hdmi_control_auto_device_off_enabled";
@@ -10126,6 +10753,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String HDMI_CONTROL_SEND_STANDBY_ON_SLEEP =
"hdmi_control_send_standby_on_sleep";
@@ -10133,6 +10761,7 @@ public final class Settings {
* Whether or not media is shown automatically when bypassing as a heads up.
* @hide
*/
+ @Readable
public static final String SHOW_MEDIA_ON_QUICK_SETTINGS =
"qs_media_player";
@@ -10142,6 +10771,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS =
"location_background_throttle_interval_ms";
@@ -10150,6 +10780,7 @@ public final class Settings {
* to request.
* @hide
*/
+ @Readable
public static final String LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS =
"location_background_throttle_proximity_alert_interval_ms";
@@ -10157,6 +10788,7 @@ public final class Settings {
* Packages that are whitelisted for background throttling (throttling will not be applied).
* @hide
*/
+ @Readable
public static final String LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST =
"location_background_throttle_package_whitelist";
@@ -10166,31 +10798,42 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST =
"location_ignore_settings_package_whitelist";
/**
+ * Whether to throttle location when the device is in doze and still.
+ * @hide
+ */
+ public static final String LOCATION_ENABLE_STATIONARY_THROTTLE =
+ "location_enable_stationary_throttle";
+
+ /**
* Whether TV will switch to MHL port when a mobile device is plugged in.
* (0 = false, 1 = true)
* @hide
*/
- public static final String MHL_INPUT_SWITCHING_ENABLED = "mhl_input_switching_enabled";
+ @Readable
+ public static final String MHL_INPUT_SWITCHING_ENABLED = "mhl_input_switching_enabled";
- /**
+ /**
* Whether TV will charge the mobile device connected at MHL port. (0 = false, 1 = true)
* @hide
*/
- public static final String MHL_POWER_CHARGE_ENABLED = "mhl_power_charge_enabled";
+ @Readable
+ public static final String MHL_POWER_CHARGE_ENABLED = "mhl_power_charge_enabled";
- /**
+ /**
* Whether mobile data connections are allowed by the user. See
* ConnectivityManager for more info.
* @hide
*/
- @UnsupportedAppUsage
- public static final String MOBILE_DATA = "mobile_data";
+ @UnsupportedAppUsage
+ @Readable
+ public static final String MOBILE_DATA = "mobile_data";
- /**
+ /**
* Whether the mobile data connection should remain active even when higher
* priority networks like WiFi are active, to help make network switching faster.
*
@@ -10199,7 +10842,8 @@ public final class Settings {
* (0 = disabled, 1 = enabled)
* @hide
*/
- public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
+ @Readable
+ public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
/**
* Whether the wifi data connection should remain active even when higher
@@ -10212,197 +10856,252 @@ public final class Settings {
* (0 = disabled, 1 = enabled)
* @hide
*/
+ @Readable
public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested";
/**
* Size of the event buffer for IP connectivity metrics.
* @hide
*/
+ @Readable
public static final String CONNECTIVITY_METRICS_BUFFER_SIZE =
"connectivity_metrics_buffer_size";
- /** {@hide} */
- public static final String NETSTATS_ENABLED = "netstats_enabled";
- /** {@hide} */
- public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval";
- /** {@hide} */
- @Deprecated
- public static final String NETSTATS_TIME_CACHE_MAX_AGE = "netstats_time_cache_max_age";
- /** {@hide} */
- public static final String NETSTATS_GLOBAL_ALERT_BYTES = "netstats_global_alert_bytes";
- /** {@hide} */
- public static final String NETSTATS_SAMPLE_ENABLED = "netstats_sample_enabled";
- /** {@hide} */
- public static final String NETSTATS_AUGMENT_ENABLED = "netstats_augment_enabled";
- /** {@hide} */
- public static final String NETSTATS_COMBINE_SUBTYPE_ENABLED = "netstats_combine_subtype_enabled";
-
- /** {@hide} */
- public static final String NETSTATS_DEV_BUCKET_DURATION = "netstats_dev_bucket_duration";
- /** {@hide} */
- public static final String NETSTATS_DEV_PERSIST_BYTES = "netstats_dev_persist_bytes";
- /** {@hide} */
- public static final String NETSTATS_DEV_ROTATE_AGE = "netstats_dev_rotate_age";
- /** {@hide} */
- public static final String NETSTATS_DEV_DELETE_AGE = "netstats_dev_delete_age";
-
- /** {@hide} */
- public static final String NETSTATS_UID_BUCKET_DURATION = "netstats_uid_bucket_duration";
- /** {@hide} */
- public static final String NETSTATS_UID_PERSIST_BYTES = "netstats_uid_persist_bytes";
- /** {@hide} */
- public static final String NETSTATS_UID_ROTATE_AGE = "netstats_uid_rotate_age";
- /** {@hide} */
- public static final String NETSTATS_UID_DELETE_AGE = "netstats_uid_delete_age";
-
- /** {@hide} */
- public static final String NETSTATS_UID_TAG_BUCKET_DURATION = "netstats_uid_tag_bucket_duration";
- /** {@hide} */
- public static final String NETSTATS_UID_TAG_PERSIST_BYTES = "netstats_uid_tag_persist_bytes";
- /** {@hide} */
- public static final String NETSTATS_UID_TAG_ROTATE_AGE = "netstats_uid_tag_rotate_age";
- /** {@hide} */
- public static final String NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age";
-
- /** {@hide} */
- public static final String NETPOLICY_QUOTA_ENABLED = "netpolicy_quota_enabled";
- /** {@hide} */
- public static final String NETPOLICY_QUOTA_UNLIMITED = "netpolicy_quota_unlimited";
- /** {@hide} */
- public static final String NETPOLICY_QUOTA_LIMITED = "netpolicy_quota_limited";
- /** {@hide} */
- public static final String NETPOLICY_QUOTA_FRAC_JOBS = "netpolicy_quota_frac_jobs";
- /** {@hide} */
- public static final String NETPOLICY_QUOTA_FRAC_MULTIPATH = "netpolicy_quota_frac_multipath";
-
- /** {@hide} */
- public static final String NETPOLICY_OVERRIDE_ENABLED = "netpolicy_override_enabled";
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_ENABLED = "netstats_enabled";
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval";
+ /**
+ * @deprecated
+ * {@hide}
+ */
+ @Deprecated
+ @Readable
+ public static final String NETSTATS_TIME_CACHE_MAX_AGE = "netstats_time_cache_max_age";
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_GLOBAL_ALERT_BYTES = "netstats_global_alert_bytes";
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_SAMPLE_ENABLED = "netstats_sample_enabled";
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_AUGMENT_ENABLED = "netstats_augment_enabled";
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_COMBINE_SUBTYPE_ENABLED =
+ "netstats_combine_subtype_enabled";
- /**
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_DEV_BUCKET_DURATION = "netstats_dev_bucket_duration";
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_DEV_PERSIST_BYTES = "netstats_dev_persist_bytes";
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_DEV_ROTATE_AGE = "netstats_dev_rotate_age";
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_DEV_DELETE_AGE = "netstats_dev_delete_age";
+
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_UID_BUCKET_DURATION = "netstats_uid_bucket_duration";
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_UID_PERSIST_BYTES = "netstats_uid_persist_bytes";
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_UID_ROTATE_AGE = "netstats_uid_rotate_age";
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_UID_DELETE_AGE = "netstats_uid_delete_age";
+
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_UID_TAG_BUCKET_DURATION =
+ "netstats_uid_tag_bucket_duration";
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_UID_TAG_PERSIST_BYTES =
+ "netstats_uid_tag_persist_bytes";
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_UID_TAG_ROTATE_AGE = "netstats_uid_tag_rotate_age";
+ /** {@hide} */
+ @Readable
+ public static final String NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age";
+
+ /** {@hide} */
+ @Readable
+ public static final String NETPOLICY_QUOTA_ENABLED = "netpolicy_quota_enabled";
+ /** {@hide} */
+ @Readable
+ public static final String NETPOLICY_QUOTA_UNLIMITED = "netpolicy_quota_unlimited";
+ /** {@hide} */
+ @Readable
+ public static final String NETPOLICY_QUOTA_LIMITED = "netpolicy_quota_limited";
+ /** {@hide} */
+ @Readable
+ public static final String NETPOLICY_QUOTA_FRAC_JOBS = "netpolicy_quota_frac_jobs";
+ /** {@hide} */
+ @Readable
+ public static final String NETPOLICY_QUOTA_FRAC_MULTIPATH =
+ "netpolicy_quota_frac_multipath";
+
+ /** {@hide} */
+ @Readable
+ public static final String NETPOLICY_OVERRIDE_ENABLED = "netpolicy_override_enabled";
+
+ /**
* User preference for which network(s) should be used. Only the
* connectivity service should touch this.
*/
- public static final String NETWORK_PREFERENCE = "network_preference";
+ @Readable
+ public static final String NETWORK_PREFERENCE = "network_preference";
- /**
+ /**
* Which package name to use for network scoring. If null, or if the package is not a valid
* scorer app, external network scores will neither be requested nor accepted.
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final String NETWORK_SCORER_APP = "network_scorer_app";
+ @Readable
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public static final String NETWORK_SCORER_APP = "network_scorer_app";
/**
* Whether night display forced auto mode is available.
* 0 = unavailable, 1 = available.
* @hide
*/
+ @Readable
public static final String NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE =
"night_display_forced_auto_mode_available";
- /**
+ /**
* If the NITZ_UPDATE_DIFF time is exceeded then an automatic adjustment
* to SystemClock will be allowed even if NITZ_UPDATE_SPACING has not been
* exceeded.
* @hide
*/
- public static final String NITZ_UPDATE_DIFF = "nitz_update_diff";
+ @Readable
+ public static final String NITZ_UPDATE_DIFF = "nitz_update_diff";
- /**
+ /**
* The length of time in milli-seconds that automatic small adjustments to
* SystemClock are ignored if NITZ_UPDATE_DIFF is not exceeded.
* @hide
*/
- public static final String NITZ_UPDATE_SPACING = "nitz_update_spacing";
+ @Readable
+ public static final String NITZ_UPDATE_SPACING = "nitz_update_spacing";
/** Preferred NTP server. {@hide} */
+ @Readable
public static final String NTP_SERVER = "ntp_server";
/** Timeout in milliseconds to wait for NTP server. {@hide} */
+ @Readable
public static final String NTP_TIMEOUT = "ntp_timeout";
/** Secondary NTP server. {@hide} */
+ @Readable
public static final String NTP_SERVER_2 = "ntp_server_2";
- /** {@hide} */
- public static final String STORAGE_BENCHMARK_INTERVAL = "storage_benchmark_interval";
+ /** {@hide} */
+ @Readable
+ public static final String STORAGE_BENCHMARK_INTERVAL = "storage_benchmark_interval";
/**
* Whether or not Settings should enable psd API.
* {@hide}
*/
+ @Readable
public static final String SETTINGS_USE_PSD_API = "settings_use_psd_api";
/**
* Whether or not Settings should enable external provider API.
* {@hide}
*/
+ @Readable
public static final String SETTINGS_USE_EXTERNAL_PROVIDER_API =
"settings_use_external_provider_api";
- /**
+ /**
* Sample validity in seconds to configure for the system DNS resolver.
* {@hide}
*/
- public static final String DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS =
+ @Readable
+ public static final String DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS =
"dns_resolver_sample_validity_seconds";
- /**
+ /**
* Success threshold in percent for use with the system DNS resolver.
* {@hide}
*/
- public static final String DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT =
+ @Readable
+ public static final String DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT =
"dns_resolver_success_threshold_percent";
- /**
+ /**
* Minimum number of samples needed for statistics to be considered meaningful in the
* system DNS resolver.
* {@hide}
*/
- public static final String DNS_RESOLVER_MIN_SAMPLES = "dns_resolver_min_samples";
+ @Readable
+ public static final String DNS_RESOLVER_MIN_SAMPLES = "dns_resolver_min_samples";
- /**
+ /**
* Maximum number taken into account for statistics purposes in the system DNS resolver.
* {@hide}
*/
- public static final String DNS_RESOLVER_MAX_SAMPLES = "dns_resolver_max_samples";
+ @Readable
+ public static final String DNS_RESOLVER_MAX_SAMPLES = "dns_resolver_max_samples";
- /**
+ /**
* Whether to disable the automatic scheduling of system updates.
* 1 = system updates won't be automatically scheduled (will always
* present notification instead).
* 0 = system updates will be automatically scheduled. (default)
* @hide
*/
- @SystemApi
- public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
+ @SystemApi
+ @Readable
+ public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
- /** Timeout for package verification.
+ /** Timeout for package verification.
* @hide */
- public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout";
+ @Readable
+ public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout";
/** Timeout for app integrity verification.
* @hide */
+ @Readable
public static final String APP_INTEGRITY_VERIFICATION_TIMEOUT =
"app_integrity_verification_timeout";
- /** Default response code for package verification.
+ /** Default response code for package verification.
* @hide */
- public static final String PACKAGE_VERIFIER_DEFAULT_RESPONSE = "verifier_default_response";
+ @Readable
+ public static final String PACKAGE_VERIFIER_DEFAULT_RESPONSE = "verifier_default_response";
- /**
+ /**
* Show package verification setting in the Settings app.
* 1 = show (default)
* 0 = hide
* @hide
*/
- public static final String PACKAGE_VERIFIER_SETTING_VISIBLE = "verifier_setting_visible";
+ @Readable
+ public static final String PACKAGE_VERIFIER_SETTING_VISIBLE = "verifier_setting_visible";
- /**
+ /**
* Run package verification on apps installed through ADB/ADT/USB
* 1 = perform package verification on ADB installs (default)
* 0 = bypass package verification on ADB installs
* @hide
*/
- public static final String PACKAGE_VERIFIER_INCLUDE_ADB = "verifier_verify_adb_installs";
+ @Readable
+ public static final String PACKAGE_VERIFIER_INCLUDE_ADB = "verifier_verify_adb_installs";
/**
* Run integrity checks for integrity rule providers.
@@ -10410,117 +11109,131 @@ public final class Settings {
* 1 = perform integrity verification on installs from rule providers
* @hide
*/
+ @Readable
public static final String INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER =
"verify_integrity_for_rule_provider";
- /**
+ /**
* Time since last fstrim (milliseconds) after which we force one to happen
* during device startup. If unset, the default is 3 days.
* @hide
*/
- public static final String FSTRIM_MANDATORY_INTERVAL = "fstrim_mandatory_interval";
+ @Readable
+ public static final String FSTRIM_MANDATORY_INTERVAL = "fstrim_mandatory_interval";
- /**
+ /**
* The interval in milliseconds at which to check packet counts on the
* mobile data interface when screen is on, to detect possible data
* connection problems.
* @hide
*/
- public static final String PDP_WATCHDOG_POLL_INTERVAL_MS =
+ @Readable
+ public static final String PDP_WATCHDOG_POLL_INTERVAL_MS =
"pdp_watchdog_poll_interval_ms";
- /**
+ /**
* The interval in milliseconds at which to check packet counts on the
* mobile data interface when screen is off, to detect possible data
* connection problems.
* @hide
*/
- public static final String PDP_WATCHDOG_LONG_POLL_INTERVAL_MS =
+ @Readable
+ public static final String PDP_WATCHDOG_LONG_POLL_INTERVAL_MS =
"pdp_watchdog_long_poll_interval_ms";
- /**
+ /**
* The interval in milliseconds at which to check packet counts on the
* mobile data interface after {@link #PDP_WATCHDOG_TRIGGER_PACKET_COUNT}
* outgoing packets has been reached without incoming packets.
* @hide
*/
- public static final String PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS =
+ @Readable
+ public static final String PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS =
"pdp_watchdog_error_poll_interval_ms";
- /**
+ /**
* The number of outgoing packets sent without seeing an incoming packet
* that triggers a countdown (of {@link #PDP_WATCHDOG_ERROR_POLL_COUNT}
* device is logged to the event log
* @hide
*/
- public static final String PDP_WATCHDOG_TRIGGER_PACKET_COUNT =
+ @Readable
+ public static final String PDP_WATCHDOG_TRIGGER_PACKET_COUNT =
"pdp_watchdog_trigger_packet_count";
- /**
+ /**
* The number of polls to perform (at {@link #PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS})
* after hitting {@link #PDP_WATCHDOG_TRIGGER_PACKET_COUNT} before
* attempting data connection recovery.
* @hide
*/
- public static final String PDP_WATCHDOG_ERROR_POLL_COUNT =
+ @Readable
+ public static final String PDP_WATCHDOG_ERROR_POLL_COUNT =
"pdp_watchdog_error_poll_count";
- /**
+ /**
* The number of failed PDP reset attempts before moving to something more
* drastic: re-registering to the network.
* @hide
*/
- public static final String PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT =
+ @Readable
+ public static final String PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT =
"pdp_watchdog_max_pdp_reset_fail_count";
- /**
+ /**
* URL to open browser on to allow user to manage a prepay account
* @hide
*/
- public static final String SETUP_PREPAID_DATA_SERVICE_URL =
+ @Readable
+ public static final String SETUP_PREPAID_DATA_SERVICE_URL =
"setup_prepaid_data_service_url";
- /**
+ /**
* URL to attempt a GET on to see if this is a prepay device
* @hide
*/
- public static final String SETUP_PREPAID_DETECTION_TARGET_URL =
+ @Readable
+ public static final String SETUP_PREPAID_DETECTION_TARGET_URL =
"setup_prepaid_detection_target_url";
- /**
+ /**
* Host to check for a redirect to after an attempt to GET
* SETUP_PREPAID_DETECTION_TARGET_URL. (If we redirected there,
* this is a prepaid device with zero balance.)
* @hide
*/
- public static final String SETUP_PREPAID_DETECTION_REDIR_HOST =
+ @Readable
+ public static final String SETUP_PREPAID_DETECTION_REDIR_HOST =
"setup_prepaid_detection_redir_host";
- /**
+ /**
* The interval in milliseconds at which to check the number of SMS sent out without asking
* for use permit, to limit the un-authorized SMS usage.
*
* @hide
*/
- public static final String SMS_OUTGOING_CHECK_INTERVAL_MS =
+ @Readable
+ public static final String SMS_OUTGOING_CHECK_INTERVAL_MS =
"sms_outgoing_check_interval_ms";
- /**
+ /**
* The number of outgoing SMS sent without asking for user permit (of {@link
* #SMS_OUTGOING_CHECK_INTERVAL_MS}
*
* @hide
*/
- public static final String SMS_OUTGOING_CHECK_MAX_COUNT =
+ @Readable
+ public static final String SMS_OUTGOING_CHECK_MAX_COUNT =
"sms_outgoing_check_max_count";
- /**
+ /**
* Used to disable SMS short code confirmation - defaults to true.
* True indcates we will do the check, etc. Set to false to disable.
* @see com.android.internal.telephony.SmsUsageMonitor
* @hide
*/
- public static final String SMS_SHORT_CODE_CONFIRMATION = "sms_short_code_confirmation";
+ @Readable
+ public static final String SMS_SHORT_CODE_CONFIRMATION = "sms_short_code_confirmation";
/**
* Used to select which country we use to determine premium sms codes.
@@ -10529,6 +11242,7 @@ public final class Settings {
* or com.android.internal.telephony.SMSDispatcher.PREMIUM_RULE_USE_BOTH.
* @hide
*/
+ @Readable
public static final String SMS_SHORT_CODE_RULE = "sms_short_code_rule";
/**
@@ -10536,6 +11250,7 @@ public final class Settings {
* build config value.
* @hide
*/
+ @Readable
public static final String TCP_DEFAULT_INIT_RWND = "tcp_default_init_rwnd";
/**
@@ -10543,6 +11258,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String TETHER_SUPPORTED = "tether_supported";
/**
@@ -10550,6 +11266,7 @@ public final class Settings {
* which defaults to false.
* @hide
*/
+ @Readable
public static final String TETHER_DUN_REQUIRED = "tether_dun_required";
/**
@@ -10561,6 +11278,7 @@ public final class Settings {
* note that empty fields can be omitted: "name,apn,,,,,,,,,310,260,,DUN"
* @hide
*/
+ @Readable
public static final String TETHER_DUN_APN = "tether_dun_apn";
/**
@@ -10571,6 +11289,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
/**
@@ -10580,6 +11299,7 @@ public final class Settings {
* is interpreted as |false|.
* @hide
*/
+ @Readable
public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
"tether_enable_legacy_dhcp_server";
@@ -10594,6 +11314,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
/**
@@ -10604,62 +11325,71 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String CARRIER_APP_NAMES = "carrier_app_names";
- /**
+ /**
* USB Mass Storage Enabled
*/
- public static final String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
+ @Readable
+ public static final String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
- /**
+ /**
* If this setting is set (to anything), then all references
* to Gmail on the device must change to Google Mail.
*/
- public static final String USE_GOOGLE_MAIL = "use_google_mail";
+ @Readable
+ public static final String USE_GOOGLE_MAIL = "use_google_mail";
/**
* Whether or not switching/creating users is enabled by user.
* @hide
*/
+ @Readable
public static final String USER_SWITCHER_ENABLED = "user_switcher_enabled";
/**
* Webview Data reduction proxy key.
* @hide
*/
+ @Readable
public static final String WEBVIEW_DATA_REDUCTION_PROXY_KEY =
"webview_data_reduction_proxy_key";
- /**
+ /**
* Name of the package used as WebView provider (if unset the provider is instead determined
* by the system).
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final String WEBVIEW_PROVIDER = "webview_provider";
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
+ public static final String WEBVIEW_PROVIDER = "webview_provider";
- /**
+ /**
* Developer setting to enable WebView multiprocess rendering.
* @hide
*/
- @SystemApi
- public static final String WEBVIEW_MULTIPROCESS = "webview_multiprocess";
+ @SystemApi
+ @Readable
+ public static final String WEBVIEW_MULTIPROCESS = "webview_multiprocess";
- /**
+ /**
* The maximum number of notifications shown in 24 hours when switching networks.
* @hide
*/
- public static final String NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT =
+ @Readable
+ public static final String NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT =
"network_switch_notification_daily_limit";
- /**
+ /**
* The minimum time in milliseconds between notifications when switching networks.
* @hide
*/
- public static final String NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS =
+ @Readable
+ public static final String NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS =
"network_switch_notification_rate_limit_millis";
- /**
+ /**
* 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:
@@ -10670,16 +11400,18 @@ public final class Settings {
*
* @hide
*/
- public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi";
+ @Readable
+ 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.
*
* @hide
*/
- public static final String NETWORK_METERED_MULTIPATH_PREFERENCE =
+ @Readable
+ public static final String NETWORK_METERED_MULTIPATH_PREFERENCE =
"network_metered_multipath_preference";
/**
@@ -10688,6 +11420,7 @@ public final class Settings {
* from data plan or data limit/warning set by the user.
* @hide
*/
+ @Readable
public static final String NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES =
"network_default_daily_multipath_quota_bytes";
@@ -10695,10 +11428,11 @@ public final class Settings {
* Network watchlist last report time.
* @hide
*/
+ @Readable
public static final String NETWORK_WATCHLIST_LAST_REPORT_TIME =
"network_watchlist_last_report_time";
- /**
+ /**
* The thresholds of the wifi throughput badging (SD, HD etc.) as a comma-delimited list of
* colon-delimited key-value pairs. The key is the badging enum value defined in
* android.net.ScoredNetwork and the value is the minimum sustained network throughput in
@@ -10706,25 +11440,28 @@ public final class Settings {
*
* @hide
*/
- @SystemApi
- public static final String WIFI_BADGING_THRESHOLDS = "wifi_badging_thresholds";
+ @SystemApi
+ @Readable
+ public static final String WIFI_BADGING_THRESHOLDS = "wifi_badging_thresholds";
- /**
+ /**
* Whether Wifi display is enabled/disabled
* 0=disabled. 1=enabled.
* @hide
*/
- public static final String WIFI_DISPLAY_ON = "wifi_display_on";
+ @Readable
+ public static final String WIFI_DISPLAY_ON = "wifi_display_on";
- /**
+ /**
* Whether Wifi display certification mode is enabled/disabled
* 0=disabled. 1=enabled.
* @hide
*/
- public static final String WIFI_DISPLAY_CERTIFICATION_ON =
+ @Readable
+ public static final String WIFI_DISPLAY_CERTIFICATION_ON =
"wifi_display_certification_on";
- /**
+ /**
* WPS Configuration method used by Wifi display, this setting only
* takes effect when WIFI_DISPLAY_CERTIFICATION_ON is 1 (enabled).
*
@@ -10736,10 +11473,11 @@ public final class Settings {
* WpsInfo.DISPLAY: use Display
* @hide
*/
- public static final String WIFI_DISPLAY_WPS_CONFIG =
+ @Readable
+ public static final String WIFI_DISPLAY_WPS_CONFIG =
"wifi_display_wps_config";
- /**
+ /**
* Whether to notify the user of open networks.
* <p>
* If not connected and the scan results have an open network, we will
@@ -10751,68 +11489,78 @@ public final class Settings {
* @deprecated This feature is no longer controlled by this setting in
* {@link android.os.Build.VERSION_CODES#O}.
*/
- @Deprecated
- public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON =
+ @Deprecated
+ @Readable
+ public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON =
"wifi_networks_available_notification_on";
- /**
+ /**
* {@hide}
*/
- public static final String WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON =
+ @Readable
+ public static final String WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON =
"wimax_networks_available_notification_on";
- /**
+ /**
* Delay (in seconds) before repeating the Wi-Fi networks available notification.
* Connecting to a network will reset the timer.
* @deprecated This is no longer used or set by the platform.
*/
- @Deprecated
- public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
+ @Deprecated
+ @Readable
+ public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
"wifi_networks_available_repeat_delay";
- /**
+ /**
* 802.11 country code in ISO 3166 format
* @hide
*/
- public static final String WIFI_COUNTRY_CODE = "wifi_country_code";
+ @Readable
+ public static final String WIFI_COUNTRY_CODE = "wifi_country_code";
- /**
+ /**
* The interval in milliseconds to issue wake up scans when wifi needs
* to connect. This is necessary to connect to an access point when
* device is on the move and the screen is off.
* @hide
*/
- public static final String WIFI_FRAMEWORK_SCAN_INTERVAL_MS =
+ @Readable
+ public static final String WIFI_FRAMEWORK_SCAN_INTERVAL_MS =
"wifi_framework_scan_interval_ms";
- /**
+ /**
* The interval in milliseconds after which Wi-Fi is considered idle.
* When idle, it is possible for the device to be switched from Wi-Fi to
* the mobile data network.
* @hide
*/
- public static final String WIFI_IDLE_MS = "wifi_idle_ms";
+ @Readable
+ public static final String WIFI_IDLE_MS = "wifi_idle_ms";
- /**
+ /**
* When the number of open networks exceeds this number, the
* least-recently-used excess networks will be removed.
* @deprecated This is no longer used or set by the platform.
*/
- @Deprecated
- public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
+ @Deprecated
+ @Readable
+ public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
- /**
+ /**
* Whether the Wi-Fi should be on. Only the Wi-Fi service should touch this.
*/
- public static final String WIFI_ON = "wifi_on";
+ @Readable
+ public static final String WIFI_ON = "wifi_on";
- /**
+ /**
* Setting to allow scans to be enabled even wifi is turned off for connectivity.
* @hide
* @deprecated To be removed. Use {@link WifiManager#setScanAlwaysAvailable(boolean)} for
* setting the value and {@link WifiManager#isScanAlwaysAvailable()} for query.
*/
- public static final String WIFI_SCAN_ALWAYS_AVAILABLE =
+ @Deprecated
+ @Readable
+ public static final String WIFI_SCAN_ALWAYS_AVAILABLE =
"wifi_scan_always_enabled";
/**
@@ -10822,6 +11570,8 @@ public final class Settings {
* @hide
* @deprecated To be removed.
*/
+ @Deprecated
+ @Readable
public static final String WIFI_P2P_PENDING_FACTORY_RESET =
"wifi_p2p_pending_factory_reset";
@@ -10834,6 +11584,8 @@ public final class Settings {
* setAutoShutdownEnabled(boolean)} for setting the value and {@link SoftApConfiguration#
* isAutoShutdownEnabled()} for query.
*/
+ @Deprecated
+ @Readable
public static final String SOFT_AP_TIMEOUT_ENABLED = "soft_ap_timeout_enabled";
/**
@@ -10846,6 +11598,7 @@ public final class Settings {
*/
@Deprecated
@SystemApi
+ @Readable
public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
/**
@@ -10855,6 +11608,7 @@ public final class Settings {
* Type: int (0 for false, 1 for true)
* @hide
*/
+ @Readable
public static final String WIFI_MIGRATION_COMPLETED = "wifi_migration_completed";
/**
@@ -10863,6 +11617,7 @@ public final class Settings {
* Type: int (0 for false, 1 for true)
* @hide
*/
+ @Readable
public static final String NETWORK_SCORING_UI_ENABLED = "network_scoring_ui_enabled";
/**
@@ -10872,6 +11627,7 @@ public final class Settings {
* Type: long
* @hide
*/
+ @Readable
public static final String SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS =
"speed_label_cache_eviction_age_millis";
@@ -10890,6 +11646,8 @@ public final class Settings {
* @hide
* @deprecated To be removed.
*/
+ @Deprecated
+ @Readable
public static final String NETWORK_RECOMMENDATIONS_ENABLED =
"network_recommendations_enabled";
@@ -10903,6 +11661,7 @@ public final class Settings {
* Type: string - package name
* @hide
*/
+ @Readable
public static final String NETWORK_RECOMMENDATIONS_PACKAGE =
"network_recommendations_package";
@@ -10914,6 +11673,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
/**
@@ -10923,6 +11683,7 @@ public final class Settings {
* Type: long
* @hide
*/
+ @Readable
public static final String RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS =
"recommended_network_evaluator_cache_expiry_ms";
@@ -10934,6 +11695,8 @@ public final class Settings {
* @deprecated Use {@link WifiManager#setScanThrottleEnabled(boolean)} for setting the value
* and {@link WifiManager#isScanThrottleEnabled()} for query.
*/
+ @Deprecated
+ @Readable
public static final String WIFI_SCAN_THROTTLE_ENABLED = "wifi_scan_throttle_enabled";
/**
@@ -10941,24 +11704,28 @@ public final class Settings {
* connectivity.
* @hide
*/
+ @Readable
public static final String BLE_SCAN_ALWAYS_AVAILABLE = "ble_scan_always_enabled";
/**
* The length in milliseconds of a BLE scan window in a low-power scan mode.
* @hide
*/
+ @Readable
public static final String BLE_SCAN_LOW_POWER_WINDOW_MS = "ble_scan_low_power_window_ms";
/**
* The length in milliseconds of a BLE scan window in a balanced scan mode.
* @hide
*/
+ @Readable
public static final String BLE_SCAN_BALANCED_WINDOW_MS = "ble_scan_balanced_window_ms";
/**
* The length in milliseconds of a BLE scan window in a low-latency scan mode.
* @hide
*/
+ @Readable
public static final String BLE_SCAN_LOW_LATENCY_WINDOW_MS =
"ble_scan_low_latency_window_ms";
@@ -10966,6 +11733,7 @@ public final class Settings {
* The length in milliseconds of a BLE scan interval in a low-power scan mode.
* @hide
*/
+ @Readable
public static final String BLE_SCAN_LOW_POWER_INTERVAL_MS =
"ble_scan_low_power_interval_ms";
@@ -10973,6 +11741,7 @@ public final class Settings {
* The length in milliseconds of a BLE scan interval in a balanced scan mode.
* @hide
*/
+ @Readable
public static final String BLE_SCAN_BALANCED_INTERVAL_MS =
"ble_scan_balanced_interval_ms";
@@ -10980,6 +11749,7 @@ public final class Settings {
* The length in milliseconds of a BLE scan interval in a low-latency scan mode.
* @hide
*/
+ @Readable
public static final String BLE_SCAN_LOW_LATENCY_INTERVAL_MS =
"ble_scan_low_latency_interval_ms";
@@ -10987,26 +11757,30 @@ public final class Settings {
* The mode that BLE scanning clients will be moved to when in the background.
* @hide
*/
+ @Readable
public static final String BLE_SCAN_BACKGROUND_MODE = "ble_scan_background_mode";
- /**
+ /**
* The interval in milliseconds to scan as used by the wifi supplicant
* @hide
*/
- public static final String WIFI_SUPPLICANT_SCAN_INTERVAL_MS =
+ @Readable
+ public static final String WIFI_SUPPLICANT_SCAN_INTERVAL_MS =
"wifi_supplicant_scan_interval_ms";
/**
* whether frameworks handles wifi auto-join
* @hide
*/
- public static final String WIFI_ENHANCED_AUTO_JOIN =
+ @Readable
+ public static final String WIFI_ENHANCED_AUTO_JOIN =
"wifi_enhanced_auto_join";
/**
* whether settings show RSSI
* @hide
*/
+ @Readable
public static final String WIFI_NETWORK_SHOW_RSSI =
"wifi_network_show_rssi";
@@ -11014,31 +11788,36 @@ public final class Settings {
* The interval in milliseconds to scan at supplicant when p2p is connected
* @hide
*/
- public static final String WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS =
+ @Readable
+ public static final String WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS =
"wifi_scan_interval_p2p_connected_ms";
- /**
+ /**
* Whether the Wi-Fi watchdog is enabled.
*/
- public static final String WIFI_WATCHDOG_ON = "wifi_watchdog_on";
+ @Readable
+ public static final String WIFI_WATCHDOG_ON = "wifi_watchdog_on";
- /**
+ /**
* Setting to turn off poor network avoidance on Wi-Fi. Feature is enabled by default and
* the setting needs to be set to 0 to disable it.
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final String WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED =
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
+ public static final String WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED =
"wifi_watchdog_poor_network_test_enabled";
- /**
+ /**
* Setting to enable verbose logging in Wi-Fi; disabled by default, and setting to 1
* will enable it. In the future, additional values may be supported.
* @hide
* @deprecated Use {@link WifiManager#setVerboseLoggingEnabled(boolean)} for setting the
* value and {@link WifiManager#isVerboseLoggingEnabled()} for query.
*/
- public static final String WIFI_VERBOSE_LOGGING_ENABLED =
+ @Deprecated
+ @Readable
+ public static final String WIFI_VERBOSE_LOGGING_ENABLED =
"wifi_verbose_logging_enabled";
/**
@@ -11048,6 +11827,7 @@ public final class Settings {
* @hide
*/
@Deprecated
+ @Readable
public static final String WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED =
"wifi_connected_mac_randomization_enabled";
@@ -11064,24 +11844,28 @@ public final class Settings {
* @hide
* @deprecated This is no longer used or set by the platform.
*/
+ @Deprecated
+ @Readable
public static final String WIFI_SCORE_PARAMS =
"wifi_score_params";
- /**
+ /**
* The maximum number of times we will retry a connection to an access
* point for which we have failed in acquiring an IP address from DHCP.
* A value of N means that we will make N+1 connection attempts in all.
*/
- public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
+ @Readable
+ public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
- /**
+ /**
* Maximum amount of time in milliseconds to hold a wakelock while waiting for mobile
* data connectivity to be established after a disconnect from Wi-Fi.
*/
- public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS =
+ @Readable
+ public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS =
"wifi_mobile_data_transition_wakelock_timeout_ms";
- /**
+ /**
* This setting controls whether WiFi configurations created by a Device Owner app
* should be locked down (that is, be editable or removable only by the Device Owner App,
* not even by Settings app).
@@ -11089,10 +11873,11 @@ public final class Settings {
* are locked down. Value of zero means they are not. Default value in the absence of
* actual value to this setting is 0.
*/
- public static final String WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN =
+ @Readable
+ public static final String WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN =
"wifi_device_owner_configs_lockdown";
- /**
+ /**
* The operational wifi frequency band
* Set to one of {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
* {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ} or
@@ -11100,18 +11885,21 @@ public final class Settings {
*
* @hide
*/
- public static final String WIFI_FREQUENCY_BAND = "wifi_frequency_band";
+ @Readable
+ public static final String WIFI_FREQUENCY_BAND = "wifi_frequency_band";
- /**
+ /**
* The Wi-Fi peer-to-peer device name
* @hide
* @deprecated Use {@link WifiP2pManager#setDeviceName(WifiP2pManager.Channel, String,
* WifiP2pManager.ActionListener)} for setting the value and
* {@link android.net.wifi.p2p.WifiP2pDevice#deviceName} for query.
*/
- public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
+ @Deprecated
+ @Readable
+ public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
- /**
+ /**
* Timeout for ephemeral networks when all known BSSIDs go out of range. We will disconnect
* from an ephemeral network if there is no BSSID for that network with a non-null score that
* has been seen in this time period.
@@ -11120,53 +11908,60 @@ public final class Settings {
* for a non-null score from the currently connected or target BSSID.
* @hide
*/
- public static final String WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS =
+ @Readable
+ public static final String WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS =
"wifi_ephemeral_out_of_range_timeout_ms";
- /**
+ /**
* The number of milliseconds to delay when checking for data stalls during
* non-aggressive detection. (screen is turned off.)
* @hide
*/
- public static final String DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS =
+ @Readable
+ public static final String DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS =
"data_stall_alarm_non_aggressive_delay_in_ms";
- /**
+ /**
* The number of milliseconds to delay when checking for data stalls during
* aggressive detection. (screen on or suspected data stall)
* @hide
*/
- public static final String DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS =
+ @Readable
+ public static final String DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS =
"data_stall_alarm_aggressive_delay_in_ms";
- /**
+ /**
* The number of milliseconds to allow the provisioning apn to remain active
* @hide
*/
- public static final String PROVISIONING_APN_ALARM_DELAY_IN_MS =
+ @Readable
+ public static final String PROVISIONING_APN_ALARM_DELAY_IN_MS =
"provisioning_apn_alarm_delay_in_ms";
- /**
+ /**
* The interval in milliseconds at which to check gprs registration
* after the first registration mismatch of gprs and voice service,
* to detect possible data network registration problems.
*
* @hide
*/
- public static final String GPRS_REGISTER_CHECK_PERIOD_MS =
+ @Readable
+ public static final String GPRS_REGISTER_CHECK_PERIOD_MS =
"gprs_register_check_period_ms";
- /**
+ /**
* Nonzero causes Log.wtf() to crash.
* @hide
*/
- public static final String WTF_IS_FATAL = "wtf_is_fatal";
+ @Readable
+ public static final String WTF_IS_FATAL = "wtf_is_fatal";
- /**
+ /**
* Ringer mode. This is used internally, changing this value will not
* change the ringer mode. See AudioManager.
*/
- public static final String MODE_RINGER = "mode_ringer";
+ @Readable
+ public static final String MODE_RINGER = "mode_ringer";
/**
* Overlay display devices setting.
@@ -11207,6 +12002,7 @@ public final class Settings {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @Readable
public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices";
/**
@@ -11215,10 +12011,12 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String
BATTERY_DISCHARGE_DURATION_THRESHOLD = "battery_discharge_duration_threshold";
/** @hide */
+ @Readable
public static final String BATTERY_DISCHARGE_THRESHOLD = "battery_discharge_threshold";
/**
@@ -11230,6 +12028,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String SEND_ACTION_APP_ERROR = "send_action_app_error";
/**
@@ -11237,6 +12036,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String DROPBOX_AGE_SECONDS = "dropbox_age_seconds";
/**
@@ -11245,6 +12045,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String DROPBOX_MAX_FILES = "dropbox_max_files";
/**
@@ -11253,6 +12054,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String DROPBOX_QUOTA_KB = "dropbox_quota_kb";
/**
@@ -11261,6 +12063,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String DROPBOX_QUOTA_PERCENT = "dropbox_quota_percent";
/**
@@ -11269,6 +12072,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String DROPBOX_RESERVE_PERCENT = "dropbox_reserve_percent";
/**
@@ -11276,6 +12080,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String DROPBOX_TAG_PREFIX = "dropbox:";
/**
@@ -11286,6 +12091,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ERROR_LOGCAT_PREFIX = "logcat_for_";
/**
@@ -11299,6 +12105,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String MAX_ERROR_BYTES_PREFIX = "max_error_bytes_for_";
/**
@@ -11307,6 +12114,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String SYS_FREE_STORAGE_LOG_INTERVAL = "sys_free_storage_log_interval";
/**
@@ -11316,6 +12124,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String
DISK_FREE_CHANGE_REPORTING_THRESHOLD = "disk_free_change_reporting_threshold";
@@ -11328,6 +12137,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String
SYS_STORAGE_THRESHOLD_PERCENTAGE = "sys_storage_threshold_percentage";
@@ -11339,6 +12149,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String
SYS_STORAGE_THRESHOLD_MAX_BYTES = "sys_storage_threshold_max_bytes";
@@ -11349,6 +12160,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String
SYS_STORAGE_FULL_THRESHOLD_BYTES = "sys_storage_full_threshold_bytes";
@@ -11358,6 +12170,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String
SYS_STORAGE_CACHE_PERCENTAGE = "sys_storage_cache_percentage";
@@ -11367,6 +12180,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String
SYS_STORAGE_CACHE_MAX_BYTES = "sys_storage_cache_max_bytes";
@@ -11376,6 +12190,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String
SYNC_MAX_RETRY_DELAY_IN_SECONDS = "sync_max_retry_delay_in_seconds";
@@ -11385,6 +12200,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CONNECTIVITY_CHANGE_DELAY = "connectivity_change_delay";
@@ -11394,7 +12210,7 @@ public final class Settings {
*
* @hide
*/
-
+ @Readable
public static final String CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS =
"connectivity_sampling_interval_in_seconds";
@@ -11404,6 +12220,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String PAC_CHANGE_DELAY = "pac_change_delay";
/**
@@ -11436,6 +12253,7 @@ public final class Settings {
* The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT.
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode";
/**
@@ -11446,6 +12264,7 @@ public final class Settings {
* @hide
*/
@Deprecated
+ @Readable
public static final String
CAPTIVE_PORTAL_DETECTION_ENABLED = "captive_portal_detection_enabled";
@@ -11456,6 +12275,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_SERVER = "captive_portal_server";
/**
@@ -11464,6 +12284,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url";
/**
@@ -11472,6 +12293,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url";
/**
@@ -11480,6 +12302,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url";
/**
@@ -11488,6 +12311,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS =
"captive_portal_other_fallback_urls";
@@ -11497,6 +12321,7 @@ public final class Settings {
* by "@@,@@".
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS =
"captive_portal_fallback_probe_specs";
@@ -11507,6 +12332,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https";
/**
@@ -11515,6 +12341,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent";
/**
@@ -11522,6 +12349,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String DATA_STALL_RECOVERY_ON_BAD_NETWORK =
"data_stall_recovery_on_bad_network";
@@ -11530,6 +12358,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS =
"min_duration_between_recovery_steps";
/**
@@ -11537,6 +12366,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String NSD_ON = "nsd_on";
/**
@@ -11544,6 +12374,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String SET_INSTALL_LOCATION = "set_install_location";
/**
@@ -11553,6 +12384,7 @@ public final class Settings {
* 2 = sdcard
* @hide
*/
+ @Readable
public static final String DEFAULT_INSTALL_LOCATION = "default_install_location";
/**
@@ -11561,6 +12393,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String
INET_CONDITION_DEBOUNCE_UP_DELAY = "inet_condition_debounce_up_delay";
@@ -11570,10 +12403,12 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String
INET_CONDITION_DEBOUNCE_DOWN_DELAY = "inet_condition_debounce_down_delay";
/** {@hide} */
+ @Readable
public static final String
READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT = "read_external_storage_enforced_default";
@@ -11581,6 +12416,7 @@ public final class Settings {
* Host name and port for global http proxy. Uses ':' seperator for
* between host and port.
*/
+ @Readable
public static final String HTTP_PROXY = "http_proxy";
/**
@@ -11588,6 +12424,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host";
/**
@@ -11595,6 +12432,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port";
/**
@@ -11606,6 +12444,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String
GLOBAL_HTTP_PROXY_EXCLUSION_LIST = "global_http_proxy_exclusion_list";
@@ -11613,6 +12452,7 @@ public final class Settings {
* The location PAC File for the proxy.
* @hide
*/
+ @Readable
public static final String
GLOBAL_HTTP_PROXY_PAC = "global_proxy_pac_url";
@@ -11622,6 +12462,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String SET_GLOBAL_HTTP_PROXY = "set_global_http_proxy";
/**
@@ -11629,6 +12470,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String DEFAULT_DNS_SERVER = "default_dns_server";
/**
@@ -11641,11 +12483,13 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String PRIVATE_DNS_MODE = "private_dns_mode";
/**
* @hide
*/
+ @Readable
public static final String PRIVATE_DNS_SPECIFIER = "private_dns_specifier";
/**
@@ -11657,46 +12501,60 @@ public final class Settings {
*
* {@hide}
*/
+ @Readable
public static final String PRIVATE_DNS_DEFAULT_MODE = "private_dns_default_mode";
/** {@hide} */
+ @Readable
public static final String
BLUETOOTH_BTSNOOP_DEFAULT_MODE = "bluetooth_btsnoop_default_mode";
/** {@hide} */
+ @Readable
public static final String
BLUETOOTH_HEADSET_PRIORITY_PREFIX = "bluetooth_headset_priority_";
/** {@hide} */
+ @Readable
public static final String
BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX = "bluetooth_a2dp_sink_priority_";
/** {@hide} */
+ @Readable
public static final String
BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX = "bluetooth_a2dp_src_priority_";
/** {@hide} */
+ @Readable
public static final String BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX =
"bluetooth_a2dp_supports_optional_codecs_";
/** {@hide} */
+ @Readable
public static final String BLUETOOTH_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX =
"bluetooth_a2dp_optional_codecs_enabled_";
/** {@hide} */
+ @Readable
public static final String
BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX = "bluetooth_input_device_priority_";
/** {@hide} */
+ @Readable
public static final String
BLUETOOTH_MAP_PRIORITY_PREFIX = "bluetooth_map_priority_";
/** {@hide} */
+ @Readable
public static final String
BLUETOOTH_MAP_CLIENT_PRIORITY_PREFIX = "bluetooth_map_client_priority_";
/** {@hide} */
+ @Readable
public static final String
BLUETOOTH_PBAP_CLIENT_PRIORITY_PREFIX = "bluetooth_pbap_client_priority_";
/** {@hide} */
+ @Readable
public static final String
BLUETOOTH_SAP_PRIORITY_PREFIX = "bluetooth_sap_priority_";
/** {@hide} */
+ @Readable
public static final String
BLUETOOTH_PAN_PRIORITY_PREFIX = "bluetooth_pan_priority_";
/** {@hide} */
+ @Readable
public static final String
BLUETOOTH_HEARING_AID_PRIORITY_PREFIX = "bluetooth_hearing_aid_priority_";
@@ -11705,6 +12563,7 @@ public final class Settings {
*
* {@hide}
*/
+ @Readable
public static final String
ENABLE_RADIO_BUG_DETECTION = "enable_radio_bug_detection";
@@ -11713,6 +12572,7 @@ public final class Settings {
*
* {@hide}
*/
+ @Readable
public static final String
RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD =
"radio_bug_wakelock_timeout_count_threshold";
@@ -11722,6 +12582,7 @@ public final class Settings {
*
* {@hide}
*/
+ @Readable
public static final String
RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD =
"radio_bug_system_error_count_threshold";
@@ -11768,6 +12629,7 @@ public final class Settings {
* @hide
* @see com.android.server.am.ActivityManagerConstants
*/
+ @Readable
public static final String ACTIVITY_MANAGER_CONSTANTS = "activity_manager_constants";
/**
@@ -11776,6 +12638,7 @@ public final class Settings {
* Default: 1
* @hide
*/
+ @Readable
public static final String ACTIVITY_STARTS_LOGGING_ENABLED
= "activity_starts_logging_enabled";
@@ -11785,6 +12648,7 @@ public final class Settings {
* Default: 1
* @hide
*/
+ @Readable
public static final String FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED =
"foreground_service_starts_logging_enabled";
@@ -11792,6 +12656,7 @@ public final class Settings {
* @hide
* @see com.android.server.appbinding.AppBindingConstants
*/
+ @Readable
public static final String APP_BINDING_CONSTANTS = "app_binding_constants";
/**
@@ -11814,6 +12679,7 @@ public final class Settings {
* @see com.android.server.AppOpsService.Constants
*/
@TestApi
+ @Readable
public static final String APP_OPS_CONSTANTS = "app_ops_constants";
/**
@@ -11849,6 +12715,7 @@ public final class Settings {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @Readable
public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants";
/**
@@ -11866,6 +12733,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS =
"battery_saver_device_specific_constants";
@@ -11895,6 +12763,7 @@ public final class Settings {
* </pre>
* @hide
*/
+ @Readable
public static final String BATTERY_TIP_CONSTANTS = "battery_tip_constants";
/**
@@ -11920,6 +12789,7 @@ public final class Settings {
* </pre>
* @hide
*/
+ @Readable
public static final String ANOMALY_DETECTION_CONSTANTS = "anomaly_detection_constants";
/**
@@ -11927,6 +12797,7 @@ public final class Settings {
* current version is 1.
* @hide
*/
+ @Readable
public static final String ANOMALY_CONFIG_VERSION = "anomaly_config_version";
/**
@@ -11934,6 +12805,7 @@ public final class Settings {
* {@link android.app.StatsManager}.
* @hide
*/
+ @Readable
public static final String ANOMALY_CONFIG = "anomaly_config";
/**
@@ -11953,6 +12825,7 @@ public final class Settings {
* </pre>
* @hide
*/
+ @Readable
public static final String ALWAYS_ON_DISPLAY_CONSTANTS = "always_on_display_constants";
/**
@@ -11963,6 +12836,7 @@ public final class Settings {
* Any other value defaults to enabled.
* @hide
*/
+ @Readable
public static final String SYS_UIDCPUPOWER = "sys_uidcpupower";
/**
@@ -11974,6 +12848,7 @@ public final class Settings {
* Any other value defaults to disabled.
* @hide
*/
+ @Readable
public static final String SYS_TRACED = "sys_traced";
/**
@@ -11982,6 +12857,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String FPS_DEVISOR = "fps_divisor";
/**
@@ -11991,6 +12867,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String DISPLAY_PANEL_LPM = "display_panel_lpm";
/**
@@ -12005,6 +12882,7 @@ public final class Settings {
* Need to reboot the device for this setting to take effect.
* @hide
*/
+ @Readable
public static final String APP_TIME_LIMIT_USAGE_SOURCE = "app_time_limit_usage_source";
/**
@@ -12012,6 +12890,7 @@ public final class Settings {
* 0 = disable, 1 = enable.
* @hide
*/
+ @Readable
public static final String ART_VERIFIER_VERIFY_DEBUGGABLE =
"art_verifier_verify_debuggable";
@@ -12032,6 +12911,7 @@ public final class Settings {
* @hide
* @see com.android.server.power.PowerManagerConstants
*/
+ @Readable
public static final String POWER_MANAGER_CONSTANTS = "power_manager_constants";
/**
@@ -12057,6 +12937,7 @@ public final class Settings {
* @hide
* @see com.android.server.pm.ShortcutService.ConfigConstants
*/
+ @Readable
public static final String SHORTCUT_MANAGER_CONSTANTS = "shortcut_manager_constants";
/**
@@ -12074,6 +12955,7 @@ public final class Settings {
* @hide
* see also com.android.server.devicepolicy.DevicePolicyConstants
*/
+ @Readable
public static final String DEVICE_POLICY_CONSTANTS = "device_policy_constants";
/**
@@ -12110,6 +12992,7 @@ public final class Settings {
* @hide
* see also android.view.textclassifier.TextClassificationConstants
*/
+ @Readable
public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants";
/**
@@ -12134,6 +13017,7 @@ public final class Settings {
* @hide
* see also com.android.internal.os.BatteryStatsImpl.Constants
*/
+ @Readable
public static final String BATTERY_STATS_CONSTANTS = "battery_stats_constants";
/**
@@ -12144,6 +13028,7 @@ public final class Settings {
* @hide
* @see com.android.server.content.SyncManagerConstants
*/
+ @Readable
public static final String SYNC_MANAGER_CONSTANTS = "sync_manager_constants";
/**
@@ -12163,6 +13048,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String BROADCAST_FG_CONSTANTS = "bcast_fg_constants";
/**
@@ -12173,6 +13059,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String BROADCAST_BG_CONSTANTS = "bcast_bg_constants";
/**
@@ -12183,6 +13070,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String BROADCAST_OFFLOAD_CONSTANTS = "bcast_offload_constants";
/**
@@ -12195,6 +13083,7 @@ public final class Settings {
* @see #ADAPTIVE_BATTERY_MANAGEMENT_ENABLED
*/
@SystemApi
+ @Readable
public static final String APP_STANDBY_ENABLED = "app_standby_enabled";
/**
@@ -12205,6 +13094,7 @@ public final class Settings {
* @hide
* @see #APP_STANDBY_ENABLED
*/
+ @Readable
public static final String ADAPTIVE_BATTERY_MANAGEMENT_ENABLED =
"adaptive_battery_management_enabled";
@@ -12216,6 +13106,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ENABLE_RESTRICTED_BUCKET = "enable_restricted_bucket";
/**
@@ -12233,6 +13124,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String APP_AUTO_RESTRICTION_ENABLED =
"app_auto_restriction_enabled";
@@ -12242,6 +13134,7 @@ public final class Settings {
* Default: 1
* @hide
*/
+ @Readable
public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled";
/**
@@ -12250,6 +13143,7 @@ public final class Settings {
* Default: 0
* @hide
*/
+ @Readable
public static final String FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED
= "forced_app_standby_for_small_battery_enabled";
@@ -12259,6 +13153,7 @@ public final class Settings {
* Default: 0
* @hide
*/
+ @Readable
public static final String USER_ABSENT_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED
= "user_absent_radios_off_for_small_battery_enabled";
@@ -12268,6 +13163,7 @@ public final class Settings {
* Default: 0
* @hide
*/
+ @Readable
public static final String USER_ABSENT_TOUCH_OFF_FOR_SMALL_BATTERY_ENABLED
= "user_absent_touch_off_for_small_battery_enabled";
@@ -12277,6 +13173,7 @@ public final class Settings {
* Default: 1
* @hide
*/
+ @Readable
public static final String WIFI_ON_WHEN_PROXY_DISCONNECTED
= "wifi_on_when_proxy_disconnected";
@@ -12295,6 +13192,7 @@ public final class Settings {
* Type: string
* @hide
*/
+ @Readable
public static final String TIME_ONLY_MODE_CONSTANTS
= "time_only_mode_constants";
@@ -12305,6 +13203,7 @@ public final class Settings {
* Default: 0
* @hide
*/
+ @Readable
public static final String UNGAZE_SLEEP_ENABLED = "ungaze_sleep_enabled";
/**
@@ -12313,6 +13212,7 @@ public final class Settings {
* Default: 0
* @hide
*/
+ @Readable
public static final String NETWORK_WATCHLIST_ENABLED = "network_watchlist_enabled";
/**
@@ -12321,6 +13221,7 @@ public final class Settings {
* Default: 1
* @hide
*/
+ @Readable
public static final String SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED =
"show_hidden_icon_apps_enabled";
@@ -12330,6 +13231,7 @@ public final class Settings {
* Default: 0
* @hide
*/
+ @Readable
public static final String SHOW_NEW_APP_INSTALLED_NOTIFICATION_ENABLED =
"show_new_app_installed_notification_enabled";
@@ -12343,6 +13245,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String KEEP_PROFILE_IN_BACKGROUND = "keep_profile_in_background";
/**
@@ -12364,6 +13267,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ADB_ALLOWED_CONNECTION_TIME =
"adb_allowed_connection_time";
@@ -12371,12 +13275,14 @@ public final class Settings {
* Scaling factor for normal window animations. Setting to 0 will
* disable window animations.
*/
+ @Readable
public static final String WINDOW_ANIMATION_SCALE = "window_animation_scale";
/**
* Scaling factor for activity transition animations. Setting to 0 will
* disable window animations.
*/
+ @Readable
public static final String TRANSITION_ANIMATION_SCALE = "transition_animation_scale";
/**
@@ -12384,6 +13290,7 @@ public final class Settings {
* start delay and duration of all such animations. Setting to 0 will
* cause animations to end immediately. The default value is 1.
*/
+ @Readable
public static final String ANIMATOR_DURATION_SCALE = "animator_duration_scale";
/**
@@ -12392,6 +13299,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String FANCY_IME_ANIMATIONS = "fancy_ime_animations";
/**
@@ -12400,6 +13308,7 @@ public final class Settings {
* TODO: remove this settings before code freeze (bug/1907571)
* @hide
*/
+ @Readable
public static final String COMPATIBILITY_MODE = "compatibility_mode";
/**
@@ -12409,6 +13318,7 @@ public final class Settings {
* 2 = Vibrate
* @hide
*/
+ @Readable
public static final String EMERGENCY_TONE = "emergency_tone";
/**
@@ -12417,6 +13327,7 @@ public final class Settings {
* boolean (1 or 0).
* @hide
*/
+ @Readable
public static final String CALL_AUTO_RETRY = "call_auto_retry";
/**
@@ -12424,6 +13335,7 @@ public final class Settings {
* The value is a boolean (1 or 0).
* @hide
*/
+ @Readable
public static final String EMERGENCY_AFFORDANCE_NEEDED = "emergency_affordance_needed";
/**
@@ -12433,6 +13345,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS =
"enable_automatic_system_server_heap_dumps";
@@ -12441,18 +13354,21 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String PREFERRED_NETWORK_MODE =
"preferred_network_mode";
/**
* Name of an application package to be debugged.
*/
+ @Readable
public static final String DEBUG_APP = "debug_app";
/**
* If 1, when launching DEBUG_APP it will wait for the debugger before
* starting user code. If 0, it will run normally.
*/
+ @Readable
public static final String WAIT_FOR_DEBUGGER = "wait_for_debugger";
/**
@@ -12461,12 +13377,14 @@ public final class Settings {
* 1 = yes
* @hide
*/
+ @Readable
public static final String ENABLE_GPU_DEBUG_LAYERS = "enable_gpu_debug_layers";
/**
* App allowed to load GPU debug layers
* @hide
*/
+ @Readable
public static final String GPU_DEBUG_APP = "gpu_debug_app";
/**
@@ -12474,6 +13392,7 @@ public final class Settings {
* to dumpable apps that opt-in.
* @hide
*/
+ @Readable
public static final String ANGLE_DEBUG_PACKAGE = "angle_debug_package";
/**
@@ -12481,12 +13400,14 @@ public final class Settings {
* The value is a boolean (1 or 0).
* @hide
*/
+ @Readable
public static final String ANGLE_GL_DRIVER_ALL_ANGLE = "angle_gl_driver_all_angle";
/**
* List of PKGs that have an OpenGL driver selected
* @hide
*/
+ @Readable
public static final String ANGLE_GL_DRIVER_SELECTION_PKGS =
"angle_gl_driver_selection_pkgs";
@@ -12494,6 +13415,7 @@ public final class Settings {
* List of selected OpenGL drivers, corresponding to the PKGs in GLOBAL_SETTINGS_DRIVER_PKGS
* @hide
*/
+ @Readable
public static final String ANGLE_GL_DRIVER_SELECTION_VALUES =
"angle_gl_driver_selection_values";
@@ -12501,6 +13423,7 @@ public final class Settings {
* List of package names that should check ANGLE rules
* @hide
*/
+ @Readable
public static final String ANGLE_ALLOWLIST = "angle_allowlist";
/**
@@ -12510,6 +13433,7 @@ public final class Settings {
* e.g. feature1:feature2:feature3,feature1:feature3:feature5
* @hide
*/
+ @Readable
public static final String ANGLE_EGL_FEATURES = "angle_egl_features";
/**
@@ -12517,6 +13441,7 @@ public final class Settings {
* The value is a boolean (1 or 0).
* @hide
*/
+ @Readable
public static final String SHOW_ANGLE_IN_USE_DIALOG_BOX = "show_angle_in_use_dialog_box";
/**
@@ -12527,6 +13452,7 @@ public final class Settings {
* 3 = All Apps use system graphics driver
* @hide
*/
+ @Readable
public static final String UPDATABLE_DRIVER_ALL_APPS = "updatable_driver_all_apps";
/**
@@ -12534,6 +13460,7 @@ public final class Settings {
* i.e. <pkg1>,<pkg2>,...,<pkgN>
* @hide
*/
+ @Readable
public static final String UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS =
"updatable_driver_production_opt_in_apps";
@@ -12542,6 +13469,7 @@ public final class Settings {
* i.e. <pkg1>,<pkg2>,...,<pkgN>
* @hide
*/
+ @Readable
public static final String UPDATABLE_DRIVER_PRERELEASE_OPT_IN_APPS =
"updatable_driver_prerelease_opt_in_apps";
@@ -12550,6 +13478,7 @@ public final class Settings {
* i.e. <pkg1>,<pkg2>,...,<pkgN>
* @hide
*/
+ @Readable
public static final String UPDATABLE_DRIVER_PRODUCTION_OPT_OUT_APPS =
"updatable_driver_production_opt_out_apps";
@@ -12557,6 +13486,7 @@ public final class Settings {
* Apps on the denylist that are forbidden to use updatable production driver.
* @hide
*/
+ @Readable
public static final String UPDATABLE_DRIVER_PRODUCTION_DENYLIST =
"updatable_driver_production_denylist";
@@ -12565,6 +13495,7 @@ public final class Settings {
* updatable production driver.
* @hide
*/
+ @Readable
public static final String UPDATABLE_DRIVER_PRODUCTION_DENYLISTS =
"updatable_driver_production_denylists";
@@ -12574,6 +13505,7 @@ public final class Settings {
* i.e. <apk1>,<apk2>,...,<apkN>
* @hide
*/
+ @Readable
public static final String UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST =
"updatable_driver_production_allowlist";
@@ -12583,6 +13515,7 @@ public final class Settings {
* i.e. <lib1>:<lib2>:...:<libN>
* @hide
*/
+ @Readable
public static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES =
"updatable_driver_sphal_libraries";
@@ -12591,6 +13524,7 @@ public final class Settings {
* i.e. <layer1>:<layer2>:...:<layerN>
* @hide
*/
+ @Readable
public static final String GPU_DEBUG_LAYERS = "gpu_debug_layers";
/**
@@ -12598,12 +13532,14 @@ public final class Settings {
* i.e. <layer1>:<layer2>:...:<layerN>
* @hide
*/
+ @Readable
public static final String GPU_DEBUG_LAYERS_GLES = "gpu_debug_layers_gles";
/**
* Addition app for GPU layer discovery
* @hide
*/
+ @Readable
public static final String GPU_DEBUG_LAYER_APP = "gpu_debug_layer_app";
/**
@@ -12613,6 +13549,7 @@ public final class Settings {
* {@link android.os.Build.VERSION_CODES#N_MR1}.
*/
@Deprecated
+ @Readable
public static final String SHOW_PROCESSES = "show_processes";
/**
@@ -12620,6 +13557,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String LOW_POWER_MODE = "low_power";
/**
@@ -12628,6 +13566,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String LOW_POWER_MODE_STICKY = "low_power_sticky";
/**
@@ -12637,6 +13576,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL =
"low_power_sticky_auto_disable_level";
@@ -12646,6 +13586,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED =
"low_power_sticky_auto_disable_enabled";
@@ -12659,6 +13600,7 @@ public final class Settings {
* @see android.os.PowerManager#getPowerSaveModeTrigger()
* @hide
*/
+ @Readable
public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";
/**
@@ -12669,6 +13611,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String AUTOMATIC_POWER_SAVE_MODE = "automatic_power_save_mode";
/**
@@ -12679,6 +13622,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD =
"dynamic_power_savings_disable_threshold";
@@ -12689,6 +13633,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled";
/**
@@ -12700,6 +13645,7 @@ public final class Settings {
* @hide
*/
@Deprecated
+ @Readable
public static final String TIME_REMAINING_ESTIMATE_MILLIS =
"time_remaining_estimate_millis";
@@ -12713,6 +13659,7 @@ public final class Settings {
* @hide
*/
@Deprecated
+ @Readable
public static final String TIME_REMAINING_ESTIMATE_BASED_ON_USAGE =
"time_remaining_estimate_based_on_usage";
@@ -12725,6 +13672,7 @@ public final class Settings {
* @hide
*/
@Deprecated
+ @Readable
public static final String AVERAGE_TIME_TO_DISCHARGE = "average_time_to_discharge";
/**
@@ -12736,6 +13684,7 @@ public final class Settings {
* @deprecated No longer needed due to {@link PowerManager#getBatteryDischargePrediction}.
*/
@Deprecated
+ @Readable
public static final String BATTERY_ESTIMATES_LAST_UPDATE_TIME =
"battery_estimates_last_update_time";
@@ -12745,12 +13694,14 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String LOW_POWER_MODE_TRIGGER_LEVEL_MAX = "low_power_trigger_level_max";
/**
* See com.android.settingslib.fuelgauge.BatterySaverUtils.
* @hide
*/
+ @Readable
public static final String LOW_POWER_MODE_SUGGESTION_PARAMS =
"low_power_mode_suggestion_params";
@@ -12759,6 +13710,7 @@ public final class Settings {
* processes as soon as they are no longer needed. If 0, the normal
* extended lifetime is used.
*/
+ @Readable
public static final String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities";
/**
@@ -12768,6 +13720,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String HIDE_ERROR_DIALOGS = "hide_error_dialogs";
/**
@@ -12776,6 +13729,7 @@ public final class Settings {
* 1 = enabled
* @hide
*/
+ @Readable
public static final String DOCK_AUDIO_MEDIA_ENABLED = "dock_audio_media_enabled";
/**
@@ -12835,6 +13789,7 @@ public final class Settings {
* ENCODED_SURROUND_OUTPUT_MANUAL
* @hide
*/
+ @Readable
public static final String ENCODED_SURROUND_OUTPUT = "encoded_surround_output";
/**
@@ -12846,6 +13801,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS =
"encoded_surround_output_enabled_formats";
@@ -12853,36 +13809,42 @@ public final class Settings {
* Persisted safe headphone volume management state by AudioService
* @hide
*/
+ @Readable
public static final String AUDIO_SAFE_VOLUME_STATE = "audio_safe_volume_state";
/**
* URL for tzinfo (time zone) updates
* @hide
*/
+ @Readable
public static final String TZINFO_UPDATE_CONTENT_URL = "tzinfo_content_url";
/**
* URL for tzinfo (time zone) update metadata
* @hide
*/
+ @Readable
public static final String TZINFO_UPDATE_METADATA_URL = "tzinfo_metadata_url";
/**
* URL for selinux (mandatory access control) updates
* @hide
*/
+ @Readable
public static final String SELINUX_UPDATE_CONTENT_URL = "selinux_content_url";
/**
* URL for selinux (mandatory access control) update metadata
* @hide
*/
+ @Readable
public static final String SELINUX_UPDATE_METADATA_URL = "selinux_metadata_url";
/**
* URL for sms short code updates
* @hide
*/
+ @Readable
public static final String SMS_SHORT_CODES_UPDATE_CONTENT_URL =
"sms_short_codes_content_url";
@@ -12890,6 +13852,7 @@ public final class Settings {
* URL for sms short code update metadata
* @hide
*/
+ @Readable
public static final String SMS_SHORT_CODES_UPDATE_METADATA_URL =
"sms_short_codes_metadata_url";
@@ -12897,30 +13860,35 @@ public final class Settings {
* URL for apn_db updates
* @hide
*/
+ @Readable
public static final String APN_DB_UPDATE_CONTENT_URL = "apn_db_content_url";
/**
* URL for apn_db update metadata
* @hide
*/
+ @Readable
public static final String APN_DB_UPDATE_METADATA_URL = "apn_db_metadata_url";
/**
* URL for cert pinlist updates
* @hide
*/
+ @Readable
public static final String CERT_PIN_UPDATE_CONTENT_URL = "cert_pin_content_url";
/**
* URL for cert pinlist updates
* @hide
*/
+ @Readable
public static final String CERT_PIN_UPDATE_METADATA_URL = "cert_pin_metadata_url";
/**
* URL for intent firewall updates
* @hide
*/
+ @Readable
public static final String INTENT_FIREWALL_UPDATE_CONTENT_URL =
"intent_firewall_content_url";
@@ -12928,6 +13896,7 @@ public final class Settings {
* URL for intent firewall update metadata
* @hide
*/
+ @Readable
public static final String INTENT_FIREWALL_UPDATE_METADATA_URL =
"intent_firewall_metadata_url";
@@ -12935,18 +13904,21 @@ public final class Settings {
* URL for lang id model updates
* @hide
*/
+ @Readable
public static final String LANG_ID_UPDATE_CONTENT_URL = "lang_id_content_url";
/**
* URL for lang id model update metadata
* @hide
*/
+ @Readable
public static final String LANG_ID_UPDATE_METADATA_URL = "lang_id_metadata_url";
/**
* URL for smart selection model updates
* @hide
*/
+ @Readable
public static final String SMART_SELECTION_UPDATE_CONTENT_URL =
"smart_selection_content_url";
@@ -12954,6 +13926,7 @@ public final class Settings {
* URL for smart selection model update metadata
* @hide
*/
+ @Readable
public static final String SMART_SELECTION_UPDATE_METADATA_URL =
"smart_selection_metadata_url";
@@ -12961,6 +13934,7 @@ public final class Settings {
* URL for conversation actions model updates
* @hide
*/
+ @Readable
public static final String CONVERSATION_ACTIONS_UPDATE_CONTENT_URL =
"conversation_actions_content_url";
@@ -12968,6 +13942,7 @@ public final class Settings {
* URL for conversation actions model update metadata
* @hide
*/
+ @Readable
public static final String CONVERSATION_ACTIONS_UPDATE_METADATA_URL =
"conversation_actions_metadata_url";
@@ -12975,12 +13950,14 @@ public final class Settings {
* SELinux enforcement status. If 0, permissive; if 1, enforcing.
* @hide
*/
+ @Readable
public static final String SELINUX_STATUS = "selinux_status";
/**
* Developer setting to force RTL layout.
* @hide
*/
+ @Readable
public static final String DEVELOPMENT_FORCE_RTL = "debug.force_rtl";
/**
@@ -12991,6 +13968,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String LOW_BATTERY_SOUND_TIMEOUT = "low_battery_sound_timeout";
/**
@@ -13000,6 +13978,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String WIFI_BOUNCE_DELAY_OVERRIDE_MS = "wifi_bounce_delay_override_ms";
/**
@@ -13009,6 +13988,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String POLICY_CONTROL = "policy_control";
/**
@@ -13016,6 +13996,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String EMULATE_DISPLAY_CUTOUT = "emulate_display_cutout";
/** @hide */ public static final int EMULATE_DISPLAY_CUTOUT_OFF = 0;
@@ -13026,6 +14007,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String BLOCKED_SLICES = "blocked_slices";
/**
@@ -13035,6 +14017,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String ZEN_MODE = "zen_mode";
/** @hide */
@@ -13074,6 +14057,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ZEN_MODE_RINGER_LEVEL = "zen_mode_ringer_level";
/**
@@ -13082,6 +14066,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String ZEN_MODE_CONFIG_ETAG = "zen_mode_config_etag";
/**
@@ -13111,6 +14096,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String HEADS_UP_NOTIFICATIONS_ENABLED =
"heads_up_notifications_enabled";
@@ -13124,6 +14110,7 @@ public final class Settings {
/**
* The name of the device
*/
+ @Readable
public static final String DEVICE_NAME = "device_name";
/**
@@ -13132,6 +14119,7 @@ public final class Settings {
* Type: int (0 for false, 1 for true)
* @hide
*/
+ @Readable
public static final String NETWORK_SCORING_PROVISIONED = "network_scoring_provisioned";
/**
@@ -13143,6 +14131,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt";
/**
@@ -13156,6 +14145,7 @@ public final class Settings {
* {@link android.provider.Telephony.SimInfo#COLUMN_ENHANCED_4G_MODE_ENABLED} instead.
*/
@Deprecated
+ @Readable
public static final String ENHANCED_4G_MODE_ENABLED =
Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED;
@@ -13168,6 +14158,7 @@ public final class Settings {
* @deprecated Use {@link android.provider.Telephony.SimInfo#COLUMN_VT_IMS_ENABLED} instead.
*/
@Deprecated
+ @Readable
public static final String VT_IMS_ENABLED = Telephony.SimInfo.COLUMN_VT_IMS_ENABLED;
/**
@@ -13180,6 +14171,7 @@ public final class Settings {
* {@link android.provider.Telephony.SimInfo#COLUMN_WFC_IMS_ENABLED} instead.
*/
@Deprecated
+ @Readable
public static final String WFC_IMS_ENABLED = Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED;
/**
@@ -13191,6 +14183,7 @@ public final class Settings {
* @deprecated Use {@link android.provider.Telephony.SimInfo#COLUMN_WFC_IMS_MODE} instead.
*/
@Deprecated
+ @Readable
public static final String WFC_IMS_MODE = Telephony.SimInfo.COLUMN_WFC_IMS_MODE;
/**
@@ -13203,6 +14196,7 @@ public final class Settings {
* instead.
*/
@Deprecated
+ @Readable
public static final String WFC_IMS_ROAMING_MODE =
Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE;
@@ -13216,6 +14210,7 @@ public final class Settings {
* instead
*/
@Deprecated
+ @Readable
public static final String WFC_IMS_ROAMING_ENABLED =
Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED;
@@ -13226,6 +14221,7 @@ public final class Settings {
* Type: int (0 for false, 1 for true)
* @hide
*/
+ @Readable
public static final String LTE_SERVICE_FORCED = "lte_service_forced";
@@ -13235,6 +14231,7 @@ public final class Settings {
* See WindowManagerPolicy.WindowManagerFuncs
* @hide
*/
+ @Readable
public static final String LID_BEHAVIOR = "lid_behavior";
/**
@@ -13243,6 +14240,7 @@ public final class Settings {
* Type: int
* @hide
*/
+ @Readable
public static final String EPHEMERAL_COOKIE_MAX_SIZE_BYTES =
"ephemeral_cookie_max_size_bytes";
@@ -13254,6 +14252,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ENABLE_EPHEMERAL_FEATURE = "enable_ephemeral_feature";
/**
@@ -13264,6 +14263,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String INSTANT_APP_DEXOPT_ENABLED = "instant_app_dexopt_enabled";
/**
@@ -13272,6 +14272,7 @@ public final class Settings {
* Type: long
* @hide
*/
+ @Readable
public static final String INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD =
"installed_instant_app_min_cache_period";
@@ -13281,6 +14282,7 @@ public final class Settings {
* Type: long
* @hide
*/
+ @Readable
public static final String INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD =
"installed_instant_app_max_cache_period";
@@ -13290,6 +14292,7 @@ public final class Settings {
* Type: long
* @hide
*/
+ @Readable
public static final String UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD =
"uninstalled_instant_app_min_cache_period";
@@ -13299,6 +14302,7 @@ public final class Settings {
* Type: long
* @hide
*/
+ @Readable
public static final String UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD =
"uninstalled_instant_app_max_cache_period";
@@ -13308,6 +14312,7 @@ public final class Settings {
* Type: long
* @hide
*/
+ @Readable
public static final String UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD =
"unused_static_shared_lib_min_cache_period";
@@ -13317,6 +14322,7 @@ public final class Settings {
* Type: int
* @hide
*/
+ @Readable
public static final String ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED =
"allow_user_switching_when_system_user_locked";
@@ -13325,6 +14331,7 @@ public final class Settings {
* <p>
* Type: int
*/
+ @Readable
public static final String BOOT_COUNT = "boot_count";
/**
@@ -13335,6 +14342,7 @@ public final class Settings {
* before the user restrictions are loaded.
* @hide
*/
+ @Readable
public static final String SAFE_BOOT_DISALLOWED = "safe_boot_disallowed";
/**
@@ -13346,6 +14354,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String DEVICE_DEMO_MODE = "device_demo_mode";
/**
@@ -13355,6 +14364,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String NETWORK_ACCESS_TIMEOUT_MS = "network_access_timeout_ms";
/**
@@ -13365,6 +14375,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String DATABASE_DOWNGRADE_REASON = "database_downgrade_reason";
/**
@@ -13375,6 +14386,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String DATABASE_CREATION_BUILDID = "database_creation_buildid";
/**
@@ -13383,6 +14395,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CONTACTS_DATABASE_WAL_ENABLED = "contacts_database_wal_enabled";
/**
@@ -13390,6 +14403,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED =
"location_settings_link_to_permissions_enabled";
@@ -13400,6 +14414,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS =
"euicc_removing_invisible_profiles_timeout_millis";
@@ -13409,6 +14424,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String EUICC_FACTORY_RESET_TIMEOUT_MILLIS =
"euicc_factory_reset_timeout_millis";
@@ -13418,6 +14434,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String EUICC_SWITCH_SLOT_TIMEOUT_MILLIS =
"euicc_switch_slot_timeout_millis";
@@ -13427,6 +14444,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ENABLE_MULTI_SLOT_TIMEOUT_MILLIS =
"enable_multi_slot_timeout_millis";
@@ -13436,6 +14454,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String STORAGE_SETTINGS_CLOBBER_THRESHOLD =
"storage_settings_clobber_threshold";
@@ -13445,6 +14464,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String OVERRIDE_SETTINGS_PROVIDER_RESTORE_ANY_VERSION =
"override_settings_provider_restore_any_version";
/**
@@ -13455,6 +14475,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String CHAINED_BATTERY_ATTRIBUTION_ENABLED =
"chained_battery_attribution_enabled";
@@ -13467,6 +14488,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ENABLE_ADB_INCREMENTAL_INSTALL_DEFAULT =
"enable_adb_incremental_install_default";
@@ -13484,6 +14506,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES =
"autofill_compat_mode_allowed_packages";
@@ -13497,6 +14520,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String AUTOFILL_LOGGING_LEVEL = "autofill_logging_level";
/**
@@ -13504,6 +14528,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String AUTOFILL_MAX_PARTITIONS_SIZE = "autofill_max_partitions_size";
/**
@@ -13512,6 +14537,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String AUTOFILL_MAX_VISIBLE_DATASETS = "autofill_max_visible_datasets";
/**
@@ -13520,6 +14546,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS =
"hidden_api_blacklist_exemptions";
@@ -13532,14 +14559,16 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
public static final String HIDDEN_API_POLICY = "hidden_api_policy";
- /**
+ /**
* Flag for forcing {@link com.android.server.compat.OverrideValidatorImpl}
* to consider this a non-debuggable build.
*
* @hide
*/
+ @Readable
public static final String FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT =
"force_non_debuggable_final_build_for_compat";
@@ -13549,6 +14578,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String SIGNED_CONFIG_VERSION = "signed_config_version";
/**
@@ -13557,6 +14587,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT =
"sound_trigger_detection_service_op_timeout";
@@ -13566,6 +14597,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY =
"max_sound_trigger_detection_service_ops_per_day";
@@ -13573,6 +14605,7 @@ public final class Settings {
* Indicates whether aware is available in the current location.
* @hide
*/
+ @Readable
public static final String AWARE_ALLOWED = "aware_allowed";
/**
@@ -13581,6 +14614,7 @@ public final class Settings {
* Used by PhoneWindowManager.
* @hide
*/
+ @Readable
public static final String POWER_BUTTON_LONG_PRESS =
"power_button_long_press";
@@ -13590,31 +14624,10 @@ public final class Settings {
* Used by PhoneWindowManager.
* @hide
*/
+ @Readable
public static final String POWER_BUTTON_VERY_LONG_PRESS =
"power_button_very_long_press";
-
- /**
- * Keyguard should be on the left hand side of the screen, for wide screen layouts.
- *
- * @hide
- */
- public static final int ONE_HANDED_KEYGUARD_SIDE_LEFT = 0;
-
- /**
- * Keyguard should be on the right hand side of the screen, for wide screen layouts.
- *
- * @hide
- */
- public static final int ONE_HANDED_KEYGUARD_SIDE_RIGHT = 1;
- /**
- * In one handed mode, which side the keyguard should be on. Allowable values are one of
- * the ONE_HANDED_KEYGUARD_SIDE_* constants.
- *
- * @hide
- */
- public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side";
-
/**
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
@@ -13637,7 +14650,8 @@ public final class Settings {
CONTENT_URI,
CALL_METHOD_GET_GLOBAL,
CALL_METHOD_PUT_GLOBAL,
- sProviderHolder);
+ sProviderHolder,
+ Global.class);
// Certain settings have been moved from global to the per-user secure namespace
@UnsupportedAppUsage
@@ -13666,6 +14680,11 @@ public final class Settings {
sNameValueCache.clearGenerationTrackerForTest();
}
+ /** @hide */
+ public static void getPublicSettings(Set<String> allKeys, Set<String> readableKeys) {
+ getPublicSettingsForClass(Global.class, allKeys, readableKeys);
+ }
+
/**
* Look up a name in the database.
* @param resolver to access the database with
@@ -14075,6 +15094,7 @@ public final class Settings {
* Subscription Id to be used for voice call on a multi sim device.
* @hide
*/
+ @Readable
public static final String MULTI_SIM_VOICE_CALL_SUBSCRIPTION = "multi_sim_voice_call";
/**
@@ -14083,18 +15103,21 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String MULTI_SIM_VOICE_PROMPT = "multi_sim_voice_prompt";
/**
* Subscription Id to be used for data call on a multi sim device.
* @hide
*/
+ @Readable
public static final String MULTI_SIM_DATA_CALL_SUBSCRIPTION = "multi_sim_data_call";
/**
* Subscription Id to be used for SMS on a multi sim device.
* @hide
*/
+ @Readable
public static final String MULTI_SIM_SMS_SUBSCRIPTION = "multi_sim_sms";
/**
@@ -14102,6 +15125,7 @@ public final class Settings {
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String MULTI_SIM_SMS_PROMPT = "multi_sim_sms_prompt";
/** User preferred subscriptions setting.
@@ -14111,6 +15135,7 @@ public final class Settings {
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String[] MULTI_SIM_USER_PREFERRED_SUBS = {"user_preferred_sub1",
"user_preferred_sub2","user_preferred_sub3"};
@@ -14118,6 +15143,7 @@ public final class Settings {
* Which subscription is enabled for a physical slot.
* @hide
*/
+ @Readable
public static final String ENABLED_SUBSCRIPTION_FOR_SLOT = "enabled_subscription_for_slot";
/**
@@ -14125,6 +15151,7 @@ public final class Settings {
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String MODEM_STACK_ENABLED_FOR_SLOT = "modem_stack_enabled_for_slot";
/**
@@ -14132,6 +15159,7 @@ public final class Settings {
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String NEW_CONTACT_AGGREGATOR = "new_contact_aggregator";
/**
@@ -14141,12 +15169,14 @@ public final class Settings {
* @removed
*/
@Deprecated
+ @Readable
public static final String CONTACT_METADATA_SYNC = "contact_metadata_sync";
/**
* Whether to enable contacts metadata syncing or not
* The value 1 - enable, 0 - disable
*/
+ @Readable
public static final String CONTACT_METADATA_SYNC_ENABLED = "contact_metadata_sync_enabled";
/**
@@ -14154,6 +15184,7 @@ public final class Settings {
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String ENABLE_CELLULAR_ON_BOOT = "enable_cellular_on_boot";
/**
@@ -14162,6 +15193,7 @@ public final class Settings {
* Should be a float, and includes updates only.
* @hide
*/
+ @Readable
public static final String MAX_NOTIFICATION_ENQUEUE_RATE = "max_notification_enqueue_rate";
/**
@@ -14170,6 +15202,7 @@ public final class Settings {
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String SHOW_NOTIFICATION_CHANNEL_WARNINGS =
"show_notification_channel_warnings";
@@ -14177,6 +15210,7 @@ public final class Settings {
* Whether cell is enabled/disabled
* @hide
*/
+ @Readable
public static final String CELL_ON = "cell_on";
/**
@@ -14213,30 +15247,35 @@ public final class Settings {
* Whether to show the high temperature warning notification.
* @hide
*/
+ @Readable
public static final String SHOW_TEMPERATURE_WARNING = "show_temperature_warning";
/**
* Whether to show the usb high temperature alarm notification.
* @hide
*/
+ @Readable
public static final String SHOW_USB_TEMPERATURE_ALARM = "show_usb_temperature_alarm";
/**
* Temperature at which the high temperature warning notification should be shown.
* @hide
*/
+ @Readable
public static final String WARNING_TEMPERATURE = "warning_temperature";
/**
* Whether the diskstats logging task is enabled/disabled.
* @hide
*/
+ @Readable
public static final String ENABLE_DISKSTATS_LOGGING = "enable_diskstats_logging";
/**
* Whether the cache quota calculation task is enabled/disabled.
* @hide
*/
+ @Readable
public static final String ENABLE_CACHE_QUOTA_CALCULATION =
"enable_cache_quota_calculation";
@@ -14244,6 +15283,7 @@ public final class Settings {
* Whether the Deletion Helper no threshold toggle is available.
* @hide
*/
+ @Readable
public static final String ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE =
"enable_deletion_helper_no_threshold_toggle";
@@ -14264,6 +15304,7 @@ public final class Settings {
* Options will be used in order up to the maximum allowed by the UI.
* @hide
*/
+ @Readable
public static final String NOTIFICATION_SNOOZE_OPTIONS =
"notification_snooze_options";
@@ -14275,6 +15316,7 @@ public final class Settings {
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String NOTIFICATION_FEEDBACK_ENABLED = "notification_feedback_enabled";
/**
@@ -14286,6 +15328,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT =
"blocking_helper_dismiss_to_view_ratio";
@@ -14297,6 +15340,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String BLOCKING_HELPER_STREAK_LIMIT = "blocking_helper_streak_limit";
/**
@@ -14324,6 +15368,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String SQLITE_COMPATIBILITY_WAL_FLAGS =
"sqlite_compatibility_wal_flags";
@@ -14333,6 +15378,7 @@ public final class Settings {
* 1 = yes
* @hide
*/
+ @Readable
public static final String ENABLE_GNSS_RAW_MEAS_FULL_TRACKING =
"enable_gnss_raw_meas_full_tracking";
@@ -14344,6 +15390,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT =
"install_carrier_app_notification_persistent";
@@ -14355,6 +15402,7 @@ public final class Settings {
* @hide
*/
@SystemApi
+ @Readable
public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS =
"install_carrier_app_notification_sleep_millis";
@@ -14364,6 +15412,7 @@ public final class Settings {
* everything else is unspecified.
* @hide
*/
+ @Readable
public static final String ZRAM_ENABLED =
"zram_enabled";
@@ -14373,6 +15422,7 @@ public final class Settings {
* "device_default" will let the system decide whether to enable the freezer or not
* @hide
*/
+ @Readable
public static final String CACHED_APPS_FREEZER_ENABLED = "cached_apps_freezer";
/**
@@ -14395,6 +15445,7 @@ public final class Settings {
* @see com.android.systemui.statusbar.policy.SmartReplyConstants
* @hide
*/
+ @Readable
public static final String SMART_REPLIES_IN_NOTIFICATIONS_FLAGS =
"smart_replies_in_notifications_flags";
@@ -14411,6 +15462,7 @@ public final class Settings {
* </pre>
* @hide
*/
+ @Readable
public static final String SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS =
"smart_suggestions_in_notifications_flags";
@@ -14420,6 +15472,7 @@ public final class Settings {
* @hide
*/
@TestApi
+ @Readable
@SuppressLint("NoSettingsProvider")
public static final String SHOW_FIRST_CRASH_DIALOG = "show_first_crash_dialog";
@@ -14427,6 +15480,7 @@ public final class Settings {
* If nonzero, crash dialogs will show an option to restart the app.
* @hide
*/
+ @Readable
public static final String SHOW_RESTART_IN_CRASH_DIALOG = "show_restart_in_crash_dialog";
/**
@@ -14434,6 +15488,7 @@ public final class Settings {
* this app.
* @hide
*/
+ @Readable
public static final String SHOW_MUTE_IN_CRASH_DIALOG = "show_mute_in_crash_dialog";
@@ -14489,6 +15544,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String BACKUP_AGENT_TIMEOUT_PARAMETERS =
"backup_agent_timeout_parameters";
@@ -14503,6 +15559,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String GNSS_SATELLITE_BLOCKLIST = "gnss_satellite_blocklist";
/**
@@ -14514,6 +15571,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS =
"gnss_hal_location_request_duration_millis";
@@ -14530,6 +15588,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String BINDER_CALLS_STATS = "binder_calls_stats";
/**
@@ -14543,6 +15602,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String LOOPER_STATS = "looper_stats";
/**
@@ -14557,6 +15617,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String KERNEL_CPU_THREAD_READER = "kernel_cpu_thread_reader";
/**
@@ -14564,6 +15625,7 @@ public final class Settings {
* reboot. The value "1" enables native flags health check; otherwise it's disabled.
* @hide
*/
+ @Readable
public static final String NATIVE_FLAGS_HEALTH_CHECK_ENABLED =
"native_flags_health_check_enabled";
@@ -14573,6 +15635,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String APPOP_HISTORY_MODE = "mode";
/**
@@ -14582,6 +15645,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String APPOP_HISTORY_BASE_INTERVAL_MILLIS = "baseIntervalMillis";
/**
@@ -14590,6 +15654,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String APPOP_HISTORY_INTERVAL_MULTIPLIER = "intervalMultiplier";
/**
@@ -14611,6 +15676,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String APPOP_HISTORY_PARAMETERS =
"appop_history_parameters";
@@ -14628,6 +15694,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String AUTO_REVOKE_PARAMETERS =
"auto_revoke_parameters";
@@ -14639,6 +15706,7 @@ public final class Settings {
* @see com.android.internal.os.BatteryStatsImpl.Constants.KEY_BATTERY_CHARGED_DELAY_MS
* @hide
*/
+ @Readable
public static final String BATTERY_CHARGING_STATE_UPDATE_DELAY =
"battery_charging_state_update_delay";
@@ -14647,6 +15715,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String TEXT_CLASSIFIER_ACTION_MODEL_PARAMS =
"text_classifier_action_model_params";
@@ -14660,6 +15729,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE =
"power_button_suppression_delay_after_gesture_wake";
@@ -14668,6 +15738,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String ADVANCED_BATTERY_USAGE_AMOUNT = "advanced_battery_usage_amount";
/**
@@ -14682,6 +15753,7 @@ public final class Settings {
* 2: always on - All 5G NSA tracking indications are on whether the screen is on or off.
* @hide
*/
+ @Readable
public static final String NR_NSA_TRACKING_SCREEN_OFF_MODE =
"nr_nsa_tracking_screen_off_mode";
@@ -14692,6 +15764,7 @@ public final class Settings {
* 1: Enabled
* @hide
*/
+ @Readable
public static final String SHOW_PEOPLE_SPACE = "show_people_space";
/**
@@ -14702,6 +15775,7 @@ public final class Settings {
* 2: All conversations
* @hide
*/
+ @Readable
public static final String PEOPLE_SPACE_CONVERSATION_TYPE =
"people_space_conversation_type";
@@ -14712,6 +15786,7 @@ public final class Settings {
* 1: Enabled
* @hide
*/
+ @Readable
public static final String SHOW_NEW_NOTIF_DISMISS = "show_new_notif_dismiss";
/**
@@ -14726,6 +15801,7 @@ 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";
/**
@@ -14740,6 +15816,7 @@ 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";
@@ -14753,6 +15830,7 @@ 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";
@@ -14769,6 +15847,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String BLOCK_UNTRUSTED_TOUCHES_MODE = "block_untrusted_touches";
/**
@@ -14794,6 +15873,7 @@ public final class Settings {
*
* @hide
*/
+ @Readable
public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH =
"maximum_obscuring_opacity_for_touch";
@@ -14806,6 +15886,7 @@ public final class Settings {
* 1: enabled
* @hide
*/
+ @Readable
public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode";
}
@@ -14827,7 +15908,8 @@ public final class Settings {
CALL_METHOD_PUT_CONFIG,
CALL_METHOD_LIST_CONFIG,
CALL_METHOD_SET_ALL_CONFIG,
- sProviderHolder);
+ sProviderHolder,
+ Config.class);
/**
* Look up a name in the database.
diff --git a/core/java/android/provider/SimPhonebookContract.java b/core/java/android/provider/SimPhonebookContract.java
index 2efc21229422..074d5f167ec3 100644
--- a/core/java/android/provider/SimPhonebookContract.java
+++ b/core/java/android/provider/SimPhonebookContract.java
@@ -29,11 +29,8 @@ import android.annotation.SystemApi;
import android.annotation.WorkerThread;
import android.content.ContentResolver;
import android.content.ContentValues;
-import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
@@ -63,7 +60,6 @@ public final class SimPhonebookContract {
*
* @hide
*/
- @SystemApi
public static final String SUBSCRIPTION_ID_PATH_SEGMENT = "subid";
private SimPhonebookContract() {
@@ -76,7 +72,6 @@ public final class SimPhonebookContract {
* @hide
*/
@NonNull
- @SystemApi
public static String getEfUriPath(@ElementaryFiles.EfType int efType) {
switch (efType) {
case EF_ADN:
@@ -122,12 +117,12 @@ public final class SimPhonebookContract {
* The name for this record.
*
* <p>An {@link IllegalArgumentException} will be thrown by insert and update if this
- * exceeds the maximum supported length or contains unsupported characters.
- * {@link #validateName(ContentResolver, int, int, String)} )} can be used to
- * check whether the name is supported.
+ * exceeds the maximum supported length. Use
+ * {@link #getEncodedNameLength(ContentResolver, String)} to check how long the name
+ * will be after encoding.
*
* @see ElementaryFiles#NAME_MAX_LENGTH
- * @see #validateName(ContentResolver, int, int, String) )
+ * @see #getEncodedNameLength(ContentResolver, String)
*/
public static final String NAME = "name";
/**
@@ -149,24 +144,31 @@ public final class SimPhonebookContract {
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
/**
- * The path segment that is appended to {@link #getContentUri(int, int)} which indicates
- * that the following path segment contains a name to be validated.
+ * Value returned from {@link #getEncodedNameLength(ContentResolver, String)} when the name
+ * length could not be determined because the name could not be encoded.
+ */
+ public static final int ERROR_NAME_UNSUPPORTED = -1;
+
+ /**
+ * The method name used to get the encoded length of a value for {@link SimRecords#NAME}
+ * column.
*
* @hide
- * @see #validateName(ContentResolver, int, int, String)
+ * @see #getEncodedNameLength(ContentResolver, String)
+ * @see ContentResolver#call(String, String, String, Bundle)
*/
- @SystemApi
- public static final String VALIDATE_NAME_PATH_SEGMENT = "validate_name";
+ public static final String GET_ENCODED_NAME_LENGTH_METHOD_NAME = "get_encoded_name_length";
/**
- * The key for a cursor extra that contains the result of a validate name query.
+ * Extra key used for an integer value that contains the length in bytes of an encoded
+ * name.
*
* @hide
- * @see #validateName(ContentResolver, int, int, String)
+ * @see #getEncodedNameLength(ContentResolver, String)
+ * @see #GET_ENCODED_NAME_LENGTH_METHOD_NAME
*/
- @SystemApi
- public static final String EXTRA_NAME_VALIDATION_RESULT =
- "android.provider.extra.NAME_VALIDATION_RESULT";
+ public static final String EXTRA_ENCODED_NAME_LENGTH =
+ "android.provider.extra.ENCODED_NAME_LENGTH";
/**
@@ -244,32 +246,34 @@ public final class SimPhonebookContract {
}
/**
- * Validates a value that is being provided for the {@link #NAME} column.
+ * Returns the number of bytes required to encode the specified name when it is stored
+ * on the SIM.
*
- * <p>The return value can be used to check if the name is valid. If it is not valid then
- * inserts and updates to the specified elementary file that use the provided name value
- * will throw an {@link IllegalArgumentException}.
+ * <p>{@link ElementaryFiles#NAME_MAX_LENGTH} is specified in bytes but the encoded name
+ * may require more than 1 byte per character depending on the characters it contains. So
+ * this method can be used to check whether a name exceeds the max length.
*
- * <p>If the specified SIM or elementary file don't exist then
- * {@link NameValidationResult#getMaxEncodedLength()} will be zero and
- * {@link NameValidationResult#isValid()} will return false.
+ * @return the number of bytes required by the encoded name or
+ * {@link #ERROR_NAME_UNSUPPORTED} if the name could not be encoded.
+ * @throws IllegalStateException if the provider fails to return the length.
+ * @see SimRecords#NAME
+ * @see ElementaryFiles#NAME_MAX_LENGTH
*/
- @NonNull
@WorkerThread
- public static NameValidationResult validateName(
- @NonNull ContentResolver resolver, int subscriptionId,
- @ElementaryFiles.EfType int efType,
- @NonNull String name) {
- Bundle queryArgs = new Bundle();
- queryArgs.putString(SimRecords.NAME, name);
- try (Cursor cursor =
- resolver.query(buildContentUri(subscriptionId, efType)
- .appendPath(VALIDATE_NAME_PATH_SEGMENT)
- .build(), null, queryArgs, null)) {
- NameValidationResult result = cursor.getExtras()
- .getParcelable(EXTRA_NAME_VALIDATION_RESULT);
- return result != null ? result : new NameValidationResult(name, "", 0, 0);
+ public static int getEncodedNameLength(
+ @NonNull ContentResolver resolver, @NonNull String name) {
+ Objects.requireNonNull(name);
+ Bundle result = resolver.call(AUTHORITY, GET_ENCODED_NAME_LENGTH_METHOD_NAME, name,
+ null);
+ if (result == null || !result.containsKey(EXTRA_ENCODED_NAME_LENGTH)) {
+ throw new IllegalStateException("Provider malfunction: no length was returned.");
}
+ int length = result.getInt(EXTRA_ENCODED_NAME_LENGTH, ERROR_NAME_UNSUPPORTED);
+ if (length < 0 && length != ERROR_NAME_UNSUPPORTED) {
+ throw new IllegalStateException(
+ "Provider malfunction: invalid length was returned.");
+ }
+ return length;
}
private static Uri.Builder buildContentUri(
@@ -281,106 +285,6 @@ public final class SimPhonebookContract {
.appendPath(getEfUriPath(efType));
}
- /** Contains details about the validity of a value provided for the {@link #NAME} column. */
- public static final class NameValidationResult implements Parcelable {
-
- @NonNull
- public static final Creator<NameValidationResult> CREATOR =
- new Creator<NameValidationResult>() {
-
- @Override
- public NameValidationResult createFromParcel(@NonNull Parcel in) {
- return new NameValidationResult(in);
- }
-
- @NonNull
- @Override
- public NameValidationResult[] newArray(int size) {
- return new NameValidationResult[size];
- }
- };
-
- private final String mName;
- private final String mSanitizedName;
- private final int mEncodedLength;
- private final int mMaxEncodedLength;
-
- /** Creates a new instance from the provided values. */
- public NameValidationResult(@NonNull String name, @NonNull String sanitizedName,
- int encodedLength, int maxEncodedLength) {
- this.mName = Objects.requireNonNull(name);
- this.mSanitizedName = Objects.requireNonNull(sanitizedName);
- this.mEncodedLength = encodedLength;
- this.mMaxEncodedLength = maxEncodedLength;
- }
-
- private NameValidationResult(Parcel in) {
- this(in.readString(), in.readString(), in.readInt(), in.readInt());
- }
-
- /** Returns the original name that is being validated. */
- @NonNull
- public String getName() {
- return mName;
- }
-
- /**
- * Returns a sanitized copy of the original name with all unsupported characters
- * replaced with spaces.
- */
- @NonNull
- public String getSanitizedName() {
- return mSanitizedName;
- }
-
- /**
- * Returns whether the original name isValid.
- *
- * <p>If this returns false then inserts and updates using the name will throw an
- * {@link IllegalArgumentException}
- */
- public boolean isValid() {
- return mMaxEncodedLength > 0 && mEncodedLength <= mMaxEncodedLength
- && Objects.equals(
- mName, mSanitizedName);
- }
-
- /** Returns whether the character at the specified position is supported by the SIM. */
- public boolean isSupportedCharacter(int position) {
- return mName.charAt(position) == mSanitizedName.charAt(position);
- }
-
- /**
- * Returns the number of bytes required to save the name.
- *
- * <p>This may be more than the number of characters in the name.
- */
- public int getEncodedLength() {
- return mEncodedLength;
- }
-
- /**
- * Returns the maximum number of bytes that are supported for the name.
- *
- * @see ElementaryFiles#NAME_MAX_LENGTH
- */
- public int getMaxEncodedLength() {
- return mMaxEncodedLength;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString(mName);
- dest.writeString(mSanitizedName);
- dest.writeInt(mEncodedLength);
- dest.writeInt(mMaxEncodedLength);
- }
- }
}
/** Constants for metadata about the elementary files of the SIM cards in the phone. */
@@ -446,13 +350,10 @@ public final class SimPhonebookContract {
*/
public static final int EF_SDN = 3;
/** @hide */
- @SystemApi
public static final String EF_ADN_PATH_SEGMENT = "adn";
/** @hide */
- @SystemApi
public static final String EF_FDN_PATH_SEGMENT = "fdn";
/** @hide */
- @SystemApi
public static final String EF_SDN_PATH_SEGMENT = "sdn";
/** The MIME type of CONTENT_URI providing a directory of ADN-like elementary files. */
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
@@ -464,7 +365,6 @@ public final class SimPhonebookContract {
*
* @hide
*/
- @SystemApi
public static final String ELEMENTARY_FILES_PATH_SEGMENT = "elementary_files";
/** Content URI for the ADN-like elementary files available on the device. */
@@ -480,8 +380,7 @@ public final class SimPhonebookContract {
* Returns a content uri for a specific elementary file.
*
* <p>If a SIM with the specified subscriptionId is not present an exception will be thrown.
- * If the SIM doesn't support the specified elementary file it will have a zero value for
- * {@link #MAX_RECORDS}.
+ * If the SIM doesn't support the specified elementary file it will return an empty cursor.
*/
@NonNull
public static Uri getItemUri(int subscriptionId, @EfType int efType) {
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index c9d5708d69f2..edb2742d17f3 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5337,7 +5337,8 @@ public final class Telephony {
* which network types are allowed for
* {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER},
* {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER},
- * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER}.
+ * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER},
+ * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}.
* <P>Type: TEXT </P>
*
* @hide
diff --git a/core/java/android/rotationresolver/RotationResolverInternal.java b/core/java/android/rotationresolver/RotationResolverInternal.java
index db879a7e3f8f..1f1a66c17d97 100644
--- a/core/java/android/rotationresolver/RotationResolverInternal.java
+++ b/core/java/android/rotationresolver/RotationResolverInternal.java
@@ -46,7 +46,6 @@ public abstract class RotationResolverInternal {
* error is captured. {@link RotationResolverCallbackInternal}
* @param proposedRotation the screen rotation that is proposed by the system.
* @param currentRotation the current screen rotation.
- * @param packageName the package name of the current activity that is running in foreground.
* @param timeoutMillis the timeout in millisecond for the query. If the query doesn't get
* fulfilled within this amount of time. It will be discarded and the
* callback will receive a failure result code {@link
@@ -55,8 +54,7 @@ public abstract class RotationResolverInternal {
*/
public abstract void resolveRotation(@NonNull RotationResolverCallbackInternal callback,
@Surface.Rotation int proposedRotation, @Surface.Rotation int currentRotation,
- String packageName, @DurationMillisLong long timeoutMillis,
- @NonNull CancellationSignal cancellationSignal);
+ @DurationMillisLong long timeoutMillis, @NonNull CancellationSignal cancellationSignal);
/**
* Internal interfaces for the rotation resolver callback.
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index c39b8c5eb6d1..5a89cdf1d340 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -130,6 +130,15 @@ public final class KeymasterDefs {
public static final int KM_TAG_ASSOCIATED_DATA = Tag.ASSOCIATED_DATA; // KM_BYTES | 1000;
public static final int KM_TAG_NONCE = Tag.NONCE; // KM_BYTES | 1001;
public static final int KM_TAG_MAC_LENGTH = Tag.MAC_LENGTH; // KM_UINT | 1003;
+ public static final int KM_TAG_RESET_SINCE_ID_ROTATION =
+ Tag.RESET_SINCE_ID_ROTATION; // KM_BOOL | 1004
+ public static final int KM_TAG_CONFIRMATION_TOKEN = Tag.CONFIRMATION_TOKEN; // KM_BYTES | 1005;
+ public static final int KM_TAG_CERTIFICATE_SERIAL = Tag.CERTIFICATE_SERIAL; // KM_UINT | 1006;
+ public static final int KM_TAG_CERTIFICATE_SUBJECT = Tag.CERTIFICATE_SUBJECT; // KM_UINT | 1007;
+ public static final int KM_TAG_CERTIFICATE_NOT_BEFORE =
+ Tag.CERTIFICATE_NOT_BEFORE; // KM_DATE | 1008;
+ public static final int KM_TAG_CERTIFICATE_NOT_AFTER =
+ Tag.CERTIFICATE_NOT_AFTER; // KM_DATE | 1009;
// Algorithm values.
public static final int KM_ALGORITHM_RSA = Algorithm.RSA;
@@ -179,6 +188,7 @@ public final class KeymasterDefs {
public static final int KM_PURPOSE_VERIFY = KeyPurpose.VERIFY;
public static final int KM_PURPOSE_WRAP = KeyPurpose.WRAP_KEY;
public static final int KM_PURPOSE_AGREE_KEY = KeyPurpose.AGREE_KEY;
+ public static final int KM_PURPOSE_ATTEST_KEY = KeyPurpose.ATTEST_KEY;
// Key formats.
public static final int KM_KEY_FORMAT_X509 = KeyFormat.X509;
@@ -317,6 +327,10 @@ public final class KeymasterDefs {
ErrorCode.HARDWARE_TYPE_UNAVAILABLE; // -68;
public static final int KM_ERROR_DEVICE_LOCKED =
ErrorCode.DEVICE_LOCKED; // -72;
+ public static final int KM_ERROR_MISSING_NOT_BEFORE =
+ ErrorCode.MISSING_NOT_BEFORE; // -80;
+ public static final int KM_ERROR_MISSING_NOT_AFTER =
+ ErrorCode.MISSING_NOT_AFTER; // -80;
public static final int KM_ERROR_UNIMPLEMENTED =
ErrorCode.UNIMPLEMENTED; // -100;
public static final int KM_ERROR_VERSION_MISMATCH =
diff --git a/core/java/android/service/displayhash/DisplayHasherService.java b/core/java/android/service/displayhash/DisplayHasherService.java
new file mode 100644
index 000000000000..331dbe91f6c7
--- /dev/null
+++ b/core/java/android/service/displayhash/DisplayHasherService.java
@@ -0,0 +1,168 @@
+/*
+ * 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 static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallback;
+import android.view.displayhash.DisplayHash;
+import android.view.displayhash.DisplayHashResultCallback;
+import android.view.displayhash.VerifiedDisplayHash;
+
+/**
+ * A service that handles generating and verify {@link DisplayHash}.
+ *
+ * The service will generate a DisplayHash based on arguments passed in. Then later that
+ * same DisplayHash can be verified to determine that it was created by the system.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class DisplayHasherService extends Service {
+
+ /** @hide **/
+ public static final String EXTRA_VERIFIED_DISPLAY_HASH =
+ "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.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String SERVICE_INTERFACE =
+ "android.service.displayhash.DisplayHasherService";
+
+ private DisplayHasherServiceWrapper mWrapper;
+ private Handler mHandler;
+
+ public DisplayHasherService() {
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWrapper = new DisplayHasherServiceWrapper();
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ }
+
+ @NonNull
+ @Override
+ public final IBinder onBind(@NonNull Intent intent) {
+ return mWrapper;
+ }
+
+ /**
+ * Generates the DisplayHash that can be used to validate that the system generated the
+ * token.
+ *
+ * @param salt The salt to use when generating the hmac. This should be unique to the
+ * caller so the token cannot be verified by any other process.
+ * @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)}.
+ * @param callback The callback to invoke
+ * {@link DisplayHashResultCallback#onDisplayHashResult(DisplayHash)}
+ * if successfully generated a DisplayHash or {@link
+ * DisplayHashResultCallback#onDisplayHashError(int)} if failed.
+ */
+ @Nullable
+ public abstract void onGenerateDisplayHash(@NonNull byte[] salt,
+ @NonNull HardwareBuffer buffer, @NonNull Rect bounds,
+ @NonNull String hashAlgorithm, @NonNull DisplayHashResultCallback callback);
+
+ /**
+ * 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
+ * same value that was passed to
+ * {@link #onGenerateDisplayHash(byte[],
+ * HardwareBuffer, Rect, String, DisplayHashResultCallback)} to
+ * generate the token.
+ * @param displayHash The token to verify that it was generated by the system.
+ * @return a {@link VerifiedDisplayHash} if the token was generated by the system or null
+ * if the token cannot be verified.
+ */
+ @Nullable
+ public abstract VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[] salt,
+ @NonNull DisplayHash displayHash);
+
+ private void verifyDisplayHash(byte[] salt, DisplayHash displayHash,
+ RemoteCallback callback) {
+ VerifiedDisplayHash verifiedDisplayHash = onVerifyDisplayHash(salt,
+ displayHash);
+ final Bundle data = new Bundle();
+ data.putParcelable(EXTRA_VERIFIED_DISPLAY_HASH, verifiedDisplayHash);
+ callback.sendResult(data);
+ }
+
+ private final class DisplayHasherServiceWrapper extends IDisplayHasherService.Stub {
+ @Override
+ public void generateDisplayHash(byte[] salt, HardwareBuffer buffer, Rect bounds,
+ String hashAlgorithm, RemoteCallback callback) {
+ mHandler.sendMessage(
+ obtainMessage(DisplayHasherService::onGenerateDisplayHash,
+ DisplayHasherService.this, salt, buffer, bounds,
+ hashAlgorithm, new DisplayHashResultCallback() {
+ @Override
+ public void onDisplayHashResult(
+ @NonNull DisplayHash displayHash) {
+ Bundle result = new Bundle();
+ result.putParcelable(EXTRA_DISPLAY_HASH, displayHash);
+ callback.sendResult(result);
+ }
+
+ @Override
+ public void onDisplayHashError(int errorCode) {
+ Bundle result = new Bundle();
+ result.putInt(EXTRA_DISPLAY_HASH_ERROR_CODE, errorCode);
+ callback.sendResult(result);
+ }
+ }));
+ }
+
+ @Override
+ public void verifyDisplayHash(byte[] salt, DisplayHash displayHash,
+ RemoteCallback callback) {
+ mHandler.sendMessage(
+ obtainMessage(DisplayHasherService::verifyDisplayHash,
+ DisplayHasherService.this, salt, displayHash, callback));
+ }
+ }
+}
diff --git a/core/java/android/service/screenshot/IScreenshotHasherService.aidl b/core/java/android/service/displayhash/IDisplayHasherService.aidl
index d14d147e096c..236bc28c74c8 100644
--- a/core/java/android/service/screenshot/IScreenshotHasherService.aidl
+++ b/core/java/android/service/displayhash/IDisplayHasherService.aidl
@@ -14,43 +14,41 @@
* limitations under the License.
*/
-package android.service.screenshot;
+package android.service.displayhash;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.RemoteCallback;
-import android.service.screenshot.ScreenshotHash;
+import android.view.displayhash.DisplayHash;
/**
- * Service used to handle ScreenshotHash requests.
+ * Service used to handle DisplayHash requests.
*
* @hide
*/
-oneway interface IScreenshotHasherService {
+oneway interface IDisplayHasherService {
/**
- * Generates the ScreenshotHash token that can be used to validate that the system generated the
- * token.
+ * Generates the DisplayHash that can be used to validate that the system generated the token.
*
* @param salt The salt to use when generating the hmac. This should be unique to the caller so
* the token cannot be verified by any other process.
- * @param screenshot The screenshot to generate the hash and add to the token.
- * @param bounds The size and position of the content being screenshot in the window.
- * @param hashAlgorithm The String for the hashing algorithm to use based on values in
+ * @param buffer The buffer to generate the hash for.
+ * @param bounds The size and position of the content being hashed in window space.
+ * @param hashAlgorithm The String for the hash algorithm to use based on values in
* {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS}.
- * @param Callback The callback invoked to send back the ScreenshotHash token.
+ * @param Callback The callback invoked to send back the DisplayHash.
*/
- void generateScreenshotHash(in byte[] salt, in HardwareBuffer screenshot, in Rect bounds,
+ void generateDisplayHash(in byte[] salt, in HardwareBuffer buffer, in Rect bounds,
in String hashAlgorithm, in RemoteCallback callback);
/**
- * Call to verify that the ScreenshotHash passed in was generated by the system. The result
- * will be sent in the callback as a boolean with the key {@link #EXTRA_VERIFICATION_STATUS}.
+ * Call to verify that the DisplayHash passed in was generated by the system. The result
+ * will be sent in the callback as a boolean with the key {@link #EXTRA_VERIFIED_DISPLAY_HASH}.
*
* @param salt The salt value to use when verifying the hmac. This should be the same value that
- * was passed to {@link generateScreenshotHash()} to generate the token.
- * @param screenshotHash The hash to verify that it was generated by the system.
- * @param callback The callback invoked to send back the verification status.
+ * was passed to {@link generateDisplayHash()} to generate the DisplayHash.
+ * @param displayHash The hash to verify that it was generated by the system.
+ * @param callback The callback invoked to send back the VerifiedDisplayHash.
*/
- void verifyScreenshotHash(in byte[] salt, in ScreenshotHash screenshotHash,
- in RemoteCallback callback);
+ void verifyDisplayHash(in byte[] salt, in DisplayHash displayHash, in RemoteCallback callback);
}
diff --git a/core/java/android/service/screenshot/OWNERS b/core/java/android/service/displayhash/OWNERS
index 0862c05e0ee4..0862c05e0ee4 100644
--- a/core/java/android/service/screenshot/OWNERS
+++ b/core/java/android/service/displayhash/OWNERS
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 44daeff76997..b384b66bf680 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -20,6 +20,7 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.content.pm.ParceledListSlice;
+import android.os.Bundle;
import android.os.UserHandle;
import android.service.notification.NotificationStats;
import android.service.notification.IStatusBarNotificationHolder;
@@ -41,7 +42,7 @@ oneway interface INotificationListener
void onListenerHintsChanged(int hints);
void onInterruptionFilterChanged(int interruptionFilter);
- // companion device managers only
+ // companion device managers and assistants only
void onNotificationChannelModification(String pkgName, in UserHandle user, in NotificationChannel channel, int modificationType);
void onNotificationChannelGroupModification(String pkgName, in UserHandle user, in NotificationChannelGroup group, int modificationType);
@@ -58,4 +59,5 @@ oneway interface INotificationListener
void onActionClicked(String key, in Notification.Action action, int source);
void onNotificationClicked(String key);
void onAllowedAdjustmentsChanged();
+ void onNotificationFeedbackReceived(String key, in NotificationRankingUpdate update, in Bundle feedback);
}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 1d49a7206023..4fd36e590d18 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -30,6 +30,7 @@ import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -90,6 +91,11 @@ public abstract class NotificationAssistantService extends NotificationListenerS
= "android.service.notification.NotificationAssistantService";
/**
+ * Data type: int, the feedback rating score provided by user
+ */
+ public static final String FEEDBACK_RATING = "feedback.rating";
+
+ /**
* @hide
*/
protected Handler mHandler;
@@ -272,6 +278,17 @@ public abstract class NotificationAssistantService extends NotificationListenerS
}
/**
+ * Implement this to know when user provides a feedback.
+ * @param key the notification key
+ * @param rankingMap The current ranking map that can be used to retrieve ranking information
+ * for active notifications.
+ * @param feedback the feedback detail
+ */
+ public void onNotificationFeedbackReceived(@NonNull String key, @NonNull RankingMap rankingMap,
+ @NonNull Bundle feedback) {
+ }
+
+ /**
* Updates a notification. N.B. this won’t cause
* an existing notification to alert, but might allow a future update to
* this notification to alert.
@@ -455,6 +472,18 @@ public abstract class NotificationAssistantService extends NotificationListenerS
public void onAllowedAdjustmentsChanged() {
mHandler.obtainMessage(MyHandler.MSG_ON_ALLOWED_ADJUSTMENTS_CHANGED).sendToTarget();
}
+
+ @Override
+ public void onNotificationFeedbackReceived(String key, NotificationRankingUpdate update,
+ Bundle feedback) {
+ applyUpdateLocked(update);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ args.arg2 = getCurrentRanking();
+ args.arg3 = feedback;
+ mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_FEEDBACK_RECEIVED,
+ args).sendToTarget();
+ }
}
private void setAdjustmentIssuer(@Nullable Adjustment adjustment) {
@@ -476,6 +505,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS
public static final int MSG_ON_PANEL_HIDDEN = 10;
public static final int MSG_ON_NOTIFICATION_VISIBILITY_CHANGED = 11;
public static final int MSG_ON_NOTIFICATION_CLICKED = 12;
+ public static final int MSG_ON_NOTIFICATION_FEEDBACK_RECEIVED = 13;
public MyHandler(Looper looper) {
super(looper, null, false);
@@ -589,6 +619,15 @@ public abstract class NotificationAssistantService extends NotificationListenerS
onNotificationClicked(key);
break;
}
+ case MSG_ON_NOTIFICATION_FEEDBACK_RECEIVED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ RankingMap ranking = (RankingMap) args.arg2;
+ Bundle feedback = (Bundle) args.arg3;
+ args.recycle();
+ onNotificationFeedbackReceived(key, ranking, feedback);
+ break;
+ }
}
}
}
diff --git a/core/java/android/service/notification/NotificationListenerFilter.java b/core/java/android/service/notification/NotificationListenerFilter.java
index 6fdfaabb009b..9de75cac159a 100644
--- a/core/java/android/service/notification/NotificationListenerFilter.java
+++ b/core/java/android/service/notification/NotificationListenerFilter.java
@@ -20,6 +20,7 @@ import static android.service.notification.NotificationListenerService.FLAG_FILT
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
+import android.content.pm.VersionedPackage;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
@@ -31,7 +32,8 @@ import android.util.ArraySet;
*/
public class NotificationListenerFilter implements Parcelable {
private int mAllowedNotificationTypes;
- private ArraySet<String> mDisallowedPackages;
+ // VersionedPackage is holding the pkg name and pkg uid
+ private ArraySet<VersionedPackage> mDisallowedPackages;
public NotificationListenerFilter() {
mAllowedNotificationTypes = FLAG_FILTER_TYPE_CONVERSATIONS
@@ -41,7 +43,7 @@ public class NotificationListenerFilter implements Parcelable {
mDisallowedPackages = new ArraySet<>();
}
- public NotificationListenerFilter(int types, ArraySet<String> pkgs) {
+ public NotificationListenerFilter(int types, ArraySet<VersionedPackage> pkgs) {
mAllowedNotificationTypes = types;
mDisallowedPackages = pkgs;
}
@@ -51,7 +53,8 @@ public class NotificationListenerFilter implements Parcelable {
*/
protected NotificationListenerFilter(Parcel in) {
mAllowedNotificationTypes = in.readInt();
- mDisallowedPackages = (ArraySet<String>) in.readArraySet(String.class.getClassLoader());
+ mDisallowedPackages = (ArraySet<VersionedPackage>) in.readArraySet(
+ VersionedPackage.class.getClassLoader());
}
@Override
@@ -77,7 +80,7 @@ public class NotificationListenerFilter implements Parcelable {
return (mAllowedNotificationTypes & type) != 0;
}
- public boolean isPackageAllowed(String pkg) {
+ public boolean isPackageAllowed(VersionedPackage pkg) {
return !mDisallowedPackages.contains(pkg);
}
@@ -85,7 +88,7 @@ public class NotificationListenerFilter implements Parcelable {
return mAllowedNotificationTypes;
}
- public ArraySet<String> getDisallowedPackages() {
+ public ArraySet<VersionedPackage> getDisallowedPackages() {
return mDisallowedPackages;
}
@@ -93,10 +96,18 @@ public class NotificationListenerFilter implements Parcelable {
mAllowedNotificationTypes = types;
}
- public void setDisallowedPackages(ArraySet<String> pkgs) {
+ public void setDisallowedPackages(ArraySet<VersionedPackage> pkgs) {
mDisallowedPackages = pkgs;
}
+ public void removePackage(VersionedPackage pkg) {
+ mDisallowedPackages.remove(pkg);
+ }
+
+ public void addPackage(VersionedPackage pkg) {
+ mDisallowedPackages.add(pkg);
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 64cddc35d2bb..73e66d0212fa 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -43,6 +43,7 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -80,6 +81,10 @@ import java.util.Objects;
* &lt;intent-filter>
* &lt;action android:name="android.service.notification.NotificationListenerService" />
* &lt;/intent-filter>
+ * &lt;meta-data
+ * android:name="android.service.notification.default_filter_types"
+ * android:value="1,2">
+ * &lt;/meta-data>
* &lt;/service></pre>
*
* <p>The service should wait for the {@link #onListenerConnected()} event
@@ -103,6 +108,21 @@ public abstract class NotificationListenerService extends Service {
private final String TAG = getClass().getSimpleName();
/**
+ * The name of the {@code meta-data} tag containing a comma separated list of default
+ * integer notification types that should be provided to this listener. See
+ * {@link #FLAG_FILTER_TYPE_ONGOING},
+ * {@link #FLAG_FILTER_TYPE_CONVERSATIONS}, {@link #FLAG_FILTER_TYPE_ALERTING),
+ * and {@link #FLAG_FILTER_TYPE_SILENT}.
+ * <p>This value will only be read if the app has not previously specified a default type list,
+ * and if the user has not overridden the allowed types.</p>
+ * <p>An absent value means 'allow all types'.
+ * A present but empty value means 'allow no types'.</p>
+ *
+ */
+ public static final String META_DATA_DEFAULT_FILTER_TYPES
+ = "android.service.notification.default_filter_types";
+
+ /**
* {@link #getCurrentInterruptionFilter() Interruption filter} constant -
* Normal interruption filter.
*/
@@ -254,23 +274,19 @@ public abstract class NotificationListenerService extends Service {
/**
* A flag value indicating that this notification listener can see conversation type
* notifications.
- * @hide
*/
public static final int FLAG_FILTER_TYPE_CONVERSATIONS = 1;
/**
* A flag value indicating that this notification listener can see altering type notifications.
- * @hide
*/
public static final int FLAG_FILTER_TYPE_ALERTING = 2;
/**
* A flag value indicating that this notification listener can see silent type notifications.
- * @hide
*/
public static final int FLAG_FILTER_TYPE_SILENT = 4;
/**
* A flag value indicating that this notification listener can see important
* ( > {@link NotificationManager#IMPORTANCE_MIN}) ongoing type notifications.
- * @hide
*/
public static final int FLAG_FILTER_TYPE_ONGOING = 8;
@@ -1528,6 +1544,14 @@ public abstract class NotificationListenerService extends Service {
mHandler.obtainMessage(MyHandler.MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED,
hideSilentStatusIcons).sendToTarget();
}
+
+ @Override
+ public void onNotificationFeedbackReceived(String key, NotificationRankingUpdate update,
+ Bundle feedback) {
+ // no-op in the listener
+ }
+
+
}
/**
@@ -1613,6 +1637,7 @@ public abstract class NotificationListenerService extends Service {
private int mSuppressedVisualEffects;
private @NotificationManager.Importance int mImportance;
private CharSequence mImportanceExplanation;
+ private float mRankingScore;
// System specified group key.
private String mOverrideGroupKey;
// Notification assistant channel override.
@@ -1653,6 +1678,7 @@ public abstract class NotificationListenerService extends Service {
out.writeInt(mSuppressedVisualEffects);
out.writeInt(mImportance);
out.writeCharSequence(mImportanceExplanation);
+ out.writeFloat(mRankingScore);
out.writeString(mOverrideGroupKey);
out.writeParcelable(mChannel, flags);
out.writeStringList(mOverridePeople);
@@ -1690,6 +1716,7 @@ public abstract class NotificationListenerService extends Service {
mSuppressedVisualEffects = in.readInt();
mImportance = in.readInt();
mImportanceExplanation = in.readCharSequence(); // may be null
+ mRankingScore = in.readFloat();
mOverrideGroupKey = in.readString(); // may be null
mChannel = in.readParcelable(cl); // may be null
mOverridePeople = in.createStringArrayList();
@@ -1787,6 +1814,17 @@ public abstract class NotificationListenerService extends Service {
}
/**
+ * Returns the ranking score provided by the {@link NotificationAssistantService} to
+ * sort the notifications in the shade
+ *
+ * @return the ranking score of the notification, range from -1 to 1
+ * @hide
+ */
+ public float getRankingScore() {
+ return mRankingScore;
+ }
+
+ /**
* If the system has overridden the group key, then this will be non-null, and this
* key should be used to bundle notifications.
*/
diff --git a/core/java/android/service/rotationresolver/RotationResolutionRequest.java b/core/java/android/service/rotationresolver/RotationResolutionRequest.java
index 94a6052c246d..8e76e2fc9202 100644
--- a/core/java/android/service/rotationresolver/RotationResolutionRequest.java
+++ b/core/java/android/service/rotationresolver/RotationResolutionRequest.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.Surface;
/**
* This class represents a request to an {@link RotationResolverService}. The request contains
@@ -54,7 +55,7 @@ public final class RotationResolutionRequest implements Parcelable {
mTimeoutMillis = timeoutMillis;
}
- public int getProposedRotation() {
+ @Surface.Rotation public int getProposedRotation() {
return mProposedRotation;
}
diff --git a/core/java/android/service/rotationresolver/RotationResolverService.java b/core/java/android/service/rotationresolver/RotationResolverService.java
index 593a642b4a38..604dd0ac8298 100644
--- a/core/java/android/service/rotationresolver/RotationResolverService.java
+++ b/core/java/android/service/rotationresolver/RotationResolverService.java
@@ -146,11 +146,8 @@ public abstract class RotationResolverService extends Service {
}
mPendingCallback = new RotationResolverCallbackWrapper(callback, this);
mCancellationSignal = CancellationSignal.fromTransport(transport);
- try {
- onResolveRotation(request, mCancellationSignal, mPendingCallback);
- } catch (UnsupportedOperationException e) {
- reportFailures(callback, ROTATION_RESULT_FAILURE_CANCELLED);
- }
+
+ onResolveRotation(request, mCancellationSignal, mPendingCallback);
}
@MainThread
diff --git a/core/java/android/service/screenshot/ScreenshotHash.java b/core/java/android/service/screenshot/ScreenshotHash.java
deleted file mode 100644
index 9ae4192bb676..000000000000
--- a/core/java/android/service/screenshot/ScreenshotHash.java
+++ /dev/null
@@ -1,256 +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 android.service.screenshot;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.graphics.Rect;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.DataClass;
-
-/**
- * The screenshot hash used to validate information about what was present on screen.
- * @hide
- *
- * TODO: Remove hide and SystemAPI since this will be a public class
- */
-@SystemApi
-@DataClass(genToString = true, genAidl = true)
-public final class ScreenshotHash implements Parcelable {
- /**
- * The timestamp when the screenshot was generated.
- */
- private final long mScreenshotTimeMillis;
-
- /**
- * The bounds of the requested area to take the screenshot. This is in window space passed in
- * by the client.
- */
- @NonNull
- private final Rect mBoundsInWindow;
-
- /**
- * The selected hashing algorithm that generated the image hash.
- */
- @NonNull
- private final String mHashingAlgorithm;
-
- /**
- * The image hash generated when creating the ScreenshotHash from the screenshot taken.
- */
- @NonNull
- private final byte[] mImageHash;
-
- /**
- * The hmac generated by the system and used to verify whether this token was generated by
- * the system. This should only be accessed by a system process.
- */
- @NonNull
- private final byte[] mHmac;
-
- /**
- * The hmac generated by the system and used to verify whether this token was generated by
- * the system. This should only be accessed by a system process.
- *
- * @hide
- */
- @SystemApi
- @NonNull
- public byte[] getHmac() {
- return mHmac;
- }
-
-
-
- // 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/screenshot
- // /ScreenshotHash.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- /**
- * Creates a new ScreenshotHash.
- *
- * @param screenshotTimeMillis
- * The timestamp when the screenshot was generated.
- * @param boundsInWindow
- * The bounds of the requested area to take the screenshot. This is in window space passed in
- * by the client.
- * @param hashingAlgorithm
- * The selected hashing algorithm that generated the image hash.
- * @param imageHash
- * The image hash generated when creating the ScreenshotHash from the screenshot taken.
- * @param hmac
- * The hmac generated by the system and used to verify whether this token was generated by
- * the system. This should only be accessed by a system process.
- */
- @DataClass.Generated.Member
- public ScreenshotHash(
- long screenshotTimeMillis,
- @NonNull Rect boundsInWindow,
- @NonNull String hashingAlgorithm,
- @NonNull byte[] imageHash,
- @NonNull byte[] hmac) {
- this.mScreenshotTimeMillis = screenshotTimeMillis;
- this.mBoundsInWindow = boundsInWindow;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mBoundsInWindow);
- this.mHashingAlgorithm = hashingAlgorithm;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mHashingAlgorithm);
- this.mImageHash = imageHash;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mImageHash);
- this.mHmac = hmac;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mHmac);
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- /**
- * The timestamp when the screenshot was generated.
- */
- @DataClass.Generated.Member
- public long getScreenshotTimeMillis() {
- return mScreenshotTimeMillis;
- }
-
- /**
- * The bounds of the requested area to take the screenshot. This is in window space passed in
- * by the client.
- */
- @DataClass.Generated.Member
- public @NonNull Rect getBoundsInWindow() {
- return mBoundsInWindow;
- }
-
- /**
- * The selected hashing algorithm that generated the image hash.
- */
- @DataClass.Generated.Member
- public @NonNull String getHashingAlgorithm() {
- return mHashingAlgorithm;
- }
-
- /**
- * The image hash generated when creating the ScreenshotHash from the screenshot taken.
- */
- @DataClass.Generated.Member
- public @NonNull byte[] getImageHash() {
- return mImageHash;
- }
-
- @Override
- @DataClass.Generated.Member
- public String toString() {
- // You can override field toString logic by defining methods like:
- // String fieldNameToString() { ... }
-
- return "ScreenshotHash { " +
- "screenshotTimeMillis = " + mScreenshotTimeMillis + ", " +
- "boundsInWindow = " + mBoundsInWindow + ", " +
- "hashingAlgorithm = " + mHashingAlgorithm + ", " +
- "imageHash = " + java.util.Arrays.toString(mImageHash) + ", " +
- "hmac = " + java.util.Arrays.toString(mHmac) +
- " }";
- }
-
- @Override
- @DataClass.Generated.Member
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- // You can override field parcelling by defining methods like:
- // void parcelFieldName(Parcel dest, int flags) { ... }
-
- dest.writeLong(mScreenshotTimeMillis);
- dest.writeTypedObject(mBoundsInWindow, flags);
- dest.writeString(mHashingAlgorithm);
- dest.writeByteArray(mImageHash);
- dest.writeByteArray(mHmac);
- }
-
- @Override
- @DataClass.Generated.Member
- public int describeContents() { return 0; }
-
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- @DataClass.Generated.Member
- /* package-private */ ScreenshotHash(@NonNull Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- long screenshotTimeMillis = in.readLong();
- Rect boundsInWindow = (Rect) in.readTypedObject(Rect.CREATOR);
- String hashingAlgorithm = in.readString();
- byte[] imageHash = in.createByteArray();
- byte[] hmac = in.createByteArray();
-
- this.mScreenshotTimeMillis = screenshotTimeMillis;
- this.mBoundsInWindow = boundsInWindow;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mBoundsInWindow);
- this.mHashingAlgorithm = hashingAlgorithm;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mHashingAlgorithm);
- this.mImageHash = imageHash;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mImageHash);
- this.mHmac = hmac;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mHmac);
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- @DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<ScreenshotHash> CREATOR
- = new Parcelable.Creator<ScreenshotHash>() {
- @Override
- public ScreenshotHash[] newArray(int size) {
- return new ScreenshotHash[size];
- }
-
- @Override
- public ScreenshotHash createFromParcel(@NonNull Parcel in) {
- return new ScreenshotHash(in);
- }
- };
-
- @DataClass.Generated(
- time = 1612383172822L,
- codegenVersion = "1.0.22",
- sourceFile = "frameworks/base/core/java/android/service/screenshot/ScreenshotHash.java",
- inputSignatures = "private final long mScreenshotTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashingAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate final @android.annotation.NonNull byte[] mHmac\npublic @android.annotation.SystemApi @android.annotation.NonNull byte[] getHmac()\nclass ScreenshotHash extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)")
- @Deprecated
- private void __metadata() {}
-
-
- //@formatter:on
- // End of generated code
-
-}
diff --git a/core/java/android/service/screenshot/ScreenshotHasherService.java b/core/java/android/service/screenshot/ScreenshotHasherService.java
deleted file mode 100644
index d96cc7eaba7a..000000000000
--- a/core/java/android/service/screenshot/ScreenshotHasherService.java
+++ /dev/null
@@ -1,160 +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 android.service.screenshot;
-
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.app.Service;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.hardware.HardwareBuffer;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteCallback;
-
-/**
- * A service that handles generating and verify {@link ScreenshotHash}.
- *
- * The service will generate a ScreenshotHash based on arguments passed in. Then later that
- * same ScreenshotHash can be verified to determine that it was created by the system.
- *
- * @hide
- */
-@SystemApi
-public abstract class ScreenshotHasherService extends Service {
- /** @hide **/
- public static final String EXTRA_SCREENSHOT_HASH =
- "android.service.screenshot.extra.SCREENSHOT_HASH";
-
- /** @hide **/
- public static final String EXTRA_VERIFICATION_STATUS =
- "android.service.screenshot.extra.VERIFICATION_STATUS";
-
- /**
- * 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.screenshot.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 ScreenshotHash providing service.
- *
- * @hide
- */
- @SystemApi
- public static final String SERVICE_INTERFACE =
- "android.service.screenshot.ScreenshotHasherService";
-
- private ScreenshotHasherServiceWrapper mWrapper;
- private Handler mHandler;
-
- public ScreenshotHasherService() {
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- mWrapper = new ScreenshotHasherServiceWrapper();
- mHandler = new Handler(Looper.getMainLooper(), null, true);
- }
-
- @NonNull
- @Override
- public final IBinder onBind(@NonNull Intent intent) {
- return mWrapper;
- }
-
- /**
- * Generates the ScreenshotHash that can be used to validate that the system generated the
- * token.
- *
- * @param salt The salt to use when generating the hmac. This should be unique to the
- * caller so the token cannot be verified by any other process.
- * @param screenshot The screenshot buffer for the content.
- * @param bounds The size and position of the content being screenshot in the window.
- * @param hashAlgorithm The String for the hashing algorithm to use based values in
- * {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}.
- * @return A ScreenshotHash that can be used to validate information about the content.
- * Returns null when the arguments sent are invalid.
- */
- @Nullable
- public abstract ScreenshotHash onGenerateScreenshotHash(@NonNull byte[] salt,
- @NonNull HardwareBuffer screenshot, @NonNull Rect bounds,
- @NonNull String hashAlgorithm);
-
- /**
- * Call to verify that the ScreenshotHash passed in was generated by the system.
- *
- * @param salt The salt value to use when verifying the hmac. This should be the
- * same value that was passed to
- * {@link #onGenerateScreenshotHash(byte[],
- * HardwareBuffer, Rect, String)} to
- * generate the token.
- * @param screenshotHash The token to verify that it was generated by the system.
- * @return true if the token can be verified that it was generated by the system.
- */
- public abstract boolean onVerifyScreenshotHash(@NonNull byte[] salt,
- @NonNull ScreenshotHash screenshotHash);
-
- private void generateScreenshotHash(byte[] salt, HardwareBuffer screenshot, Rect bounds,
- String hashAlgorithm, RemoteCallback callback) {
- ScreenshotHash screenshotHash = onGenerateScreenshotHash(salt, screenshot,
- bounds,
- hashAlgorithm);
- final Bundle data = new Bundle();
- data.putParcelable(EXTRA_SCREENSHOT_HASH, screenshotHash);
- callback.sendResult(data);
- }
-
- private void verifyScreenshotHash(byte[] salt, ScreenshotHash screenshotHash,
- RemoteCallback callback) {
- boolean verificationStatus = onVerifyScreenshotHash(salt, screenshotHash);
- final Bundle data = new Bundle();
- data.putBoolean(EXTRA_VERIFICATION_STATUS, verificationStatus);
- callback.sendResult(data);
- }
-
- private final class ScreenshotHasherServiceWrapper extends
- IScreenshotHasherService.Stub {
- @Override
- public void generateScreenshotHash(byte[] salt, HardwareBuffer screenshot, Rect bounds,
- String hashAlgorithm, RemoteCallback callback) {
- mHandler.sendMessage(
- obtainMessage(ScreenshotHasherService::generateScreenshotHash,
- ScreenshotHasherService.this, salt, screenshot, bounds,
- hashAlgorithm, callback));
- }
-
- @Override
- public void verifyScreenshotHash(byte[] salt, ScreenshotHash screenshotHash,
- RemoteCallback callback) {
- mHandler.sendMessage(
- obtainMessage(ScreenshotHasherService::verifyScreenshotHash,
- ScreenshotHasherService.this, salt, screenshotHash,
- callback));
- }
- }
-}
diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java
index a750b689ee02..87add57f383d 100644
--- a/core/java/android/service/storage/ExternalStorageService.java
+++ b/core/java/android/service/storage/ExternalStorageService.java
@@ -95,6 +95,21 @@ public abstract class ExternalStorageService extends Service {
public static final String EXTRA_ERROR =
"android.service.storage.extra.error";
+ /**
+ * {@link Bundle} key for a package name {@link String} value.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_PACKAGE_NAME = "android.service.storage.extra.package_name";
+
+ /**
+ * {@link Bundle} key for a {@link Long} value.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_ANR_TIMEOUT_MS =
+ "android.service.storage.extra.anr_timeout_ms";
+
/** @hide */
@IntDef(flag = true, prefix = {"FLAG_SESSION_"},
value = {FLAG_SESSION_TYPE_FUSE, FLAG_SESSION_ATTRIBUTE_INDEXABLE})
@@ -162,6 +177,15 @@ public abstract class ExternalStorageService extends Service {
throw new UnsupportedOperationException("onFreeCacheRequested not implemented");
}
+ /**
+ * Called when {@code packageName} is about to ANR
+ *
+ * @return ANR dialog delay in milliseconds
+ */
+ public long onGetAnrDelayMillis(@NonNull String packageName, int uid) {
+ throw new UnsupportedOperationException("onGetAnrDelayMillis not implemented");
+ }
+
@Override
@NonNull
public final IBinder onBind(@NonNull Intent intent) {
@@ -222,6 +246,19 @@ public abstract class ExternalStorageService extends Service {
});
}
+ @Override
+ public void getAnrDelayMillis(String packageName, int uid, RemoteCallback callback)
+ throws RemoteException {
+ mHandler.post(() -> {
+ try {
+ long timeoutMs = onGetAnrDelayMillis(packageName, uid);
+ sendTimeoutResult(packageName, timeoutMs, null /* throwable */, callback);
+ } catch (Throwable t) {
+ sendTimeoutResult(packageName, 0 /* timeoutMs */, t, callback);
+ }
+ });
+ }
+
private void sendResult(String sessionId, Throwable throwable, RemoteCallback callback) {
Bundle bundle = new Bundle();
bundle.putString(EXTRA_SESSION_ID, sessionId);
@@ -230,5 +267,16 @@ public abstract class ExternalStorageService extends Service {
}
callback.sendResult(bundle);
}
+
+ private void sendTimeoutResult(String packageName, long timeoutMs, Throwable throwable,
+ RemoteCallback callback) {
+ Bundle bundle = new Bundle();
+ bundle.putString(EXTRA_PACKAGE_NAME, packageName);
+ bundle.putLong(EXTRA_ANR_TIMEOUT_MS, timeoutMs);
+ if (throwable != null) {
+ bundle.putParcelable(EXTRA_ERROR, new ParcelableException(throwable));
+ }
+ callback.sendResult(bundle);
+ }
}
}
diff --git a/core/java/android/service/storage/IExternalStorageService.aidl b/core/java/android/service/storage/IExternalStorageService.aidl
index d06671b3fb9f..2e0bd86c3d7d 100644
--- a/core/java/android/service/storage/IExternalStorageService.aidl
+++ b/core/java/android/service/storage/IExternalStorageService.aidl
@@ -32,4 +32,5 @@ oneway interface IExternalStorageService
in RemoteCallback callback);
void freeCache(@utf8InCpp String sessionId, in String volumeUuid, long bytes,
in RemoteCallback callback);
+ void getAnrDelayMillis(String packageName, int uid, in RemoteCallback callback);
} \ No newline at end of file
diff --git a/core/java/android/service/wallpaper/Android.bp b/core/java/android/service/wallpaper/Android.bp
index ffbdb035c36c..a527d3d10fdf 100644
--- a/core/java/android/service/wallpaper/Android.bp
+++ b/core/java/android/service/wallpaper/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "WallpaperSharedLib",
diff --git a/core/java/android/service/wallpaper/EngineWindowPage.java b/core/java/android/service/wallpaper/EngineWindowPage.java
new file mode 100644
index 000000000000..5ed0ad6f2aeb
--- /dev/null
+++ b/core/java/android/service/wallpaper/EngineWindowPage.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.wallpaper;
+
+import android.app.WallpaperColors;
+import android.graphics.Bitmap;
+import android.graphics.RectF;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
+/**
+ * This class represents a page of a launcher page used by the wallpaper
+ * @hide
+ */
+public class EngineWindowPage {
+ private Bitmap mScreenShot;
+ private volatile long mLastUpdateTime = 0;
+ private Set<RectF> mCallbackAreas = new ArraySet<>();
+ private Map<RectF, WallpaperColors> mRectFColors = new ArrayMap<>();
+
+ /** should be locked extrnally */
+ public void addArea(RectF area) {
+ mCallbackAreas.add(area);
+ }
+
+ /** should be locked extrnally */
+ public void addWallpaperColors(RectF area, WallpaperColors colors) {
+ mCallbackAreas.add(area);
+ mRectFColors.put(area, colors);
+ }
+
+ /** get screenshot bitmap */
+ public Bitmap getBitmap() {
+ if (mScreenShot == null || mScreenShot.isRecycled()) return null;
+ return mScreenShot;
+ }
+
+ /** remove callbacks for an area */
+ public void removeArea(RectF area) {
+ mCallbackAreas.remove(area);
+ mRectFColors.remove(area);
+ }
+
+ /** set the last time the screenshot was updated */
+ public void setLastUpdateTime(long lastUpdateTime) {
+ mLastUpdateTime = lastUpdateTime;
+ }
+
+ /** get last screenshot time */
+ public long getLastUpdateTime() {
+ return mLastUpdateTime;
+ }
+
+ /** get colors for an area */
+ public WallpaperColors getColors(RectF rect) {
+ return mRectFColors.get(rect);
+ }
+
+ /** set the new bitmap version */
+ public void setBitmap(Bitmap screenShot) {
+ mScreenShot = screenShot;
+ }
+
+ /** get areas of interest */
+ public Set<RectF> getAreas() {
+ return mCallbackAreas;
+ }
+
+ /** run operations on this page */
+ public synchronized void execSync(Consumer<EngineWindowPage> run) {
+ run.accept(this);
+ }
+
+ /** nullify the area color */
+ public void removeColor(RectF colorArea) {
+ mRectFColors.remove(colorArea);
+ }
+}
diff --git a/core/java/android/service/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
index f334d9d3e874..f81ed3410d58 100644
--- a/core/java/android/service/wallpaper/IWallpaperConnection.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
@@ -16,6 +16,7 @@
package android.service.wallpaper;
+import android.graphics.RectF;
import android.os.ParcelFileDescriptor;
import android.service.wallpaper.IWallpaperEngine;
import android.app.WallpaperColors;
@@ -28,4 +29,5 @@ interface IWallpaperConnection {
void engineShown(IWallpaperEngine engine);
ParcelFileDescriptor setWallpaper(String name);
void onWallpaperColorsChanged(in WallpaperColors colors, int displayId);
+ void onLocalWallpaperColorsChanged(in RectF area, in WallpaperColors colors, int displayId);
}
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
index 90392e65794a..fbb449d3875e 100644
--- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -16,7 +16,10 @@
package android.service.wallpaper;
+import android.app.ILocalWallpaperColorConsumer;
+import android.app.WallpaperColors;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.view.MotionEvent;
import android.os.Bundle;
@@ -39,4 +42,6 @@ oneway interface IWallpaperEngine {
void destroy();
void setZoomOut(float scale);
void scalePreview(in Rect positionInWindow);
+ void removeLocalColorsAreas(in List<RectF> regions);
+ void addLocalColorsAreas(in List<RectF> regions);
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 3b66ddbdf4af..28cf16473e0f 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -23,6 +23,7 @@ import static android.graphics.Matrix.MSKEW_Y;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
import android.annotation.FloatRange;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -42,6 +43,7 @@ import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
@@ -53,6 +55,8 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.Trace;
+import android.util.ArraySet;
import android.util.Log;
import android.util.MergedConfiguration;
import android.view.Display;
@@ -66,6 +70,8 @@ import android.view.InputEventReceiver;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.MotionEvent;
+import android.view.PixelCopy;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.View;
@@ -83,6 +89,9 @@ import com.android.internal.view.BaseSurfaceHolder;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
@@ -116,6 +125,11 @@ public abstract class WallpaperService extends Service {
static final String TAG = "WallpaperService";
static final boolean DEBUG = false;
+ static final float MIN_PAGE_ALLOWED_MARGIN = .05f;
+ private static final int MIN_BITMAP_SCREENSHOT_WIDTH = 64;
+ private static final long DEFAULT_UPDATE_SCREENSHOT_DURATION = 60 * 1000; //Once per minute
+ private static final @NonNull RectF LOCAL_COLOR_BOUNDS =
+ new RectF(0, 0, 1, 1);
private static final int DO_ATTACH = 10;
private static final int DO_DETACH = 20;
@@ -134,6 +148,8 @@ public abstract class WallpaperService extends Service {
private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050;
private static final int MSG_ZOOM = 10100;
private static final int MSG_SCALE_PREVIEW = 10110;
+ private static final List<Float> PROHIBITED_STEPS = Arrays.asList(0f, Float.POSITIVE_INFINITY,
+ Float.NEGATIVE_INFINITY);
private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000;
@@ -158,6 +174,14 @@ public abstract class WallpaperService extends Service {
*/
public class Engine {
IWallpaperEngineWrapper mIWallpaperEngine;
+ final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4);
+ final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4);
+
+ // 2D matrix [x][y] to represent a page of a portion of a window
+ EngineWindowPage[] mWindowPages = new EngineWindowPage[1];
+ Bitmap mLastScreenshot;
+ int mLastWindowPage = -1;
+ float mLastPageOffset = 0;
// Copies from mIWallpaperEngine.
HandlerCaller mCaller;
@@ -409,8 +433,8 @@ public abstract class WallpaperService extends Service {
*/
@VisibleForTesting
public Engine(Supplier<Long> clockFunction, Handler handler) {
- mClockFunction = clockFunction;
- mHandler = handler;
+ mClockFunction = clockFunction;
+ mHandler = handler;
}
/**
@@ -448,6 +472,19 @@ public abstract class WallpaperService extends Service {
}
/**
+ * Return whether the wallpaper is capable of extracting local colors in a rectangle area,
+ * Must implement without calling super:
+ * {@link #addLocalColorsAreas(List)}
+ * {@link #removeLocalColorsAreas(List)}
+ * When local colors change, call {@link #notifyLocalColorsChanged(List, List)}
+ * See {@link com.android.systemui.ImageWallpaper} for an example
+ * @hide
+ */
+ public boolean supportsLocalColorExtraction() {
+ return false;
+ }
+
+ /**
* Returns true if this engine is running in preview mode -- that is,
* it is being shown to the user before they select it as the actual
* wallpaper.
@@ -691,6 +728,8 @@ public abstract class WallpaperService extends Service {
Log.w(TAG, "Can't notify system because wallpaper connection "
+ "was not established.");
}
+ resetWindowPages();
+ processLocalColors(mPendingXOffset, mPendingXOffsetStep);
} catch (RemoteException e) {
Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e);
}
@@ -714,6 +753,28 @@ public abstract class WallpaperService extends Service {
}
/**
+ * Send the changed local color areas for the connection
+ * @param regions
+ * @param colors
+ * @hide
+ */
+ public void notifyLocalColorsChanged(@NonNull List<RectF> regions,
+ @NonNull List<WallpaperColors> colors)
+ throws RuntimeException {
+ for (int i = 0; i < regions.size() && i < colors.size() && colors.get(i) != null; i++) {
+ try {
+ mConnection.onLocalWallpaperColorsChanged(
+ regions.get(i),
+ colors.get(i),
+ mDisplayContext.getDisplayId()
+ );
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
* Sets internal engine state. Only for testing.
* @param created {@code true} or {@code false}.
* @hide
@@ -1068,7 +1129,9 @@ public abstract class WallpaperService extends Service {
mIsCreating = false;
mSurfaceCreated = true;
if (redrawNeeded) {
+ resetWindowPages();
mSession.finishDrawing(mWindow, null /* postDrawTransaction */);
+ processLocalColors(mPendingXOffset, mPendingXOffsetStep);
}
reposition();
mIWallpaperEngine.reportShown();
@@ -1209,6 +1272,7 @@ public abstract class WallpaperService extends Service {
if (!mDestroyed) {
mVisible = visible;
reportVisibility();
+ if (visible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
}
}
@@ -1278,6 +1342,403 @@ public abstract class WallpaperService extends Service {
} catch (RemoteException e) {
}
}
+
+ // setup local color extraction data
+ processLocalColors(xOffset, xOffsetStep);
+ }
+
+ private void processLocalColors(float xOffset, float xOffsetStep) {
+ // implemented by the wallpaper
+ if (supportsLocalColorExtraction()) return;
+ if (DEBUG) {
+ Log.d(TAG, "processLocalColors " + xOffset + " of step "
+ + xOffsetStep);
+ }
+ //below is the default implementation
+ if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN) return;
+ int xPage;
+ int xPages;
+ if (!validStep(xOffsetStep)) {
+ if (DEBUG) {
+ Log.w(TAG, "invalid offset step " + xOffsetStep);
+ }
+ xOffset = 0;
+ xOffsetStep = 1;
+ xPage = 0;
+ xPages = 1;
+ } else {
+ xPages = Math.round(1 / xOffsetStep) + 1;
+ xOffsetStep = (float) 1 / (float) xPages;
+ float shrink = (float) (xPages - 1) / (float) xPages;
+ xOffset *= shrink;
+ xPage = Math.round(xOffset / xOffsetStep);
+ }
+ if (DEBUG) {
+ Log.d(TAG, "xPages " + xPages + " xPage " + xPage);
+ Log.d(TAG, "xOffsetStep " + xOffsetStep + " xOffset " + xOffset);
+ }
+ EngineWindowPage current;
+ synchronized (mLock) {
+ if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) {
+ mWindowPages = new EngineWindowPage[xPages];
+ initWindowPages(mWindowPages, xOffsetStep);
+ }
+ if (mLocalColorsToAdd.size() != 0) {
+ for (RectF colorArea : mLocalColorsToAdd) {
+ if (!isValid(colorArea)) continue;
+ mLocalColorAreas.add(colorArea);
+ int colorPage = getRectFPage(colorArea, xOffsetStep);
+ EngineWindowPage currentPage = mWindowPages[colorPage];
+ if (currentPage == null) {
+ currentPage = new EngineWindowPage();
+ currentPage.addArea(colorArea);
+ mWindowPages[colorPage] = currentPage;
+ } else {
+ currentPage.setLastUpdateTime(0);
+ currentPage.removeColor(colorArea);
+ }
+ }
+ mLocalColorsToAdd.clear();
+ }
+ if (xPage >= mWindowPages.length) {
+ if (DEBUG) {
+ Log.e(TAG, "error xPage >= mWindowPages.length page: " + xPage);
+ Log.e(TAG, "error on page " + xPage + " out of " + xPages);
+ Log.e(TAG,
+ "error on xOffsetStep " + xOffsetStep + " xOffset " + xOffset);
+ }
+ xPage = mWindowPages.length - 1;
+ }
+ current = mWindowPages[xPage];
+ if (current == null) {
+ if (DEBUG) Log.d(TAG, "making page " + xPage + " out of " + xPages);
+ if (DEBUG) {
+ Log.d(TAG, "xOffsetStep " + xOffsetStep + " xOffset " + xOffset);
+ }
+ current = new EngineWindowPage();
+ mWindowPages[xPage] = current;
+ }
+ }
+ updatePage(current, xPage, xPages, xOffsetStep);
+ }
+
+ private void initWindowPages(EngineWindowPage[] windowPages, float step) {
+ for (int i = 0; i < windowPages.length; i++) {
+ windowPages[i] = new EngineWindowPage();
+ }
+ mLocalColorAreas.addAll(mLocalColorsToAdd);
+ mLocalColorsToAdd.clear();
+ for (RectF area: mLocalColorAreas) {
+ if (!isValid(area)) {
+ mLocalColorAreas.remove(area);
+ continue;
+ }
+ int pageNum = getRectFPage(area, step);
+ windowPages[pageNum].addArea(area);
+ }
+ }
+
+ void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages,
+ float xOffsetStep) {
+ // to save creating a runnable, check twice
+ long current = System.nanoTime() / 1_000_000;
+ long lapsed = current - currentPage.getLastUpdateTime();
+ if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) {
+ return;
+ }
+ Surface surface = mSurfaceHolder.getSurface();
+ boolean widthIsLarger =
+ mSurfaceControl.getWidth() > mSurfaceControl.getHeight();
+ int smaller = widthIsLarger ? mSurfaceControl.getWidth()
+ : mSurfaceControl.getHeight();
+ float ratio = (float) MIN_BITMAP_SCREENSHOT_WIDTH / (float) smaller;
+ int width = (int) (ratio * mSurfaceControl.getWidth());
+ int height = (int) (ratio * mSurfaceControl.getHeight());
+ if (width <= 0 || height <= 0) {
+ Log.e(TAG, "wrong width and height values of bitmap " + width + " " + height);
+ return;
+ }
+ Bitmap screenShot = Bitmap.createBitmap(width, height,
+ Bitmap.Config.ARGB_8888);
+ final Bitmap finalScreenShot = screenShot;
+ Trace.beginSection("WallpaperService#pixelCopy");
+ PixelCopy.request(surface, screenShot, (res) -> {
+ Trace.endSection();
+ if (DEBUG) Log.d(TAG, "result of pixel copy is " + res);
+ if (res != PixelCopy.SUCCESS) {
+ Bitmap lastBitmap = currentPage.getBitmap();
+ currentPage.execSync((p) -> {
+ // assign the last bitmap taken for now
+ p.setBitmap(mLastScreenshot);
+ });
+ Bitmap lastScreenshot = mLastScreenshot;
+ if (lastScreenshot != null && !lastScreenshot.isRecycled()
+ && !Objects.equals(lastBitmap, lastScreenshot)) {
+ updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
+ }
+ } else {
+ mLastScreenshot = finalScreenShot;
+ // going to hold this lock for a while
+ currentPage.execSync((p) -> {
+ p.setBitmap(finalScreenShot);
+ p.setLastUpdateTime(current);
+ });
+ updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
+ }
+ }, mHandler);
+
+ }
+ // locked by the passed page
+ private void updatePageColors(EngineWindowPage page, int pageIndx, int numPages,
+ float xOffsetStep) {
+ if (page.getBitmap() == null) return;
+ if (DEBUG) {
+ Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas "
+ + page.getAreas().size() + " and bitmap size of "
+ + page.getBitmap().getWidth() + " x " + page.getBitmap().getHeight());
+ }
+ for (RectF area: page.getAreas()) {
+ RectF subArea = generateSubRect(area, pageIndx, numPages);
+ Bitmap b = page.getBitmap();
+ int x = Math.round(b.getWidth() * subArea.left);
+ int y = Math.round(b.getHeight() * subArea.top);
+ int width = Math.round(b.getWidth() * subArea.width());
+ int height = Math.round(b.getHeight() * subArea.height());
+ Bitmap target;
+ try {
+ target = Bitmap.createBitmap(page.getBitmap(), x, y, width, height);
+ } catch (Exception e) {
+ Log.e(TAG, "Error creating page local color bitmap", e);
+ continue;
+ }
+ WallpaperColors color = WallpaperColors.fromBitmap(target);
+ target.recycle();
+ WallpaperColors currentColor = page.getColors(area);
+
+ if (DEBUG) {
+ Log.d(TAG, "getting local bitmap area x " + x + " y " + y
+ + " width " + width + " height " + height + " for sub area " + subArea
+ + " and with page " + pageIndx + " of " + numPages);
+
+ }
+ if (currentColor == null || !color.equals(currentColor)) {
+ page.addWallpaperColors(area, color);
+ if (DEBUG) {
+ Log.d(TAG, "onLocalWallpaperColorsChanged"
+ + " local color callback for area" + area + " for page " + pageIndx
+ + " of " + numPages);
+ }
+ try {
+ mConnection.onLocalWallpaperColorsChanged(area, color,
+ mDisplayContext.getDisplayId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
+ }
+ }
+ }
+ }
+
+ private RectF generateSubRect(RectF in, int pageInx, int numPages) {
+ float minLeft = (float) (pageInx) / (float) (numPages);
+ float maxRight = (float) (pageInx + 1) / (float) (numPages);
+ float left = in.left;
+ float right = in.right;
+
+ // bound rect
+ if (left < minLeft) left = minLeft;
+ if (right > maxRight) right = maxRight;
+
+ // scale up the sub area then trim
+ left = (left * (float) numPages) % 1f;
+ right = (right * (float) numPages) % 1f;
+ if (right == 0f) {
+ right = 1f;
+ }
+
+ return new RectF(left, in.top, right, in.bottom);
+ }
+
+ private void resetWindowPages() {
+ if (supportsLocalColorExtraction()) return;
+ mLastWindowPage = -1;
+ synchronized (mLock) {
+ for (int i = 0; i < mWindowPages.length; i++) {
+ EngineWindowPage page = mWindowPages[i];
+ if (page != null) {
+ page.execSync((p) -> {
+ p.setLastUpdateTime(0L);
+ });
+ }
+ }
+ }
+ }
+
+ private int getRectFPage(RectF area, float step) {
+ if (!isValid(area)) return 0;
+ if (!validStep(step)) return 0;
+ int pages = Math.round(1 / step);
+ int page = Math.round(area.centerX() * pages);
+ if (page == pages) return pages - 1;
+ if (page == mWindowPages.length) page = mWindowPages.length - 1;
+ return page;
+ }
+
+ /**
+ * Add local colors areas of interest
+ * @param regions list of areas
+ * @hide
+ */
+ public void addLocalColorsAreas(@NonNull List<RectF> regions) {
+ if (supportsLocalColorExtraction()) return;
+ if (DEBUG) {
+ Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions);
+ }
+ float step = mPendingXOffsetStep;
+
+ List<WallpaperColors> colors = getLocalWallpaperColors(regions);
+ synchronized (mLock) {
+ if (!validStep(step)) {
+ step = 0;
+ }
+ for (int i = 0; i < regions.size(); i++) {
+ RectF area = regions.get(i);
+ if (!isValid(area)) continue;
+ int pageInx = getRectFPage(area, step);
+ // no page should be null
+ EngineWindowPage page = mWindowPages[pageInx];
+
+ if (page != null) {
+ mLocalColorAreas.add(area);
+ page.addArea(area);
+ WallpaperColors color = colors.get(i);
+ if (color != null && !color.equals(page.getColors(area))) {
+ page.execSync(p -> {
+ p.addWallpaperColors(area, color);
+ });
+ }
+ } else {
+ mLocalColorsToAdd.add(area);
+ }
+ }
+ }
+
+ for (int i = 0; i < colors.size() && colors.get(i) != null; i++) {
+ try {
+ mConnection.onLocalWallpaperColorsChanged(regions.get(i), colors.get(i),
+ mDisplayContext.getDisplayId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Remove local colors areas of interest if they exist
+ * @param regions list of areas
+ * @hide
+ */
+ public void removeLocalColorsAreas(@NonNull List<RectF> regions) {
+ if (supportsLocalColorExtraction()) return;
+ synchronized (mLock) {
+ float step = mPendingXOffsetStep;
+ mLocalColorsToAdd.removeAll(regions);
+ mLocalColorAreas.removeAll(regions);
+ if (!validStep(step)) {
+ return;
+ }
+ for (int i = 0; i < regions.size(); i++) {
+ RectF area = regions.get(i);
+ if (!isValid(area)) continue;
+ int pageInx = getRectFPage(area, step);
+ // no page should be null
+ EngineWindowPage page = mWindowPages[pageInx];
+ if (page != null) {
+ page.execSync(p -> {
+ p.removeArea(area);
+ });
+ }
+ }
+ }
+ }
+
+ private @NonNull List<WallpaperColors> getLocalWallpaperColors(@NonNull List<RectF> areas) {
+ ArrayList<WallpaperColors> colors = new ArrayList<>(areas.size());
+ float step = mPendingXOffsetStep;
+ if (!validStep(step)) {
+ if (DEBUG) Log.d(TAG, "invalid step size " + step);
+ step = 1.0f;
+ }
+ for (int i = 0; i < areas.size(); i++) {
+ RectF currentArea = areas.get(i);
+ EngineWindowPage page;
+ RectF area;
+ int pageIndx;
+ synchronized (mLock) {
+ pageIndx = getRectFPage(currentArea, step);
+ if (mWindowPages.length == 0 || pageIndx < 0
+ || pageIndx > mWindowPages.length || !isValid(currentArea)) {
+ colors.add(null);
+ continue;
+ }
+ area = generateSubRect(currentArea, pageIndx, mWindowPages.length);
+ page = mWindowPages[pageIndx];
+ }
+ if (page == null) {
+ colors.add(null);
+ continue;
+ }
+ float finalStep = step;
+ int finalPageIndx = pageIndx;
+ Bitmap screenShot = page.getBitmap();
+ if (screenShot == null || screenShot.isRecycled()) {
+ if (DEBUG) {
+ Log.d(TAG, "invalid bitmap " + screenShot
+ + " for page " + finalPageIndx);
+ }
+ page.setLastUpdateTime(0);
+ colors.add(null);
+ continue;
+ }
+ Bitmap b = screenShot;
+ Rect subImage = new Rect(
+ Math.round(area.left * b.getWidth() / finalStep),
+ Math.round(area.top * b.getHeight()),
+ Math.round(area.right * b.getWidth() / finalStep),
+ Math.round(area.bottom * b.getHeight())
+ );
+ subImage = fixRect(b, subImage);
+ if (DEBUG) {
+ Log.d(TAG, "getting subbitmap of " + subImage.toString()
+ + " for RectF " + area.toString()
+ + " screenshot width " + screenShot.getWidth() + " height "
+ + screenShot.getHeight());
+ }
+ Bitmap colorImg = Bitmap.createBitmap(screenShot,
+ subImage.left, subImage.top, subImage.width(), subImage.height());
+ if (DEBUG) {
+ Log.d(TAG, "created bitmap " + colorImg.getWidth() + ", "
+ + colorImg.getHeight());
+ }
+ WallpaperColors color = WallpaperColors.fromBitmap(colorImg);
+ colors.add(color);
+ }
+ return colors;
+ }
+
+ // fix the rect to be included within the bounds of the bitmap
+ private Rect fixRect(Bitmap b, Rect r) {
+ r.left = r.left >= r.right || r.left >= b.getWidth() || r.left > 0
+ ? 0
+ : r.left;
+ r.right = r.left >= r.right || r.right > b.getWidth()
+ ? b.getWidth()
+ : r.right;
+ return r;
+ }
+
+ private boolean validStep(float step) {
+ return !PROHIBITED_STEPS.contains(step) && step > 0. && step <= 1.;
}
void doCommand(WallpaperCommand cmd) {
@@ -1371,6 +1832,16 @@ public abstract class WallpaperService extends Service {
};
}
+ private boolean isValid(RectF area) {
+ boolean valid = area.bottom > area.top && area.left < area.right
+ && LOCAL_COLOR_BOUNDS.contains(area);
+ return valid;
+ }
+
+ private boolean inRectFRange(float number) {
+ return number >= 0f && number <= 1f;
+ }
+
class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
implements HandlerCaller.Callback {
private final HandlerCaller mCaller;
@@ -1477,6 +1948,14 @@ public abstract class WallpaperService extends Service {
mCaller.sendMessage(msg);
}
+ public void addLocalColorsAreas(List<RectF> regions) {
+ mEngine.addLocalColorsAreas(regions);
+ }
+
+ public void removeLocalColorsAreas(List<RectF> regions) {
+ mEngine.removeLocalColorsAreas(regions);
+ }
+
public void destroy() {
Message msg = mCaller.obtainMessage(DO_DETACH);
mCaller.sendMessage(msg);
@@ -1516,14 +1995,15 @@ public abstract class WallpaperService extends Service {
}
switch (message.what) {
case DO_ATTACH: {
+ Engine engine = onCreateEngine();
+ mEngine = engine;
try {
mConnection.attachEngine(this, mDisplayId);
} catch (RemoteException e) {
+ engine.detach();
Log.w(TAG, "Wallpaper host disappeared", e);
return;
}
- Engine engine = onCreateEngine();
- mEngine = engine;
mActiveEngines.add(engine);
engine.attach(this);
return;
diff --git a/core/java/android/speech/IRecognitionService.aidl b/core/java/android/speech/IRecognitionService.aidl
index f91e122ea9cb..cc1cdedd0f96 100644
--- a/core/java/android/speech/IRecognitionService.aidl
+++ b/core/java/android/speech/IRecognitionService.aidl
@@ -43,7 +43,7 @@ oneway interface IRecognitionService {
* @param featureId The feature in the package
*/
void startListening(in Intent recognizerIntent, in IRecognitionListener listener,
- String packageName, String featureId);
+ String packageName, String featureId, int callingUid);
/**
* Stops listening for speech. Speech captured so far will be recognized as
@@ -62,6 +62,7 @@ oneway interface IRecognitionService {
* @param listener to receive callbacks, note that this must be non-null
* @param packageName the package name calling this API
* @param featureId The feature in the package
+ * @param isShutdown Whether the cancellation is caused by a client calling #shutdown
*/
- void cancel(in IRecognitionListener listener, String packageName, String featureId);
+ void cancel(in IRecognitionListener listener, String packageName, String featureId, boolean isShutdown);
}
diff --git a/core/java/android/speech/IRecognitionServiceManager.aidl b/core/java/android/speech/IRecognitionServiceManager.aidl
index 7158ba2f9f63..8e5292d1ddf1 100644
--- a/core/java/android/speech/IRecognitionServiceManager.aidl
+++ b/core/java/android/speech/IRecognitionServiceManager.aidl
@@ -16,6 +16,8 @@
package android.speech;
+import android.content.ComponentName;
+
import android.speech.IRecognitionServiceManagerCallback;
/**
@@ -23,6 +25,10 @@ import android.speech.IRecognitionServiceManagerCallback;
*
* {@hide}
*/
-interface IRecognitionServiceManager {
- void createSession(in IRecognitionServiceManagerCallback callback);
+oneway interface IRecognitionServiceManager {
+ void createSession(
+ in ComponentName componentName,
+ in IBinder clientToken,
+ boolean onDevice,
+ in IRecognitionServiceManagerCallback callback);
}
diff --git a/core/java/android/speech/IRecognitionServiceManagerCallback.aidl b/core/java/android/speech/IRecognitionServiceManagerCallback.aidl
index d760810deda8..26afdaafd919 100644
--- a/core/java/android/speech/IRecognitionServiceManagerCallback.aidl
+++ b/core/java/android/speech/IRecognitionServiceManagerCallback.aidl
@@ -25,5 +25,5 @@ import android.speech.IRecognitionService;
*/
oneway interface IRecognitionServiceManagerCallback {
void onSuccess(in IRecognitionService service);
- void onError();
+ void onError(int errorCode);
}
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index c97dbfe38ead..fd584f191743 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -105,17 +105,6 @@ public abstract class RecognitionService extends Service {
int callingUid) {
if (mCurrentCallback == null) {
if (DBG) Log.d(TAG, "created new mCurrentCallback, listener = " + listener.asBinder());
- try {
- listener.asBinder().linkToDeath(new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_CANCEL, listener));
- }
- }, 0);
- } catch (RemoteException re) {
- Log.e(TAG, "dead listener on startListening");
- return;
- }
mCurrentCallback = new Callback(listener, callingUid);
RecognitionService.this.onStartListening(intent, mCurrentCallback);
} else {
@@ -352,7 +341,6 @@ public abstract class RecognitionService extends Service {
* Return the Linux uid assigned to the process that sent you the current transaction that
* is being processed. This is obtained from {@link Binder#getCallingUid()}.
*/
- // TODO(b/176578753): need to make sure this is fixed when proxied through system.
public int getCallingUid() {
return mCallingUid;
}
@@ -368,7 +356,7 @@ public abstract class RecognitionService extends Service {
@Override
public void startListening(Intent recognizerIntent, IRecognitionListener listener,
- String packageName, String featureId) {
+ String packageName, String featureId, int callingUid) {
Preconditions.checkNotNull(packageName);
if (DBG) Log.d(TAG, "startListening called by:" + listener.asBinder());
@@ -377,7 +365,7 @@ public abstract class RecognitionService extends Service {
packageName, featureId)) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
MSG_START_LISTENING, service.new StartListeningArgs(
- recognizerIntent, listener, Binder.getCallingUid())));
+ recognizerIntent, listener, callingUid)));
}
}
@@ -397,7 +385,7 @@ public abstract class RecognitionService extends Service {
@Override
public void cancel(IRecognitionListener listener, String packageName,
- String featureId) {
+ String featureId, boolean isShutdown) {
Preconditions.checkNotNull(packageName);
if (DBG) Log.d(TAG, "cancel called by:" + listener.asBinder());
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index 7439d324b5b9..f3ecc6c95819 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -20,8 +20,8 @@ import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
import android.content.pm.ResolveInfo;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -32,10 +32,9 @@ import android.os.ServiceManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Slog;
-import java.util.LinkedList;
import java.util.List;
-import java.util.Queue;
/**
* This class provides access to the speech recognition service. This service allows access to the
@@ -107,6 +106,12 @@ public class SpeechRecognizer {
/** Insufficient permissions */
public static final int ERROR_INSUFFICIENT_PERMISSIONS = 9;
+ /** Too many requests from the same client. */
+ public static final int ERROR_TOO_MANY_REQUESTS = 10;
+
+ /** Server has been disconnected, e.g. because the app has crashed. */
+ public static final int ERROR_SERVER_DISCONNECTED = 11;
+
/** action codes */
private final static int MSG_START = 1;
private final static int MSG_STOP = 2;
@@ -116,9 +121,6 @@ public class SpeechRecognizer {
/** The actual RecognitionService endpoint */
private IRecognitionService mService;
- /** The connection to the actual service */
- private Connection mConnection;
-
/** Context with which the manager was created */
private final Context mContext;
@@ -151,15 +153,11 @@ public class SpeechRecognizer {
}
};
- /**
- * Temporary queue, saving the messages until the connection will be established, afterwards,
- * only mHandler will receive the messages
- */
- private final Queue<Message> mPendingTasks = new LinkedList<Message>();
-
/** The Listener that will receive all the callbacks */
private final InternalListener mListener = new InternalListener();
+ private final IBinder mClientToken = new Binder();
+
/**
* The right way to create a {@code SpeechRecognizer} is by using
* {@link #createSpeechRecognizer} static factory method
@@ -181,30 +179,6 @@ public class SpeechRecognizer {
}
/**
- * Basic ServiceConnection that records the mService variable. Additionally, on creation it
- * invokes the {@link IRecognitionService#startListening(Intent, IRecognitionListener)}.
- */
- private class Connection implements ServiceConnection {
-
- public void onServiceConnected(final ComponentName name, final IBinder service) {
- // always done on the application main thread, so no need to send message to mHandler
- mService = IRecognitionService.Stub.asInterface(service);
- if (DBG) Log.d(TAG, "onServiceConnected - Success");
- while (!mPendingTasks.isEmpty()) {
- mHandler.sendMessage(mPendingTasks.poll());
- }
- }
-
- public void onServiceDisconnected(final ComponentName name) {
- // always done on the application main thread, so no need to send message to mHandler
- mService = null;
- mConnection = null;
- mPendingTasks.clear();
- if (DBG) Log.d(TAG, "onServiceDisconnected - Success");
- }
- }
-
- /**
* Checks whether a speech recognition service is available on the system. If this method
* returns {@code false}, {@link SpeechRecognizer#createSpeechRecognizer(Context)} will
* fail.
@@ -304,87 +278,52 @@ public class SpeechRecognizer {
throw new IllegalArgumentException("intent must not be null");
}
checkIsCalledFromMainThread();
- if (mConnection == null) { // first time connection
- // TODO(b/176578753): both flows should go through system service.
- if (mOnDevice) {
- connectToSystemService();
- } else {
- connectToService();
- }
- }
- putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent));
- }
- private void connectToSystemService() {
- mManagerService = IRecognitionServiceManager.Stub.asInterface(
- ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE));
-
- if (mManagerService == null) {
- mListener.onError(ERROR_CLIENT);
- return;
- }
-
- try {
- // TODO(b/176578753): this has to supply information on whether to use on-device impl.
- mManagerService.createSession(new IRecognitionServiceManagerCallback.Stub(){
- @Override
- public void onSuccess(IRecognitionService service) throws RemoteException {
- mService = service;
- }
-
- @Override
- public void onError() throws RemoteException {
- Log.e(TAG, "Bind to system recognition service failed");
- mListener.onError(ERROR_CLIENT);
- }
- });
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
-
- private void connectToService() {
- mConnection = new Connection();
-
- Intent serviceIntent = new Intent(RecognitionService.SERVICE_INTERFACE);
-
- if (mServiceComponent == null) {
- String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(),
- Settings.Secure.VOICE_RECOGNITION_SERVICE);
-
- if (TextUtils.isEmpty(serviceComponent)) {
- Log.e(TAG, "no selected voice recognition service");
- mListener.onError(ERROR_CLIENT);
- return;
+ if (DBG) {
+ Slog.i(TAG, "#startListening called");
+ if (mService == null) {
+ Slog.i(TAG, "Connection is not established yet");
}
+ }
- serviceIntent.setComponent(
- ComponentName.unflattenFromString(serviceComponent));
+ if (mService == null) {
+ // First time connection: first establish a connection, then dispatch #startListening.
+ connectToSystemService(
+ () -> putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent)));
} else {
- serviceIntent.setComponent(mServiceComponent);
- }
- if (!mContext.bindService(serviceIntent, mConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES)) {
- Log.e(TAG, "bind to recognition service failed");
- mConnection = null;
- mService = null;
- mListener.onError(ERROR_CLIENT);
- return;
+ putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent));
}
}
/**
* Stops listening for speech. Speech captured so far will be recognized as if the user had
- * stopped speaking at this point. Note that in the default case, this does not need to be
- * called, as the speech endpointer will automatically stop the recognizer listening when it
- * determines speech has completed. However, you can manipulate endpointer parameters directly
- * using the intent extras defined in {@link RecognizerIntent}, in which case you may sometimes
- * want to manually call this method to stop listening sooner. Please note that
+ * stopped speaking at this point.
+ *
+ * <p>Note that in the default case, this does not need to be called, as the speech endpointer
+ * will automatically stop the recognizer listening when it determines speech has completed.
+ * However, you can manipulate endpointer parameters directly using the intent extras defined in
+ * {@link RecognizerIntent}, in which case you may sometimes want to manually call this method
+ * to stop listening sooner.
+ *
+ * <p>Upon invocation clients must wait until {@link RecognitionListener#onResults} or
+ * {@link RecognitionListener#onError} are invoked before calling
+ * {@link SpeechRecognizer#startListening} again. Otherwise such an attempt would be rejected by
+ * recognition service.
+ *
+ * <p>Please note that
* {@link #setRecognitionListener(RecognitionListener)} should be called beforehand, otherwise
* no notifications will be received.
*/
public void stopListening() {
checkIsCalledFromMainThread();
+
+ if (DBG) {
+ Slog.i(TAG, "#stopListening called");
+ if (mService == null) {
+ Slog.i(TAG, "Connection is not established yet");
+ }
+ }
+
putMessage(Message.obtain(mHandler, MSG_STOP));
}
@@ -406,11 +345,7 @@ public class SpeechRecognizer {
}
private void putMessage(Message msg) {
- if (mService == null) {
- mPendingTasks.offer(msg);
- } else {
- mHandler.sendMessage(msg);
- }
+ mHandler.sendMessage(msg);
}
/** sends the actual message to the service */
@@ -420,7 +355,7 @@ public class SpeechRecognizer {
}
try {
mService.startListening(recognizerIntent, mListener, mContext.getOpPackageName(),
- mContext.getAttributionTag());
+ mContext.getAttributionTag(), android.os.Process.myUid());
if (DBG) Log.d(TAG, "service start listening command succeded");
} catch (final RemoteException e) {
Log.e(TAG, "startListening() failed", e);
@@ -449,7 +384,11 @@ public class SpeechRecognizer {
return;
}
try {
- mService.cancel(mListener, mContext.getOpPackageName(), mContext.getAttributionTag());
+ mService.cancel(
+ mListener,
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag(),
+ false /* isShutdown */);
if (DBG) Log.d(TAG, "service cancel command succeded");
} catch (final RemoteException e) {
Log.e(TAG, "cancel() failed", e);
@@ -472,28 +411,94 @@ public class SpeechRecognizer {
mListener.mInternalListener = listener;
}
- /**
- * Destroys the {@code SpeechRecognizer} object.
- */
+ /** Destroys the {@code SpeechRecognizer} object. */
public void destroy() {
if (mService != null) {
try {
mService.cancel(mListener, mContext.getOpPackageName(),
- mContext.getAttributionTag());
+ mContext.getAttributionTag(), true /* isShutdown */);
} catch (final RemoteException e) {
// Not important
}
}
- if (mConnection != null) {
- mContext.unbindService(mConnection);
- }
- mPendingTasks.clear();
mService = null;
- mConnection = null;
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);
+ return;
+ }
+
+ ComponentName componentName = getSpeechRecognizerComponentName();
+
+ if (!mOnDevice && componentName == null) {
+ mListener.onError(ERROR_CLIENT);
+ return;
+ }
+
+ try {
+ mManagerService.createSession(
+ componentName,
+ mClientToken,
+ mOnDevice,
+ new IRecognitionServiceManagerCallback.Stub(){
+ @Override
+ public void onSuccess(IRecognitionService service) throws RemoteException {
+ mService = service;
+ onSuccess.run();
+ }
+
+ @Override
+ public void onError(int errorCode) throws RemoteException {
+ Log.e(TAG, "Bind to system recognition service failed");
+ mListener.onError(errorCode);
+ }
+ });
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the component name to be used for establishing a connection, based on the parameters
+ * used during initialization.
+ *
+ * <p>Note the 3 different scenarios:
+ * <ol>
+ * <li>On-device speech recognizer which is determined by the manufacturer and not
+ * changeable by the user
+ * <li>Default user-selected speech recognizer as specified by
+ * {@code Settings.Secure.VOICE_RECOGNITION_SERVICE}
+ * <li>Custom speech recognizer supplied by the client.
+ */
+ private ComponentName getSpeechRecognizerComponentName() {
+ if (mOnDevice) {
+ return null;
+ }
+
+ if (mServiceComponent != null) {
+ return mServiceComponent;
+ }
+
+ String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.VOICE_RECOGNITION_SERVICE);
+
+ if (TextUtils.isEmpty(serviceComponent)) {
+ Log.e(TAG, "no selected voice recognition service");
+ mListener.onError(ERROR_CLIENT);
+ return null;
+ }
+
+ return ComponentName.unflattenFromString(serviceComponent);
+ }
+
/**
* Internal wrapper of IRecognitionListener which will propagate the results to
* RecognitionListener
diff --git a/core/java/android/speech/tts/ITextToSpeechManager.aidl b/core/java/android/speech/tts/ITextToSpeechManager.aidl
new file mode 100644
index 000000000000..e6b63dff1553
--- /dev/null
+++ b/core/java/android/speech/tts/ITextToSpeechManager.aidl
@@ -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 android.speech.tts;
+
+import android.speech.tts.ITextToSpeechSessionCallback;
+
+/**
+ * TextToSpeechManagerService interface. Allows opening {@link TextToSpeech} session with the
+ * specified provider proxied by the system service.
+ *
+ * @hide
+ */
+oneway interface ITextToSpeechManager {
+ void createSession(in String engine, in ITextToSpeechSessionCallback managerCallback);
+}
diff --git a/core/java/android/speech/tts/ITextToSpeechSession.aidl b/core/java/android/speech/tts/ITextToSpeechSession.aidl
new file mode 100644
index 000000000000..b2afeb0d1ba8
--- /dev/null
+++ b/core/java/android/speech/tts/ITextToSpeechSession.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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.speech.tts;
+
+/**
+ * TextToSpeech session interface. Allows to control remote TTS service session once connected.
+ *
+ * @see ITextToSpeechManager
+ * @see ITextToSpeechSessionCallback
+ *
+ * {@hide}
+ */
+oneway interface ITextToSpeechSession {
+
+ /**
+ * Disconnects the client from the TTS provider.
+ */
+ void disconnect();
+} \ No newline at end of file
diff --git a/core/java/android/speech/tts/ITextToSpeechSessionCallback.aidl b/core/java/android/speech/tts/ITextToSpeechSessionCallback.aidl
new file mode 100644
index 000000000000..545622a007f3
--- /dev/null
+++ b/core/java/android/speech/tts/ITextToSpeechSessionCallback.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.speech.tts;
+import android.speech.tts.ITextToSpeechSession;
+
+/**
+ * Callback interface for a session created by {@link ITextToSpeechManager} API.
+ *
+ * @hide
+ */
+oneway interface ITextToSpeechSessionCallback {
+
+ void onConnected(in ITextToSpeechSession session, in IBinder serviceBinder);
+
+ void onDisconnected();
+
+ void onError(in String errorInfo);
+} \ No newline at end of file
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 7a185382a631..78e5eabb69a8 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -35,6 +35,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.Log;
@@ -51,6 +52,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;
+import java.util.concurrent.Executor;
/**
*
@@ -695,6 +697,8 @@ public class TextToSpeech {
public static final String KEY_FEATURE_NETWORK_RETRIES_COUNT = "networkRetriesCount";
}
+ private static final boolean DEBUG = false;
+
private final Context mContext;
@UnsupportedAppUsage
private Connection mConnectingServiceConnection;
@@ -716,6 +720,9 @@ public class TextToSpeech {
private final Map<CharSequence, Uri> mUtterances;
private final Bundle mParams = new Bundle();
private final TtsEngines mEnginesHelper;
+ private final boolean mIsSystem;
+ @Nullable private final Executor mInitExecutor;
+
@UnsupportedAppUsage
private volatile String mCurrentEngine = null;
@@ -758,8 +765,21 @@ public class TextToSpeech {
*/
public TextToSpeech(Context context, OnInitListener listener, String engine,
String packageName, boolean useFallback) {
+ this(context, /* initExecutor= */ null, listener, engine, packageName,
+ useFallback, /* isSystem= */ true);
+ }
+
+ /**
+ * Used internally to instantiate TextToSpeech objects.
+ *
+ * @hide
+ */
+ private TextToSpeech(Context context, @Nullable Executor initExecutor,
+ OnInitListener initListener, String engine, String packageName, boolean useFallback,
+ boolean isSystem) {
mContext = context;
- mInitListener = listener;
+ mInitExecutor = initExecutor;
+ mInitListener = initListener;
mRequestedEngine = engine;
mUseFallback = useFallback;
@@ -768,6 +788,9 @@ public class TextToSpeech {
mUtteranceProgressListener = null;
mEnginesHelper = new TtsEngines(mContext);
+
+ mIsSystem = isSystem;
+
initTts();
}
@@ -842,10 +865,14 @@ public class TextToSpeech {
}
private boolean connectToEngine(String engine) {
- Connection connection = new Connection();
- Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
- intent.setPackage(engine);
- boolean bound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
+ Connection connection;
+ if (mIsSystem) {
+ connection = new SystemConnection();
+ } else {
+ connection = new DirectConnection();
+ }
+
+ boolean bound = connection.connect(engine);
if (!bound) {
Log.e(TAG, "Failed to bind to " + engine);
return false;
@@ -857,11 +884,19 @@ public class TextToSpeech {
}
private void dispatchOnInit(int result) {
- synchronized (mStartLock) {
- if (mInitListener != null) {
- mInitListener.onInit(result);
- mInitListener = null;
+ Runnable onInitCommand = () -> {
+ synchronized (mStartLock) {
+ if (mInitListener != null) {
+ mInitListener.onInit(result);
+ mInitListener = null;
+ }
}
+ };
+
+ if (mInitExecutor != null) {
+ mInitExecutor.execute(onInitCommand);
+ } else {
+ onInitCommand.run();
}
}
@@ -878,7 +913,7 @@ public class TextToSpeech {
// Special case, we are asked to shutdown connection that did finalize its connection.
synchronized (mStartLock) {
if (mConnectingServiceConnection != null) {
- mContext.unbindService(mConnectingServiceConnection);
+ mConnectingServiceConnection.disconnect();
mConnectingServiceConnection = null;
return;
}
@@ -2127,13 +2162,17 @@ public class TextToSpeech {
return mEnginesHelper.getEngines();
}
- private class Connection implements ServiceConnection {
+ private abstract class Connection implements ServiceConnection {
private ITextToSpeechService mService;
private SetupConnectionAsyncTask mOnSetupConnectionAsyncTask;
private boolean mEstablished;
+ abstract boolean connect(String engine);
+
+ abstract void disconnect();
+
private final ITextToSpeechCallback.Stub mCallback =
new ITextToSpeechCallback.Stub() {
public void onStop(String utteranceId, boolean isStarted)
@@ -2199,11 +2238,6 @@ public class TextToSpeech {
};
private class SetupConnectionAsyncTask extends AsyncTask<Void, Void, Integer> {
- private final ComponentName mName;
-
- public SetupConnectionAsyncTask(ComponentName name) {
- mName = name;
- }
@Override
protected Integer doInBackground(Void... params) {
@@ -2227,7 +2261,7 @@ public class TextToSpeech {
mParams.putString(Engine.KEY_PARAM_VOICE_NAME, defaultVoiceName);
}
- Log.i(TAG, "Set up connection to " + mName);
+ Log.i(TAG, "Setting up the connection to TTS engine...");
return SUCCESS;
} catch (RemoteException re) {
Log.e(TAG, "Error connecting to service, setCallback() failed");
@@ -2249,11 +2283,11 @@ public class TextToSpeech {
}
@Override
- public void onServiceConnected(ComponentName name, IBinder service) {
+ public void onServiceConnected(ComponentName componentName, IBinder service) {
synchronized(mStartLock) {
mConnectingServiceConnection = null;
- Log.i(TAG, "Connected to " + name);
+ Log.i(TAG, "Connected to TTS engine");
if (mOnSetupConnectionAsyncTask != null) {
mOnSetupConnectionAsyncTask.cancel(false);
@@ -2263,7 +2297,7 @@ public class TextToSpeech {
mServiceConnection = Connection.this;
mEstablished = false;
- mOnSetupConnectionAsyncTask = new SetupConnectionAsyncTask(name);
+ mOnSetupConnectionAsyncTask = new SetupConnectionAsyncTask();
mOnSetupConnectionAsyncTask.execute();
}
}
@@ -2277,7 +2311,7 @@ public class TextToSpeech {
*
* @return true if we cancel mOnSetupConnectionAsyncTask in progress.
*/
- private boolean clearServiceConnection() {
+ protected boolean clearServiceConnection() {
synchronized(mStartLock) {
boolean result = false;
if (mOnSetupConnectionAsyncTask != null) {
@@ -2295,8 +2329,8 @@ public class TextToSpeech {
}
@Override
- public void onServiceDisconnected(ComponentName name) {
- Log.i(TAG, "Asked to disconnect from " + name);
+ public void onServiceDisconnected(ComponentName componentName) {
+ Log.i(TAG, "Disconnected from TTS engine");
if (clearServiceConnection()) {
/* We need to protect against a rare case where engine
* dies just after successful connection - and we process onServiceDisconnected
@@ -2308,11 +2342,6 @@ public class TextToSpeech {
}
}
- public void disconnect() {
- mContext.unbindService(this);
- clearServiceConnection();
- }
-
public boolean isEstablished() {
return mService != null && mEstablished;
}
@@ -2342,6 +2371,91 @@ public class TextToSpeech {
}
}
+ // Currently all the clients are routed through the System connection. Direct connection
+ // is left for debugging, testing and benchmarking purposes.
+ // TODO(b/179599071): Remove direct connection once system one is fully tested.
+ private class DirectConnection extends Connection {
+ @Override
+ boolean connect(String engine) {
+ Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
+ intent.setPackage(engine);
+ return mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
+ }
+
+ @Override
+ void disconnect() {
+ mContext.unbindService(this);
+ clearServiceConnection();
+ }
+ }
+
+ private class SystemConnection extends Connection {
+
+ @Nullable
+ private volatile ITextToSpeechSession mSession;
+
+ @Override
+ boolean connect(String engine) {
+ IBinder binder = ServiceManager.getService(Context.TEXT_TO_SPEECH_MANAGER_SERVICE);
+
+ ITextToSpeechManager manager = ITextToSpeechManager.Stub.asInterface(binder);
+
+ if (manager == null) {
+ Log.e(TAG, "System service is not available!");
+ return false;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "Connecting to engine: " + engine);
+ }
+
+ try {
+ manager.createSession(engine, new ITextToSpeechSessionCallback.Stub() {
+ @Override
+ public void onConnected(ITextToSpeechSession session, IBinder serviceBinder) {
+ mSession = session;
+ onServiceConnected(
+ /* componentName= */ null,
+ serviceBinder);
+ }
+
+ @Override
+ public void onDisconnected() {
+ onServiceDisconnected(/* componentName= */ null);
+ }
+
+ @Override
+ public void onError(String errorInfo) {
+ Log.w(TAG, "System TTS connection error: " + errorInfo);
+ // The connection was not established successfully - handle as
+ // disconnection: clear the state and notify the user.
+ onServiceDisconnected(/* componentName= */ null);
+ }
+ });
+
+ return true;
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Error communicating with the System Server: ", ex);
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ void disconnect() {
+ ITextToSpeechSession session = mSession;
+
+ if (session != null) {
+ try {
+ session.disconnect();
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Error disconnecting session", ex);
+ }
+
+ clearServiceConnection();
+ }
+ }
+ }
+
private interface Action<R> {
R run(ITextToSpeechService service) throws RemoteException;
}
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 77f9c1a6fc90..e7ceada6180a 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -1840,13 +1840,15 @@ public class PhoneStateListener {
*
* @param allowedNetworkTypesList Map associating all allowed network type reasons
* ({@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER},
- * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER}, and
- * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER}) with reason's allowed
+ * {@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.
* 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_CARRIER, long type value},
+ * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, long type value}}
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
void onAllowedNetworkTypesChanged(
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 16b45c30b69f..7f45b384ca5f 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -475,9 +475,8 @@ public class DynamicLayout extends Layout {
mObjects.insertAt(0, dirs);
- final int baseLength = mBase.length();
- // Update from 0 characters to whatever the real text is
- reflow(mBase, 0, 0, baseLength);
+ // Update from 0 characters to whatever the displayed text is
+ reflow(mBase, 0, 0, mDisplay.length());
if (mBase instanceof Spannable) {
if (mWatcher == null)
@@ -485,6 +484,7 @@ public class DynamicLayout extends Layout {
// Strip out any watchers for other DynamicLayouts.
final Spannable sp = (Spannable) mBase;
+ final int baseLength = mBase.length();
final ChangeWatcher[] spans = sp.getSpans(0, baseLength, ChangeWatcher.class);
for (int i = 0; i < spans.length; i++) {
sp.removeSpan(spans[i]);
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 2de755857b70..aef185c77633 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -36,6 +36,7 @@ import java.io.File;
import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
@@ -155,6 +156,32 @@ public final class FontConfig implements Parcelable {
}
};
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ FontConfig that = (FontConfig) o;
+ return mLastModifiedTimeMillis == that.mLastModifiedTimeMillis
+ && mConfigVersion == that.mConfigVersion
+ && Objects.equals(mFamilies, that.mFamilies)
+ && Objects.equals(mAliases, that.mAliases);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFamilies, mAliases, mLastModifiedTimeMillis, mConfigVersion);
+ }
+
+ @Override
+ public String toString() {
+ return "FontConfig{"
+ + "mFamilies=" + mFamilies
+ + ", mAliases=" + mAliases
+ + ", mLastModifiedTimeMillis=" + mLastModifiedTimeMillis
+ + ", mConfigVersion=" + mConfigVersion
+ + '}';
+ }
+
/**
* Represents single font entry in system font configuration.
*
@@ -317,6 +344,37 @@ public final class FontConfig implements Parcelable {
public boolean isItalic() {
return getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Font font = (Font) o;
+ return mIndex == font.mIndex
+ && Objects.equals(mFile, font.mFile)
+ && Objects.equals(mOriginalFile, font.mOriginalFile)
+ && Objects.equals(mStyle, font.mStyle)
+ && Objects.equals(mFontVariationSettings, font.mFontVariationSettings)
+ && Objects.equals(mFontFamilyName, font.mFontFamilyName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFile, mOriginalFile, mStyle, mIndex, mFontVariationSettings,
+ mFontFamilyName);
+ }
+
+ @Override
+ public String toString() {
+ return "Font{"
+ + "mFile=" + mFile
+ + ", mOriginalFile=" + mOriginalFile
+ + ", mStyle=" + mStyle
+ + ", mIndex=" + mIndex
+ + ", mFontVariationSettings='" + mFontVariationSettings + '\''
+ + ", mFontFamilyName='" + mFontFamilyName + '\''
+ + '}';
+ }
}
/**
@@ -398,6 +456,30 @@ public final class FontConfig implements Parcelable {
return new Alias[size];
}
};
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Alias alias = (Alias) o;
+ return mWeight == alias.mWeight
+ && Objects.equals(mName, alias.mName)
+ && Objects.equals(mOriginal, alias.mOriginal);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mName, mOriginal, mWeight);
+ }
+
+ @Override
+ public String toString() {
+ return "Alias{"
+ + "mName='" + mName + '\''
+ + ", mOriginal='" + mOriginal + '\''
+ + ", mWeight=" + mWeight
+ + '}';
+ }
}
/**
@@ -413,7 +495,7 @@ public final class FontConfig implements Parcelable {
public static final class FontFamily implements Parcelable {
private final @NonNull List<Font> mFonts;
private final @Nullable String mName;
- private final @Nullable LocaleList mLocaleList;
+ private final @NonNull LocaleList mLocaleList;
private final @Variant int mVariant;
/** @hide */
@@ -454,7 +536,7 @@ public final class FontConfig implements Parcelable {
* @hide Only system server can create this instance and passed via IPC.
*/
public FontFamily(@NonNull List<Font> fonts, @Nullable String name,
- @Nullable LocaleList localeList, @Variant int variant) {
+ @NonNull LocaleList localeList, @Variant int variant) {
mFonts = fonts;
mName = name;
mLocaleList = localeList;
@@ -493,8 +575,6 @@ public final class FontConfig implements Parcelable {
*
* The locale list will be used for deciding which font family should be used in fallback
* list.
- *
- * @return non-null if a locale list is available. Otherwise null.
*/
public @NonNull LocaleList getLocaleList() {
return mLocaleList;
@@ -559,5 +639,31 @@ public final class FontConfig implements Parcelable {
public @NonNull String getLanguages() {
return mLocaleList.toLanguageTags();
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ FontFamily that = (FontFamily) o;
+ return mVariant == that.mVariant
+ && Objects.equals(mFonts, that.mFonts)
+ && Objects.equals(mName, that.mName)
+ && Objects.equals(mLocaleList, that.mLocaleList);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFonts, mName, mLocaleList, mVariant);
+ }
+
+ @Override
+ public String toString() {
+ return "FontFamily{"
+ + "mFonts=" + mFonts
+ + ", mName='" + mName + '\''
+ + ", mLocaleList=" + mLocaleList
+ + ", mVariant=" + mVariant
+ + '}';
+ }
}
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 85911fffc1bf..f99d4300a848 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -977,6 +977,9 @@ public class StaticLayout extends Layout {
calculateEllipsis(start, end, measured, widthStart,
ellipsisWidth, ellipsize, j,
textWidth, paint, forceEllipsis);
+ } else {
+ mLines[mColumns * j + ELLIPSIS_START] = 0;
+ mLines[mColumns * j + ELLIPSIS_COUNT] = 0;
}
}
diff --git a/core/java/android/tracing/ITracingServiceProxy.aidl b/core/java/android/tracing/ITracingServiceProxy.aidl
new file mode 100644
index 000000000000..4520db3915a2
--- /dev/null
+++ b/core/java/android/tracing/ITracingServiceProxy.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.tracing;
+
+/**
+ * Binder interface for the TracingServiceProxy running in system_server.
+ *
+ * {@hide}
+ */
+interface ITracingServiceProxy
+{
+ /**
+ * Notifies system tracing app that a tracing session has ended. If a session is repurposed
+ * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
+ * there is no buffer available to dump.
+ */
+ oneway void notifyTraceSessionEnded(boolean sessionStolen);
+}
diff --git a/core/java/android/tracing/OWNERS b/core/java/android/tracing/OWNERS
new file mode 100644
index 000000000000..f5de4eb05c54
--- /dev/null
+++ b/core/java/android/tracing/OWNERS
@@ -0,0 +1,2 @@
+cfijalkovich@google.com
+carmenjackson@google.com
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 790773fd83c5..b22921233f05 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -65,7 +65,7 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true");
DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
- DEFAULT_FLAGS.put("settings_silky_home", "true");
+ DEFAULT_FLAGS.put("settings_silky_home", "false");
DEFAULT_FLAGS.put("settings_contextual_home", "false");
DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
}
diff --git a/core/java/android/uwb/OWNERS b/core/java/android/uwb/OWNERS
index ea41c3984dfd..17936ae7bb73 100644
--- a/core/java/android/uwb/OWNERS
+++ b/core/java/android/uwb/OWNERS
@@ -1,5 +1,6 @@
bstack@google.com
eliptus@google.com
jsolnit@google.com
+matbev@google.com
siyuanh@google.com
zachoverflow@google.com
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index 5ac95d49c1bb..c0d818774ba0 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -94,7 +94,8 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub {
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG,
- "onRangingOpened - received unexpected SessionHandle: " + sessionHandle);
+ "onRangingOpenedFailed - received unexpected SessionHandle: "
+ + sessionHandle);
return;
}
@@ -124,7 +125,7 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub {
@RangingChangeReason int reason, PersistableBundle params) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
- Log.w(TAG, "onRangingStartFailed - received unexpected SessionHandle: "
+ Log.w(TAG, "onRangingReconfigureFailed - received unexpected SessionHandle: "
+ sessionHandle);
return;
}
diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java
index bfa8bf21ec6a..52ec5bde0dac 100644
--- a/core/java/android/uwb/RangingSession.java
+++ b/core/java/android/uwb/RangingSession.java
@@ -16,8 +16,10 @@
package android.uwb;
+import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.os.Binder;
import android.os.PersistableBundle;
@@ -247,6 +249,7 @@ public final class RangingSession implements AutoCloseable {
*
* @param params configuration parameters for starting the session
*/
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public void start(@NonNull PersistableBundle params) {
if (mState != State.IDLE) {
throw new IllegalStateException();
@@ -271,6 +274,7 @@ public final class RangingSession implements AutoCloseable {
*
* @param params the parameters to reconfigure and their new values
*/
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public void reconfigure(@NonNull PersistableBundle params) {
if (mState != State.ACTIVE && mState != State.IDLE) {
throw new IllegalStateException();
@@ -302,6 +306,7 @@ public final class RangingSession implements AutoCloseable {
* <p>On failure to stop the session,
* {@link RangingSession.Callback#onStopFailed(int, PersistableBundle)} is invoked.
*/
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public void stop() {
if (mState != State.ACTIVE) {
throw new IllegalStateException();
@@ -333,6 +338,7 @@ public final class RangingSession implements AutoCloseable {
* {@link #close()}, even if the {@link RangingSession} is already closed.
*/
@Override
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public void close() {
if (mState == State.CLOSED) {
mExecutor.execute(() -> mCallback.onClosed(
diff --git a/core/java/android/uwb/SessionHandle.java b/core/java/android/uwb/SessionHandle.java
index 928fcbdcf1c7..b23f5ad603ff 100644
--- a/core/java/android/uwb/SessionHandle.java
+++ b/core/java/android/uwb/SessionHandle.java
@@ -19,6 +19,8 @@ package android.uwb;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* @hide
*/
@@ -73,6 +75,11 @@ public final class SessionHandle implements Parcelable {
}
@Override
+ public int hashCode() {
+ return Objects.hashCode(mId);
+ }
+
+ @Override
public String toString() {
return "SessionHandle [id=" + mId + "]";
}
diff --git a/core/java/android/uwb/TEST_MAPPING b/core/java/android/uwb/TEST_MAPPING
index 9e50bd64d089..08ed2c7b71d9 100644
--- a/core/java/android/uwb/TEST_MAPPING
+++ b/core/java/android/uwb/TEST_MAPPING
@@ -2,6 +2,9 @@
"presubmit": [
{
"name": "UwbManagerTests"
+ },
+ {
+ "name": "CtsUwbTestCases"
}
]
-} \ No newline at end of file
+}
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
index 8adfe0601b14..2dc0ba0b9b80 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -16,9 +16,11 @@
package android.uwb;
+import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -154,6 +156,7 @@ public final class UwbManager {
* @param executor an {@link Executor} to execute given callback
* @param callback user implementation of the {@link AdapterStateCallback}
*/
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public void registerAdapterStateCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull AdapterStateCallback callback) {
mAdapterStateListener.register(executor, callback);
@@ -168,6 +171,7 @@ public final class UwbManager {
*
* @param callback user implementation of the {@link AdapterStateCallback}
*/
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public void unregisterAdapterStateCallback(@NonNull AdapterStateCallback callback) {
mAdapterStateListener.unregister(callback);
}
@@ -181,6 +185,7 @@ public final class UwbManager {
* @return {@link PersistableBundle} of the device's supported UWB protocols and parameters
*/
@NonNull
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public PersistableBundle getSpecificationInfo() {
try {
return mUwbAdapter.getSpecificationInfo();
@@ -194,6 +199,7 @@ public final class UwbManager {
*
* @return true if ranging is supported
*/
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public boolean isRangingSupported() {
try {
return mUwbAdapter.isRangingSupported();
@@ -250,6 +256,7 @@ public final class UwbManager {
* @return angle of arrival type supported
*/
@AngleOfArrivalSupportType
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public int getAngleOfArrivalSupport() {
try {
switch (mUwbAdapter.getAngleOfArrivalSupport()) {
@@ -281,6 +288,7 @@ public final class UwbManager {
* @return {@link List} of supported channel numbers ordered by preference
*/
@NonNull
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public List<Integer> getSupportedChannelNumbers() {
List<Integer> channels = new ArrayList<>();
try {
@@ -300,6 +308,7 @@ public final class UwbManager {
* @return {@link List} of supported preamble code indices
*/
@NonNull
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public Set<Integer> getSupportedPreambleCodeIndices() {
Set<Integer> preambles = new HashSet<>();
try {
@@ -320,6 +329,7 @@ public final class UwbManager {
* @return the timestamp resolution in nanoseconds
*/
@SuppressLint("MethodNameUnits")
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public long elapsedRealtimeResolutionNanos() {
try {
return mUwbAdapter.getTimestampResolutionNanos();
@@ -333,6 +343,7 @@ public final class UwbManager {
*
* @return the maximum allowed number of simultaneously open {@link RangingSession} instances.
*/
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public int getMaxSimultaneousSessions() {
try {
return mUwbAdapter.getMaxSimultaneousSessions();
@@ -347,6 +358,7 @@ public final class UwbManager {
*
* @return the maximum number of remote devices per {@link RangingSession}
*/
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public int getMaxRemoteDevicesPerInitiatorSession() {
try {
return mUwbAdapter.getMaxRemoteDevicesPerInitiatorSession();
@@ -361,6 +373,7 @@ public final class UwbManager {
*
* @return the maximum number of remote devices per {@link RangingSession}
*/
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public int getMaxRemoteDevicesPerResponderSession() {
try {
return mUwbAdapter.getMaxRemoteDevicesPerResponderSession();
@@ -396,6 +409,7 @@ public final class UwbManager {
* {@link RangingSession.Callback#onOpened(RangingSession)}.
*/
@NonNull
+ @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
public AutoCloseable openRangingSession(@NonNull PersistableBundle parameters,
@NonNull @CallbackExecutor Executor executor,
@NonNull RangingSession.Callback callbacks) {
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 5d4a4e52975a..e6cd25275ca2 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -58,11 +58,11 @@ public abstract class DisplayEventReceiver {
public static final int VSYNC_SOURCE_SURFACE_FLINGER = 1;
/**
- * Specifies to generate config changed events from Surface Flinger.
+ * Specifies to generate mode changed events from Surface Flinger.
* <p>
* Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h
*/
- public static final int EVENT_REGISTRATION_CONFIG_CHANGED_FLAG = 0x1;
+ public static final int EVENT_REGISTRATION_MODE_CHANGED_FLAG = 0x1;
/**
* Specifies to generate frame rate override events from Surface Flinger.
@@ -197,14 +197,14 @@ public abstract class DisplayEventReceiver {
}
/**
- * Called when a display config changed event is received.
+ * Called when a display mode changed event is received.
*
* @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
* timebase.
* @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
- * @param configId The new config Id
+ * @param modeId The new mode Id
*/
- public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+ public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
}
/**
@@ -273,8 +273,8 @@ public abstract class DisplayEventReceiver {
// Called from native code.
@SuppressWarnings("unused")
- private void dispatchConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
- onConfigChanged(timestampNanos, physicalDisplayId, configId);
+ private void dispatchModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
+ onModeChanged(timestampNanos, physicalDisplayId, modeId);
}
// Called from native code.
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index 5937499744b8..6543de15f969 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -154,6 +154,24 @@ public final class FrameMetrics {
*/
public static final int VSYNC_TIMESTAMP = 11;
+ /**
+ * Metric identifier for GPU duration.
+ * <p>
+ * Represents the total time in nanoseconds this frame took to complete on the GPU.
+ * </p>
+ **/
+ public static final int GPU_DURATION = 12;
+
+ /**
+ * Metric identifier for the total duration that was available to the app to produce a frame.
+ * <p>
+ * Represents the total time in nanoseconds the system allocated for the app to produce its
+ * frame. If FrameMetrics.TOTAL_DURATION < FrameMetrics.DEADLINE, the app hit its intended
+ * deadline and there was no jank visible to the user.
+ * </p>
+ **/
+ public static final int DEADLINE = 13;
+
private static final int FRAME_INFO_FLAG_FIRST_DRAW = 1 << 0;
/**
@@ -175,6 +193,8 @@ public final class FrameMetrics {
FIRST_DRAW_FRAME,
INTENDED_VSYNC_TIMESTAMP,
VSYNC_TIMESTAMP,
+ GPU_DURATION,
+ DEADLINE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Metric {}
@@ -205,6 +225,8 @@ public final class FrameMetrics {
Index.ISSUE_DRAW_COMMANDS_START,
Index.SWAP_BUFFERS,
Index.FRAME_COMPLETED,
+ Index.GPU_COMPLETED,
+ Index.SWAP_BUFFERS_COMPLETED
})
@Retention(RetentionPolicy.SOURCE)
public @interface Index {
@@ -224,8 +246,10 @@ public final class FrameMetrics {
int ISSUE_DRAW_COMMANDS_START = 13;
int SWAP_BUFFERS = 14;
int FRAME_COMPLETED = 15;
+ int GPU_COMPLETED = 18;
+ int SWAP_BUFFERS_COMPLETED = 19;
- int FRAME_STATS_COUNT = 19; // must always be last and in sync with
+ int FRAME_STATS_COUNT = 20; // must always be last and in sync with
// FrameInfoIndex::NumIndexes in libs/hwui/FrameInfo.h
}
@@ -251,9 +275,19 @@ public final class FrameMetrics {
// COMMAND_ISSUE
Index.ISSUE_DRAW_COMMANDS_START, Index.SWAP_BUFFERS,
// SWAP_BUFFERS
- Index.SWAP_BUFFERS, Index.FRAME_COMPLETED,
+ Index.SWAP_BUFFERS, Index.SWAP_BUFFERS_COMPLETED,
// TOTAL_DURATION
Index.INTENDED_VSYNC, Index.FRAME_COMPLETED,
+ // RESERVED for FIRST_DRAW_FRAME
+ 0, 0,
+ // RESERVED forINTENDED_VSYNC_TIMESTAMP
+ 0, 0,
+ // RESERVED VSYNC_TIMESTAMP
+ 0, 0,
+ // GPU_DURATION
+ Index.SWAP_BUFFERS, Index.GPU_COMPLETED,
+ // DEADLINE
+ Index.INTENDED_VSYNC, Index.FRAME_DEADLINE,
};
/**
@@ -294,7 +328,7 @@ public final class FrameMetrics {
* @return the value of the metric or -1 if it is not available.
*/
public long getMetric(@Metric int id) {
- if (id < UNKNOWN_DELAY_DURATION || id > VSYNC_TIMESTAMP) {
+ if (id < UNKNOWN_DELAY_DURATION || id > DEADLINE) {
return -1;
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 62f4b864c7a8..4e4ba3fad246 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -32,7 +32,6 @@ import android.graphics.Region;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.ParcelFileDescriptor;
-import android.service.screenshot.ScreenshotHash;
import android.view.DisplayCutout;
import android.view.IApplicationToken;
import android.view.IAppTransitionAnimationSpecsFuture;
@@ -63,6 +62,8 @@ import android.view.AppTransitionAnimationSpec;
import android.view.WindowContentFrameStats;
import android.view.WindowManager;
import android.view.SurfaceControl;
+import android.view.displayhash.DisplayHash;
+import android.view.displayhash.VerifiedDisplayHash;
/**
* System private interface to the window manager.
@@ -757,23 +758,23 @@ interface IWindowManager
void holdLock(in IBinder token, in int durationMs);
/**
- * Gets an array of support hashing algorithms that can be used to generate the hash of the
- * screenshot. The String value of one algorithm should be used when requesting to generate
- * the ScreenshotHash.
+ * Gets an array of support hash algorithms that can be used to generate a DisplayHash. The
+ * String value of one algorithm should be used when requesting to generate
+ * the DisplayHash.
*
- * @return a String array of supported hashing algorithms.
+ * @return a String array of supported hash algorithms.
*/
- String[] getSupportedScreenshotHashingAlgorithms();
+ String[] getSupportedDisplayHashAlgorithms();
/**
- * Validate the ScreenshotHash was generated by the system. The ScreenshotHash passed in
- * should be the token generated when calling {@link IWindowSession#generateScreenshotHash}
+ * Validate the DisplayHash was generated by the system. The DisplayHash passed in should be
+ * the object generated when calling {@link IWindowSession#generateDisplayHash}
*
- * @param ScreenshotHash The token to verify that it was generated by the system.
- * @return true if the token was generated by the system or false if the token cannot be
- * verified.
+ * @param DisplayHash The hash to verify that it was generated by the system.
+ * @return a {@link VerifiedDisplayHash} if the hash was generated by the system or null
+ * if the token cannot be verified.
*/
- boolean verifyScreenshotHash(in ScreenshotHash screenshotHash);
+ VerifiedDisplayHash verifyDisplayHash(in DisplayHash displayHash);
/**
* Registers a listener for a {@link android.app.WindowContext} to handle configuration changes
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 990b7bdfa987..ce649cc7c2b2 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -22,7 +22,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
-import android.service.screenshot.ScreenshotHash;
+import android.os.RemoteCallback;
import android.util.MergedConfiguration;
import android.view.DisplayCutout;
import android.view.InputChannel;
@@ -123,14 +123,6 @@ interface IWindowSession {
boolean outOfMemory(IWindow window);
/**
- * Give the window manager a hint of the part of the window that is
- * completely transparent, allowing it to work with the surface flinger
- * to optimize compositing of this part of the window.
- */
- @UnsupportedAppUsage
- oneway void setTransparentRegion(IWindow window, in Region region);
-
- /**
* Tell the window manager about the content and visible insets of the
* given window, which can be used to adjust the <var>outContentInsets</var>
* and <var>outVisibleInsets</var> values returned by
@@ -345,14 +337,15 @@ interface IWindowSession {
void grantEmbeddedWindowFocus(IWindow window, in IBinder inputToken, boolean grantFocus);
/**
- * Generates an ScreenshotHash that can be used to validate whether specific content was on
+ * Generates an DisplayHash that can be used to validate whether specific content was on
* screen.
*
- * @param window The token for the window where the view to screenshot is shown.
- * @param boundsInWindow The size and position of the ads view in the window
- * @param hashAlgorithm The String for the hashing algorithm to use based on values returned
- * from {@link IWindowManager#getSupportedHashingAlgorithms()}
+ * @param window The token for the window to generate the hash of.
+ * @param boundsInWindow The size and position in the window of where to generate the hash.
+ * @param hashAlgorithm The String for the hash algorithm to use based on values returned
+ * from {@link IWindowManager#getSupportedDisplayHashAlgorithms()}
+ * @param callback The callback invoked to get the results of generateDisplayHash
*/
- ScreenshotHash generateScreenshotHash(IWindow window, in Rect boundsInWindow,
- in String hashAlgorithm);
+ oneway void generateDisplayHash(IWindow window, in Rect boundsInWindow,
+ in String hashAlgorithm, in RemoteCallback callback);
}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 2f40bdbff05f..5f2bccc8b857 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -140,7 +140,11 @@ public class InsetsSource implements Parcelable {
if (getType() == ITYPE_CAPTION_BAR) {
return Insets.of(0, frame.height(), 0, 0);
}
- if (!getIntersection(frame, relativeFrame, mTmpFrame)) {
+ // Checks for whether there is shared edge with insets for 0-width/height window.
+ final boolean hasIntersection = relativeFrame.isEmpty()
+ ? getIntersection(frame, relativeFrame, mTmpFrame)
+ : mTmpFrame.setIntersect(frame, relativeFrame);
+ if (!hasIntersection) {
return Insets.NONE;
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 52b7cffbc340..d67439cc9de2 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -3768,6 +3768,41 @@ public final class MotionEvent extends InputEvent implements Parcelable {
return (getButtonState() & button) == button;
}
+ /**
+ * Gets a rotation matrix that (when applied to a motionevent) will rotate that motion event
+ * such that the result coordinates end up in the same physical location on a display whose
+ * coordinates are rotated by `rotation`.
+ *
+ * For example, rotating 0,0 by 90 degrees will move a point from the physical top-left to
+ * the bottom-left of the 90-degree-rotated display.
+ *
+ * @hide
+ */
+ public static Matrix createRotateMatrix(
+ @Surface.Rotation int rotation, int displayW, int displayH) {
+ if (rotation == Surface.ROTATION_0) {
+ return new Matrix(Matrix.IDENTITY_MATRIX);
+ }
+ // values is row-major
+ float[] values = null;
+ if (rotation == Surface.ROTATION_90) {
+ values = new float[]{0, 1, 0,
+ -1, 0, displayH,
+ 0, 0, 1};
+ } else if (rotation == Surface.ROTATION_180) {
+ values = new float[]{-1, 0, displayW,
+ 0, -1, displayH,
+ 0, 0, 1};
+ } else if (rotation == Surface.ROTATION_270) {
+ values = new float[]{0, -1, displayW,
+ 1, 0, 0,
+ 0, 0, 1};
+ }
+ Matrix toOrient = new Matrix();
+ toOrient.setValues(values);
+ return toOrient;
+ }
+
public static final @android.annotation.NonNull Parcelable.Creator<MotionEvent> CREATOR
= new Parcelable.Creator<MotionEvent>() {
public MotionEvent createFromParcel(Parcel in) {
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index fbb741c64dc7..65b8afa4e27f 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -952,7 +952,9 @@ public class Surface implements Parcelable {
* seamless transition is one that doesn't have any visual interruptions, such as a black
* screen for a second or two. True indicates that any frame rate changes caused by this
* request should be seamless. False indicates that non-seamless refresh rates are also
- * acceptable.
+ * acceptable. Non-seamless switches might be used when the benefit of matching the content's
+ * frame rate outweighs the cost of the transition, for example when displaying
+ * long-running video content.
*
* @throws IllegalArgumentException If frameRate or compatibility are invalid.
*/
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 067c31c5ea81..bb98e000bfd5 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -161,8 +161,8 @@ public final class SurfaceControl implements Parcelable {
int L, int T, int R, int B);
private static native void nativeSetDisplaySize(long transactionObj, IBinder displayToken,
int width, int height);
- private static native SurfaceControl.DisplayInfo nativeGetDisplayInfo(IBinder displayToken);
- private static native SurfaceControl.DisplayConfig[] nativeGetDisplayConfigs(
+ private static native DisplayInfo nativeGetDisplayInfo(IBinder displayToken);
+ private static native DisplayMode[] nativeGetDisplayModes(
IBinder displayToken);
private static native DisplayedContentSamplingAttributes
nativeGetDisplayedContentSamplingAttributes(IBinder displayToken);
@@ -170,13 +170,13 @@ public final class SurfaceControl implements Parcelable {
boolean enable, int componentMask, int maxFrames);
private static native DisplayedContentSample nativeGetDisplayedContentSample(
IBinder displayToken, long numFrames, long timestamp);
- private static native int nativeGetActiveConfig(IBinder displayToken);
- private static native boolean nativeSetDesiredDisplayConfigSpecs(IBinder displayToken,
- SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs);
- private static native SurfaceControl.DesiredDisplayConfigSpecs
- nativeGetDesiredDisplayConfigSpecs(IBinder displayToken);
+ private static native int nativeGetActiveDisplayMode(IBinder displayToken);
+ private static native boolean nativeSetDesiredDisplayModeSpecs(IBinder displayToken,
+ DesiredDisplayModeSpecs desiredDisplayModeSpecs);
+ private static native DesiredDisplayModeSpecs
+ nativeGetDesiredDisplayModeSpecs(IBinder displayToken);
private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
- private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries(
+ private static native DisplayPrimaries nativeGetDisplayNativePrimaries(
IBinder displayToken);
private static native int[] nativeGetCompositionDataspaces();
private static native int nativeGetActiveColorMode(IBinder displayToken);
@@ -1746,11 +1746,11 @@ public final class SurfaceControl implements Parcelable {
*
* @hide
*/
- public static final class DisplayConfig {
+ public static final class DisplayMode {
/**
* Invalid display config id.
*/
- public static final int INVALID_DISPLAY_CONFIG_ID = -1;
+ public static final int INVALID_DISPLAY_MODE_ID = -1;
public int width;
public int height;
@@ -1767,7 +1767,7 @@ public final class SurfaceControl implements Parcelable {
* configs within the same group can be done seamlessly in most cases.
* @see: android.hardware.graphics.composer@2.4::IComposerClient::Attribute::CONFIG_GROUP
*/
- public int configGroup;
+ public int group;
@Override
public String toString() {
@@ -1778,7 +1778,7 @@ public final class SurfaceControl implements Parcelable {
+ ", refreshRate=" + refreshRate
+ ", appVsyncOffsetNanos=" + appVsyncOffsetNanos
+ ", presentationDeadlineNanos=" + presentationDeadlineNanos
- + ", configGroup=" + configGroup + "}";
+ + ", group=" + group + "}";
}
}
@@ -1805,21 +1805,21 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
- public static SurfaceControl.DisplayConfig[] getDisplayConfigs(IBinder displayToken) {
+ public static DisplayMode[] getDisplayModes(IBinder displayToken) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- return nativeGetDisplayConfigs(displayToken);
+ return nativeGetDisplayModes(displayToken);
}
/**
* @hide
*/
- public static int getActiveConfig(IBinder displayToken) {
+ public static int getActiveDisplayMode(IBinder displayToken) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- return nativeGetActiveConfig(displayToken);
+ return nativeGetActiveDisplayMode(displayToken);
}
/**
@@ -1866,8 +1866,8 @@ public final class SurfaceControl implements Parcelable {
*
* @hide
*/
- public static final class DesiredDisplayConfigSpecs {
- public int defaultConfig;
+ public static final class DesiredDisplayModeSpecs {
+ public int defaultMode;
/**
* The primary refresh rate range represents display manager's general guidance on the
* display configs surface flinger will consider when switching refresh rates. Unless
@@ -1892,16 +1892,16 @@ public final class SurfaceControl implements Parcelable {
*/
public boolean allowGroupSwitching;
- public DesiredDisplayConfigSpecs() {}
+ public DesiredDisplayModeSpecs() {}
- public DesiredDisplayConfigSpecs(DesiredDisplayConfigSpecs other) {
+ public DesiredDisplayModeSpecs(DesiredDisplayModeSpecs other) {
copyFrom(other);
}
- public DesiredDisplayConfigSpecs(int defaultConfig, boolean allowGroupSwitching,
+ public DesiredDisplayModeSpecs(int defaultMode, boolean allowGroupSwitching,
float primaryRefreshRateMin, float primaryRefreshRateMax,
float appRequestRefreshRateMin, float appRequestRefreshRateMax) {
- this.defaultConfig = defaultConfig;
+ this.defaultMode = defaultMode;
this.allowGroupSwitching = allowGroupSwitching;
this.primaryRefreshRateMin = primaryRefreshRateMin;
this.primaryRefreshRateMax = primaryRefreshRateMax;
@@ -1911,14 +1911,14 @@ public final class SurfaceControl implements Parcelable {
@Override
public boolean equals(@Nullable Object o) {
- return o instanceof DesiredDisplayConfigSpecs && equals((DesiredDisplayConfigSpecs) o);
+ return o instanceof DesiredDisplayModeSpecs && equals((DesiredDisplayModeSpecs) o);
}
/**
* Tests for equality.
*/
- public boolean equals(DesiredDisplayConfigSpecs other) {
- return other != null && defaultConfig == other.defaultConfig
+ public boolean equals(DesiredDisplayModeSpecs other) {
+ return other != null && defaultMode == other.defaultMode
&& primaryRefreshRateMin == other.primaryRefreshRateMin
&& primaryRefreshRateMax == other.primaryRefreshRateMax
&& appRequestRefreshRateMin == other.appRequestRefreshRateMin
@@ -1933,8 +1933,8 @@ public final class SurfaceControl implements Parcelable {
/**
* Copies the supplied object's values to this object.
*/
- public void copyFrom(DesiredDisplayConfigSpecs other) {
- defaultConfig = other.defaultConfig;
+ public void copyFrom(DesiredDisplayModeSpecs other) {
+ defaultMode = other.defaultMode;
primaryRefreshRateMin = other.primaryRefreshRateMin;
primaryRefreshRateMax = other.primaryRefreshRateMax;
appRequestRefreshRateMin = other.appRequestRefreshRateMin;
@@ -1945,7 +1945,7 @@ public final class SurfaceControl implements Parcelable {
public String toString() {
return String.format("defaultConfig=%d primaryRefreshRateRange=[%.0f %.0f]"
+ " appRequestRefreshRateRange=[%.0f %.0f]",
- defaultConfig, primaryRefreshRateMin, primaryRefreshRateMax,
+ defaultMode, primaryRefreshRateMin, primaryRefreshRateMax,
appRequestRefreshRateMin, appRequestRefreshRateMax);
}
}
@@ -1953,25 +1953,31 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
- public static boolean setDesiredDisplayConfigSpecs(IBinder displayToken,
- SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs) {
+ public static boolean setDesiredDisplayModeSpecs(IBinder displayToken,
+ DesiredDisplayModeSpecs desiredDisplayModeSpecs) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
+ if (desiredDisplayModeSpecs == null) {
+ throw new IllegalArgumentException("desiredDisplayModeSpecs must not be null");
+ }
+ if (desiredDisplayModeSpecs.defaultMode < 0) {
+ throw new IllegalArgumentException("defaultMode must be non-negative");
+ }
- return nativeSetDesiredDisplayConfigSpecs(displayToken, desiredDisplayConfigSpecs);
+ return nativeSetDesiredDisplayModeSpecs(displayToken, desiredDisplayModeSpecs);
}
/**
* @hide
*/
- public static SurfaceControl.DesiredDisplayConfigSpecs getDesiredDisplayConfigSpecs(
+ public static DesiredDisplayModeSpecs getDesiredDisplayModeSpecs(
IBinder displayToken) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- return nativeGetDesiredDisplayConfigSpecs(displayToken);
+ return nativeGetDesiredDisplayModeSpecs(displayToken);
}
/**
@@ -2042,7 +2048,7 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
- public static SurfaceControl.DisplayPrimaries getDisplayNativePrimaries(
+ public static DisplayPrimaries getDisplayNativePrimaries(
IBinder displayToken) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
@@ -2401,7 +2407,7 @@ public final class SurfaceControl implements Parcelable {
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.");
+ + " or -1 to turn the backlight off: " + brightness);
}
return nativeSetDisplayBrightness(displayToken, brightness);
}
@@ -3244,7 +3250,10 @@ public final class SurfaceControl implements Parcelable {
* interruptions, such as a black screen for a second or two. True
* indicates that any frame rate changes caused by this request
* should be seamless. False indicates that non-seamless refresh
- * rates are also acceptable.
+ * rates are also acceptable. Non-seamless switches might be
+ * used when the benefit of matching the content's frame rate
+ * outweighs the cost of the transition, for example when
+ * displaying long-running video content.
* @return This transaction object.
*/
@NonNull
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index f01964822897..18029af6a85e 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -162,7 +162,6 @@ public class SurfaceControlViewHost {
@NonNull WindowlessWindowManager wwm, boolean useSfChoreographer) {
mWm = wwm;
mViewRoot = new ViewRootImpl(c, d, mWm, useSfChoreographer);
- mViewRoot.forceDisableBLAST();
mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
}
@@ -188,7 +187,6 @@ public class SurfaceControlViewHost {
mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
mSurfaceControl, hostToken);
mViewRoot = new ViewRootImpl(context, display, mWm);
- mViewRoot.forceDisableBLAST();
mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
}
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
index cbc0479a4c07..20f05981e962 100644
--- a/core/java/android/view/SurfaceSession.java
+++ b/core/java/android/view/SurfaceSession.java
@@ -32,7 +32,6 @@ public final class SurfaceSession {
private static native long nativeCreate();
private static native void nativeDestroy(long ptr);
- private static native void nativeKill(long ptr);
/** Create a new connection with the surface flinger. */
@UnsupportedAppUsage
@@ -44,22 +43,22 @@ public final class SurfaceSession {
@Override
protected void finalize() throws Throwable {
try {
- if (mNativeClient != 0) {
- nativeDestroy(mNativeClient);
- }
+ kill();
} finally {
super.finalize();
}
}
/**
- * Forcibly detach native resources associated with this object.
- * Unlike destroy(), after this call any surfaces that were created
- * from the session will no longer work.
+ * Remove the reference to the native Session object. The native object may still exist if
+ * there are other references to it, but it cannot be accessed from this Java object anymore.
*/
@UnsupportedAppUsage
public void kill() {
- nativeKill(mNativeClient);
+ if (mNativeClient != 0) {
+ nativeDestroy(mNativeClient);
+ mNativeClient = 0;
+ }
}
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 392ed039d3d7..946cd3b9df6f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1103,7 +1103,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (creating) {
updateOpaqueFlag();
- mDeferredDestroySurfaceControl = createSurfaceControls(viewRoot);
+ final String name = "SurfaceView[" + viewRoot.getTitle().toString() + "]";
+ if (mUseBlastAdapter) {
+ createBlastSurfaceControls(viewRoot, name);
+ } else {
+ mDeferredDestroySurfaceControl = createSurfaceControls(viewRoot, name);
+ }
} else if (mSurfaceControl == null) {
return;
}
@@ -1240,53 +1245,77 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
* out, the old surface can be persevered until the new one has drawn by keeping the reference
* of the old SurfaceControl alive.
*/
- private SurfaceControl createSurfaceControls(ViewRootImpl viewRoot) {
- final String name = "SurfaceView[" + viewRoot.getTitle().toString() + "]";
-
- SurfaceControl.Builder builder = new SurfaceControl.Builder(mSurfaceSession)
+ private SurfaceControl createSurfaceControls(ViewRootImpl viewRoot, String name) {
+ final SurfaceControl previousSurfaceControl = mSurfaceControl;
+ mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
.setName(name)
.setLocalOwnerView(this)
.setParent(viewRoot.getBoundsLayer())
- .setCallsite("SurfaceView.updateSurface");
+ .setCallsite("SurfaceView.updateSurface")
+ .setBufferSize(mSurfaceWidth, mSurfaceHeight)
+ .setFlags(mSurfaceFlags)
+ .setFormat(mFormat)
+ .build();
+ mBackgroundControl = createBackgroundControl(name);
+ return previousSurfaceControl;
+ }
- final SurfaceControl previousSurfaceControl;
- if (mUseBlastAdapter) {
- mSurfaceControl = builder
+ private SurfaceControl createBackgroundControl(String name) {
+ return new SurfaceControl.Builder(mSurfaceSession)
+ .setName("Background for " + name)
+ .setLocalOwnerView(this)
+ .setOpaque(true)
+ .setColorLayer()
+ .setParent(mSurfaceControl)
+ .setCallsite("SurfaceView.updateSurface")
+ .build();
+ }
+
+ // We don't recreate the surface controls but only recreate the adapter. Since the blast layer
+ // is still alive, the old buffers will continue to be presented until replaced by buffers from
+ // the new adapter. This means we do not need to track the old surface control and destroy it
+ // after the client has drawn to avoid any flickers.
+ private void createBlastSurfaceControls(ViewRootImpl viewRoot, String name) {
+ if (mSurfaceControl == null) {
+ mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+ .setName(name)
+ .setLocalOwnerView(this)
+ .setParent(viewRoot.getBoundsLayer())
+ .setCallsite("SurfaceView.updateSurface")
.setContainerLayer()
.build();
- previousSurfaceControl = mBlastSurfaceControl;
+ }
+
+ if (mBlastSurfaceControl == null) {
mBlastSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
.setName(name + "(BLAST)")
.setLocalOwnerView(this)
- .setBufferSize(mSurfaceWidth, mSurfaceHeight)
.setParent(mSurfaceControl)
.setFlags(mSurfaceFlags)
.setHidden(false)
.setBLASTLayer()
.setCallsite("SurfaceView.updateSurface")
.build();
- mBlastBufferQueue = new BLASTBufferQueue(name, mBlastSurfaceControl, mSurfaceWidth,
- mSurfaceHeight, mFormat, true /* TODO */);
} else {
- previousSurfaceControl = mSurfaceControl;
- mSurfaceControl = builder
- .setBufferSize(mSurfaceWidth, mSurfaceHeight)
- .setFlags(mSurfaceFlags)
- .setFormat(mFormat)
- .build();
- mBlastSurfaceControl = null;
- mBlastBufferQueue = null;
+ // update blast layer
+ mTmpTransaction
+ .setOpaque(mBlastSurfaceControl, (mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
+ .setSecure(mBlastSurfaceControl, (mSurfaceFlags & SurfaceControl.SECURE) != 0)
+ .show(mBlastSurfaceControl)
+ .apply();
}
- mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
- .setName("Background for " + name)
- .setLocalOwnerView(this)
- .setOpaque(true)
- .setColorLayer()
- .setParent(mSurfaceControl)
- .setCallsite("SurfaceView.updateSurface")
- .build();
- return previousSurfaceControl;
+ if (mBackgroundControl == null) {
+ mBackgroundControl = createBackgroundControl(name);
+ }
+
+ // Always recreate the IGBP for compatibility. This can be optimized in the future but
+ // the behavior change will need to be gated by SDK version.
+ if (mBlastBufferQueue != null) {
+ mBlastBufferQueue.destroy();
+ }
+ mBlastBufferQueue = new BLASTBufferQueue(name, mBlastSurfaceControl, mSurfaceWidth,
+ mSurfaceHeight, mFormat, true /* TODO */);
}
private void onDrawFinished() {
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index b10370aa5d4c..acbcbfad1a75 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -85,12 +85,13 @@ public class SyncRtSurfaceTransactionApplier {
for (int i = params.length - 1; i >= 0; i--) {
SurfaceParams surfaceParams = params[i];
SurfaceControl surface = surfaceParams.surface;
- if (frame > 0) {
- t.deferTransactionUntil(surface, mTargetSc, frame);
- }
applyParams(t, surfaceParams, mTmpFloat9);
}
- t.apply();
+ if (mTargetViewRootImpl != null) {
+ mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
+ } else {
+ t.apply();
+ }
}
public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3bf34f4cdbf5..feb8c4bf6dc5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -18,6 +18,12 @@ package android.view;
import static android.content.res.Resources.ID_NULL;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
+import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
+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;
+import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS;
@@ -89,6 +95,7 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
@@ -135,6 +142,9 @@ import android.view.autofill.AutofillValue;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureSession;
+import android.view.displayhash.DisplayHash;
+import android.view.displayhash.DisplayHashManager;
+import android.view.displayhash.DisplayHashResultCallback;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inspector.InspectableProperty;
@@ -176,6 +186,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
@@ -740,6 +751,7 @@ import java.util.function.Predicate;
* @attr ref android.R.styleable#View_alpha
* @attr ref android.R.styleable#View_background
* @attr ref android.R.styleable#View_clickable
+ * @attr ref android.R.styleable#View_clipToOutline
* @attr ref android.R.styleable#View_contentDescription
* @attr ref android.R.styleable#View_drawingCacheQuality
* @attr ref android.R.styleable#View_duplicateParentState
@@ -5968,6 +5980,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
case R.styleable.View_scrollCaptureHint:
setScrollCaptureHint((a.getInt(attr, SCROLL_CAPTURE_HINT_AUTO)));
break;
+ case R.styleable.View_clipToOutline:
+ setClipToOutline(a.getBoolean(attr, false));
+ break;
}
}
@@ -17925,6 +17940,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @see #setOutlineProvider(ViewOutlineProvider)
* @see #getClipToOutline()
+ *
+ * @attr ref android.R.styleable#View_clipToOutline
*/
@RemotableViewMethod
public void setClipToOutline(boolean clipToOutline) {
@@ -22162,6 +22179,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* and hardware acceleration.
*/
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
+ // Clear the overscroll effect:
+ // TODO: Use internal API instead of overriding the existing RenderEffect
+ setRenderEffect(null);
+
final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
/* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList.
*
@@ -24026,6 +24047,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #getBackgroundTintMode()
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setBackgroundTintBlendMode(@Nullable BlendMode blendMode) {
if (mBackgroundTint == null) {
mBackgroundTint = new TintInfo();
@@ -24288,6 +24310,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #getForegroundTintMode()
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setForegroundTintBlendMode(@Nullable BlendMode blendMode) {
if (mForegroundInfo == null) {
mForegroundInfo = new ForegroundInfo();
@@ -30699,4 +30722,71 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public void onTranslationComplete(@NonNull TranslationRequest request) {
// no-op
}
+
+ /**
+ * Called to generate a {@link DisplayHash} for this view.
+ *
+ * @param hashAlgorithm The hash algorithm to use when hashing the display. Must be one of
+ * the values returned from
+ * {@link DisplayHashManager#getSupportedHashAlgorithms()}
+ * @param bounds The bounds for the content within the View to generate the hash for. If
+ * bounds are null, the entire View's bounds will be used. If empty, it will
+ * invoke the callback
+ * {@link DisplayHashResultCallback#onDisplayHashError} with error
+ * {@link DisplayHashResultCallback#DISPLAY_HASH_ERROR_INVALID_BOUNDS}
+ * @param executor The executor that the callback should be invoked on.
+ * @param callback The callback to handle the results of generating the display hash
+ */
+ @Nullable
+ public void generateDisplayHash(@NonNull String hashAlgorithm,
+ @Nullable Rect bounds, @NonNull Executor executor,
+ @NonNull DisplayHashResultCallback callback) {
+ IWindowSession session = getWindowSession();
+ if (session == null) {
+ callback.onDisplayHashError(DISPLAY_HASH_ERROR_MISSING_WINDOW);
+ return;
+ }
+ IWindow window = getWindow();
+ if (window == null) {
+ callback.onDisplayHashError(DISPLAY_HASH_ERROR_MISSING_WINDOW);
+ return;
+ }
+
+ Rect visibleBounds = new Rect();
+ getGlobalVisibleRect(visibleBounds);
+
+ if (bounds != null && bounds.isEmpty()) {
+ callback.onDisplayHashError(DISPLAY_HASH_ERROR_INVALID_BOUNDS);
+ return;
+ }
+
+ if (bounds != null) {
+ bounds.offset(visibleBounds.left, visibleBounds.top);
+ visibleBounds.intersectUnchecked(bounds);
+ }
+
+ if (visibleBounds.isEmpty()) {
+ callback.onDisplayHashError(DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
+ return;
+ }
+
+ RemoteCallback remoteCallback = new RemoteCallback(result ->
+ executor.execute(() -> {
+ DisplayHash displayHash = result.getParcelable(EXTRA_DISPLAY_HASH);
+ int errorCode = result.getInt(EXTRA_DISPLAY_HASH_ERROR_CODE,
+ DISPLAY_HASH_ERROR_UNKNOWN);
+ if (displayHash != null) {
+ callback.onDisplayHashResult(displayHash);
+ } else {
+ callback.onDisplayHashError(errorCode);
+ }
+ }));
+
+ try {
+ session.generateDisplayHash(window, visibleBounds, hashAlgorithm, remoteCallback);
+ } catch (RemoteException e) {
+ Log.e(VIEW_LOG_TAG, "Failed to call generateDisplayHash");
+ callback.onDisplayHashError(DISPLAY_HASH_ERROR_UNKNOWN);
+ }
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8fe8473c2e13..c97b3f8118e9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1377,6 +1377,7 @@ public final class ViewRootImpl implements ViewParent,
final boolean translucent = attrs.format != PixelFormat.OPAQUE || hasSurfaceInsets;
mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,
attrs.getTitle().toString());
+ mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
updateColorModeIfNeeded(attrs.getColorMode());
updateForceDarkMode();
if (mAttachInfo.mThreadedRenderer != null) {
@@ -1677,7 +1678,8 @@ public final class ViewRootImpl implements ViewParent,
requestLayout();
// See comment for View.sForceLayoutWhenInsetsChanged
- if (View.sForceLayoutWhenInsetsChanged && mView != null) {
+ if (View.sForceLayoutWhenInsetsChanged && mView != null
+ && mWindowAttributes.softInputMode == SOFT_INPUT_ADJUST_RESIZE) {
forceLayout(mView);
}
@@ -1899,11 +1901,12 @@ public final class ViewRootImpl implements ViewParent,
}
private void setBoundsLayerCrop(Transaction t) {
- // mWinFrame is already adjusted for surface insets. So offset it and use it as
- // the cropping bounds.
- mTempBoundsRect.set(mWinFrame);
- mTempBoundsRect.offsetTo(mWindowAttributes.surfaceInsets.left,
- mWindowAttributes.surfaceInsets.top);
+ // Adjust of insets and update the bounds layer so child surfaces do not draw into
+ // the surface inset region.
+ mTempBoundsRect.set(0, 0, mSurfaceSize.x, mSurfaceSize.y);
+ mTempBoundsRect.inset(mWindowAttributes.surfaceInsets.left,
+ mWindowAttributes.surfaceInsets.top,
+ mWindowAttributes.surfaceInsets.right, mWindowAttributes.surfaceInsets.bottom);
t.setWindowCrop(mBoundsLayer, mTempBoundsRect);
}
@@ -1914,25 +1917,18 @@ public final class ViewRootImpl implements ViewParent,
private boolean updateBoundsLayer(SurfaceControl.Transaction t) {
if (mBoundsLayer != null) {
setBoundsLayerCrop(t);
- t.deferTransactionUntil(mBoundsLayer, getSurfaceControl(),
- mSurface.getNextFrameNumber());
return true;
}
return false;
}
- private void prepareSurfaces(boolean sizeChanged) {
+ private void prepareSurfaces() {
final SurfaceControl.Transaction t = mTransaction;
final SurfaceControl sc = getSurfaceControl();
if (!sc.isValid()) return;
- boolean applyTransaction = updateBoundsLayer(t);
- if (sizeChanged) {
- applyTransaction = true;
- t.setBufferSize(sc, mSurfaceSize.x, mSurfaceSize.y);
- }
- if (applyTransaction) {
- t.apply();
+ if (updateBoundsLayer(t)) {
+ mergeWithNextTransaction(t, mSurface.getNextFrameNumber());
}
}
@@ -1948,6 +1944,10 @@ public final class ViewRootImpl implements ViewParent,
mBlastBufferQueue.destroy();
mBlastBufferQueue = null;
}
+
+ if (mAttachInfo.mThreadedRenderer != null) {
+ mAttachInfo.mThreadedRenderer.setSurfaceControl(null);
+ }
}
/**
@@ -2843,8 +2843,7 @@ public final class ViewRootImpl implements ViewParent,
mScroller.abortAnimation();
}
// Our surface is gone
- if (mAttachInfo.mThreadedRenderer != null &&
- mAttachInfo.mThreadedRenderer.isEnabled()) {
+ if (isHardwareEnabled()) {
mAttachInfo.mThreadedRenderer.destroy();
}
} else if ((surfaceReplaced
@@ -3037,7 +3036,7 @@ public final class ViewRootImpl implements ViewParent,
// stopping, but on the client side it doesn't get stopped since it's restarted quick
// enough. WMS doesn't want to keep around old children since they will leak when the
// client creates new children.
- prepareSurfaces(surfaceSizeChanged);
+ prepareSurfaces();
}
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
@@ -3065,10 +3064,15 @@ public final class ViewRootImpl implements ViewParent,
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
mFullRedrawNeeded = true;
- // reconfigure window manager
- try {
- mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
- } catch (RemoteException e) {
+ // TODO: Ideally we would do this in prepareSurfaces,
+ // but prepareSurfaces is currently working under
+ // the assumption that we paused the render thread
+ // via the WM relayout code path. We probably eventually
+ // want to synchronize transparent region hint changes
+ // with draws.
+ SurfaceControl sc = getSurfaceControl();
+ if (sc.isValid()) {
+ mTransaction.setTransparentRegionHint(sc, mTransparentRegion).apply();
}
}
}
@@ -3925,8 +3929,15 @@ public final class ViewRootImpl implements ViewParent,
};
}
+ /**
+ * @hide
+ */
+ public boolean isHardwareEnabled() {
+ return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled();
+ }
+
private boolean addFrameCompleteCallbackIfNeeded() {
- if (mAttachInfo.mThreadedRenderer == null || !mAttachInfo.mThreadedRenderer.isEnabled()) {
+ if (!isHardwareEnabled()) {
return false;
}
@@ -4270,7 +4281,7 @@ public final class ViewRootImpl implements ViewParent,
boolean useAsyncReport = false;
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
- if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
+ if (isHardwareEnabled()) {
// If accessibility focus moved, always invalidate the root.
boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested;
mInvalidateRootRequested = false;
@@ -7653,6 +7664,9 @@ public final class ViewRootImpl implements ViewParent,
mSurface.transferFrom(blastSurface);
}
}
+ if (mAttachInfo.mThreadedRenderer != null) {
+ mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
+ }
} else {
destroySurface();
}
@@ -10146,9 +10160,11 @@ public final class ViewRootImpl implements ViewParent,
* Merges the transaction passed in with the next transaction in BLASTBufferQueue. This ensures
* you can add transactions to the upcoming frame.
*/
- void mergeWithNextTransaction(Transaction t, long frameNumber) {
+ public void mergeWithNextTransaction(Transaction t, long frameNumber) {
if (mBlastBufferQueue != null) {
mBlastBufferQueue.mergeWithNextTransaction(t, frameNumber);
+ } else {
+ t.apply();
}
}
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index af18293398da..4ecdd78f5a42 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1703,6 +1703,30 @@ public abstract class Window {
public abstract void setBackgroundDrawable(Drawable drawable);
/**
+ * Blurs the screen behind the window within the bounds of the window.
+ *
+ * The density of the blur is set by the blur radius. The radius defines the size
+ * of the neighbouring area, from which pixels will be averaged to form the final
+ * color for each pixel. The operation approximates a Gaussian blur.
+ * A radius of 0 means no blur. The higher the radius, the denser the blur.
+ *
+ * The window background drawable is drawn on top of the blurred region. The blur
+ * region bounds and rounded corners will mimic those of the background drawable.
+ *
+ * For the blur region to be visible, the window has to be translucent. See
+ * {@link android.R.styleable#Window_windowIsTranslucent}.
+ *
+ * Note the difference with {@link android.view.WindowManager.LayoutParams#blurBehindRadius},
+ * which blurs the whole screen behind the window. Background blur blurs the screen behind
+ * only within the bounds of the window.
+ *
+ * @param blurRadius The blur radius to use for window background blur in pixels
+ *
+ * @see android.R.styleable#Window_windowBackgroundBlurRadius
+ */
+ public void setBackgroundBlurRadius(int blurRadius) {}
+
+ /**
* Set the value for a drawable feature of this window, from a resource
* identifier. You must have called requestFeature(featureId) before
* calling this function.
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index b85f10799210..7cc2db1c0dab 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -23,8 +23,8 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.IBinder;
+import android.os.RemoteCallback;
import android.os.RemoteException;
-import android.service.screenshot.ScreenshotHash;
import android.util.Log;
import android.util.MergedConfiguration;
import android.window.ClientWindowFrames;
@@ -71,7 +71,7 @@ public class WindowlessWindowManager implements IWindowSession {
new HashMap<IBinder, ResizeCompleteCallback>();
private final SurfaceSession mSurfaceSession = new SurfaceSession();
- private final SurfaceControl mRootSurface;
+ protected final SurfaceControl mRootSurface;
private final Configuration mConfiguration;
private final IWindowSession mRealWm;
private final IBinder mHostInputToken;
@@ -126,7 +126,7 @@ public class WindowlessWindowManager implements IWindowSession {
}
}
- protected void attachToParentSurface(SurfaceControl.Builder b) {
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
b.setParent(mRootSurface);
}
@@ -140,10 +140,10 @@ public class WindowlessWindowManager implements IWindowSession {
InsetsSourceControl[] outActiveControls) {
final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
.setFormat(attrs.format)
- .setBufferSize(getSurfaceWidth(attrs), getSurfaceHeight(attrs))
+ .setBLASTLayer()
.setName(attrs.getTitle().toString())
.setCallsite("WindowlessWindowManager.addToDisplay");
- attachToParentSurface(b);
+ attachToParentSurface(window, b);
final SurfaceControl sc = b.build();
if (((attrs.inputFeatures &
@@ -162,7 +162,11 @@ public class WindowlessWindowManager implements IWindowSession {
mStateForWindow.put(window.asBinder(), state);
}
- return WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
+ final int res = WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE |
+ WindowManagerGlobal.ADD_FLAG_USE_BLAST;
+
+ // Include whether the window is in touch mode.
+ return isInTouchMode() ? res | WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE : res;
}
/**
@@ -210,6 +214,26 @@ public class WindowlessWindowManager implements IWindowSession {
return !PixelFormat.formatHasAlpha(attrs.format);
}
+ private boolean isInTouchMode() {
+ try {
+ return WindowManagerGlobal.getWindowSession().getInTouchMode();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to check if the window is in touch mode", e);
+ }
+ return false;
+ }
+
+ /** Access to package members for SystemWindow leashing
+ * @hide
+ */
+ protected IBinder getWindowBinder(View rootView) {
+ final ViewRootImpl root = rootView.getViewRootImpl();
+ if (root == null) {
+ return null;
+ }
+ return root.mWindow.asBinder();
+ }
+
/** @hide */
@Nullable
protected SurfaceControl getSurfaceControl(View rootView) {
@@ -254,8 +278,8 @@ public class WindowlessWindowManager implements IWindowSession {
WindowManager.LayoutParams attrs = state.mParams;
if (viewFlags == View.VISIBLE) {
- t.setBufferSize(sc, getSurfaceWidth(attrs), getSurfaceHeight(attrs))
- .setOpaque(sc, isOpaque(attrs)).show(sc).apply();
+ outSurfaceSize.set(getSurfaceWidth(attrs), getSurfaceHeight(attrs));
+ t.setOpaque(sc, isOpaque(attrs)).show(sc).apply();
outSurfaceControl.copyFrom(sc, "WindowlessWindowManager.relayout");
} else {
t.hide(sc).apply();
@@ -276,7 +300,8 @@ public class WindowlessWindowManager implements IWindowSession {
}
}
- return 0;
+ // Include whether the window is in touch mode.
+ return isInTouchMode() ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
}
@Override
@@ -289,10 +314,6 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
- public void setTransparentRegion(android.view.IWindow window, android.graphics.Region region) {
- }
-
- @Override
public void setInsets(android.view.IWindow window, int touchableInsets,
android.graphics.Rect contentInsets, android.graphics.Rect visibleInsets,
android.graphics.Region touchableRegion) {
@@ -466,8 +487,7 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
- public ScreenshotHash generateScreenshotHash(IWindow window, Rect boundsInWindow,
- String hashAlgorithm) {
- return null;
+ public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
+ RemoteCallback callback) {
}
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 794181e388cf..decbf8c0c59e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -200,6 +200,34 @@ public final class AutofillManager {
"android.view.autofill.extra.AUTHENTICATION_RESULT";
/**
+ * Intent extra: The optional boolean extra field provided by the
+ * {@link android.service.autofill.AutofillService} accompanying the {@link
+ * android.service.autofill.Dataset} result of an authentication operation.
+ *
+ * <p> Before {@link android.os.Build.VERSION_CODES#R}, if the authentication result is a
+ * {@link android.service.autofill.Dataset}, it'll be used to autofill the fields, and also
+ * replace the existing dataset in the cached {@link android.service.autofill.FillResponse}.
+ * That means if the user clears the field values, the autofill suggestion will show up again
+ * with the new authenticated Dataset.
+ *
+ * <p> In {@link android.os.Build.VERSION_CODES#R}, we added an exception to this behavior
+ * that if the Dataset being authenticated is a pinned dataset (see
+ * {@link android.service.autofill.InlinePresentation#isPinned()}), the old Dataset will not be
+ * replaced.
+ *
+ * <p> In {@link android.os.Build.VERSION_CODES#S}, we added this boolean extra field to
+ * allow the {@link android.service.autofill.AutofillService} to explicitly specify whether
+ * the returned authenticated Dataset is ephemeral. An ephemeral Dataset will be used to
+ * autofill once and then thrown away. Therefore, when the boolean extra is set to true, the
+ * returned Dataset will not replace the old dataset from the existing
+ * {@link android.service.autofill.FillResponse}. When it's set to false, it will. When it's not
+ * set, the old dataset will be replaced, unless it is a pinned inline suggestion, which is
+ * consistent with the behavior in {@link android.os.Build.VERSION_CODES#R}.
+ */
+ public static final String EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET =
+ "android.view.autofill.extra.AUTHENTICATION_RESULT_EPHEMERAL_DATASET";
+
+ /**
* Intent extra: The optional extras provided by the
* {@link android.service.autofill.AutofillService}.
*
@@ -1755,6 +1783,11 @@ public final class AutofillManager {
if (newClientState != null) {
responseData.putBundle(EXTRA_CLIENT_STATE, newClientState);
}
+ if (data.getExtras().containsKey(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) {
+ responseData.putBoolean(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET,
+ data.getBooleanExtra(EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET,
+ false));
+ }
try {
mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
mContext.getUserId());
diff --git a/core/java/android/view/displayhash/DisplayHash.aidl b/core/java/android/view/displayhash/DisplayHash.aidl
new file mode 100644
index 000000000000..cabf57573fa0
--- /dev/null
+++ b/core/java/android/view/displayhash/DisplayHash.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.view.displayhash;
+
+parcelable DisplayHash;
diff --git a/core/java/android/view/displayhash/DisplayHash.java b/core/java/android/view/displayhash/DisplayHash.java
new file mode 100644
index 000000000000..41484865b98e
--- /dev/null
+++ b/core/java/android/view/displayhash/DisplayHash.java
@@ -0,0 +1,226 @@
+/*
+ * 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.displayhash;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+
+/**
+ * The DisplayHash used to validate information about what was present on screen.
+ */
+public final class DisplayHash implements Parcelable {
+ /**
+ * The timestamp when the hash was generated.
+ */
+ private final long mTimeMillis;
+
+ /**
+ * The bounds of the requested area to generate the hash. This is in window space passed in
+ * by the client.
+ */
+ @NonNull
+ private final Rect mBoundsInWindow;
+
+ /**
+ * The selected hash algorithm that generated the image hash.
+ */
+ @NonNull
+ private final String mHashAlgorithm;
+
+ /**
+ * The image hash generated when creating the DisplayHash.
+ */
+ @NonNull
+ private final byte[] mImageHash;
+
+ /**
+ * The hmac generated by the system and used to verify whether this token was generated by
+ * the system.
+ */
+ @NonNull
+ private final byte[] mHmac;
+
+ /**
+ * Creates a new DisplayHash.
+ *
+ * @param timeMillis The timestamp when the hash was generated.
+ * @param boundsInWindow The bounds of the requested area to generate the hash. This is
+ * in window space passed in by the client.
+ * @param hashAlgorithm The selected hash algorithm that generated the image hash.
+ * @param imageHash The image hash generated when creating the DisplayHash.
+ * @param hmac The hmac generated by the system and used to verify whether this
+ * token was generated by
+ * the system. This should only be accessed by a system process.
+ * @hide
+ */
+ @SystemApi
+ public DisplayHash(long timeMillis, @NonNull Rect boundsInWindow,
+ @NonNull String hashAlgorithm, @NonNull byte[] imageHash, @NonNull byte[] hmac) {
+ mTimeMillis = timeMillis;
+ mBoundsInWindow = boundsInWindow;
+ AnnotationValidations.validate(NonNull.class, null, mBoundsInWindow);
+ mHashAlgorithm = hashAlgorithm;
+ AnnotationValidations.validate(NonNull.class, null, mHashAlgorithm);
+ mImageHash = imageHash;
+ AnnotationValidations.validate(NonNull.class, null, mImageHash);
+ mHmac = hmac;
+ AnnotationValidations.validate(NonNull.class, null, mHmac);
+ }
+
+ /**
+ * The timestamp when the hash was generated.
+ *
+ * @hide
+ */
+ @SystemApi
+ public long getTimeMillis() {
+ return mTimeMillis;
+ }
+
+ /**
+ * The bounds of the requested area to to generate the hash. This is in window space passed in
+ * by the client.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public Rect getBoundsInWindow() {
+ return mBoundsInWindow;
+ }
+
+ /**
+ * The selected hash algorithm that generated the image hash.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public String getHashAlgorithm() {
+ return mHashAlgorithm;
+ }
+
+ /**
+ * The image hash generated when creating the DisplayHash.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public byte[] getImageHash() {
+ return mImageHash;
+ }
+
+ /**
+ * The hmac generated by the system and used to verify whether this token was generated by
+ * the system. This should only be accessed by a system process.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public byte[] getHmac() {
+ return mHmac;
+ }
+
+ /** @hide **/
+ @Override
+ public String toString() {
+ return "DisplayHash { "
+ + "timeMillis = " + mTimeMillis + ", "
+ + "boundsInWindow = " + mBoundsInWindow + ", "
+ + "hashAlgorithm = " + mHashAlgorithm + ", "
+ + "imageHash = " + byteArrayToString(mImageHash) + ", "
+ + "hmac = " + byteArrayToString(mHmac)
+ + " }";
+ }
+
+ private String byteArrayToString(byte[] byteArray) {
+ if (byteArray == null) {
+ return "null";
+ }
+ int iMax = byteArray.length - 1;
+ if (iMax == -1) {
+ return "[]";
+ }
+
+ StringBuilder b = new StringBuilder();
+ b.append('[');
+ for (int i = 0; ; i++) {
+ String formatted = String.format("%02X", byteArray[i] & 0xFF);
+ b.append(formatted);
+ if (i == iMax) {
+ return b.append(']').toString();
+ }
+ b.append(", ");
+ }
+ }
+
+ /** @hide **/
+ @SystemApi
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mTimeMillis);
+ dest.writeTypedObject(mBoundsInWindow, flags);
+ dest.writeString(mHashAlgorithm);
+ dest.writeByteArray(mImageHash);
+ dest.writeByteArray(mHmac);
+ }
+
+ /** @hide **/
+ @SystemApi
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private DisplayHash(@NonNull Parcel in) {
+ mTimeMillis = in.readLong();
+ Rect boundsInWindow = in.readTypedObject(Rect.CREATOR);
+ String hashAlgorithm = in.readString();
+ byte[] imageHash = in.createByteArray();
+ byte[] hmac = in.createByteArray();
+
+ mBoundsInWindow = boundsInWindow;
+ AnnotationValidations.validate(NonNull.class, null, mBoundsInWindow);
+ mHashAlgorithm = hashAlgorithm;
+ AnnotationValidations.validate(NonNull.class, null, mHashAlgorithm);
+ mImageHash = imageHash;
+ AnnotationValidations.validate(NonNull.class, null, mImageHash);
+ mHmac = hmac;
+ AnnotationValidations.validate(NonNull.class, null, mHmac);
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<DisplayHash> CREATOR =
+ new Parcelable.Creator<DisplayHash>() {
+ @Override
+ public DisplayHash[] newArray(int size) {
+ return new DisplayHash[size];
+ }
+
+ @Override
+ public DisplayHash createFromParcel(@NonNull Parcel in) {
+ return new DisplayHash(in);
+ }
+ };
+}
diff --git a/core/java/android/view/displayhash/DisplayHashManager.java b/core/java/android/view/displayhash/DisplayHashManager.java
new file mode 100644
index 000000000000..6b0c1a6d7633
--- /dev/null
+++ b/core/java/android/view/displayhash/DisplayHashManager.java
@@ -0,0 +1,97 @@
+/*
+ * 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.displayhash;
+
+
+import static android.content.Context.DISPLAY_HASH_SERVICE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Utility class for DisplayHash requests.
+ */
+@SystemService(DISPLAY_HASH_SERVICE)
+public final class DisplayHashManager {
+ private static final String TAG = "DisplayHashManager";
+
+ private final Object mSupportedHashingAlgorithmLock = new Object();
+
+ @GuardedBy("mSupportedAlgorithmLock")
+ private static Set<String> sSupportedHashAlgorithms;
+
+ /**
+ * @hide
+ */
+ public DisplayHashManager() {
+ }
+
+ /**
+ * Get a Set of DisplayHash algorithms that the device supports.
+ *
+ * @return a String Set of supported hashing algorithms. The String value of one
+ * algorithm should be used when requesting to generate the DisplayHash.
+ */
+ @NonNull
+ public Set<String> getSupportedHashAlgorithms() {
+ synchronized (mSupportedHashingAlgorithmLock) {
+ if (sSupportedHashAlgorithms != null) {
+ return sSupportedHashAlgorithms;
+ }
+
+ try {
+ String[] supportedAlgorithms = WindowManagerGlobal.getWindowManagerService()
+ .getSupportedDisplayHashAlgorithms();
+ if (supportedAlgorithms == null) {
+ return Collections.emptySet();
+ }
+ sSupportedHashAlgorithms = new ArraySet<>(supportedAlgorithms);
+ return sSupportedHashAlgorithms;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send request getSupportedHashingAlgorithms", e);
+ return Collections.emptySet();
+ }
+ }
+ }
+
+ /**
+ * Call to verify that the DisplayHash passed in was generated by the system.
+ *
+ * @param displayHash The hash to verify that it was generated by the system.
+ * @return a {@link VerifiedDisplayHash} if the hash was generated by the system or null
+ * if the hash cannot be verified.
+ */
+ @Nullable
+ public VerifiedDisplayHash verifyDisplayHash(@NonNull DisplayHash displayHash) {
+ try {
+ return WindowManagerGlobal.getWindowManagerService().verifyDisplayHash(displayHash);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send request verifyImpressionToken", e);
+ return null;
+ }
+ }
+}
diff --git a/core/java/android/view/displayhash/DisplayHashResultCallback.java b/core/java/android/view/displayhash/DisplayHashResultCallback.java
new file mode 100644
index 000000000000..15b29adafddd
--- /dev/null
+++ b/core/java/android/view/displayhash/DisplayHashResultCallback.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.displayhash;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.view.View;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * Use when calling {@link View#generateDisplayHash(String, Rect, Executor,
+ * DisplayHashResultCallback)}.
+ *
+ * The callback will only invoke either {@link #onDisplayHashResult} when the system successfully
+ * generated the {@link DisplayHash} or {@link #onDisplayHashError(int)} when it failed.
+ */
+public interface DisplayHashResultCallback {
+ /**
+ * @hide
+ */
+ String EXTRA_DISPLAY_HASH = "DISPLAY_HASH";
+
+ /**
+ * @hide
+ */
+ String EXTRA_DISPLAY_HASH_ERROR_CODE = "DISPLAY_HASH_ERROR_CODE";
+
+ /**
+ * An unknown error occurred.
+ */
+ int DISPLAY_HASH_ERROR_UNKNOWN = -1;
+
+ /**
+ * The bounds used when requesting the hash hash were invalid or empty.
+ */
+ int DISPLAY_HASH_ERROR_INVALID_BOUNDS = -2;
+
+ /**
+ * The window for the view that requested the hash is no longer around. This can happen if the
+ * window is getting torn down.
+ */
+ int DISPLAY_HASH_ERROR_MISSING_WINDOW = -3;
+
+ /**
+ * The view that requested the hash is not visible on screen. This could either mean
+ * that the view bounds are offscreen, window bounds are offscreen, view is not visible, or
+ * window is not visible.
+ */
+ int DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN = -4;
+
+ /** @hide */
+ @IntDef(prefix = {"DISPLAY_HASH_ERROR_"}, value = {
+ DISPLAY_HASH_ERROR_UNKNOWN,
+ DISPLAY_HASH_ERROR_INVALID_BOUNDS,
+ DISPLAY_HASH_ERROR_MISSING_WINDOW,
+ DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface DisplayHashErrorCode {
+ }
+
+ /**
+ * Callback invoked when calling
+ * {@link android.view.View#generateDisplayHash(String, Rect, Executor,
+ * DisplayHashResultCallback)}
+ *
+ * @param displayHash The DisplayHash generated. If the hash cannot be generated,
+ * {@link #onDisplayHashError(int)} will be called instead
+ */
+ void onDisplayHashResult(@NonNull DisplayHash displayHash);
+
+ /**
+ * Callback invoked when
+ * {@link android.view.View#generateDisplayHash(String, Rect, Executor,
+ * DisplayHashResultCallback)} results in an error and cannot generate a display hash.
+ *
+ * @param errorCode One of the values in {@link DisplayHashErrorCode}
+ */
+ void onDisplayHashError(@DisplayHashErrorCode int errorCode);
+}
diff --git a/core/java/android/view/displayhash/OWNERS b/core/java/android/view/displayhash/OWNERS
new file mode 100644
index 000000000000..0862c05e0ee4
--- /dev/null
+++ b/core/java/android/view/displayhash/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/core/java/android/view/displayhash/VerifiedDisplayHash.aidl b/core/java/android/view/displayhash/VerifiedDisplayHash.aidl
new file mode 100644
index 000000000000..9e7ebefa7ca1
--- /dev/null
+++ b/core/java/android/view/displayhash/VerifiedDisplayHash.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.view.displayhash;
+
+parcelable VerifiedDisplayHash;
diff --git a/core/java/android/view/displayhash/VerifiedDisplayHash.java b/core/java/android/view/displayhash/VerifiedDisplayHash.java
new file mode 100644
index 000000000000..16a428e73005
--- /dev/null
+++ b/core/java/android/view/displayhash/VerifiedDisplayHash.java
@@ -0,0 +1,242 @@
+/*
+ * 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.displayhash;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * The verified display hash used to validate information about what was present on screen.
+ */
+@DataClass(genToString = true, genAidl = true)
+public final class VerifiedDisplayHash implements Parcelable {
+ /**
+ * The timestamp when the hash was generated.
+ */
+ private final long mTimeMillis;
+
+ /**
+ * The bounds of the requested area to generate the hash. This is in window space passed in
+ * by the client.
+ */
+ @NonNull
+ private final Rect mBoundsInWindow;
+
+ /**
+ * The selected hash algorithm that generated the image hash.
+ */
+ @NonNull
+ private final String mHashAlgorithm;
+
+ /**
+ * The image hash generated when creating the DisplayHash.
+ */
+ @NonNull
+ private final byte[] mImageHash;
+
+ private String imageHashToString() {
+ return byteArrayToString(mImageHash);
+ }
+
+ private String byteArrayToString(byte[] byteArray) {
+ if (byteArray == null) {
+ return "null";
+ }
+ int iMax = byteArray.length - 1;
+ if (iMax == -1) {
+ return "[]";
+ }
+
+ StringBuilder b = new StringBuilder();
+ b.append('[');
+ for (int i = 0; ; i++) {
+ String formatted = String.format("%02X", byteArray[i] & 0xFF);
+ b.append(formatted);
+ if (i == iMax) {
+ return b.append(']').toString();
+ }
+ b.append(", ");
+ }
+ }
+
+
+
+ // 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/view/displayhash/VerifiedDisplayHash.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new VerifiedDisplayHash.
+ *
+ * @param timeMillis
+ * The timestamp when the hash was generated.
+ * @param boundsInWindow
+ * The bounds of the requested area to generate the hash. This is in window space passed in
+ * by the client.
+ * @param hashAlgorithm
+ * The selected hash algorithm that generated the image hash.
+ * @param imageHash
+ * The image hash generated when creating the DisplayHash.
+ */
+ @DataClass.Generated.Member
+ public VerifiedDisplayHash(
+ long timeMillis,
+ @NonNull Rect boundsInWindow,
+ @NonNull String hashAlgorithm,
+ @NonNull byte[] imageHash) {
+ this.mTimeMillis = timeMillis;
+ this.mBoundsInWindow = boundsInWindow;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mBoundsInWindow);
+ this.mHashAlgorithm = hashAlgorithm;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHashAlgorithm);
+ this.mImageHash = imageHash;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mImageHash);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The timestamp when the hash was generated.
+ */
+ @DataClass.Generated.Member
+ public long getTimeMillis() {
+ return mTimeMillis;
+ }
+
+ /**
+ * The bounds of the requested area to generate the hash. This is in window space passed in
+ * by the client.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Rect getBoundsInWindow() {
+ return mBoundsInWindow;
+ }
+
+ /**
+ * The selected hash algorithm that generated the image hash.
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getHashAlgorithm() {
+ return mHashAlgorithm;
+ }
+
+ /**
+ * The image hash generated when creating the DisplayHash.
+ */
+ @DataClass.Generated.Member
+ public @NonNull byte[] getImageHash() {
+ return mImageHash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "VerifiedDisplayHash { " +
+ "timeMillis = " + mTimeMillis + ", " +
+ "boundsInWindow = " + mBoundsInWindow + ", " +
+ "hashAlgorithm = " + mHashAlgorithm + ", " +
+ "imageHash = " + imageHashToString() +
+ " }";
+ }
+
+ @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) { ... }
+
+ dest.writeLong(mTimeMillis);
+ dest.writeTypedObject(mBoundsInWindow, flags);
+ dest.writeString(mHashAlgorithm);
+ dest.writeByteArray(mImageHash);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ VerifiedDisplayHash(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ long timeMillis = in.readLong();
+ Rect boundsInWindow = (Rect) in.readTypedObject(Rect.CREATOR);
+ String hashAlgorithm = in.readString();
+ byte[] imageHash = in.createByteArray();
+
+ this.mTimeMillis = timeMillis;
+ this.mBoundsInWindow = boundsInWindow;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mBoundsInWindow);
+ this.mHashAlgorithm = hashAlgorithm;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHashAlgorithm);
+ this.mImageHash = imageHash;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mImageHash);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<VerifiedDisplayHash> CREATOR
+ = new Parcelable.Creator<VerifiedDisplayHash>() {
+ @Override
+ public VerifiedDisplayHash[] newArray(int size) {
+ return new VerifiedDisplayHash[size];
+ }
+
+ @Override
+ public VerifiedDisplayHash createFromParcel(@NonNull android.os.Parcel in) {
+ return new VerifiedDisplayHash(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1613168749684L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/view/displayhash/VerifiedDisplayHash.java",
+ inputSignatures = "private final long mTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate java.lang.String imageHashToString()\nprivate java.lang.String byteArrayToString(byte[])\nclass VerifiedDisplayHash extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 6300320f5eb5..0ab4e05227ba 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -19,6 +19,10 @@ package android.view.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.LocaleList;
@@ -69,7 +73,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
/**
* The IME provided locales for the request. If non-empty, the inline suggestions should
- * return languages from the supported locales. If not provided, it'll default to system locale.
+ * return languages from the supported locales. If not provided, it'll default to be empty if
+ * target SDK is S or above, and default to system locale otherwise.
*
* <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions
* to have one locale to guarantee consistent UI rendering.</p>
@@ -156,7 +161,18 @@ public final class InlineSuggestionsRequest implements Parcelable {
return ActivityThread.currentPackageName();
}
+ /**
+ * The {@link InlineSuggestionsRequest#getSupportedLocales()} now returns empty locale list when
+ * it's not set, instead of the default system locale.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+ private static final long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY = 169273070L;
+
private static LocaleList defaultSupportedLocales() {
+ if (CompatChanges.isChangeEnabled(IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY)) {
+ return LocaleList.getEmptyLocaleList();
+ }
return LocaleList.getDefault();
}
@@ -189,7 +205,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
- // Code below generated by codegen v1.0.15.
+ // Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -264,7 +280,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
/**
* The IME provided locales for the request. If non-empty, the inline suggestions should
- * return languages from the supported locales. If not provided, it'll default to system locale.
+ * return languages from the supported locales. If not provided, it'll default to be empty if
+ * target SDK is S or above, and default to system locale otherwise.
*
* <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions
* to have one locale to guarantee consistent UI rendering.</p>
@@ -522,7 +539,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
/**
* The IME provided locales for the request. If non-empty, the inline suggestions should
- * return languages from the supported locales. If not provided, it'll default to system locale.
+ * return languages from the supported locales. If not provided, it'll default to be empty if
+ * target SDK is S or above, and default to system locale otherwise.
*
* <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions
* to have one locale to guarantee consistent UI rendering.</p>
@@ -622,10 +640,10 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
@DataClass.Generated(
- time = 1595457701315L,
- codegenVersion = "1.0.15",
+ time = 1612206506050L,
+ 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\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]\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 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 []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 90c8e17a6984..a8fff8bb6a43 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -115,7 +115,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/**
@@ -750,7 +749,7 @@ public final class InputMethodManager {
@Override
public boolean hasActiveConnection(View view) {
synchronized (mH) {
- if (!hasServedByInputMethodLocked(view)) {
+ if (!hasServedByInputMethodLocked(view) || mCurMethod == null) {
return false;
}
@@ -766,6 +765,17 @@ public final class InputMethodManager {
return mDelegate;
}
+ /**
+ * Checks whether the active input connection (if any) is for the given view.
+ *
+ * @hide
+ * @see ImeFocusController#getImmDelegate()#hasActiveInputConnection(View)
+ */
+ @TestApi
+ public boolean hasActiveInputConnection(@Nullable View view) {
+ return mDelegate.hasActiveConnection(view);
+ }
+
private View getServedViewLocked() {
return mCurRootView != null ? mCurRootView.getImeFocusController().getServedView() : null;
}
@@ -1083,19 +1093,6 @@ public final class InputMethodManager {
}
}
- private static class ImeThreadFactory implements ThreadFactory {
- private final String mThreadName;
-
- ImeThreadFactory(String name) {
- mThreadName = name;
- }
-
- @Override
- public Thread newThread(Runnable r) {
- return new Thread(r, mThreadName);
- }
- }
-
final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index da346d0a1e5c..65a1671166a1 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -31,6 +31,7 @@ import android.content.pm.Signature;
import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemClock;
import android.os.Trace;
import android.util.AndroidRuntimeException;
import android.util.ArraySet;
@@ -261,7 +262,7 @@ public final class WebViewFactory {
// us honest and minimize usage of WebView internals when binding the proxy.
if (sProviderInstance != null) return sProviderInstance;
- sTimestamps[WEBVIEW_LOAD_START] = System.currentTimeMillis();
+ sTimestamps[WEBVIEW_LOAD_START] = SystemClock.elapsedRealtime();
final int uid = android.os.Process.myUid();
if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID
|| uid == android.os.Process.PHONE_UID || uid == android.os.Process.NFC_UID
@@ -401,7 +402,7 @@ public final class WebViewFactory {
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
"initialApplication.createApplicationContext");
- sTimestamps[CREATE_CONTEXT_START] = System.currentTimeMillis();
+ sTimestamps[CREATE_CONTEXT_START] = SystemClock.elapsedRealtime();
try {
// Construct an app context to load the Java code into the current app.
Context webViewContext = initialApplication.createApplicationContext(
@@ -410,7 +411,7 @@ public final class WebViewFactory {
sPackageInfo = newPackageInfo;
return webViewContext;
} finally {
- sTimestamps[CREATE_CONTEXT_END] = System.currentTimeMillis();
+ sTimestamps[CREATE_CONTEXT_END] = SystemClock.elapsedRealtime();
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (RemoteException | PackageManager.NameNotFoundException e) {
@@ -436,26 +437,26 @@ public final class WebViewFactory {
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
try {
- sTimestamps[ADD_ASSETS_START] = System.currentTimeMillis();
+ sTimestamps[ADD_ASSETS_START] = SystemClock.elapsedRealtime();
for (String newAssetPath : webViewContext.getApplicationInfo().getAllApkPaths()) {
initialApplication.getAssets().addAssetPathAsSharedLibrary(newAssetPath);
}
sTimestamps[ADD_ASSETS_END] = sTimestamps[GET_CLASS_LOADER_START] =
- System.currentTimeMillis();
+ SystemClock.elapsedRealtime();
ClassLoader clazzLoader = webViewContext.getClassLoader();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
sTimestamps[GET_CLASS_LOADER_END] = sTimestamps[NATIVE_LOAD_START] =
- System.currentTimeMillis();
+ SystemClock.elapsedRealtime();
WebViewLibraryLoader.loadNativeLibrary(clazzLoader,
getWebViewLibrary(sPackageInfo.applicationInfo));
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
sTimestamps[NATIVE_LOAD_END] = sTimestamps[PROVIDER_CLASS_FOR_NAME_START] =
- System.currentTimeMillis();
+ SystemClock.elapsedRealtime();
try {
return getWebViewProviderClass(clazzLoader);
} finally {
- sTimestamps[PROVIDER_CLASS_FOR_NAME_END] = System.currentTimeMillis();
+ sTimestamps[PROVIDER_CLASS_FOR_NAME_END] = SystemClock.elapsedRealtime();
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (ClassNotFoundException e) {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 1901159e67da..0c46698e3e9c 100755
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -86,7 +86,7 @@ import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.SurroundingText;
import android.view.inspector.InspectableProperty;
import android.view.inspector.InspectableProperty.EnumEntry;
-import android.widget.RemoteViews.OnClickHandler;
+import android.widget.RemoteViews.InteractionHandler;
import com.android.internal.R;
@@ -6499,11 +6499,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
*
* @hide
*/
- public void setRemoteViewsOnClickHandler(OnClickHandler handler) {
+ public void setRemoteViewsInteractionHandler(InteractionHandler handler) {
// Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing
// service handling the specified intent.
if (mRemoteAdapter != null) {
- mRemoteAdapter.setRemoteViewsOnClickHandler(handler);
+ mRemoteAdapter.setRemoteViewsInteractionHandler(handler);
}
}
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index d93b635cbcf5..d8f7f4c5f326 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -29,7 +29,7 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
-import android.widget.RemoteViews.OnClickHandler;
+import android.widget.RemoteViews.InteractionHandler;
import java.util.ArrayList;
import java.util.HashMap;
@@ -1016,11 +1016,11 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
*
* @hide
*/
- public void setRemoteViewsOnClickHandler(OnClickHandler handler) {
+ public void setRemoteViewsOnClickHandler(InteractionHandler handler) {
// Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing
// service handling the specified intent.
if (mRemoteViewsAdapter != null) {
- mRemoteViewsAdapter.setRemoteViewsOnClickHandler(handler);
+ mRemoteViewsAdapter.setRemoteViewsInteractionHandler(handler);
}
}
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index c10ffbee686a..1b62266c12e2 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -17,19 +17,29 @@
package android.widget;
import android.annotation.ColorInt;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.BlendMode;
import android.graphics.Canvas;
+import android.graphics.Matrix;
import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
import android.graphics.Rect;
+import android.graphics.RenderEffect;
+import android.graphics.RenderNode;
import android.os.Build;
+import android.util.AttributeSet;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* This class performs the graphical effect used at the edges of scrollable widgets
* when the user scrolls beyond the content bounds in 2D space.
@@ -55,6 +65,24 @@ public class EdgeEffect {
*/
public static final BlendMode DEFAULT_BLEND_MODE = BlendMode.SRC_ATOP;
+ /**
+ * Use a color edge glow for the edge effect. From XML, use
+ * <code>android:edgeEffectType="glow"</code>.
+ */
+ public static final int TYPE_GLOW = 0;
+
+ /**
+ * Use a stretch for the edge effect. From XML, use
+ * <code>android:edgeEffectType="stretch"</code>.
+ */
+ public static final int TYPE_STRETCH = 1;
+
+ /** @hide */
+ @IntDef({TYPE_GLOW, TYPE_STRETCH})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EdgeEffectType {
+ }
+
@SuppressWarnings("UnusedDeclaration")
private static final String TAG = "EdgeEffect";
@@ -89,11 +117,14 @@ public class EdgeEffect {
private float mGlowAlpha;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private float mGlowScaleY;
+ private float mDistance;
private float mGlowAlphaStart;
private float mGlowAlphaFinish;
private float mGlowScaleYStart;
private float mGlowScaleYFinish;
+ private float mDistanceStart;
+ private float mDistanceFinish;
private long mStartTime;
private float mDuration;
@@ -121,17 +152,31 @@ public class EdgeEffect {
private float mBaseGlowScale;
private float mDisplacement = 0.5f;
private float mTargetDisplacement = 0.5f;
+ private @EdgeEffectType int mEdgeEffectType = TYPE_GLOW;
+ private Matrix mTmpMatrix = null;
+ private float[] mTmpPoints = null;
/**
* Construct a new EdgeEffect with a theme appropriate for the provided context.
* @param context Context used to provide theming and resource information for the EdgeEffect
*/
public EdgeEffect(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Construct a new EdgeEffect with a theme appropriate for the provided context.
+ * @param context Context used to provide theming and resource information for the 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);
final TypedArray a = context.obtainStyledAttributes(
- com.android.internal.R.styleable.EdgeEffect);
+ 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);
a.recycle();
mPaint.setColor((themeColor & 0xffffff) | 0x33000000);
mPaint.setStyle(Paint.Style.FILL);
@@ -211,11 +256,18 @@ public class EdgeEffect {
public void onPull(float deltaDistance, float displacement) {
final long now = AnimationUtils.currentAnimationTimeMillis();
mTargetDisplacement = displacement;
- if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) {
+ if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration
+ && mEdgeEffectType == TYPE_GLOW) {
return;
}
if (mState != STATE_PULL) {
- mGlowScaleY = Math.max(PULL_GLOW_BEGIN, mGlowScaleY);
+ if (mEdgeEffectType == TYPE_STRETCH) {
+ // Restore the mPullDistance to the fraction it is currently showing -- we want
+ // to "catch" the current stretch value.
+ mPullDistance = mDistance;
+ } else {
+ mGlowScaleY = Math.max(PULL_GLOW_BEGIN, mGlowScaleY);
+ }
}
mState = STATE_PULL;
@@ -223,6 +275,7 @@ 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,
@@ -242,6 +295,65 @@ public class EdgeEffect {
}
/**
+ * A view should call this when content is pulled away from an edge by the user.
+ * This will update the state of the current visual effect and its associated animation.
+ * The host view should always {@link android.view.View#invalidate()} after this
+ * and draw the results accordingly. This works similarly to {@link #onPull(float, float)},
+ * but returns the amount of <code>deltaDistance</code> that has been consumed. If the
+ * {@link #getDistance()} is currently 0 and <code>deltaDistance</code> is negative, this
+ * function will return 0 and the drawn value will remain unchanged.
+ *
+ * This method can be used to reverse the effect from a pull or absorb and partially consume
+ * some of a motion:
+ *
+ * <pre class="prettyprint">
+ * if (deltaY < 0) {
+ * float consumed = edgeEffect.onPullDistance(deltaY / getHeight(), x / getWidth());
+ * deltaY -= consumed * getHeight();
+ * if (edgeEffect.getDistance() == 0f) edgeEffect.onRelease();
+ * }
+ * </pre>
+ *
+ * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
+ * 1.f (full length of the view) or negative values to express change
+ * back toward the edge reached to initiate the effect.
+ * @param displacement The displacement from the starting side of the effect of the point
+ * initiating the pull. In the case of touch this is the finger position.
+ * Values may be from 0-1.
+ * @return The amount of <code>deltaDistance</code> that was consumed, a number between
+ * 0 and <code>deltaDistance</code>.
+ */
+ public float onPullDistance(float deltaDistance, float displacement) {
+ float finalDistance = Math.max(0f, deltaDistance + mDistance);
+ float delta = finalDistance - mDistance;
+ if (delta == 0f && mDistance == 0f) {
+ return 0f; // No pull, don't do anything.
+ }
+
+ if (mState != STATE_PULL && mState != STATE_PULL_DECAY && mEdgeEffectType == TYPE_GLOW) {
+ // Catch the edge glow in the middle of an animation.
+ mPullDistance = mDistance;
+ mState = STATE_PULL;
+ }
+ onPull(delta, displacement);
+ return delta;
+ }
+
+ /**
+ * Returns the pull distance needed to be released to remove the showing effect.
+ * It is determined by the {@link #onPull(float, float)} <code>deltaDistance</code> and
+ * any animating values, including from {@link #onAbsorb(int)} and {@link #onRelease()}.
+ *
+ * This can be used in conjunction with {@link #onPullDistance(float, float)} to
+ * release the currently showing effect.
+ *
+ * @return The pull distance that must be released to remove the showing effect.
+ */
+ public float getDistance() {
+ return mDistance;
+ }
+
+ /**
* Call when the object is released after being pulled.
* This will begin the "decay" phase of the effect. After calling this method
* the host view should {@link android.view.View#invalidate()} and thereby
@@ -257,9 +369,11 @@ public class EdgeEffect {
mState = STATE_RECEDE;
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
+ mDistanceStart = mDistance;
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
+ mDistanceFinish = 0.f;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = RECEDE_TIME;
@@ -286,7 +400,7 @@ public class EdgeEffect {
// 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
@@ -297,6 +411,9 @@ public class EdgeEffect {
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);
}
/**
@@ -309,6 +426,17 @@ public class EdgeEffect {
}
/**
+ * Sets the edge effect type to use. The default without a theme attribute set is
+ * {@link EdgeEffect#TYPE_GLOW}.
+ *
+ * @param type The edge effect type to use.
+ * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
+ */
+ public void setType(@EdgeEffectType int type) {
+ mEdgeEffectType = type;
+ }
+
+ /**
* Set or clear the blend mode. A blend mode defines how source pixels
* (generated by a drawing command) are composited with the destination pixels
* (content of the render target).
@@ -333,6 +461,15 @@ public class EdgeEffect {
return mPaint.getColor();
}
+ /**
+ * Return the edge effect type to use.
+ *
+ * @return The edge effect type to use.
+ * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
+ */
+ public @EdgeEffectType int getType() {
+ return mEdgeEffectType;
+ }
/**
* Returns the blend mode. A blend mode defines how source pixels
@@ -351,33 +488,59 @@ public class EdgeEffect {
* Draw into the provided canvas. Assumes that the canvas has been rotated
* accordingly and the size has been set. The effect will be drawn the full
* width of X=0 to X=width, beginning from Y=0 and extending to some factor <
- * 1.f of height.
+ * 1.f of height. The {@link #TYPE_STRETCH} effect will only be visible on a
+ * hardware canvas, e.g. {@link RenderNode#beginRecording()}.
*
* @param canvas Canvas to draw into
* @return true if drawing should continue beyond this frame to continue the
* animation
*/
public boolean draw(Canvas canvas) {
- update();
-
- final int count = canvas.save();
-
- final float centerX = mBounds.centerX();
- final float centerY = mBounds.height() - mRadius;
-
- canvas.scale(1.f, Math.min(mGlowScaleY, 1.f) * mBaseGlowScale, centerX, 0);
-
- final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f;
- float translateX = mBounds.width() * displacement / 2;
-
- canvas.clipRect(mBounds);
- canvas.translate(translateX, 0);
- mPaint.setAlpha((int) (0xff * mGlowAlpha));
- canvas.drawCircle(centerX, centerY, mRadius, mPaint);
- canvas.restoreToCount(count);
+ if (mEdgeEffectType == TYPE_GLOW) {
+ update();
+ final int count = canvas.save();
+
+ final float centerX = mBounds.centerX();
+ final float centerY = mBounds.height() - mRadius;
+
+ canvas.scale(1.f, Math.min(mGlowScaleY, 1.f) * mBaseGlowScale, centerX, 0);
+
+ final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f;
+ float translateX = mBounds.width() * displacement / 2;
+
+ canvas.clipRect(mBounds);
+ canvas.translate(translateX, 0);
+ mPaint.setAlpha((int) (0xff * mGlowAlpha));
+ canvas.drawCircle(centerX, centerY, mRadius, mPaint);
+ canvas.restoreToCount(count);
+ } else if (canvas instanceof RecordingCanvas) {
+ if (mState != STATE_PULL) {
+ update();
+ }
+ RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
+ if (mTmpMatrix == null) {
+ mTmpMatrix = new Matrix();
+ mTmpPoints = new float[4];
+ }
+ //noinspection deprecation
+ recordingCanvas.getMatrix(mTmpMatrix);
+ mTmpPoints[0] = mBounds.width() * mDisplacement;
+ mTmpPoints[1] = mDistance * mBounds.height();
+ mTmpPoints[2] = mTmpPoints[0];
+ mTmpPoints[3] = 0;
+ mTmpMatrix.mapPoints(mTmpPoints);
+ float x = mTmpPoints[0] - mTmpPoints[2];
+ float y = mTmpPoints[1] - mTmpPoints[3];
+
+ RenderNode renderNode = recordingCanvas.mNode;
+
+ // TODO: use stretchy RenderEffect and use internal API when it is ready
+ // TODO: wrap existing RenderEffect
+ renderNode.setRenderEffect(RenderEffect.createOffsetEffect(x, y));
+ }
boolean oneLastFrame = false;
- if (mState == STATE_RECEDE && mGlowScaleY == 0) {
+ if (mState == STATE_RECEDE && mDistance == 0) {
mState = STATE_IDLE;
oneLastFrame = true;
}
@@ -402,6 +565,7 @@ public class EdgeEffect {
mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
+ mDistance = mDistanceStart + (mDistanceFinish - mDistanceStart) * interp;
mDisplacement = (mDisplacement + mTargetDisplacement) / 2;
if (t >= 1.f - EPSILON) {
@@ -413,10 +577,12 @@ 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;
@@ -425,10 +591,12 @@ 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;
@@ -439,4 +607,20 @@ public class EdgeEffect {
}
}
}
+
+ /**
+ * @return The estimated pull distance as calculated from mGlowScaleY.
+ */
+ private float calculateDistanceFromGlowValues(float scale, float alpha) {
+ if (scale >= 1f) {
+ // It should asymptotically approach 1, but not reach there.
+ // Here, we're just choosing a value that is large.
+ return 1f;
+ }
+ if (scale > 0f) {
+ float v = 1f / 0.7f / (mGlowScaleY - 1f);
+ return v * v / mBounds.height();
+ }
+ return alpha / PULL_DISTANCE_ALPHA_GLOW_FACTOR;
+ }
}
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 97dbb1542717..6dedd12a2730 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -89,7 +89,7 @@ public class HorizontalScrollView extends FrameLayout {
*/
@NonNull
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124053130)
- private EdgeEffect mEdgeGlowLeft = new EdgeEffect(getContext());
+ private EdgeEffect mEdgeGlowLeft;
/**
* Tracks the state of the bottom edge glow.
@@ -98,7 +98,7 @@ public class HorizontalScrollView extends FrameLayout {
* setting it via reflection and they need to keep working until they target Q.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124052619)
- private EdgeEffect mEdgeGlowRight = new EdgeEffect(getContext());
+ private EdgeEffect mEdgeGlowRight;
/**
* Position of the last motion event.
@@ -186,6 +186,8 @@ public class HorizontalScrollView extends FrameLayout {
public HorizontalScrollView(
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mEdgeGlowLeft = new EdgeEffect(context, attrs);
+ mEdgeGlowRight = new EdgeEffect(context, attrs);
initScrollView();
final TypedArray a = context.obtainStyledAttributes(
@@ -631,7 +633,15 @@ public class HorizontalScrollView extends FrameLayout {
* otherwise don't. mScroller.isFinished should be false when
* being flinged.
*/
- mIsBeingDragged = !mScroller.isFinished();
+ mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowLeft.isFinished()
+ || !mEdgeGlowRight.isFinished();
+ // Catch the edge effect if it is active.
+ if (!mEdgeGlowLeft.isFinished()) {
+ mEdgeGlowLeft.onPullDistance(0f, 1f - ev.getY() / getHeight());
+ }
+ if (!mEdgeGlowRight.isFinished()) {
+ mEdgeGlowRight.onPullDistance(0f, ev.getY() / getHeight());
+ }
break;
}
@@ -675,7 +685,8 @@ public class HorizontalScrollView extends FrameLayout {
if (getChildCount() == 0) {
return false;
}
- if ((mIsBeingDragged = !mScroller.isFinished())) {
+ if ((mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowRight.isFinished()
+ || !mEdgeGlowLeft.isFinished())) {
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
@@ -721,12 +732,26 @@ public class HorizontalScrollView extends FrameLayout {
mLastMotionX = x;
final int oldX = mScrollX;
- final int oldY = mScrollY;
final int range = getScrollRange();
final int overscrollMode = getOverScrollMode();
final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
+ final float displacement = ev.getY(activePointerIndex) / getHeight();
+ if (canOverscroll) {
+ int consumed = 0;
+ if (deltaX < 0 && mEdgeGlowRight.getDistance() != 0f) {
+ consumed = Math.round(getHeight()
+ * mEdgeGlowRight.onPullDistance((float) deltaX / getWidth(),
+ displacement));
+ } else if (deltaX > 0 && mEdgeGlowLeft.getDistance() != 0f) {
+ consumed = Math.round(-getHeight()
+ * mEdgeGlowLeft.onPullDistance((float) -deltaX / getWidth(),
+ 1 - displacement));
+ }
+ deltaX -= consumed;
+ }
+
// Calling overScrollBy will call onOverScrolled, which
// calls onScrollChanged if applicable.
if (overScrollBy(deltaX, 0, mScrollX, 0, range, 0,
@@ -735,17 +760,17 @@ public class HorizontalScrollView extends FrameLayout {
mVelocityTracker.clear();
}
- if (canOverscroll) {
+ if (canOverscroll && deltaX != 0f) {
final int pulledToX = oldX + deltaX;
if (pulledToX < 0) {
- mEdgeGlowLeft.onPull((float) deltaX / getWidth(),
- 1.f - ev.getY(activePointerIndex) / getHeight());
+ mEdgeGlowLeft.onPullDistance((float) -deltaX / getWidth(),
+ 1.f - displacement);
if (!mEdgeGlowRight.isFinished()) {
mEdgeGlowRight.onRelease();
}
} else if (pulledToX > range) {
- mEdgeGlowRight.onPull((float) deltaX / getWidth(),
- ev.getY(activePointerIndex) / getHeight());
+ mEdgeGlowRight.onPullDistance((float) deltaX / getWidth(),
+ displacement);
if (!mEdgeGlowLeft.isFinished()) {
mEdgeGlowLeft.onRelease();
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index ed20d26a8b47..0a08ccd34f02 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -695,6 +695,7 @@ public class ImageView extends View {
* @see #getImageTintMode()
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setImageTintBlendMode(@Nullable BlendMode blendMode) {
mDrawableBlendMode = blendMode;
mHasDrawableBlendMode = true;
diff --git a/core/java/android/widget/ImeAwareEditText.java b/core/java/android/widget/ImeAwareEditText.java
index 9cd458558de3..0d9808507fd7 100644
--- a/core/java/android/widget/ImeAwareEditText.java
+++ b/core/java/android/widget/ImeAwareEditText.java
@@ -80,7 +80,7 @@ public class ImeAwareEditText extends EditText {
public void scheduleShowSoftInput() {
final InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
- if (imm.isActive(this)) {
+ if (imm.hasActiveInputConnection(this)) {
// This means that ImeAwareEditText is already connected to the IME.
// InputMethodManager#showSoftInput() is guaranteed to pass client-side focus check.
mHasPendingShowSoftInputRequest = false;
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 67216f5955f7..a44808eb3120 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -823,6 +823,7 @@ public class ProgressBar extends View {
* @see #setIndeterminateTintList(ColorStateList)
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setIndeterminateTintBlendMode(@Nullable BlendMode blendMode) {
if (mProgressTintInfo == null) {
mProgressTintInfo = new ProgressTintInfo();
@@ -1132,6 +1133,7 @@ public class ProgressBar extends View {
* @see #getProgressTintMode()
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setProgressTintBlendMode(@Nullable BlendMode blendMode) {
if (mProgressTintInfo == null) {
mProgressTintInfo = new ProgressTintInfo();
@@ -1248,6 +1250,7 @@ public class ProgressBar extends View {
* @see #setProgressBackgroundTintList(ColorStateList)
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setProgressBackgroundTintBlendMode(@Nullable BlendMode blendMode) {
if (mProgressTintInfo == null) {
mProgressTintInfo = new ProgressTintInfo();
@@ -1360,6 +1363,7 @@ public class ProgressBar extends View {
* @see #setSecondaryProgressTintList(ColorStateList)
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setSecondaryProgressTintBlendMode(@Nullable BlendMode blendMode) {
if (mProgressTintInfo == null) {
mProgressTintInfo = new ProgressTintInfo();
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index dfef7ca825a1..2112fb1de63f 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -47,6 +47,9 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
+import android.graphics.BlendMode;
+import android.graphics.Outline;
+import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -79,9 +82,11 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.ViewManager;
+import android.view.ViewOutlineProvider;
import android.view.ViewParent;
import android.view.ViewStub;
import android.widget.AdapterView.OnItemClickListener;
+import android.widget.CompoundButton.OnCheckedChangeListener;
import com.android.internal.R;
import com.android.internal.util.ContrastColorUtil;
@@ -97,6 +102,8 @@ import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Stack;
@@ -145,6 +152,9 @@ public class RemoteViews implements Parcelable, Filter {
private static final String LOG_TAG = "RemoteViews";
+ /** The intent extra for whether the view whose checked state changed is currently checked. */
+ public static final String EXTRA_CHECKED = "android.widget.extra.CHECKED";
+
/**
* The intent extra that contains the appWidgetId.
* @hide
@@ -169,6 +179,11 @@ public class RemoteViews implements Parcelable, Filter {
*/
private static final int MAX_NESTED_VIEWS = 10;
+ /**
+ * Maximum number of RemoteViews that can be specified in constructor.
+ */
+ private static final int MAX_INIT_VIEW_COUNT = 16;
+
// The unique identifiers for each custom {@link Action}.
private static final int SET_ON_CLICK_RESPONSE_TAG = 1;
private static final int REFLECTION_ACTION_TAG = 2;
@@ -194,6 +209,8 @@ public class RemoteViews implements Parcelable, Filter {
private static final int COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG = 25;
private static final int SET_COMPOUND_BUTTON_CHECKED_TAG = 26;
private static final int SET_RADIO_GROUP_CHECKED = 27;
+ private static final int SET_VIEW_OUTLINE_RADIUS_TAG = 28;
+ private static final int SET_ON_CHECKED_CHANGE_RESPONSE_TAG = 29;
/** @hide **/
@IntDef(prefix = "MARGIN_", value = {
@@ -287,7 +304,7 @@ public class RemoteViews implements Parcelable, Filter {
* The resource ID of the layout file. (Added to the parcel)
*/
@UnsupportedAppUsage
- private final int mLayoutId;
+ private int mLayoutId;
/**
* The resource ID of the layout file in dark text mode. (Added to the parcel)
@@ -319,6 +336,7 @@ public class RemoteViews implements Parcelable, Filter {
*/
private static final int MODE_NORMAL = 0;
private static final int MODE_HAS_LANDSCAPE_AND_PORTRAIT = 1;
+ private static final int MODE_HAS_SIZED_REMOTEVIEWS = 2;
/**
* Used in conjunction with the special constructor
@@ -328,15 +346,30 @@ public class RemoteViews implements Parcelable, Filter {
private RemoteViews mLandscape = null;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private RemoteViews mPortrait = null;
+ /**
+ * List of RemoteViews with their ideal size. There must be at least two if the map is not null.
+ *
+ * The smallest remote view is always the last element in the list.
+ */
+ private List<RemoteViews> mSizedRemoteViews = null;
+
+ /**
+ * Ideal size for this RemoteViews.
+ *
+ * Only to be used on children views used in a {@link RemoteViews} with
+ * {@link RemoteViews#hasSizedRemoteViews()}.
+ */
+ private PointF mIdealSize = null;
@ApplyFlags
private int mApplyFlags = 0;
/** Class cookies of the Parcel this instance was read from. */
- private final Map<Class, Object> mClassCookies;
+ private Map<Class, Object> mClassCookies;
- private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = (view, pendingIntent, response)
- -> startPendingIntent(view, pendingIntent, response.getLaunchOptions(view));
+ private static final InteractionHandler DEFAULT_INTERACTION_HANDLER =
+ (view, pendingIntent, response) ->
+ startPendingIntent(view, pendingIntent, response.getLaunchOptions(view));
private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>();
@@ -475,11 +508,26 @@ public class RemoteViews implements Parcelable, Filter {
}
}
- /** @hide */
- public interface OnClickHandler {
-
- /** @hide */
- boolean onClickHandler(View view, PendingIntent pendingIntent, RemoteResponse response);
+ /**
+ * Handler for view interactions (such as clicks) within a RemoteViews.
+ *
+ * @hide
+ */
+ public interface InteractionHandler {
+ /**
+ * Invoked when the user performs an interaction on the View.
+ *
+ * @param view the View with which the user interacted
+ * @param pendingIntent the base PendingIntent associated with the view
+ * @param response the response to the interaction, which knows how to fill in the
+ * attached PendingIntent
+ *
+ * @hide
+ */
+ boolean onInteraction(
+ View view,
+ PendingIntent pendingIntent,
+ RemoteResponse response);
}
/**
@@ -490,7 +538,7 @@ public class RemoteViews implements Parcelable, Filter {
*/
private abstract static class Action implements Parcelable {
public abstract void apply(View root, ViewGroup rootParent,
- OnClickHandler handler) throws ActionException;
+ InteractionHandler handler) throws ActionException;
public static final int MERGE_REPLACE = 0;
public static final int MERGE_APPEND = 1;
@@ -520,7 +568,8 @@ public class RemoteViews implements Parcelable, Filter {
* and return the final action which will run on the UI thread.
* Override this if some of the tasks can be performed async.
*/
- public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
+ public Action initActionAsync(
+ ViewTree root, ViewGroup rootParent, InteractionHandler handler) {
return this;
}
@@ -563,7 +612,7 @@ public class RemoteViews implements Parcelable, Filter {
// Constant used during async execution. It is not parcelable.
private static final Action ACTION_NOOP = new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { }
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) { }
};
/**
@@ -681,7 +730,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final View view = root.findViewById(viewId);
if (!(view instanceof AdapterView<?>)) return;
@@ -716,7 +765,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, final InteractionHandler handler) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -724,32 +773,29 @@ public class RemoteViews implements Parcelable, Filter {
if (target instanceof AdapterView<?>) {
AdapterView<?> av = (AdapterView<?>) target;
// The PendingIntent template is stored in the view's tag.
- OnItemClickListener listener = new OnItemClickListener() {
- public void onItemClick(AdapterView<?> parent, View view,
- int position, long id) {
- // The view should be a frame layout
- if (view instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) view;
-
- // AdapterViews contain their children in a frame
- // so we need to go one layer deeper here.
- if (parent instanceof AdapterViewAnimator) {
- vg = (ViewGroup) vg.getChildAt(0);
- }
- if (vg == null) return;
-
- RemoteResponse response = null;
- int childCount = vg.getChildCount();
- for (int i = 0; i < childCount; i++) {
- Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent);
- if (tag instanceof RemoteResponse) {
- response = (RemoteResponse) tag;
- break;
- }
+ OnItemClickListener listener = (parent, view, position, id) -> {
+ // The view should be a frame layout
+ if (view instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup) view;
+
+ // AdapterViews contain their children in a frame
+ // so we need to go one layer deeper here.
+ if (parent instanceof AdapterViewAnimator) {
+ vg = (ViewGroup) vg.getChildAt(0);
+ }
+ if (vg == null) return;
+
+ RemoteResponse response = null;
+ int childCount = vg.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ Object tag = vg.getChildAt(i).getTag(R.id.fillInIntent);
+ if (tag instanceof RemoteResponse) {
+ response = (RemoteResponse) tag;
+ break;
}
- if (response == null) return;
- response.handleViewClick(view, handler);
}
+ if (response == null) return;
+ response.handleViewInteraction(view, handler);
}
};
av.setOnItemClickListener(listener);
@@ -791,7 +837,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -853,7 +899,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -880,7 +926,7 @@ public class RemoteViews implements Parcelable, Filter {
if (target instanceof AbsListView) {
AbsListView v = (AbsListView) target;
v.setRemoteViewsAdapter(intent, isAsync);
- v.setRemoteViewsOnClickHandler(handler);
+ v.setRemoteViewsInteractionHandler(handler);
} else if (target instanceof AdapterViewAnimator) {
AdapterViewAnimator v = (AdapterViewAnimator) target;
v.setRemoteViewsAdapter(intent, isAsync);
@@ -890,7 +936,7 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- OnClickHandler handler) {
+ InteractionHandler handler) {
SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
copy.isAsync = true;
return copy;
@@ -929,7 +975,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, final InteractionHandler handler) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -964,11 +1010,13 @@ public class RemoteViews implements Parcelable, Filter {
return;
}
} else {
- // No intent to apply
+ // No intent to apply, clear the listener and any tags that were previously set.
target.setOnClickListener(null);
+ target.setTagInternal(R.id.pending_intent_tag, null);
+ target.setTagInternal(com.android.internal.R.id.fillInIntent, null);
return;
}
- target.setOnClickListener(v -> mResponse.handleViewClick(v, handler));
+ target.setOnClickListener(v -> mResponse.handleViewInteraction(v, handler));
}
@Override
@@ -979,6 +1027,77 @@ public class RemoteViews implements Parcelable, Filter {
final RemoteResponse mResponse;
}
+ /**
+ * Equivalent to calling
+ * {@link android.widget.CompoundButton#setOnCheckedChangeListener(
+ * android.widget.CompoundButton.OnCheckedChangeListener)}
+ * to launch the provided {@link PendingIntent}.
+ */
+ private class SetOnCheckedChangeResponse extends Action {
+
+ private final RemoteResponse mResponse;
+
+ SetOnCheckedChangeResponse(@IdRes int id, RemoteResponse response) {
+ this.viewId = id;
+ this.mResponse = response;
+ }
+
+ SetOnCheckedChangeResponse(Parcel parcel) {
+ viewId = parcel.readInt();
+ mResponse = new RemoteResponse();
+ mResponse.readFromParcel(parcel);
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(viewId);
+ mResponse.writeToParcel(dest, flags);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent, final InteractionHandler handler) {
+ final View target = root.findViewById(viewId);
+ if (target == null) return;
+ if (!(target instanceof CompoundButton)) {
+ Log.w(LOG_TAG, "setOnCheckedChange methods cannot be used on "
+ + "non-CompoundButton child (id: " + viewId + ")");
+ return;
+ }
+ CompoundButton button = (CompoundButton) target;
+
+ if (mResponse.mPendingIntent != null) {
+ // setOnCheckedChangePendingIntent cannot be used with collection children, which
+ // must use setOnCheckedChangeFillInIntent instead.
+ if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
+ Log.w(LOG_TAG, "Cannot setOnCheckedChangePendingIntent for collection item "
+ + "(id: " + viewId + ")");
+ return;
+ }
+ target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent);
+ } else if (mResponse.mFillIntent != null) {
+ if (!hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
+ Log.e(LOG_TAG, "The method setOnCheckedChangeFillInIntent is available "
+ + "only from RemoteViewsFactory (ie. on collection items).");
+ return;
+ }
+ } else {
+ // No intent to apply, clear any existing listener or tag.
+ button.setOnCheckedChangeListener(null);
+ button.setTagInternal(R.id.remote_checked_change_listener_tag, null);
+ return;
+ }
+
+ OnCheckedChangeListener onCheckedChangeListener =
+ (v, isChecked) -> mResponse.handleViewInteraction(v, handler);
+ button.setTagInternal(R.id.remote_checked_change_listener_tag, onCheckedChangeListener);
+ button.setOnCheckedChangeListener(onCheckedChangeListener);
+ }
+
+ @Override
+ public int getActionTag() {
+ return SET_ON_CHECKED_CHANGE_RESPONSE_TAG;
+ }
+ }
+
/** @hide **/
public static Rect getSourceBounds(View v) {
final float appScale = v.getContext().getResources()
@@ -1028,6 +1147,8 @@ public class RemoteViews implements Parcelable, Filter {
return ColorStateList.class;
case BaseReflectionAction.ICON:
return Icon.class;
+ case BaseReflectionAction.BLEND_MODE:
+ return BlendMode.class;
default:
return null;
}
@@ -1136,7 +1257,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1193,7 +1314,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1230,7 +1351,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final View view = root.findViewById(viewId);
if (view == null) return;
@@ -1334,7 +1455,7 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public void apply(View root, ViewGroup rootParent,
- OnClickHandler handler) throws ActionException {
+ InteractionHandler handler) throws ActionException {
ReflectionAction ra = new ReflectionAction(viewId, methodName,
BaseReflectionAction.BITMAP,
bitmap);
@@ -1374,6 +1495,7 @@ public class RemoteViews implements Parcelable, Filter {
static final int INTENT = 14;
static final int COLOR_STATE_LIST = 15;
static final int ICON = 16;
+ static final int BLEND_MODE = 17;
@UnsupportedAppUsage
String methodName;
@@ -1413,7 +1535,7 @@ public class RemoteViews implements Parcelable, Filter {
protected abstract Object getParameterValue(@Nullable View view) throws ActionException;
@Override
- public final void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public final void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final View view = root.findViewById(viewId);
if (view == null) return;
@@ -1431,7 +1553,7 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public final Action initActionAsync(ViewTree root, ViewGroup rootParent,
- OnClickHandler handler) {
+ InteractionHandler handler) {
final View view = root.findViewById(viewId);
if (view == null) return ACTION_NOOP;
@@ -1563,6 +1685,10 @@ public class RemoteViews implements Parcelable, Filter {
break;
case ICON:
this.value = in.readTypedObject(Icon.CREATOR);
+ break;
+ case BLEND_MODE:
+ this.value = BlendMode.fromValue(in.readInt());
+ break;
default:
break;
}
@@ -1606,6 +1732,9 @@ public class RemoteViews implements Parcelable, Filter {
case BUNDLE:
out.writeBundle((Bundle) this.value);
break;
+ case BLEND_MODE:
+ out.writeInt(BlendMode.toValue((BlendMode) this.value));
+ break;
case URI:
case BITMAP:
case INTENT:
@@ -1759,7 +1888,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
mRunnable.run();
}
}
@@ -1814,7 +1943,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final Context context = root.getContext();
final ViewGroup target = root.findViewById(viewId);
@@ -1827,7 +1956,8 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
+ public Action initActionAsync(ViewTree root, ViewGroup rootParent,
+ InteractionHandler handler) {
// In the async implementation, update the view tree so that subsequent calls to
// findViewById return the current view.
root.createTree();
@@ -1853,7 +1983,7 @@ public class RemoteViews implements Parcelable, Filter {
return new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
throws ActionException {
task.onPostExecute(tree);
targetVg.addView(task.mResult, mIndex);
@@ -1915,7 +2045,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final ViewGroup target = root.findViewById(viewId);
if (target == null) {
@@ -1931,7 +2061,8 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
+ public Action initActionAsync(ViewTree root, ViewGroup rootParent,
+ InteractionHandler handler) {
// In the async implementation, update the view tree so that subsequent calls to
// findViewById return the current view.
root.createTree();
@@ -1955,7 +2086,7 @@ public class RemoteViews implements Parcelable, Filter {
}
return new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
throws ActionException {
if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
targetVg.removeAllViews();
@@ -2011,7 +2142,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final View target = root.findViewById(viewId);
if (target == null || target == root) {
@@ -2025,7 +2156,8 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
+ public Action initActionAsync(ViewTree root, ViewGroup rootParent,
+ InteractionHandler handler) {
// In the async implementation, update the view tree so that subsequent calls to
// findViewById return the correct view.
root.createTree();
@@ -2044,7 +2176,7 @@ public class RemoteViews implements Parcelable, Filter {
parent.mChildren.remove(target);
return new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
throws ActionException {
parentVg.removeView(target.mRoot);
}
@@ -2124,7 +2256,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final TextView target = root.findViewById(viewId);
if (target == null) return;
if (drawablesLoaded) {
@@ -2154,7 +2286,8 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
+ public Action initActionAsync(ViewTree root, ViewGroup rootParent,
+ InteractionHandler handler) {
final TextView target = root.findViewById(viewId);
if (target == null) return ACTION_NOOP;
@@ -2232,7 +2365,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final TextView target = root.findViewById(viewId);
if (target == null) return;
target.setTextSize(units, size);
@@ -2277,7 +2410,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final View target = root.findViewById(viewId);
if (target == null) return;
target.setPadding(left, top, right, bottom);
@@ -2350,7 +2483,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final View target = root.findViewById(viewId);
if (target == null) {
return;
@@ -2463,7 +2596,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -2498,7 +2631,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
// Let's traverse the viewtree and override all textColors!
Stack<View> viewsToProcess = new Stack<>();
viewsToProcess.add(root);
@@ -2548,7 +2681,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler) {
final View target = root.findViewById(mViewId);
if (target == null) return;
@@ -2582,7 +2715,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
throws ActionException {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -2593,7 +2726,17 @@ public class RemoteViews implements Parcelable, Filter {
return;
}
- ((CompoundButton) target).setChecked(mChecked);
+ CompoundButton button = (CompoundButton) target;
+ Object tag = button.getTag(R.id.remote_checked_change_listener_tag);
+ // Temporarily unset the checked change listener so calling setChecked doesn't launch
+ // the intent.
+ if (tag instanceof OnCheckedChangeListener) {
+ button.setOnCheckedChangeListener(null);
+ button.setChecked(mChecked);
+ button.setOnCheckedChangeListener((OnCheckedChangeListener) tag);
+ } else {
+ button.setChecked(mChecked);
+ }
}
@Override
@@ -2623,7 +2766,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
throws ActionException {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -2633,7 +2776,32 @@ public class RemoteViews implements Parcelable, Filter {
return;
}
- ((RadioGroup) target).check(mCheckedId);
+ RadioGroup group = (RadioGroup) target;
+
+ // Temporarily unset all the checked change listeners while we check the group.
+ for (int i = 0; i < group.getChildCount(); i++) {
+ View child = group.getChildAt(i);
+ if (!(child instanceof CompoundButton)) continue;
+
+ Object tag = child.getTag(R.id.remote_checked_change_listener_tag);
+ if (!(tag instanceof OnCheckedChangeListener)) continue;
+
+ // Clear the checked change listener, we'll restore it after the check.
+ ((CompoundButton) child).setOnCheckedChangeListener(null);
+ }
+
+ group.check(mCheckedId);
+
+ // Loop through the children again and restore the checked change listeners.
+ for (int i = 0; i < group.getChildCount(); i++) {
+ View child = group.getChildAt(i);
+ if (!(child instanceof CompoundButton)) continue;
+
+ Object tag = child.getTag(R.id.remote_checked_change_listener_tag);
+ if (!(tag instanceof OnCheckedChangeListener)) continue;
+
+ ((CompoundButton) child).setOnCheckedChangeListener((OnCheckedChangeListener) tag);
+ }
}
@Override
@@ -2642,6 +2810,88 @@ public class RemoteViews implements Parcelable, Filter {
}
}
+ private static class SetViewOutlinePreferredRadiusAction extends Action {
+
+ private final boolean mIsDimen;
+ private final int mValue;
+
+ SetViewOutlinePreferredRadiusAction(@IdRes int viewId, @DimenRes int dimenResId) {
+ this.viewId = viewId;
+ this.mIsDimen = true;
+ this.mValue = dimenResId;
+ }
+
+ SetViewOutlinePreferredRadiusAction(
+ @IdRes int viewId, float radius, @ComplexDimensionUnit int units) {
+ this.viewId = viewId;
+ this.mIsDimen = false;
+ this.mValue = TypedValue.createComplexDimension(radius, units);
+
+ }
+
+ SetViewOutlinePreferredRadiusAction(Parcel in) {
+ viewId = in.readInt();
+ mIsDimen = in.readBoolean();
+ mValue = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(viewId);
+ dest.writeBoolean(mIsDimen);
+ dest.writeInt(mValue);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent, InteractionHandler handler)
+ throws ActionException {
+ final View target = root.findViewById(viewId);
+ if (target == null) return;
+
+ float radius;
+ if (mIsDimen) {
+ radius = mValue == 0 ? 0 : target.getResources().getDimension(mValue);
+ } else {
+ radius = TypedValue.complexToDimensionPixelSize(mValue,
+ target.getResources().getDisplayMetrics());
+ }
+ target.setOutlineProvider(new RemoteViewOutlineProvider(radius));
+ }
+
+ @Override
+ public int getActionTag() {
+ return SET_VIEW_OUTLINE_RADIUS_TAG;
+ }
+ }
+
+ /**
+ * OutlineProvider for a view with a radius set by
+ * {@link #setViewOutlinePreferredRadius(int, float, int)}.
+ */
+ public static final class RemoteViewOutlineProvider extends ViewOutlineProvider {
+
+ private final float mRadius;
+
+ public RemoteViewOutlineProvider(float radius) {
+ mRadius = radius;
+ }
+
+ /** Returns the corner radius used when providing the view outline. */
+ public float getRadius() {
+ return mRadius;
+ }
+
+ @Override
+ public void getOutline(@NonNull View view, @NonNull Outline outline) {
+ outline.setRoundRect(
+ 0 /*left*/,
+ 0 /* top */,
+ view.getWidth() /* right */,
+ view.getHeight() /* bottom */,
+ mRadius);
+ }
+ }
+
/**
* Create a new RemoteViews object that will display the views contained
* in the specified layout file.
@@ -2683,23 +2933,50 @@ public class RemoteViews implements Parcelable, Filter {
mClassCookies = null;
}
+ private boolean hasMultipleLayouts() {
+ return hasLandscapeAndPortraitLayouts() || hasSizedRemoteViews();
+ }
+
private boolean hasLandscapeAndPortraitLayouts() {
return (mLandscape != null) && (mPortrait != null);
}
+ private boolean hasSizedRemoteViews() {
+ return mSizedRemoteViews != null;
+ }
+
+ private @Nullable PointF getIdealSize() {
+ return mIdealSize;
+ }
+
+ private void setIdealSize(@Nullable PointF size) {
+ mIdealSize = size;
+ }
+
+ /**
+ * Finds the smallest view in {@code mSizedRemoteViews}.
+ * This method must not be called if {@code mSizedRemoteViews} is null.
+ */
+ private RemoteViews findSmallestRemoteView() {
+ return mSizedRemoteViews.get(mSizedRemoteViews.size() - 1);
+ }
+
/**
* Create a new RemoteViews object that will inflate as the specified
* landspace or portrait RemoteViews, depending on the current configuration.
*
* @param landscape The RemoteViews to inflate in landscape configuration
* @param portrait The RemoteViews to inflate in portrait configuration
+ * @throws IllegalArgumentException if either landscape or portrait are null or if they are
+ * not from the same application
*/
public RemoteViews(RemoteViews landscape, RemoteViews portrait) {
if (landscape == null || portrait == null) {
- throw new RuntimeException("Both RemoteViews must be non-null");
+ throw new IllegalArgumentException("Both RemoteViews must be non-null");
}
if (!landscape.hasSameAppInfo(portrait.mApplication)) {
- throw new RuntimeException("Both RemoteViews must share the same package and user");
+ throw new IllegalArgumentException(
+ "Both RemoteViews must share the same package and user");
}
mApplication = portrait.mApplication;
mLayoutId = portrait.mLayoutId;
@@ -2717,9 +2994,84 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Create a new RemoteViews object that will inflate the layout with the closest size
+ * specification.
+ *
+ * The default remote views in that case is always the smallest one provided.
+ *
+ * @param remoteViews Mapping of size to layout.
+ * @throws IllegalArgumentException if the map is empty, there are more than
+ * MAX_INIT_VIEW_COUNT layouts or the remote views are not all from the same application.
+ */
+ public RemoteViews(@NonNull Map<PointF, RemoteViews> remoteViews) {
+ if (remoteViews.isEmpty()) {
+ throw new IllegalArgumentException("The set of RemoteViews cannot be empty");
+ }
+ if (remoteViews.size() > MAX_INIT_VIEW_COUNT) {
+ throw new IllegalArgumentException("Too many RemoteViews in constructor");
+ }
+ if (remoteViews.size() == 1) {
+ initializeFrom(remoteViews.values().iterator().next());
+ return;
+ }
+ mBitmapCache = new BitmapCache();
+ mClassCookies = initializeSizedRemoteViews(
+ remoteViews.entrySet().stream().map(
+ entry -> {
+ entry.getValue().setIdealSize(entry.getKey());
+ return entry.getValue();
+ }
+ ).iterator()
+ );
+
+ RemoteViews smallestView = findSmallestRemoteView();
+ mApplication = smallestView.mApplication;
+ mLayoutId = smallestView.mLayoutId;
+ mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
+ }
+
+ // Initialize mSizedRemoteViews and return the class cookies.
+ private Map<Class, Object> initializeSizedRemoteViews(Iterator<RemoteViews> remoteViews) {
+ List<RemoteViews> sizedRemoteViews = new ArrayList<>();
+ Map<Class, Object> classCookies = null;
+ float viewArea = Float.MAX_VALUE;
+ RemoteViews smallestView = null;
+ while (remoteViews.hasNext()) {
+ RemoteViews view = remoteViews.next();
+ PointF size = view.getIdealSize();
+ float newViewArea = size.x * size.y;
+ if (smallestView != null && !view.hasSameAppInfo(smallestView.mApplication)) {
+ throw new IllegalArgumentException(
+ "All RemoteViews must share the same package and user");
+ }
+ if (smallestView == null || newViewArea < viewArea) {
+ if (smallestView != null) {
+ sizedRemoteViews.add(smallestView);
+ }
+ viewArea = newViewArea;
+ smallestView = view;
+ } else {
+ sizedRemoteViews.add(view);
+ }
+ configureRemoteViewsAsChild(view);
+ view.setIdealSize(size);
+ if (classCookies == null) {
+ classCookies = view.mClassCookies;
+ }
+ }
+ sizedRemoteViews.add(smallestView);
+ mSizedRemoteViews = sizedRemoteViews;
+ return classCookies;
+ }
+
+ /**
* Creates a copy of another RemoteViews.
*/
public RemoteViews(RemoteViews src) {
+ initializeFrom(src);
+ }
+
+ private void initializeFrom(RemoteViews src) {
mBitmapCache = src.mBitmapCache;
mApplication = src.mApplication;
mIsRoot = src.mIsRoot;
@@ -2727,12 +3079,20 @@ public class RemoteViews implements Parcelable, Filter {
mLightBackgroundLayoutId = src.mLightBackgroundLayoutId;
mApplyFlags = src.mApplyFlags;
mClassCookies = src.mClassCookies;
+ mIdealSize = src.mIdealSize;
if (src.hasLandscapeAndPortraitLayouts()) {
mLandscape = new RemoteViews(src.mLandscape);
mPortrait = new RemoteViews(src.mPortrait);
}
+ if (src.hasSizedRemoteViews()) {
+ mSizedRemoteViews = new ArrayList<>(src.mSizedRemoteViews.size());
+ for (RemoteViews srcView : src.mSizedRemoteViews) {
+ mSizedRemoteViews.add(new RemoteViews(srcView));
+ }
+ }
+
if (src.mActions != null) {
Parcel p = Parcel.obtain();
p.putClassCookies(mClassCookies);
@@ -2782,10 +3142,29 @@ public class RemoteViews implements Parcelable, Filter {
if (mode == MODE_NORMAL) {
mApplication = parcel.readInt() == 0 ? info :
ApplicationInfo.CREATOR.createFromParcel(parcel);
+ mIdealSize = parcel.readInt() == 0 ? null : PointF.CREATOR.createFromParcel(parcel);
mLayoutId = parcel.readInt();
mLightBackgroundLayoutId = parcel.readInt();
readActionsFromParcel(parcel, depth);
+ } else if (mode == MODE_HAS_SIZED_REMOTEVIEWS) {
+ int numViews = parcel.readInt();
+ if (numViews > MAX_INIT_VIEW_COUNT) {
+ throw new IllegalArgumentException(
+ "Too many views in mapping from size to RemoteViews.");
+ }
+ List<RemoteViews> remoteViews = new ArrayList<>(numViews);
+ for (int i = 0; i < numViews; i++) {
+ RemoteViews view = new RemoteViews(parcel, mBitmapCache, info, depth,
+ mClassCookies);
+ info = view.mApplication;
+ remoteViews.add(view);
+ }
+ initializeSizedRemoteViews(remoteViews.iterator());
+ RemoteViews smallestView = findSmallestRemoteView();
+ mApplication = smallestView.mApplication;
+ mLayoutId = smallestView.mLayoutId;
+ mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
} else {
// MODE_HAS_LANDSCAPE_AND_PORTRAIT
mLandscape = new RemoteViews(parcel, mBitmapCache, info, depth, mClassCookies);
@@ -2860,6 +3239,10 @@ public class RemoteViews implements Parcelable, Filter {
return new SetCompoundButtonCheckedAction(parcel);
case SET_RADIO_GROUP_CHECKED:
return new SetRadioGroupCheckedAction(parcel);
+ case SET_VIEW_OUTLINE_RADIUS_TAG:
+ return new SetViewOutlinePreferredRadiusAction(parcel);
+ case SET_ON_CHECKED_CHANGE_RESPONSE_TAG:
+ return new SetOnCheckedChangeResponse(parcel);
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -2903,16 +3286,20 @@ public class RemoteViews implements Parcelable, Filter {
*/
private void setBitmapCache(BitmapCache bitmapCache) {
mBitmapCache = bitmapCache;
- if (!hasLandscapeAndPortraitLayouts()) {
+ if (hasSizedRemoteViews()) {
+ for (RemoteViews remoteView : mSizedRemoteViews) {
+ remoteView.setBitmapCache(bitmapCache);
+ }
+ } else if (hasLandscapeAndPortraitLayouts()) {
+ mLandscape.setBitmapCache(bitmapCache);
+ mPortrait.setBitmapCache(bitmapCache);
+ } else {
if (mActions != null) {
final int count = mActions.size();
- for (int i= 0; i < count; ++i) {
+ for (int i = 0; i < count; ++i) {
mActions.get(i).setBitmapCache(bitmapCache);
}
}
- } else {
- mLandscape.setBitmapCache(bitmapCache);
- mPortrait.setBitmapCache(bitmapCache);
}
}
@@ -2931,10 +3318,10 @@ public class RemoteViews implements Parcelable, Filter {
* @param a The action to add
*/
private void addAction(Action a) {
- if (hasLandscapeAndPortraitLayouts()) {
- throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
- " layouts cannot be modified. Instead, fully configure the landscape and" +
- " portrait layouts individually before constructing the combined layout.");
+ if (hasMultipleLayouts()) {
+ throw new RuntimeException("RemoteViews specifying separate layouts for orientation"
+ + " or size cannot be modified. Instead, fully configure each layouts"
+ + " individually before constructing the combined layout.");
}
if (mActions == null) {
mActions = new ArrayList<>();
@@ -3313,6 +3700,41 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Equivalent to calling
+ * {@link android.widget.CompoundButton#setOnCheckedChangeListener(
+ * android.widget.CompoundButton.OnCheckedChangeListener)}
+ * to launch the provided {@link RemoteResponse}.
+ *
+ * The intent will be filled with the current checked state of the view at the key
+ * {@link #EXTRA_CHECKED}.
+ *
+ * The {@link RemoteResponse} will not be launched in response to check changes arising from
+ * {@link #setCompoundButtonChecked(int, boolean)} or {@link #setRadioGroupChecked(int, int)}
+ * usages.
+ *
+ * The {@link RemoteResponse} must be created using
+ * {@link RemoteResponse#fromFillInIntent(Intent)} in conjunction with
+ * {@link RemoteViews#setPendingIntentTemplate(int, PendingIntent)} for items inside
+ * collections (eg. {@link ListView}, {@link StackView} etc.).
+ *
+ * Otherwise, create the {@link RemoteResponse} using
+ * {@link RemoteResponse#fromPendingIntent(PendingIntent)}.
+ *
+ * @param viewId The id of the view that will trigger the {@link PendingIntent} when checked
+ * state changes.
+ * @param response The {@link RemoteResponse} to send when the checked state changes.
+ */
+ public void setOnCheckedChangeResponse(
+ @IdRes int viewId,
+ @NonNull RemoteResponse response) {
+ addAction(
+ new SetOnCheckedChangeResponse(
+ viewId,
+ response.setInteractionType(
+ RemoteResponse.INTERACTION_TYPE_CHECKED_CHANGE)));
+ }
+
+ /**
* @hide
* Equivalent to calling
* {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
@@ -3595,6 +4017,28 @@ 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.
+ *
+ * <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
+ * display with a different density.
+ */
+ public void setViewOutlinePreferredRadius(
+ @IdRes int viewId, float radius, @ComplexDimensionUnit int units) {
+ addAction(new SetViewOutlinePreferredRadiusAction(viewId, radius, units));
+ }
+
+ /**
+ * Sets an OutlineProvider on the view whose corner radius is a dimension resource with
+ * {@code resId}. This outline may change shape during system transitions.
+ */
+ public void setViewOutlinePreferredRadiusDimen(@IdRes int viewId, @DimenRes int resId) {
+ addAction(new SetViewOutlinePreferredRadiusAction(viewId, resId));
+ }
+
+ /**
* Call a method taking one boolean on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view on which to call the method.
@@ -3867,6 +4311,18 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Call a method taking one BlendMode on a view in the layout for this RemoteViews.
+ *
+ * @param viewId The id of the view on which to call the method.
+ * @param methodName The name of the method to call.
+ * @param value The value to pass to the method.
+ */
+ public void setBlendMode(@IdRes int viewId, @NonNull String methodName,
+ @Nullable BlendMode value) {
+ addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.BLEND_MODE, value));
+ }
+
+ /**
* Call a method taking one Bundle on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view on which to call the method.
@@ -3991,14 +4447,79 @@ public class RemoteViews implements Parcelable, Filter {
int orientation = context.getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
return mLandscape;
- } else {
- return mPortrait;
}
+ return mPortrait;
+ }
+ if (hasSizedRemoteViews()) {
+ return findSmallestRemoteView();
}
return this;
}
/**
+ * Returns the square distance between two points.
+ *
+ * This is particularly useful when we only care about the ordering of the distances.
+ */
+ private static float squareDistance(PointF p1, PointF p2) {
+ float dx = p1.x - p2.x;
+ float dy = p1.y - p2.y;
+ return dx * dx + dy * dy;
+ }
+
+ /**
+ * Returns whether the layout fits in the space available to the widget.
+ *
+ * A layout fits on a widget if the widget size is known (i.e. not null) and both dimensions
+ * are smaller than the ones of the widget, adding some padding to account for rounding errors.
+ */
+ private static boolean fitsIn(PointF sizeLayout, @Nullable PointF sizeWidget) {
+ return sizeWidget != null && (Math.ceil(sizeWidget.x) + 1 > sizeLayout.x)
+ && (Math.ceil(sizeWidget.y) + 1 > sizeLayout.y);
+ }
+
+ /**
+ * Returns the most appropriate {@link RemoteViews} given the context and, if not null, the
+ * size of the widget.
+ *
+ * If {@link RemoteViews#hasSizedRemoteViews()} returns true, the most appropriate view is
+ * the one that fits in the widget (according to {@link RemoteViews#fitsIn}) and has the
+ * diagonal the most similar to the widget. If no layout fits or the size of the widget is
+ * not specified, the one with the smallest area will be chosen.
+ */
+ private RemoteViews getRemoteViewsToApply(@NonNull Context context,
+ @Nullable PointF widgetSize) {
+ if (!hasSizedRemoteViews()) {
+ // If there isn't multiple remote views, fall back on the previous methods.
+ return getRemoteViewsToApply(context);
+ }
+ // Find the better remote view
+ RemoteViews bestFit = null;
+ float bestSqDist = Float.MAX_VALUE;
+ for (RemoteViews layout : mSizedRemoteViews) {
+ PointF layoutSize = layout.getIdealSize();
+ if (fitsIn(layoutSize, widgetSize)) {
+ if (bestFit == null) {
+ bestFit = layout;
+ bestSqDist = squareDistance(layoutSize, widgetSize);
+ } else {
+ float newSqDist = squareDistance(layoutSize, widgetSize);
+ if (newSqDist < bestSqDist) {
+ bestFit = layout;
+ bestSqDist = newSqDist;
+ }
+ }
+ }
+ }
+ if (bestFit == null) {
+ Log.w(LOG_TAG, "Could not find a RemoteViews fitting the current size: " + widgetSize);
+ return findSmallestRemoteView();
+ }
+ return bestFit;
+ }
+
+
+ /**
* Inflates the view hierarchy represented by this object and applies
* all of the actions.
*
@@ -4014,8 +4535,14 @@ public class RemoteViews implements Parcelable, Filter {
}
/** @hide */
- public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
- RemoteViews rvToApply = getRemoteViewsToApply(context);
+ public View apply(Context context, ViewGroup parent, InteractionHandler handler) {
+ return apply(context, parent, handler, null);
+ }
+
+ /** @hide */
+ public View apply(@NonNull Context context, @NonNull ViewGroup parent,
+ @Nullable InteractionHandler handler, @Nullable PointF size) {
+ RemoteViews rvToApply = getRemoteViewsToApply(context, size);
View result = inflateView(context, rvToApply, parent);
rvToApply.performApply(result, parent, handler);
@@ -4023,9 +4550,17 @@ public class RemoteViews implements Parcelable, Filter {
}
/** @hide */
- public View applyWithTheme(Context context, ViewGroup parent, OnClickHandler handler,
- @StyleRes int applyThemeResId) {
- RemoteViews rvToApply = getRemoteViewsToApply(context);
+ public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
+ @Nullable InteractionHandler handler, @StyleRes int applyThemeResId) {
+ return applyWithTheme(context, parent, handler, applyThemeResId, null);
+ }
+
+ /** @hide */
+ public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
+ @Nullable InteractionHandler handler,
+ @StyleRes int applyThemeResId,
+ @Nullable PointF size) {
+ RemoteViews rvToApply = getRemoteViewsToApply(context, size);
View result = inflateView(context, rvToApply, parent, applyThemeResId);
rvToApply.performApply(result, parent, handler);
@@ -4107,15 +4642,30 @@ public class RemoteViews implements Parcelable, Filter {
return applyAsync(context, parent, executor, listener, null);
}
+
+ /** @hide */
+ public CancellationSignal applyAsync(Context context, ViewGroup parent,
+ Executor executor, OnViewAppliedListener listener, InteractionHandler handler) {
+ return applyAsync(context, parent, executor, listener, handler, null);
+ }
+
/** @hide */
public CancellationSignal applyAsync(Context context, ViewGroup parent,
- Executor executor, OnViewAppliedListener listener, OnClickHandler handler) {
- return getAsyncApplyTask(context, parent, listener, handler).startTaskOnExecutor(executor);
+ Executor executor, OnViewAppliedListener listener, InteractionHandler handler,
+ PointF size) {
+ return getAsyncApplyTask(context, parent, listener, handler, size).startTaskOnExecutor(
+ executor);
}
private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
- OnViewAppliedListener listener, OnClickHandler handler) {
- return new AsyncApplyTask(getRemoteViewsToApply(context), parent, context, listener,
+ OnViewAppliedListener listener, InteractionHandler handler) {
+ return getAsyncApplyTask(context, parent, listener, handler, null);
+ }
+
+ private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
+ OnViewAppliedListener listener, InteractionHandler handler, PointF size) {
+ return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context,
+ listener,
handler, null);
}
@@ -4126,7 +4676,7 @@ public class RemoteViews implements Parcelable, Filter {
final ViewGroup mParent;
final Context mContext;
final OnViewAppliedListener mListener;
- final OnClickHandler mHandler;
+ final InteractionHandler mHandler;
private View mResult;
private ViewTree mTree;
@@ -4135,7 +4685,7 @@ public class RemoteViews implements Parcelable, Filter {
private AsyncApplyTask(
RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
- OnClickHandler handler, View result) {
+ InteractionHandler handler, View result) {
mRV = rv;
mParent = parent;
mContext = context;
@@ -4180,8 +4730,8 @@ public class RemoteViews implements Parcelable, Filter {
try {
if (mActions != null) {
- OnClickHandler handler = mHandler == null
- ? DEFAULT_ON_CLICK_HANDLER : mHandler;
+ InteractionHandler handler = mHandler == null
+ ? DEFAULT_INTERACTION_HANDLER : mHandler;
for (Action a : mActions) {
a.apply(viewTree.mRoot, mParent, handler);
}
@@ -4227,17 +4777,23 @@ public class RemoteViews implements Parcelable, Filter {
* the {@link #apply(Context,ViewGroup)} call.
*/
public void reapply(Context context, View v) {
- reapply(context, v, null);
+ reapply(context, v, null, null);
}
/** @hide */
- public void reapply(Context context, View v, OnClickHandler handler) {
- RemoteViews rvToApply = getRemoteViewsToApply(context);
+ public void reapply(Context context, View v, InteractionHandler handler) {
+ reapply(context, v, handler, null);
+ }
- // In the case that a view has this RemoteViews applied in one orientation, is persisted
- // across orientation change, and has the RemoteViews re-applied in the new orientation,
- // we throw an exception, since the layouts may be completely unrelated.
- if (hasLandscapeAndPortraitLayouts()) {
+ /** @hide */
+ public void reapply(Context context, View v, InteractionHandler handler, PointF size) {
+ RemoteViews rvToApply = getRemoteViewsToApply(context, size);
+
+ // In the case that a view has this RemoteViews applied in one orientation or size, is
+ // persisted across change, and has the RemoteViews re-applied in a different situation
+ // (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()) {
throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
" that does not share the same root layout id.");
@@ -4262,18 +4818,24 @@ public class RemoteViews implements Parcelable, Filter {
*/
public CancellationSignal reapplyAsync(
Context context, View v, Executor executor, OnViewAppliedListener listener) {
- return reapplyAsync(context, v, executor, listener, null);
+ return reapplyAsync(context, v, executor, listener, null, null);
+ }
+
+ /** @hide */
+ public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
+ OnViewAppliedListener listener, InteractionHandler handler) {
+ return reapplyAsync(context, v, executor, listener, handler, null);
}
/** @hide */
public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
- OnViewAppliedListener listener, OnClickHandler handler) {
- RemoteViews rvToApply = getRemoteViewsToApply(context);
+ OnViewAppliedListener listener, InteractionHandler handler, PointF size) {
+ RemoteViews rvToApply = getRemoteViewsToApply(context, size);
// In the case that a view has this RemoteViews applied in one orientation, is persisted
// across orientation change, and has the RemoteViews re-applied in the new orientation,
// we throw an exception, since the layouts may be completely unrelated.
- if (hasLandscapeAndPortraitLayouts()) {
+ if (hasMultipleLayouts()) {
if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
" that does not share the same root layout id.");
@@ -4284,9 +4846,9 @@ public class RemoteViews implements Parcelable, Filter {
context, listener, handler, v).startTaskOnExecutor(executor);
}
- private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
+ private void performApply(View v, ViewGroup parent, InteractionHandler handler) {
if (mActions != null) {
- handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
+ handler = handler == null ? DEFAULT_INTERACTION_HANDLER : handler;
final int count = mActions.size();
for (int i = 0; i < count; i++) {
Action a = mActions.get(i);
@@ -4357,7 +4919,7 @@ public class RemoteViews implements Parcelable, Filter {
}
public void writeToParcel(Parcel dest, int flags) {
- if (!hasLandscapeAndPortraitLayouts()) {
+ if (!hasMultipleLayouts()) {
dest.writeInt(MODE_NORMAL);
// We only write the bitmap cache if we are the root RemoteViews, as this cache
// is shared by all children.
@@ -4370,9 +4932,26 @@ public class RemoteViews implements Parcelable, Filter {
dest.writeInt(1);
mApplication.writeToParcel(dest, flags);
}
+ if (mIsRoot || mIdealSize == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ mIdealSize.writeToParcel(dest, flags);
+ }
dest.writeInt(mLayoutId);
dest.writeInt(mLightBackgroundLayoutId);
writeActionsToParcel(dest);
+ } else if (hasSizedRemoteViews()) {
+ dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS);
+ if (mIsRoot) {
+ mBitmapCache.writeBitmapsToParcel(dest, flags);
+ }
+ int childFlags = flags;
+ dest.writeInt(mSizedRemoteViews.size());
+ for (RemoteViews view : mSizedRemoteViews) {
+ view.writeToParcel(dest, childFlags);
+ childFlags |= PARCELABLE_ELIDE_DUPLICATES;
+ }
} else {
dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
// We only write the bitmap cache if we are the root RemoteViews, as this cache
@@ -4585,9 +5164,22 @@ public class RemoteViews implements Parcelable, Filter {
*/
public static class RemoteResponse {
+ /** @hide **/
+ @IntDef(prefix = "INTERACTION_TYPE_", value = {
+ INTERACTION_TYPE_CLICK,
+ INTERACTION_TYPE_CHECKED_CHANGE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface InteractionType {}
+ /** @hide */
+ public static final int INTERACTION_TYPE_CLICK = 0;
+ /** @hide */
+ public static final int INTERACTION_TYPE_CHECKED_CHANGE = 1;
+
private PendingIntent mPendingIntent;
private Intent mFillIntent;
+ private int mInteractionType = INTERACTION_TYPE_CLICK;
private IntArray mViewIds;
private ArrayList<String> mElementNames;
@@ -4626,11 +5218,9 @@ public class RemoteViews implements Parcelable, Filter {
* before starting the intent.
*
* @param fillIntent The intent which will be combined with the parent's PendingIntent in
- * order to determine the behavior of the response
- *
+ * order to determine the behavior of the response
* @see RemoteViews#setPendingIntentTemplate(int, PendingIntent)
* @see RemoteViews#setOnClickFillInIntent(int, Intent)
- * @return
*/
@NonNull
public static RemoteResponse fromFillInIntent(@NonNull Intent fillIntent) {
@@ -4645,9 +5235,8 @@ public class RemoteViews implements Parcelable, Filter {
* the epicenter for the exit Transition. The position of the associated shared element in
* the launched Activity will be the epicenter of its entering Transition.
*
- * @param viewId The id of the view to be shared as part of the transition
+ * @param viewId The id of the view to be shared as part of the transition
* @param sharedElementName The shared element name for this view
- *
* @see ActivityOptions#makeSceneTransitionAnimation(Activity, Pair[])
*/
@NonNull
@@ -4662,12 +5251,27 @@ public class RemoteViews implements Parcelable, Filter {
return this;
}
+ /**
+ * Sets the interaction type for which this RemoteResponse responds.
+ *
+ * @param type the type of interaction for which this is a response, such as clicking or
+ * checked state changing
+ *
+ * @hide
+ */
+ @NonNull
+ public RemoteResponse setInteractionType(@InteractionType int type) {
+ mInteractionType = type;
+ return this;
+ }
+
private void writeToParcel(Parcel dest, int flags) {
PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest);
if (mPendingIntent == null) {
// Only write the intent if pending intent is null
dest.writeTypedObject(mFillIntent, flags);
}
+ dest.writeInt(mInteractionType);
dest.writeIntArray(mViewIds == null ? null : mViewIds.toArray());
dest.writeStringList(mElementNames);
}
@@ -4677,50 +5281,62 @@ public class RemoteViews implements Parcelable, Filter {
if (mPendingIntent == null) {
mFillIntent = parcel.readTypedObject(Intent.CREATOR);
}
+ mInteractionType = parcel.readInt();
int[] viewIds = parcel.createIntArray();
mViewIds = viewIds == null ? null : IntArray.wrap(viewIds);
mElementNames = parcel.createStringArrayList();
}
- private void handleViewClick(View v, OnClickHandler handler) {
+ private void handleViewInteraction(
+ View v,
+ InteractionHandler handler) {
final PendingIntent pi;
if (mPendingIntent != null) {
pi = mPendingIntent;
} else if (mFillIntent != null) {
- // Insure that this view is a child of an AdapterView
- View parent = (View) v.getParent();
- // Break the for loop on the first encounter of:
- // 1) an AdapterView,
- // 2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
- // 3) a null parent.
- // 2) and 3) are unexpected and catch the case where a child is not
- // correctly parented in an AdapterView.
- while (parent != null && !(parent instanceof AdapterView<?>)
- && !((parent instanceof AppWidgetHostView)
- && !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
- parent = (View) parent.getParent();
- }
-
- if (!(parent instanceof AdapterView<?>)) {
- // Somehow they've managed to get this far without having
- // and AdapterView as a parent.
+ AdapterView<?> ancestor = getAdapterViewAncestor(v);
+ if (ancestor == null) {
Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent");
return;
}
- // Insure that a template pending intent has been set on an ancestor
- if (!(parent.getTag() instanceof PendingIntent)) {
- Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without"
- + " calling setPendingIntentTemplate on parent.");
+
+ // Ensure that a template pending intent has been set on the ancestor
+ if (!(ancestor.getTag() instanceof PendingIntent)) {
+ Log.e(LOG_TAG, "Attempting setOnClickFillInIntent or "
+ + "setOnCheckedChangeFillInIntent without calling "
+ + "setPendingIntentTemplate on parent.");
return;
}
- pi = (PendingIntent) parent.getTag();
+ pi = (PendingIntent) ancestor.getTag();
} else {
Log.e(LOG_TAG, "Response has neither pendingIntent nor fillInIntent");
return;
}
- handler.onClickHandler(v, pi, this);
+ handler.onInteraction(v, pi, this);
+ }
+
+ /**
+ * Returns the closest ancestor of the view that is an AdapterView or null if none could be
+ * found.
+ */
+ @Nullable
+ private static AdapterView<?> getAdapterViewAncestor(@Nullable View view) {
+ View parent = (View) view.getParent();
+ // Break the for loop on the first encounter of:
+ // 1) an AdapterView,
+ // 2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
+ // 3) a null parent.
+ // 2) and 3) are unexpected and catch the case where a child is not
+ // correctly parented in an AdapterView.
+ while (parent != null && !(parent instanceof AdapterView<?>)
+ && !((parent instanceof AppWidgetHostView)
+ && !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
+ parent = (View) parent.getParent();
+ }
+
+ return parent instanceof AdapterView<?> ? (AdapterView<?>) parent : null;
}
/** @hide */
@@ -4728,6 +5344,11 @@ public class RemoteViews implements Parcelable, Filter {
Intent intent = mPendingIntent != null ? new Intent() : new Intent(mFillIntent);
intent.setSourceBounds(getSourceBounds(view));
+ if (view instanceof CompoundButton
+ && mInteractionType == INTERACTION_TYPE_CHECKED_CHANGE) {
+ intent.putExtra(EXTRA_CHECKED, ((CompoundButton) view).isChecked());
+ }
+
ActivityOptions opts = null;
Context context = view.getContext();
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index c98ed6a2f5d0..9749a688a97f 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -47,7 +47,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
-import android.widget.RemoteViews.OnClickHandler;
+import android.widget.RemoteViews.InteractionHandler;
import com.android.internal.widget.IRemoteViewsFactory;
@@ -107,7 +107,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
private final boolean mOnLightBackground;
private final Executor mAsyncViewLoadExecutor;
- private OnClickHandler mRemoteViewsOnClickHandler;
+ private InteractionHandler mRemoteViewsInteractionHandler;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final FixedSizeRemoteViewsCache mCache;
private int mVisibleWindowLowerBound;
@@ -386,9 +386,9 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
* asynchronously (for eg, when we are already showing the loading
* view)
*/
- public void onRemoteViewsLoaded(RemoteViews view, OnClickHandler handler,
+ public void onRemoteViewsLoaded(RemoteViews view, InteractionHandler handler,
boolean forceApplyAsync) {
- setOnClickHandler(handler);
+ setInteractionHandler(handler);
applyRemoteViews(view, forceApplyAsync || ((view != null) && view.prefersAsyncApply()));
}
@@ -455,7 +455,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
if (refs != null) {
// Notify all the references for that position of the newly loaded RemoteViews
for (final RemoteViewsFrameLayout ref : refs) {
- ref.onRemoteViewsLoaded(view, mRemoteViewsOnClickHandler, true);
+ ref.onRemoteViewsLoaded(view, mRemoteViewsInteractionHandler, true);
}
}
}
@@ -902,9 +902,9 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
return mDataReady;
}
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public void setRemoteViewsOnClickHandler(OnClickHandler handler) {
- mRemoteViewsOnClickHandler = handler;
+ /** @hide */
+ public void setRemoteViewsInteractionHandler(InteractionHandler handler) {
+ mRemoteViewsInteractionHandler = handler;
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -1137,7 +1137,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
if (isInCache) {
// Apply the view synchronously if possible, to avoid flickering
- layout.onRemoteViewsLoaded(rv, mRemoteViewsOnClickHandler, false);
+ layout.onRemoteViewsLoaded(rv, mRemoteViewsInteractionHandler, false);
if (hasNewItems) {
mServiceHandler.sendEmptyMessage(MSG_LOAD_NEXT_ITEM);
}
@@ -1146,7 +1146,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
// exist, the layout will create a default view based on the firstView height.
layout.onRemoteViewsLoaded(
mCache.getMetaData().getLoadingTemplate(mContext).remoteViews,
- mRemoteViewsOnClickHandler,
+ mRemoteViewsInteractionHandler,
false);
mRequestedViews.add(position, layout);
mCache.queueRequestedPositionToLoad(position);
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index f3de9828c4aa..64d09de49f2d 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -98,7 +98,7 @@ public class ScrollView extends FrameLayout {
*/
@NonNull
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768600)
- private EdgeEffect mEdgeGlowTop = new EdgeEffect(getContext());
+ private EdgeEffect mEdgeGlowTop;
/**
* Tracks the state of the bottom edge glow.
@@ -108,7 +108,7 @@ public class ScrollView extends FrameLayout {
*/
@NonNull
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769386)
- private EdgeEffect mEdgeGlowBottom = new EdgeEffect(getContext());
+ private EdgeEffect mEdgeGlowBottom;
/**
* Position of the last motion event.
@@ -213,6 +213,8 @@ public class ScrollView extends FrameLayout {
public ScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mEdgeGlowTop = new EdgeEffect(context, attrs);
+ mEdgeGlowBottom = new EdgeEffect(context, attrs);
initScrollView();
final TypedArray a = context.obtainStyledAttributes(
@@ -679,7 +681,15 @@ public class ScrollView extends FrameLayout {
* isFinished() is correct.
*/
mScroller.computeScrollOffset();
- mIsBeingDragged = !mScroller.isFinished();
+ mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowBottom.isFinished()
+ || !mEdgeGlowTop.isFinished();
+ // Catch the edge effect if it is active.
+ if (!mEdgeGlowTop.isFinished()) {
+ mEdgeGlowTop.onPullDistance(0f, ev.getX() / getWidth());
+ }
+ if (!mEdgeGlowBottom.isFinished()) {
+ mEdgeGlowBottom.onPullDistance(0f, 1f - ev.getX() / getWidth());
+ }
if (mIsBeingDragged && mScrollStrictSpan == null) {
mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
}
@@ -732,7 +742,8 @@ public class ScrollView extends FrameLayout {
if (getChildCount() == 0) {
return false;
}
- if ((mIsBeingDragged = !mScroller.isFinished())) {
+ if ((mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowTop.isFinished()
+ || !mEdgeGlowBottom.isFinished())) {
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
@@ -793,6 +804,21 @@ public class ScrollView extends FrameLayout {
boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
+ final float displacement = ev.getX(activePointerIndex) / getWidth();
+ if (canOverscroll) {
+ int consumed = 0;
+ if (deltaY < 0 && mEdgeGlowBottom.getDistance() != 0f) {
+ consumed = Math.round(getHeight()
+ * mEdgeGlowBottom.onPullDistance((float) deltaY / getHeight(),
+ 1 - displacement));
+ } else if (deltaY > 0 && mEdgeGlowTop.getDistance() != 0f) {
+ consumed = Math.round(-getHeight()
+ * mEdgeGlowTop.onPullDistance((float) -deltaY / getHeight(),
+ displacement));
+ }
+ deltaY -= consumed;
+ }
+
// Calling overScrollBy will call onOverScrolled, which
// calls onScrollChanged if applicable.
if (overScrollBy(0, deltaY, 0, mScrollY, 0, range, 0, mOverscrollDistance, true)
@@ -807,17 +833,17 @@ public class ScrollView extends FrameLayout {
mLastMotionY -= mScrollOffset[1];
vtev.offsetLocation(0, mScrollOffset[1]);
mNestedYOffset += mScrollOffset[1];
- } else if (canOverscroll) {
+ } else if (canOverscroll && deltaY != 0f) {
final int pulledToY = oldY + deltaY;
if (pulledToY < 0) {
- mEdgeGlowTop.onPull((float) deltaY / getHeight(),
- ev.getX(activePointerIndex) / getWidth());
+ mEdgeGlowTop.onPullDistance((float) -deltaY / getHeight(),
+ displacement);
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
} else if (pulledToY > range) {
- mEdgeGlowBottom.onPull((float) deltaY / getHeight(),
- 1.f - ev.getX(activePointerIndex) / getWidth());
+ mEdgeGlowBottom.onPullDistance((float) deltaY / getHeight(),
+ 1.f - displacement);
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
}
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 794b642135d0..d59a415469b6 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -69,9 +69,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
private final TextView mTextView;
SpellCheckerSession mSpellCheckerSession;
- // We assume that the sentence level spell check will always provide better results than words.
- // Although word SC has a sequential option.
- private boolean mIsSentenceSpellCheckSupported;
+
final int mCookie;
// Paired arrays for the (id, spellCheckSpan) pair. A negative id means the associated
@@ -134,7 +132,6 @@ public class SpellChecker implements SpellCheckerSessionListener {
| SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO
| SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR
| SuggestionsInfo.RESULT_ATTR_DONT_SHOW_UI_FOR_SUGGESTIONS);
- mIsSentenceSpellCheckSupported = true;
}
// Restore SpellCheckSpans in pool
@@ -318,13 +315,11 @@ public class SpellChecker implements SpellCheckerSessionListener {
&& WordIterator.isMidWordPunctuation(
mCurrentLocale, Character.codePointBefore(editable, end + 1))) {
isEditing = false;
- } else if (mIsSentenceSpellCheckSupported) {
+ } else {
// 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;
- } else {
- isEditing = selectionEnd < start || selectionStart > end;
}
if (start >= 0 && end > start && (forceCheckWhenEditingWord || isEditing)) {
spellCheckSpan.setSpellCheckInProgress(true);
@@ -346,13 +341,8 @@ public class SpellChecker implements SpellCheckerSessionListener {
textInfos = textInfosCopy;
}
- if (mIsSentenceSpellCheckSupported) {
- mSpellCheckerSession.getSentenceSuggestions(
- textInfos, SuggestionSpan.SUGGESTIONS_MAX_SIZE);
- } else {
- mSpellCheckerSession.getSuggestions(textInfos, SuggestionSpan.SUGGESTIONS_MAX_SIZE,
- false /* TODO Set sequentialWords to true for initial spell check */);
- }
+ mSpellCheckerSession.getSentenceSuggestions(
+ textInfos, SuggestionSpan.SUGGESTIONS_MAX_SIZE);
}
}
@@ -381,32 +371,30 @@ public class SpellChecker implements SpellCheckerSessionListener {
editable, suggestionsInfo, spellCheckSpan, offset, length);
} else {
// Valid word -- isInDictionary || !looksLikeTypo
- if (mIsSentenceSpellCheckSupported) {
- // Allow the spell checker to remove existing misspelled span by
- // overwriting the span over the same place
- final int spellCheckSpanStart = editable.getSpanStart(spellCheckSpan);
- final int spellCheckSpanEnd = editable.getSpanEnd(spellCheckSpan);
- final int start;
- final int end;
- if (offset != USE_SPAN_RANGE && length != USE_SPAN_RANGE) {
- start = spellCheckSpanStart + offset;
- end = start + length;
- } else {
- start = spellCheckSpanStart;
- end = spellCheckSpanEnd;
- }
- if (spellCheckSpanStart >= 0 && spellCheckSpanEnd > spellCheckSpanStart
- && end > start) {
- final Long key = Long.valueOf(TextUtils.packRangeInLong(start, end));
- final SuggestionSpan tempSuggestionSpan = mSuggestionSpanCache.get(key);
- if (tempSuggestionSpan != null) {
- if (DBG) {
- Log.i(TAG, "Remove existing misspelled span. "
- + editable.subSequence(start, end));
- }
- editable.removeSpan(tempSuggestionSpan);
- mSuggestionSpanCache.remove(key);
+ // Allow the spell checker to remove existing misspelled span by
+ // overwriting the span over the same place
+ final int spellCheckSpanStart = editable.getSpanStart(spellCheckSpan);
+ final int spellCheckSpanEnd = editable.getSpanEnd(spellCheckSpan);
+ final int start;
+ final int end;
+ if (offset != USE_SPAN_RANGE && length != USE_SPAN_RANGE) {
+ start = spellCheckSpanStart + offset;
+ end = start + length;
+ } else {
+ start = spellCheckSpanStart;
+ end = spellCheckSpanEnd;
+ }
+ if (spellCheckSpanStart >= 0 && spellCheckSpanEnd > spellCheckSpanStart
+ && end > start) {
+ final Long key = Long.valueOf(TextUtils.packRangeInLong(start, end));
+ final SuggestionSpan tempSuggestionSpan = mSuggestionSpanCache.get(key);
+ if (tempSuggestionSpan != null) {
+ if (DBG) {
+ Log.i(TAG, "Remove existing misspelled span. "
+ + editable.subSequence(start, end));
}
+ editable.removeSpan(tempSuggestionSpan);
+ mSuggestionSpanCache.remove(key);
}
}
}
@@ -531,20 +519,16 @@ public class SpellChecker implements SpellCheckerSessionListener {
}
SuggestionSpan suggestionSpan =
new SuggestionSpan(mTextView.getContext(), suggestions, flags);
- // TODO: Remove mIsSentenceSpellCheckSupported by extracting an interface
- // to share the logic of word level spell checker and sentence level spell checker
- if (mIsSentenceSpellCheckSupported) {
- final Long key = Long.valueOf(TextUtils.packRangeInLong(start, end));
- final SuggestionSpan tempSuggestionSpan = mSuggestionSpanCache.get(key);
- if (tempSuggestionSpan != null) {
- if (DBG) {
- Log.i(TAG, "Cached span on the same position is cleard. "
- + editable.subSequence(start, end));
- }
- editable.removeSpan(tempSuggestionSpan);
+ final Long key = Long.valueOf(TextUtils.packRangeInLong(start, end));
+ final SuggestionSpan tempSuggestionSpan = mSuggestionSpanCache.get(key);
+ if (tempSuggestionSpan != null) {
+ if (DBG) {
+ Log.i(TAG, "Cached span on the same position is cleard. "
+ + editable.subSequence(start, end));
}
- mSuggestionSpanCache.put(key, suggestionSpan);
+ editable.removeSpan(tempSuggestionSpan);
}
+ mSuggestionSpanCache.put(key, suggestionSpan);
editable.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTextView.invalidateRegion(start, end, false /* No cursor involved */);
@@ -599,15 +583,8 @@ 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;
- if (mIsSentenceSpellCheckSupported) {
- // TODO: Find the start position of the sentence.
- // Set span with the context
- start = Math.max(
- 0, editable.getSpanStart(mRange) - MIN_SENTENCE_LENGTH);
- } else {
- start = editable.getSpanStart(mRange);
- }
+ final int start = Math.max(
+ 0, editable.getSpanStart(mRange) - MIN_SENTENCE_LENGTH);
final int end = editable.getSpanEnd(mRange);
@@ -633,155 +610,80 @@ public class SpellChecker implements SpellCheckerSessionListener {
return;
}
- // We need to expand by one character because we want to include the spans that
- // end/start at position start/end respectively.
- SpellCheckSpan[] spellCheckSpans = editable.getSpans(start - 1, end + 1,
- SpellCheckSpan.class);
- SuggestionSpan[] suggestionSpans = editable.getSpans(start - 1, end + 1,
- SuggestionSpan.class);
-
- int wordCount = 0;
boolean scheduleOtherSpellCheck = false;
- if (mIsSentenceSpellCheckSupported) {
- if (wordIteratorWindowEnd < end) {
- 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 (wordIteratorWindowEnd < end) {
+ if (DBG) {
+ Log.i(TAG, "schedule other spell check.");
}
- if (!correct) {
- if (DBG) {
- Log.i(TAG, "Incorrect range span.");
- }
- stop();
- return;
+ // 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.");
}
- do {
- // TODO: Find the start position of the sentence.
- int spellCheckStart = wordStart;
- boolean createSpellCheckSpan = true;
- // Cancel or merge overlapped spell check spans
- for (int i = 0; i < mLength; ++i) {
- final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i];
- if (mIds[i] < 0 || spellCheckSpan.isSpellCheckInProgress()) {
- continue;
- }
- final int spanStart = editable.getSpanStart(spellCheckSpan);
- final int spanEnd = editable.getSpanEnd(spellCheckSpan);
- if (spanEnd < spellCheckStart || spellCheckEnd < spanStart) {
- // No need to merge
- continue;
- }
- if (spanStart <= spellCheckStart && spellCheckEnd <= spanEnd) {
- // There is a completely overlapped spell check span
- // skip this span
- createSpellCheckSpan = false;
- if (DBG) {
- Log.i(TAG, "The range is overrapped. Skip spell check.");
- }
- break;
- }
- // This spellCheckSpan is replaced by the one we are creating
- editable.removeSpan(spellCheckSpan);
- spellCheckStart = Math.min(spanStart, spellCheckStart);
- spellCheckEnd = Math.max(spanEnd, spellCheckEnd);
- }
-
- if (DBG) {
- Log.d(TAG, "addSpellCheckSpan: "
- + ", End = " + spellCheckEnd + ", Start = " + spellCheckStart
- + ", next = " + scheduleOtherSpellCheck + "\n"
- + editable.subSequence(spellCheckStart, spellCheckEnd));
+ stop();
+ return;
+ }
+ do {
+ // TODO: Find the start position of the sentence.
+ int spellCheckStart = wordStart;
+ boolean createSpellCheckSpan = true;
+ // Cancel or merge overlapped spell check spans
+ for (int i = 0; i < mLength; ++i) {
+ final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i];
+ if (mIds[i] < 0 || spellCheckSpan.isSpellCheckInProgress()) {
+ continue;
}
-
- // Stop spell checking when there are no characters in the range.
- if (spellCheckEnd < start) {
- break;
+ final int spanStart = editable.getSpanStart(spellCheckSpan);
+ final int spanEnd = editable.getSpanEnd(spellCheckSpan);
+ if (spanEnd < spellCheckStart || spellCheckEnd < spanStart) {
+ // No need to merge
+ continue;
}
- if (spellCheckEnd <= spellCheckStart) {
- Log.w(TAG, "Trying to spellcheck invalid region, from "
- + start + " to " + end);
+ if (spanStart <= spellCheckStart && spellCheckEnd <= spanEnd) {
+ // There is a completely overlapped spell check span
+ // skip this span
+ createSpellCheckSpan = false;
+ if (DBG) {
+ Log.i(TAG, "The range is overrapped. Skip spell check.");
+ }
break;
}
- if (createSpellCheckSpan) {
- addSpellCheckSpan(editable, spellCheckStart, spellCheckEnd);
- }
- } while (false);
- wordStart = spellCheckEnd;
- } else {
- while (wordStart <= end) {
- if (wordEnd >= start && wordEnd > wordStart) {
- if (wordCount >= MAX_NUMBER_OF_WORDS) {
- scheduleOtherSpellCheck = true;
- break;
- }
- // A new word has been created across the interval boundaries with this
- // edit. The previous spans (that ended on start / started on end) are
- // not valid anymore and must be removed.
- if (wordStart < start && wordEnd > start) {
- removeSpansAt(editable, start, spellCheckSpans);
- removeSpansAt(editable, start, suggestionSpans);
- }
-
- if (wordStart < end && wordEnd > end) {
- removeSpansAt(editable, end, spellCheckSpans);
- removeSpansAt(editable, end, suggestionSpans);
- }
-
- // Do not create new boundary spans if they already exist
- boolean createSpellCheckSpan = true;
- if (wordEnd == start) {
- for (int i = 0; i < spellCheckSpans.length; i++) {
- final int spanEnd = editable.getSpanEnd(spellCheckSpans[i]);
- if (spanEnd == start) {
- createSpellCheckSpan = false;
- break;
- }
- }
- }
-
- if (wordStart == end) {
- for (int i = 0; i < spellCheckSpans.length; i++) {
- final int spanStart = editable.getSpanStart(spellCheckSpans[i]);
- if (spanStart == end) {
- createSpellCheckSpan = false;
- break;
- }
- }
- }
+ // This spellCheckSpan is replaced by the one we are creating
+ editable.removeSpan(spellCheckSpan);
+ spellCheckStart = Math.min(spanStart, spellCheckStart);
+ spellCheckEnd = Math.max(spanEnd, spellCheckEnd);
+ }
- if (createSpellCheckSpan) {
- addSpellCheckSpan(editable, wordStart, wordEnd);
- }
- wordCount++;
- }
+ if (DBG) {
+ Log.d(TAG, "addSpellCheckSpan: "
+ + ", End = " + spellCheckEnd + ", Start = " + spellCheckStart
+ + ", next = " + scheduleOtherSpellCheck + "\n"
+ + editable.subSequence(spellCheckStart, spellCheckEnd));
+ }
- // iterate word by word
- int originalWordEnd = wordEnd;
- wordEnd = mWordIterator.following(wordEnd);
- if ((wordIteratorWindowEnd < end) &&
- (wordEnd == BreakIterator.DONE || wordEnd >= wordIteratorWindowEnd)) {
- wordIteratorWindowEnd =
- Math.min(end, originalWordEnd + WORD_ITERATOR_INTERVAL);
- mWordIterator.setCharSequence(
- editable, originalWordEnd, wordIteratorWindowEnd);
- wordEnd = mWordIterator.following(originalWordEnd);
- }
- if (wordEnd == BreakIterator.DONE) break;
- wordStart = mWordIterator.getBeginning(wordEnd);
- if (wordStart == BreakIterator.DONE) {
- break;
- }
+ // 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);
+ break;
+ }
+ if (createSpellCheckSpan) {
+ addSpellCheckSpan(editable, spellCheckStart, spellCheckEnd);
+ }
+ } while (false);
+ wordStart = spellCheckEnd;
if (scheduleOtherSpellCheck && wordStart != BreakIterator.DONE && wordStart <= end) {
// Update range span: start new spell check from last wordStart
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index fe37c5350511..0f2089a5463f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -12940,17 +12940,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return false;
}
- final ClipData clipData = getClipboardManagerForUser().getPrimaryClip();
- final ClipDescription description = clipData.getDescription();
+ final ClipDescription description =
+ getClipboardManagerForUser().getPrimaryClipDescription();
final boolean isPlainType = description.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN);
- final CharSequence text = clipData.getItemAt(0).getText();
- if (isPlainType && (text instanceof Spanned)) {
- Spanned spanned = (Spanned) text;
- if (TextUtils.hasStyleSpan(spanned)) {
- return true;
- }
- }
- return description.hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML);
+ return (isPlainType && description.isStyledText())
+ || description.hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML);
}
boolean canProcessText() {
diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java
index b484dfacbf6c..2904a8c889a2 100644
--- a/core/java/android/widget/ToastPresenter.java
+++ b/core/java/android/widget/ToastPresenter.java
@@ -172,6 +172,22 @@ public class ToastPresenter {
}
/**
+ * Update the LayoutParameters of the currently showing toast view. This is used for layout
+ * updates based on orientation changes.
+ */
+ public void updateLayoutParams(int xOffset, int yOffset, float horizontalMargin,
+ float verticalMargin, int gravity) {
+ checkState(mView != null, "Toast must be showing to update its layout parameters.");
+ Configuration config = mResources.getConfiguration();
+ mParams.gravity = Gravity.getAbsoluteGravity(gravity, config.getLayoutDirection());
+ mParams.x = xOffset;
+ mParams.y = yOffset;
+ mParams.horizontalMargin = horizontalMargin;
+ mParams.verticalMargin = verticalMargin;
+ addToastView();
+ }
+
+ /**
* Sets {@link WindowManager.LayoutParams#SYSTEM_FLAG_SHOW_FOR_ALL_USERS} flag if {@code
* packageName} is a cross-user package.
*
@@ -221,18 +237,7 @@ public class ToastPresenter {
adjustLayoutParams(mParams, windowToken, duration, gravity, xOffset, yOffset,
horizontalMargin, verticalMargin, removeWindowAnimations);
- if (mView.getParent() != null) {
- mWindowManager.removeView(mView);
- }
- try {
- mWindowManager.addView(mView, mParams);
- } catch (WindowManager.BadTokenException e) {
- // Since the notification manager service cancels the token right after it notifies us
- // to cancel the toast there is an inherent race and we may attempt to add a window
- // after the token has been invalidated. Let us hedge against that.
- Log.w(TAG, "Error while attempting to show toast from " + mPackageName, e);
- return;
- }
+ addToastView();
trySendAccessibilityEvent(mView, mPackageName);
if (callback != null) {
try {
@@ -288,4 +293,19 @@ public class ToastPresenter {
view.dispatchPopulateAccessibilityEvent(event);
mAccessibilityManager.sendAccessibilityEvent(event);
}
+
+ private void addToastView() {
+ if (mView.getParent() != null) {
+ mWindowManager.removeView(mView);
+ }
+ try {
+ mWindowManager.addView(mView, mParams);
+ } catch (WindowManager.BadTokenException e) {
+ // Since the notification manager service cancels the token right after it notifies us
+ // to cancel the toast there is an inherent race and we may attempt to add a window
+ // after the token has been invalidated. Let us hedge against that.
+ Log.w(TAG, "Error while attempting to show toast from " + mPackageName, e);
+ return;
+ }
+ }
}
diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl
index 88b2257a55b1..8f541d0bd194 100644
--- a/core/java/android/window/ITaskOrganizer.aidl
+++ b/core/java/android/window/ITaskOrganizer.aidl
@@ -42,6 +42,11 @@ oneway interface ITaskOrganizer {
void removeStartingWindow(int taskId);
/**
+ * Called when the Task want to copy the splash screen.
+ */
+ void copySplashScreenView(int taskId);
+
+ /**
* A callback when the Task is available for the registered organizer. The client is responsible
* for releasing the SurfaceControl in the callback. For non-root tasks, the leash may initially
* be hidden so it is up to the organizer to show this task.
diff --git a/core/java/android/window/SplashScreen.java b/core/java/android/window/SplashScreen.java
new file mode 100644
index 000000000000..4b88a9bc39de
--- /dev/null
+++ b/core/java/android/window/SplashScreen.java
@@ -0,0 +1,188 @@
+/*
+ * 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 android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.content.Context;
+import android.os.IBinder;
+import android.util.Singleton;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+/**
+ * The interface that apps use to talk to the splash screen.
+ * <p>
+ * Each splash screen instance is bound to a particular {@link Activity}.
+ * To obtain a {@link SplashScreen} for an Activity, use
+ * <code>Activity.getSplashScreen()</code> to get the SplashScreen.</p>
+ */
+public interface SplashScreen {
+ /**
+ * <p>Specifies whether an {@link Activity} wants to handle the splash screen animation on its
+ * own. Normally the splash screen will show on screen before the content of the activity has
+ * been drawn, and disappear when the activity is showing on the screen. With this listener set,
+ * the activity will receive {@link OnExitAnimationListener#onSplashScreenExit} callback if
+ * splash screen is showed, then the activity can create its own exit animation based on the
+ * SplashScreenView.</p>
+ *
+ * <p> Note that this method must be called before splash screen leave, so it only takes effect
+ * during or before {@link Activity#onResume}.</p>
+ *
+ * @param listener the listener for receive the splash screen with
+ *
+ * @see OnExitAnimationListener#onSplashScreenExit(SplashScreenView)
+ */
+ @SuppressLint("ExecutorRegistration")
+ void setOnExitAnimationListener(@Nullable SplashScreen.OnExitAnimationListener listener);
+
+ /**
+ * Listens for the splash screen exit event.
+ */
+ interface OnExitAnimationListener {
+ /**
+ * When receiving this callback, the {@link SplashScreenView} object will be drawing on top
+ * of the activity. The {@link SplashScreenView} represents the splash screen view
+ * object, developer can make an exit animation based on this view.</p>
+ *
+ * <p>If {@link SplashScreenView#remove} is not called after 5000ms, the method will be
+ * automatically called and the splash screen removed.</p>
+ *
+ * <p>This method is never invoked if your activity sets
+ * {@link #setOnExitAnimationListener} to <code>null</code>..
+ *
+ * @param view The view object which on top of this Activity.
+ * @see #setOnExitAnimationListener
+ */
+ void onSplashScreenExit(@NonNull SplashScreenView view);
+ }
+
+ /**
+ * @hide
+ */
+ class SplashScreenImpl implements SplashScreen {
+ private OnExitAnimationListener mExitAnimationListener;
+ private final IBinder mActivityToken;
+ private final SplashScreenManagerGlobal mGlobal;
+
+ public SplashScreenImpl(Context context) {
+ mActivityToken = context.getActivityToken();
+ mGlobal = SplashScreenManagerGlobal.getInstance();
+ }
+
+ @Override
+ public void setOnExitAnimationListener(
+ @Nullable SplashScreen.OnExitAnimationListener listener) {
+ if (mActivityToken == null) {
+ // This is not an activity.
+ return;
+ }
+ synchronized (mGlobal.mGlobalLock) {
+ mExitAnimationListener = listener;
+ if (listener != null) {
+ mGlobal.addImpl(this);
+ } else {
+ mGlobal.removeImpl(this);
+ }
+ }
+ }
+ }
+
+ /**
+ * This class is only used internally to manage the activities for this process.
+ *
+ * @hide
+ */
+ class SplashScreenManagerGlobal {
+ private static final String TAG = SplashScreen.class.getSimpleName();
+ private final Object mGlobalLock = new Object();
+ private final ArrayList<SplashScreenImpl> mImpls = new ArrayList<>();
+
+ private SplashScreenManagerGlobal() {
+ ActivityThread.currentActivityThread().registerSplashScreenManager(this);
+ }
+
+ public static SplashScreenManagerGlobal getInstance() {
+ return sInstance.get();
+ }
+
+ private static final Singleton<SplashScreenManagerGlobal> sInstance =
+ new Singleton<SplashScreenManagerGlobal>() {
+ @Override
+ protected SplashScreenManagerGlobal create() {
+ return new SplashScreenManagerGlobal();
+ }
+ };
+
+ private void addImpl(SplashScreenImpl impl) {
+ synchronized (mGlobalLock) {
+ mImpls.add(impl);
+ }
+ }
+
+ private void removeImpl(SplashScreenImpl impl) {
+ synchronized (mGlobalLock) {
+ mImpls.remove(impl);
+ }
+ }
+
+ private SplashScreenImpl findImpl(IBinder token) {
+ synchronized (mGlobalLock) {
+ for (SplashScreenImpl impl : mImpls) {
+ if (impl.mActivityToken == token) {
+ return impl;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void tokenDestroyed(IBinder token) {
+ synchronized (mGlobalLock) {
+ final SplashScreenImpl impl = findImpl(token);
+ if (impl != null) {
+ removeImpl(impl);
+ }
+ }
+ }
+
+ public void dispatchOnExitAnimation(IBinder token, SplashScreenView view) {
+ synchronized (mGlobalLock) {
+ final SplashScreenImpl impl = findImpl(token);
+ if (impl == null) {
+ return;
+ }
+ if (impl.mExitAnimationListener == null) {
+ Slog.e(TAG, "cannot dispatch onExitAnimation to listener " + token);
+ return;
+ }
+ impl.mExitAnimationListener.onSplashScreenExit(view);
+ }
+ }
+
+ public boolean containsExitListener(IBinder token) {
+ synchronized (mGlobalLock) {
+ final SplashScreenImpl impl = findImpl(token);
+ return impl != null && impl.mExitAnimationListener != null;
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/screenshot/ScreenshotHash.aidl b/core/java/android/window/SplashScreenView.aidl
index a7c50db171a2..cc7ac1edce80 100644
--- a/core/java/android/service/screenshot/ScreenshotHash.aidl
+++ b/core/java/android/window/SplashScreenView.aidl
@@ -14,6 +14,7 @@
* limitations under the License.
*/
-package android.service.screenshot;
+package android.window;
-parcelable ScreenshotHash;
+/** @hide */
+parcelable SplashScreenView.SplashScreenViewParcelable;
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
new file mode 100644
index 000000000000..35ccfca101d3
--- /dev/null
+++ b/core/java/android/window/SplashScreenView.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.window;
+
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import com.android.internal.R;
+import com.android.internal.policy.DecorView;
+
+/**
+ * <p>The view which allows an activity to customize its splash screen exit animation.</p>
+ *
+ * <p>Activities will receive this view as a parameter of
+ * {@link SplashScreen.OnExitAnimationListener#onSplashScreenExit} if
+ * they set {@link SplashScreen#setOnExitAnimationListener}.
+ * When this callback is called, this view will be on top of the activity.</p>
+ *
+ * <p>This view is composed of a view containing the splashscreen icon (see
+ * windowSplashscreenAnimatedIcon) and a background.
+ * Developers can use {@link #getIconView} to get this view and replace the drawable or
+ * add animation to it. The background of this view is filled with a single color, which can be
+ * edited during the animation by {@link View#setBackground} or {@link View#setBackgroundColor}.</p>
+ *
+ * @see SplashScreen
+ */
+public final class SplashScreenView extends FrameLayout {
+ private static final String TAG = SplashScreenView.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private boolean mNotCopyable;
+ private int mInitBackgroundColor;
+ private View mIconView;
+ private Bitmap mParceledIconBitmap;
+ private View mBrandingImageView;
+ private Bitmap mParceledBrandingBitmap;
+ private long mIconAnimationDuration;
+ private long mIconAnimationStart;
+
+ private Animatable mAnimatableIcon;
+ private ValueAnimator mAnimator;
+
+ // cache original window and status
+ private Window mWindow;
+ private boolean mDrawBarBackground;
+ private int mStatusBarColor;
+ private int mNavigationBarColor;
+
+ /**
+ * Internal builder to create a SplashScreenWindowView object.
+ * @hide
+ */
+ public static class Builder {
+ private final Context mContext;
+ private int mIconSize;
+ private @ColorInt int mBackgroundColor;
+ private Bitmap mParceledIconBitmap;
+ private Drawable mIconDrawable;
+ private int mBrandingImageWidth;
+ private int mBrandingImageHeight;
+ private Drawable mBrandingDrawable;
+ private Bitmap mParceledBrandingBitmap;
+ private long mIconAnimationStart;
+ private long mIconAnimationDuration;
+
+ public Builder(@NonNull Context context) {
+ mContext = context;
+ }
+
+ /**
+ * When create from {@link SplashScreenViewParcelable}, all the materials were be settled so
+ * you do not need to call other set methods.
+ */
+ public Builder createFromParcel(SplashScreenViewParcelable parcelable) {
+ mIconSize = parcelable.getIconSize();
+ mBackgroundColor = parcelable.getBackgroundColor();
+ if (parcelable.mIconBitmap != null) {
+ mIconDrawable = new BitmapDrawable(mContext.getResources(), parcelable.mIconBitmap);
+ mParceledIconBitmap = parcelable.mIconBitmap;
+ }
+ if (parcelable.mBrandingBitmap != null) {
+ setBrandingDrawable(new BitmapDrawable(mContext.getResources(),
+ parcelable.mBrandingBitmap), parcelable.mBrandingWidth,
+ parcelable.mBrandingHeight);
+ mParceledBrandingBitmap = parcelable.mBrandingBitmap;
+ }
+ mIconAnimationStart = parcelable.mIconAnimationStart;
+ mIconAnimationDuration = parcelable.mIconAnimationDuration;
+ return this;
+ }
+
+ /**
+ * Set the rectangle size for the center view.
+ */
+ public Builder setIconSize(int iconSize) {
+ mIconSize = iconSize;
+ return this;
+ }
+
+ /**
+ * Set the background color for the view.
+ */
+ public Builder setBackgroundColor(@ColorInt int backgroundColor) {
+ mBackgroundColor = backgroundColor;
+ return this;
+ }
+
+ /**
+ * Set the Drawable object to fill the center view.
+ */
+ public Builder setCenterViewDrawable(Drawable drawable) {
+ mIconDrawable = drawable;
+ return this;
+ }
+
+ /**
+ * Set the animation duration if icon is animatable.
+ */
+ public Builder setAnimationDuration(int duration) {
+ mIconAnimationDuration = duration;
+ return this;
+ }
+
+ /**
+ * Set the Drawable object and size for the branding view.
+ */
+ public Builder setBrandingDrawable(Drawable branding, int width, int height) {
+ mBrandingDrawable = branding;
+ mBrandingImageWidth = width;
+ mBrandingImageHeight = height;
+ return this;
+ }
+
+ /**
+ * Create SplashScreenWindowView object from materials.
+ */
+ public SplashScreenView build() {
+ final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+ final SplashScreenView view = (SplashScreenView)
+ layoutInflater.inflate(R.layout.splash_screen_view, null, false);
+ view.mInitBackgroundColor = mBackgroundColor;
+ view.setBackgroundColor(mBackgroundColor);
+ view.mIconView = view.findViewById(R.id.splashscreen_icon_view);
+ view.mBrandingImageView = view.findViewById(R.id.splashscreen_branding_view);
+ // center icon
+ if (mIconSize != 0) {
+ final ViewGroup.LayoutParams params = view.mIconView.getLayoutParams();
+ params.width = mIconSize;
+ params.height = mIconSize;
+ view.mIconView.setLayoutParams(params);
+ }
+ if (mIconDrawable != null) {
+ view.mIconView.setBackground(mIconDrawable);
+ view.initIconAnimation(mIconDrawable, mIconAnimationDuration);
+ }
+ view.mIconAnimationStart = mIconAnimationStart;
+ view.mIconAnimationDuration = mIconAnimationDuration;
+ if (mParceledIconBitmap != null) {
+ view.mParceledIconBitmap = mParceledIconBitmap;
+ }
+ // branding image
+ if (mBrandingImageHeight > 0 && mBrandingImageWidth > 0) {
+ final ViewGroup.LayoutParams params = view.mBrandingImageView.getLayoutParams();
+ params.width = mBrandingImageWidth;
+ params.height = mBrandingImageHeight;
+ view.mBrandingImageView.setLayoutParams(params);
+ }
+ if (mBrandingDrawable != null) {
+ view.mBrandingImageView.setBackground(mBrandingDrawable);
+ }
+ if (mParceledBrandingBitmap != null) {
+ view.mParceledBrandingBitmap = mParceledBrandingBitmap;
+ }
+ if (DEBUG) {
+ Log.d(TAG, " build " + view + " Icon: view: " + view.mIconView + " drawable: "
+ + mIconDrawable + " size: " + mIconSize + "\n Branding: view: "
+ + view.mBrandingImageView + " drawable: " + mBrandingDrawable
+ + " size w: " + mBrandingImageWidth + " h: " + mBrandingImageHeight);
+ }
+ return view;
+ }
+ }
+
+ /** @hide */
+ public SplashScreenView(Context context) {
+ super(context);
+ }
+
+ /** @hide */
+ public SplashScreenView(Context context, AttributeSet attributeSet) {
+ super(context, attributeSet);
+ }
+
+ /**
+ * Declared this view is not copyable.
+ * @hide
+ */
+ public void setNotCopyable() {
+ mNotCopyable = true;
+ }
+
+ /**
+ * Whether this view is copyable.
+ * @hide
+ */
+ public boolean isCopyable() {
+ return !mNotCopyable;
+ }
+
+ /**
+ * Returns the duration of the icon animation if icon is animatable.
+ *
+ * @see android.R.attr#windowSplashScreenAnimatedIcon
+ * @see android.R.attr#windowSplashScreenAnimationDuration
+ */
+ public long getIconAnimationDurationMillis() {
+ return mIconAnimationDuration;
+ }
+
+ /**
+ * If the replaced icon is animatable, return the animation start time in millisecond based on
+ * system. The start time is set using {@link SystemClock#uptimeMillis()}.
+ */
+ public long getIconAnimationStartMillis() {
+ return mIconAnimationStart;
+ }
+
+ void initIconAnimation(Drawable iconDrawable, long duration) {
+ if (iconDrawable instanceof Animatable) {
+ mAnimatableIcon = (Animatable) iconDrawable;
+ mAnimator = ValueAnimator.ofInt(0, 1);
+ mAnimator.setDuration(duration);
+ mAnimator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mIconAnimationStart = SystemClock.uptimeMillis();
+ mAnimatableIcon.start();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimatableIcon.stop();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mAnimatableIcon.stop();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ // do not repeat
+ mAnimatableIcon.stop();
+ }
+ });
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void startIntroAnimation() {
+ if (mAnimatableIcon != null) {
+ mAnimator.start();
+ }
+ }
+
+ /**
+ * <p>Remove this view and release its resource. </p>
+ * <p><strong>Do not</strong> invoke this method from a drawing method
+ * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
+ */
+ public void remove() {
+ setVisibility(GONE);
+ if (mParceledIconBitmap != null) {
+ mIconView.setBackground(null);
+ mParceledIconBitmap.recycle();
+ mParceledIconBitmap = null;
+ }
+ if (mParceledBrandingBitmap != null) {
+ mBrandingImageView.setBackground(null);
+ mParceledBrandingBitmap.recycle();
+ mParceledBrandingBitmap = null;
+ }
+ if (mWindow != null) {
+ final DecorView decorView = (DecorView) mWindow.peekDecorView();
+ if (DEBUG) {
+ Log.d(TAG, "remove starting view");
+ }
+ if (decorView != null) {
+ decorView.removeView(this);
+ }
+ restoreSystemUIColors();
+ mWindow = null;
+ }
+ }
+
+ /**
+ * Cache the root window.
+ * @hide
+ */
+ public void cacheRootWindow(Window window) {
+ mWindow = window;
+ }
+
+ /**
+ * Called after SplashScreenView has added on the root window.
+ * @hide
+ */
+ public void makeSystemUIColorsTransparent() {
+ if (mWindow != null) {
+ final WindowManager.LayoutParams attr = mWindow.getAttributes();
+ mDrawBarBackground = (attr.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+ mWindow.addFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ mStatusBarColor = mWindow.getStatusBarColor();
+ mNavigationBarColor = mWindow.getNavigationBarDividerColor();
+ mWindow.setStatusBarColor(Color.TRANSPARENT);
+ mWindow.setNavigationBarColor(Color.TRANSPARENT);
+ }
+ setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+ }
+
+ private void restoreSystemUIColors() {
+ if (mWindow != null) {
+ if (!mDrawBarBackground) {
+ mWindow.clearFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ }
+ mWindow.setStatusBarColor(mStatusBarColor);
+ mWindow.setNavigationBarColor(mNavigationBarColor);
+ }
+ }
+
+ /**
+ * Get the view containing the Splash Screen icon and its background.
+ * @see android.R.attr#windowSplashScreenAnimatedIcon
+ */
+ public @Nullable View getIconView() {
+ return mIconView;
+ }
+
+ /**
+ * Get the branding image view.
+ * @hide
+ */
+ @TestApi
+ public @Nullable View getBrandingView() {
+ return mBrandingImageView;
+ }
+
+ /**
+ * Get the initial background color of this view.
+ * @hide
+ */
+ @ColorInt int getInitBackgroundColor() {
+ return mInitBackgroundColor;
+ }
+
+ /**
+ * Use to create {@link SplashScreenView} object across process.
+ * @hide
+ */
+ public static class SplashScreenViewParcelable implements Parcelable {
+ private int mIconSize;
+ private int mBackgroundColor;
+
+ private Bitmap mIconBitmap;
+ private int mBrandingWidth;
+ private int mBrandingHeight;
+ private Bitmap mBrandingBitmap;
+
+ private long mIconAnimationStart;
+ private long mIconAnimationDuration;
+
+ public SplashScreenViewParcelable(SplashScreenView view) {
+ ViewGroup.LayoutParams params = view.getIconView().getLayoutParams();
+ mIconSize = params.height;
+ mBackgroundColor = view.getInitBackgroundColor();
+
+ mIconBitmap = copyDrawable(view.getIconView().getBackground());
+ mBrandingBitmap = copyDrawable(view.getBrandingView().getBackground());
+ params = view.getBrandingView().getLayoutParams();
+ mBrandingWidth = params.width;
+ mBrandingHeight = params.height;
+
+ mIconAnimationStart = view.getIconAnimationStartMillis();
+ mIconAnimationDuration = view.getIconAnimationDurationMillis();
+ }
+
+ private Bitmap copyDrawable(Drawable drawable) {
+ if (drawable != null) {
+ final Rect initialBounds = drawable.copyBounds();
+ final int width = initialBounds.width();
+ final int height = initialBounds.height();
+
+ final Bitmap snapshot = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ final Canvas bmpCanvas = new Canvas(snapshot);
+ drawable.setBounds(0, 0, width, height);
+ drawable.draw(bmpCanvas);
+ final Bitmap copyBitmap = snapshot.createAshmemBitmap();
+ snapshot.recycle();
+ return copyBitmap;
+ }
+ return null;
+ }
+
+ private SplashScreenViewParcelable(@NonNull Parcel source) {
+ readParcel(source);
+ }
+
+ private void readParcel(@NonNull Parcel source) {
+ mIconSize = source.readInt();
+ mBackgroundColor = source.readInt();
+ mIconBitmap = source.readTypedObject(Bitmap.CREATOR);
+ mBrandingWidth = source.readInt();
+ mBrandingHeight = source.readInt();
+ mBrandingBitmap = source.readTypedObject(Bitmap.CREATOR);
+ mIconAnimationStart = source.readLong();
+ mIconAnimationDuration = source.readLong();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mIconSize);
+ dest.writeInt(mBackgroundColor);
+ dest.writeTypedObject(mIconBitmap, flags);
+ dest.writeInt(mBrandingWidth);
+ dest.writeInt(mBrandingHeight);
+ dest.writeTypedObject(mBrandingBitmap, flags);
+ dest.writeLong(mIconAnimationStart);
+ dest.writeLong(mIconAnimationDuration);
+ }
+
+ public static final @NonNull Parcelable.Creator<SplashScreenViewParcelable> CREATOR =
+ new Parcelable.Creator<SplashScreenViewParcelable>() {
+ public SplashScreenViewParcelable createFromParcel(@NonNull Parcel source) {
+ return new SplashScreenViewParcelable(source);
+ }
+ public SplashScreenViewParcelable[] newArray(int size) {
+ return new SplashScreenViewParcelable[size];
+ }
+ };
+
+ /**
+ * Release the bitmap if another process cannot handle it.
+ */
+ public void clearIfNeeded() {
+ if (mIconBitmap != null) {
+ mIconBitmap.recycle();
+ mIconBitmap = null;
+ }
+ if (mBrandingBitmap != null) {
+ mBrandingBitmap.recycle();
+ mBrandingBitmap = null;
+ }
+ }
+
+ int getIconSize() {
+ return mIconSize;
+ }
+
+ int getBackgroundColor() {
+ return mBackgroundColor;
+ }
+ }
+}
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 2282cc567936..63b9e9befb77 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -95,6 +95,12 @@ public final class StartingWindowInfo implements Parcelable {
*/
public int startingWindowTypeParameter;
+ /**
+ * Specifies a theme for the splash screen.
+ * @hide
+ */
+ public int splashScreenThemeResId;
+
public StartingWindowInfo() {
}
@@ -115,6 +121,7 @@ public final class StartingWindowInfo implements Parcelable {
dest.writeTypedObject(topOpaqueWindowInsetsState, flags);
dest.writeTypedObject(topOpaqueWindowLayoutParams, flags);
dest.writeTypedObject(mainWindowLayoutParams, flags);
+ dest.writeInt(splashScreenThemeResId);
}
void readFromParcel(@NonNull Parcel source) {
@@ -124,6 +131,7 @@ public final class StartingWindowInfo implements Parcelable {
topOpaqueWindowLayoutParams = source.readTypedObject(
WindowManager.LayoutParams.CREATOR);
mainWindowLayoutParams = source.readTypedObject(WindowManager.LayoutParams.CREATOR);
+ splashScreenThemeResId = source.readInt();
}
@Override
@@ -135,7 +143,8 @@ public final class StartingWindowInfo implements Parcelable {
+ Integer.toHexString(startingWindowTypeParameter)
+ " insetsState=" + topOpaqueWindowInsetsState
+ " topWindowLayoutParams=" + topOpaqueWindowLayoutParams
- + " mainWindowLayoutParams=" + mainWindowLayoutParams;
+ + " mainWindowLayoutParams=" + mainWindowLayoutParams
+ + " splashScreenThemeResId " + Integer.toHexString(splashScreenThemeResId);
}
public static final @android.annotation.NonNull Creator<StartingWindowInfo> CREATOR =
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index cdb4762a4f0a..217ade82b336 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -105,6 +105,12 @@ public class TaskOrganizer extends WindowOrganizer {
public void removeStartingWindow(int taskId) {}
/**
+ * Called when the Task want to copy the splash screen.
+ */
+ @BinderThread
+ public void copySplashScreenView(int taskId) {}
+
+ /**
* Called when a task with the registered windowing mode can be controlled by this task
* organizer. For non-root tasks, the leash may initially be hidden so it is up to the organizer
* to show this task.
@@ -223,6 +229,11 @@ public class TaskOrganizer extends WindowOrganizer {
}
@Override
+ public void copySplashScreenView(int taskId) {
+ mExecutor.execute(() -> TaskOrganizer.this.copySplashScreenView(taskId));
+ }
+
+ @Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
mExecutor.execute(() -> TaskOrganizer.this.onTaskAppeared(taskInfo, leash));
}
diff --git a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.aidl b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.aidl
new file mode 100644
index 000000000000..5d02a29edcd5
--- /dev/null
+++ b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.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 com.android.internal.compat;
+
+parcelable CompatibilityOverrideConfig;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
new file mode 100644
index 000000000000..1c222a73eabc
--- /dev/null
+++ b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java
@@ -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.
+ */
+
+package com.android.internal.compat;
+
+
+import android.app.compat.PackageOverride;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Parcelable containing compat config overrides for a given application.
+ * @hide
+ */
+public final class CompatibilityOverrideConfig implements Parcelable {
+ public final Map<Long, PackageOverride> overrides;
+
+ public CompatibilityOverrideConfig(Map<Long, PackageOverride> overrides) {
+ this.overrides = overrides;
+ }
+
+ private CompatibilityOverrideConfig(Parcel in) {
+ int keyCount = in.readInt();
+ overrides = new HashMap<>();
+ for (int i = 0; i < keyCount; i++) {
+ long key = in.readLong();
+ PackageOverride override = in.readParcelable(PackageOverride.class.getClassLoader());
+ overrides.put(key, override);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(overrides.size());
+ for (Long key : overrides.keySet()) {
+ dest.writeLong(key);
+ dest.writeParcelable(overrides.get(key), 0);
+ }
+ }
+
+ public static final Creator<CompatibilityOverrideConfig> CREATOR =
+ new Creator<CompatibilityOverrideConfig>() {
+
+ @Override
+ public CompatibilityOverrideConfig createFromParcel(Parcel in) {
+ return new CompatibilityOverrideConfig(in);
+ }
+
+ @Override
+ public CompatibilityOverrideConfig[] newArray(int size) {
+ return new CompatibilityOverrideConfig[size];
+ }
+ };
+}
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index a5eb5f607c12..249c13402e4f 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -21,6 +21,7 @@ import com.android.internal.compat.IOverrideValidator;
import java.util.Map;
parcelable CompatibilityChangeConfig;
+parcelable CompatibilityOverrideConfig;
parcelable CompatibilityChangeInfo;
/**
* Platform private API for talking with the PlatformCompat service.
@@ -152,6 +153,17 @@ interface IPlatformCompat {
/**
* Adds overrides to compatibility changes.
*
+ * <p>Kills the app to allow the changes to take effect.
+ *
+ * @param overrides parcelable containing the compat change overrides to be applied
+ * @param packageName the package name of the app whose changes will be overridden
+ * @throws SecurityException if overriding changes is not permitted
+ */
+ void setOverridesFromInstaller(in CompatibilityOverrideConfig overrides, in String packageName);
+
+ /**
+ * Adds overrides to compatibility changes.
+ *
* <p>Does not kill the app, to be only used in tests.
*
* @param overrides parcelable containing the compat change overrides to be applied
@@ -179,9 +191,10 @@ interface IPlatformCompat {
*
* @param changeId the ID of the change that was overridden
* @param packageName the app package name that was overridden
+ * @return {@code true} if an override existed
* @throws SecurityException if overriding changes is not permitted
*/
- void clearOverrideForTest(long changeId, String packageName);
+ boolean clearOverrideForTest(long changeId, String packageName);
/**
* Enables all compatibility changes that have enabledSinceTargetSdk ==
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 0ede1b86b524..e602cd2c8890 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -28,6 +28,7 @@ import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.graphics.Point;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.FileObserver;
@@ -504,7 +505,7 @@ public abstract class FileSystemProvider extends DocumentsProvider {
final int pfdMode = ParcelFileDescriptor.parseMode(mode);
if (pfdMode == ParcelFileDescriptor.MODE_READ_ONLY || visibleFile == null) {
- return ParcelFileDescriptor.open(file, pfdMode);
+ return openFileForRead(file);
} else {
try {
// When finished writing, kick off media scanner
@@ -519,6 +520,24 @@ public abstract class FileSystemProvider extends DocumentsProvider {
}
}
+ private ParcelFileDescriptor openFileForRead(final File target) throws FileNotFoundException {
+ final Uri uri = MediaStore.scanFile(getContext().getContentResolver(), target);
+
+ // 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
+ // upon the Provider's uid.
+ final Bundle opts = new Bundle();
+ opts.putInt(MediaStore.EXTRA_MEDIA_CAPABILITIES_UID, Binder.getCallingUid());
+
+ final AssetFileDescriptor afd =
+ getContext().getContentResolver().openTypedAssetFileDescriptor(uri, "*/*", opts);
+ if (afd == null) {
+ return null;
+ }
+
+ return afd.getParcelFileDescriptor();
+ }
+
/**
* Test if the file matches the query arguments.
*
diff --git a/core/java/com/android/internal/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index 9049ca56bc53..fae58622d91e 100644
--- a/core/java/com/android/internal/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal;
+package com.android.internal.display;
import android.content.ContentResolver;
diff --git a/core/java/com/android/internal/display/OWNERS b/core/java/com/android/internal/display/OWNERS
new file mode 100644
index 000000000000..20b75be9f11f
--- /dev/null
+++ b/core/java/com/android/internal/display/OWNERS
@@ -0,0 +1,3 @@
+include /services/core/java/com/android/server/display/OWNERS
+
+flc@google.com
diff --git a/core/java/com/android/internal/graphics/fonts/IFontManager.aidl b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl
index dfcc91421b9e..1c7eca8b6c8e 100644
--- a/core/java/com/android/internal/graphics/fonts/IFontManager.aidl
+++ b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl
@@ -20,6 +20,8 @@ import android.os.ParcelFileDescriptor;
import android.graphics.fonts.FontUpdateRequest;
import android.text.FontConfig;
+import java.util.List;
+
/**
* System private interface for talking with
* {@link com.android.server.graphics.fonts.FontManagerService}.
@@ -28,5 +30,7 @@ import android.text.FontConfig;
interface IFontManager {
FontConfig getFontConfig();
- int updateFont(int baseVersion, in FontUpdateRequest request);
+ int updateFontFile(in FontUpdateRequest request, int baseVersion);
+
+ int updateFontFamily(in List<FontUpdateRequest> request, int baseVersion);
}
diff --git a/core/java/com/android/internal/graphics/palette/OWNERS b/core/java/com/android/internal/graphics/palette/OWNERS
new file mode 100644
index 000000000000..731dca9b128f
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 484670
+dupin@google.com
+jamesoleary@google.com \ No newline at end of file
diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java
index fc95275c2e1b..af21f8183d3a 100644
--- a/core/java/com/android/internal/infra/ServiceConnector.java
+++ b/core/java/com/android/internal/infra/ServiceConnector.java
@@ -726,4 +726,43 @@ public interface ServiceConnector<I extends IInterface> {
}
}
}
+
+ /**
+ * A {@link ServiceConnector} that doesn't connect to anything.
+ *
+ * @param <T> the type of the {@link IInterface ipc interface} for the remote service
+ */
+ class NoOp<T extends IInterface> extends AndroidFuture<Object> implements ServiceConnector<T> {
+ {
+ completeExceptionally(new IllegalStateException("ServiceConnector is a no-op"));
+ }
+
+ @Override
+ public boolean run(@NonNull VoidJob<T> job) {
+ return false;
+ }
+
+ @Override
+ public AndroidFuture<Void> post(@NonNull VoidJob<T> job) {
+ return (AndroidFuture) this;
+ }
+
+ @Override
+ public <R> AndroidFuture<R> postForResult(@NonNull Job<T, R> job) {
+ return (AndroidFuture) this;
+ }
+
+ @Override
+ public <R> AndroidFuture<R> postAsync(@NonNull Job<T, CompletableFuture<R>> job) {
+ return (AndroidFuture) this;
+ }
+
+ @Override
+ public AndroidFuture<T> connect() {
+ return (AndroidFuture) this;
+ }
+
+ @Override
+ public void unbind() {}
+ }
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 3d896c866c8b..cba6af98a980 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -18,10 +18,12 @@ package com.android.internal.jank;
import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR;
@@ -114,6 +116,8 @@ public class InteractionJankMonitor {
public static final int CUJ_LOCKSCREEN_PIN_DISAPPEAR = 22;
public static final int CUJ_LOCKSCREEN_TRANSITION_FROM_AOD = 23;
public static final int CUJ_LOCKSCREEN_TRANSITION_TO_AOD = 24;
+ public static final int CUJ_LAUNCHER_OPEN_ALL_APPS = 25;
+ public static final int CUJ_LAUNCHER_ALL_APPS_SCROLL = 26;
private static final int NO_STATSD_LOGGING = -1;
@@ -147,6 +151,8 @@ public class InteractionJankMonitor {
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL,
};
private static volatile InteractionJankMonitor sInstance;
@@ -191,6 +197,8 @@ public class InteractionJankMonitor {
CUJ_LOCKSCREEN_PIN_DISAPPEAR,
CUJ_LOCKSCREEN_TRANSITION_FROM_AOD,
CUJ_LOCKSCREEN_TRANSITION_TO_AOD,
+ CUJ_LAUNCHER_OPEN_ALL_APPS,
+ CUJ_LAUNCHER_ALL_APPS_SCROLL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -457,6 +465,10 @@ public class InteractionJankMonitor {
return "CUJ_LOCKSCREEN_TRANSITION_FROM_AOD";
case CUJ_LOCKSCREEN_TRANSITION_TO_AOD:
return "CUJ_LOCKSCREEN_TRANSITION_TO_AOD";
+ case CUJ_LAUNCHER_OPEN_ALL_APPS :
+ return "CUJ_LAUNCHER_OPEN_ALL_APPS";
+ case CUJ_LAUNCHER_ALL_APPS_SCROLL:
+ return "CUJ_LAUNCHER_ALL_APPS_SCROLL";
}
return "UNKNOWN";
}
@@ -487,7 +499,7 @@ public class InteractionJankMonitor {
}
public String getPerfettoTrigger() {
- return String.format("interaction-jank-monitor-%d", mCujType);
+ return String.format("com.android.telemetry.interaction-jank-monitor-%d", mCujType);
}
public String getName() {
diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
index 8fe17fb1565c..e153eb2f0933 100644
--- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
+++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
@@ -43,18 +43,15 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator {
*/
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
final long durationMs = calculateDuration(batteryStats, rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED);
- final double powerMah = mPowerEstimator.calculatePower(durationMs);
- if (powerMah > 0) {
- builder.getOrCreateSystemBatteryConsumerBuilder(
- SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah)
- .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs);
- }
- // TODO(b/178140704): Attribute *measured* total usage for BatteryUsageStats.
+ final double powerMah = getMeasuredOrEstimatedPower(batteryStats.getScreenDozeEnergy(),
+ mPowerEstimator, durationMs, query.shouldForceUsePowerProfileModel());
+ builder.getOrCreateSystemBatteryConsumerBuilder(
+ SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah)
+ .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs);
}
/**
@@ -67,8 +64,8 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator {
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, statsType);
- final double powerMah = getMeasuredOrEstimatedPower(
- batteryStats.getScreenDozeEnergy(), durationMs);
+ final double powerMah = getMeasuredOrEstimatedPower(batteryStats.getScreenDozeEnergy(),
+ mPowerEstimator, durationMs, false);
if (powerMah > 0) {
BatterySipper bs = new BatterySipper(BatterySipper.DrainType.AMBIENT_DISPLAY, null, 0);
bs.usagePowerMah = powerMah;
@@ -82,11 +79,4 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator {
return batteryStats.getScreenDozeTime(rawRealtimeUs, statsType) / 1000;
}
- private double getMeasuredOrEstimatedPower(long measuredEnergyUJ, long durationMs) {
- if (measuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) {
- return mAhToUJ(measuredEnergyUJ);
- } else {
- return mPowerEstimator.calculatePower(durationMs);
- }
- }
}
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index af61f91841fa..4f2f973b51b1 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -133,6 +133,12 @@ public class BatterySipper implements Comparable<BatterySipper> {
public double wakeLockPowerMah;
public double wifiPowerMah;
public double systemServiceCpuPowerMah;
+ public double[] customMeasuredPowerMah;
+
+ // Power that is re-attributed to other sippers. For example, for System Server
+ // this represents the power attributed to apps requesting system services.
+ // The value should be negative or zero.
+ public double powerReattributedToOtherSippersMah;
// Do not include this sipper in results because it is included
// in an aggregate sipper.
@@ -251,6 +257,18 @@ public class BatterySipper implements Comparable<BatterySipper> {
proportionalSmearMah += other.proportionalSmearMah;
totalSmearedPowerMah += other.totalSmearedPowerMah;
systemServiceCpuPowerMah += other.systemServiceCpuPowerMah;
+ if (other.customMeasuredPowerMah != null) {
+ if (customMeasuredPowerMah == null) {
+ customMeasuredPowerMah = new double[other.customMeasuredPowerMah.length];
+ }
+ if (customMeasuredPowerMah.length == other.customMeasuredPowerMah.length) {
+ // This should always be true.
+ for (int idx = 0; idx < other.customMeasuredPowerMah.length; idx++) {
+ customMeasuredPowerMah[idx] += other.customMeasuredPowerMah[idx];
+ }
+ }
+ }
+ powerReattributedToOtherSippersMah += other.powerReattributedToOtherSippersMah;
}
/**
@@ -264,6 +282,15 @@ public class BatterySipper implements Comparable<BatterySipper> {
sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah +
flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah
+ systemServiceCpuPowerMah;
+ if (customMeasuredPowerMah != null) {
+ for (int idx = 0; idx < customMeasuredPowerMah.length; idx++) {
+ totalPowerMah += customMeasuredPowerMah[idx];
+ }
+ }
+
+ // powerAttributedToOtherSippersMah is negative or zero
+ totalPowerMah = totalPowerMah + powerReattributedToOtherSippersMah;
+
totalSmearedPowerMah = totalPowerMah + screenPowerMah + proportionalSmearMah;
return totalPowerMah;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index aa5015a61f63..b20f50d62de4 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -347,6 +347,7 @@ public class BatteryStatsHelper {
mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
mPowerCalculators.add(new SystemServicePowerCalculator(mPowerProfile));
mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
+ mPowerCalculators.add(new CustomMeasuredPowerCalculator(mPowerProfile));
mPowerCalculators.add(new UserPowerCalculator());
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 1f7a7aa42669..1f8ffe08a257 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -104,6 +104,7 @@ 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.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import com.android.internal.power.MeasuredEnergyStats;
import com.android.internal.power.MeasuredEnergyStats.StandardEnergyBucket;
import com.android.internal.util.ArrayUtils;
@@ -766,7 +767,7 @@ public class BatteryStatsImpl extends BatteryStats {
// Last recorded battery energy capacity.
// This is used for computing foregrund power per application.
// See: PowerForUid below
- private long mLastBatteryEnergyCapacityNWh = 0;
+ private long mLastBatteryEnergyCapacityNwh = 0;
private static final class PowerForUid {
public long energyNwh = 0;
@@ -1001,8 +1002,12 @@ public class BatteryStatsImpl extends BatteryStats {
int mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
/**
- * Accumulated energy consumption, that is not attributed to individual uids, of various
- * consumers while on battery.
+ * Accumulated global (generally, device-wide total) energy consumption of various consumers
+ * while on battery.
+ * Its '<b>custom</b> energy buckets' correspond to the
+ * {@link android.hardware.power.stats.EnergyConsumer.ordinal}s of (custom) energy consumer
+ * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
+ *
* If energy consumer data is completely unavailable this will be null.
*/
@GuardedBy("this")
@@ -1084,12 +1089,12 @@ public class BatteryStatsImpl extends BatteryStats {
private int mNumConnectivityChange;
- private int mBatteryVolt = -1;
- private int mBatteryCharge = -1;
- private int mEstimatedBatteryCapacity = -1;
+ private int mBatteryVoltageMv = -1;
+ private int mBatteryChargeUah = -1;
+ private int mEstimatedBatteryCapacityMah = -1;
- private int mMinLearnedBatteryCapacity = -1;
- private int mMaxLearnedBatteryCapacity = -1;
+ private int mMinLearnedBatteryCapacityUah = -1;
+ private int mMaxLearnedBatteryCapacityUah = -1;
private long mBatteryTimeToFullSeconds = -1;
@@ -1170,17 +1175,17 @@ public class BatteryStatsImpl extends BatteryStats {
@Override
public int getEstimatedBatteryCapacity() {
- return mEstimatedBatteryCapacity;
+ return mEstimatedBatteryCapacityMah;
}
@Override
public int getMinLearnedBatteryCapacity() {
- return mMinLearnedBatteryCapacity;
+ return mMinLearnedBatteryCapacityUah;
}
@Override
public int getMaxLearnedBatteryCapacity() {
- return mMaxLearnedBatteryCapacity;
+ return mMaxLearnedBatteryCapacityUah;
}
public BatteryStatsImpl() {
@@ -3411,7 +3416,7 @@ public class BatteryStatsImpl extends BatteryStats {
firstToken |= DELTA_EVENT_FLAG;
}
- final boolean batteryChargeChanged = cur.batteryChargeUAh != last.batteryChargeUAh;
+ final boolean batteryChargeChanged = cur.batteryChargeUah != last.batteryChargeUah;
if (batteryChargeChanged) {
firstToken |= DELTA_BATTERY_CHARGE_FLAG;
}
@@ -3500,8 +3505,8 @@ public class BatteryStatsImpl extends BatteryStats {
mLastHistoryStepLevel = cur.batteryLevel;
if (batteryChargeChanged) {
- if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryChargeUAh=" + cur.batteryChargeUAh);
- dest.writeInt(cur.batteryChargeUAh);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryChargeUah=" + cur.batteryChargeUah);
+ dest.writeInt(cur.batteryChargeUah);
}
dest.writeDouble(cur.modemRailChargeMah);
dest.writeDouble(cur.wifiRailChargeMah);
@@ -3751,7 +3756,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
if ((firstToken&DELTA_BATTERY_CHARGE_FLAG) != 0) {
- cur.batteryChargeUAh = src.readInt();
+ cur.batteryChargeUah = src.readInt();
}
cur.modemRailChargeMah = src.readDouble();
cur.wifiRailChargeMah = src.readDouble();
@@ -7156,20 +7161,34 @@ public class BatteryStatsImpl extends BatteryStats {
@Override
public long getScreenOnEnergy() {
+ return getMeasuredEnergyMicroJoules(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
+ }
+
+ @Override
+ public long getScreenDozeEnergy() {
+ return getMeasuredEnergyMicroJoules(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE);
+ }
+
+ /**
+ * Returns the energy in microjoules that the given standard energy bucket consumed.
+ * Will return {@link #ENERGY_DATA_UNAVAILABLE} if data is unavailable
+ *
+ * @param bucket standard energy bucket of interest
+ * @return energy (in microjoules) used for this energy bucket
+ */
+ private long getMeasuredEnergyMicroJoules(@StandardEnergyBucket int bucket) {
if (mGlobalMeasuredEnergyStats == null) {
return ENERGY_DATA_UNAVAILABLE;
}
- return mGlobalMeasuredEnergyStats
- .getAccumulatedStandardBucketEnergy(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
+ return mGlobalMeasuredEnergyStats.getAccumulatedStandardBucketEnergy(bucket);
}
@Override
- public long getScreenDozeEnergy() {
+ public @Nullable long[] getCustomMeasuredEnergiesMicroJoules() {
if (mGlobalMeasuredEnergyStats == null) {
- return ENERGY_DATA_UNAVAILABLE;
+ return null;
}
- return mGlobalMeasuredEnergyStats
- .getAccumulatedStandardBucketEnergy(MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE);
+ return mGlobalMeasuredEnergyStats.getAccumulatedCustomBucketEnergies();
}
@Override public long getStartClockTime() {
@@ -7203,6 +7222,10 @@ public class BatteryStatsImpl extends BatteryStats {
return mOnBattery;
}
+ @Override public long getStatsStartRealtime() {
+ return mRealtimeStartUs;
+ }
+
@UnsupportedAppUsage
@Override public SparseArray<? extends BatteryStats.Uid> getUidStats() {
return mUidStats;
@@ -7518,9 +7541,16 @@ public class BatteryStatsImpl extends BatteryStats {
*/
private final ArraySet<BinderCallStats> mBinderCallStats = new ArraySet<>();
- /** Measured energies attributed to this uid while on battery. */
- // We do not use a SparseArray<LongSamplingCounters> since it would cause lots of
- // unnecessary timebase references, and we're just going to use on-battery anyway...
+ /**
+ * Measured energies attributed to this uid while on battery.
+ * Its '<b>custom</b> energy buckets' correspond to the
+ * {@link android.hardware.power.stats.EnergyConsumer.ordinal}s of (custom) energy consumer
+ * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
+ *
+ * Will be null if energy consumer data is completely unavailable (in which case
+ * {@link #mGlobalMeasuredEnergyStats} will also be null) or if the power usage by this uid
+ * is 0 for every bucket.
+ */
private MeasuredEnergyStats mUidMeasuredEnergyStats;
/**
@@ -7940,6 +7970,13 @@ public class BatteryStatsImpl extends BatteryStats {
.updateStandardBucket(energyBucket, energyDeltaUJ, accumulate);
}
+ /** Adds the given energy to the given custom energy bucket for this uid. */
+ private void addEnergyToCustomBucketLocked(long energyDeltaUJ, int energyBucket,
+ boolean accumulate) {
+ getOrCreateMeasuredEnergyStatsLocked()
+ .updateCustomBucket(energyBucket, energyDeltaUJ, accumulate);
+ }
+
/**
* Returns the energy used by this uid for a standard energy bucket of interest.
* @param bucket standard energy bucket of interest
@@ -7956,6 +7993,18 @@ public class BatteryStatsImpl extends BatteryStats {
return mUidMeasuredEnergyStats.getAccumulatedStandardBucketEnergy(bucket);
}
+ @Override
+ public long[] getCustomMeasuredEnergiesMicroJoules() {
+ if (mBsi.mGlobalMeasuredEnergyStats == null) {
+ return null;
+ }
+ if (mUidMeasuredEnergyStats == null) {
+ // Custom buckets may exist. But all values for this uid are 0 so we report all 0s.
+ return new long[mBsi.mGlobalMeasuredEnergyStats.getNumberCustomEnergyBuckets()];
+ }
+ return mUidMeasuredEnergyStats.getAccumulatedCustomBucketEnergies();
+ }
+
/**
* 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.
@@ -10700,11 +10749,6 @@ public class BatteryStatsImpl extends BatteryStats {
long realtimeUs = mClocks.elapsedRealtime() * 1000;
initTimes(uptimeUs, realtimeUs);
mStartPlatformVersion = mEndPlatformVersion = Build.ID;
- mDischargeStartLevel = 0;
- mDischargeUnplugLevel = 0;
- mDischargePlugLevel = -1;
- mDischargeCurrentLevel = 0;
- mCurrentBatteryLevel = 0;
initDischarge(realtimeUs);
clearHistoryLocked();
updateDailyDeadlineLocked();
@@ -10790,6 +10834,11 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeLightDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mDischargeDeepDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
+ mDischargeStartLevel = 0;
+ mDischargeUnplugLevel = 0;
+ mDischargePlugLevel = -1;
+ mDischargeCurrentLevel = 0;
+ mCurrentBatteryLevel = 0;
}
@UnsupportedAppUsage
@@ -10828,9 +10877,9 @@ public class BatteryStatsImpl extends BatteryStats {
firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i);
}
- if (mEstimatedBatteryCapacity == -1) {
+ if (mEstimatedBatteryCapacityMah == -1) {
// Initialize the estimated battery capacity to a known preset one.
- mEstimatedBatteryCapacity = (int) mPowerProfile.getBatteryCapacity();
+ mEstimatedBatteryCapacityMah = (int) mPowerProfile.getBatteryCapacity();
}
}
@@ -10842,6 +10891,10 @@ public class BatteryStatsImpl extends BatteryStats {
mSystemServerCpuThreadReader.startTrackingThreadCpuTime();
}
+ public SystemServiceCpuThreadTimes getSystemServiceCpuThreadTimes() {
+ return mSystemServerCpuThreadReader.readAbsolute();
+ }
+
public void setCallback(BatteryCallback cb) {
mCallback = cb;
}
@@ -11390,12 +11443,12 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (mPowerProfile != null) {
- mEstimatedBatteryCapacity = (int) mPowerProfile.getBatteryCapacity();
+ mEstimatedBatteryCapacityMah = (int) mPowerProfile.getBatteryCapacity();
} else {
- mEstimatedBatteryCapacity = -1;
+ mEstimatedBatteryCapacityMah = -1;
}
- mMinLearnedBatteryCapacity = -1;
- mMaxLearnedBatteryCapacity = -1;
+ mMinLearnedBatteryCapacityUah = -1;
+ mMaxLearnedBatteryCapacityUah = -1;
mInteractiveTimer.reset(false, elapsedRealtimeUs);
mPowerSaveModeEnabledTimer.reset(false, elapsedRealtimeUs);
mLastIdleTimeStartMs = elapsedRealtimeMillis;
@@ -11514,7 +11567,9 @@ public class BatteryStatsImpl extends BatteryStats {
initDischarge(elapsedRealtimeUs);
clearHistoryLocked();
- mBatteryStatsHistory.resetAllFiles();
+ if (mBatteryStatsHistory != null) {
+ mBatteryStatsHistory.resetAllFiles();
+ }
// Flush external data, gathering snapshots, but don't process it since it is pre-reset data
mIgnoreNextExternalStats = true;
@@ -12460,6 +12515,39 @@ public class BatteryStatsImpl extends BatteryStats {
}
/**
+ * Accumulate Custom energy bucket energy, globally and for each app.
+ *
+ * @param totalEnergyUJ energy (microjoules) used for this bucket since this was last called.
+ * @param uidEnergies map of uid->energy (microjoules) for this bucket since last called.
+ * Data inside uidEnergies will not be modified (treated immutable).
+ */
+ public void updateCustomMeasuredEnergyDataLocked(int customEnergyBucket,
+ long totalEnergyUJ, @Nullable SparseLongArray uidEnergies) {
+ if (DEBUG_ENERGY) {
+ Slog.d(TAG, "Updating attributed measured energy stats for custom bucket "
+ + customEnergyBucket
+ + " with total energy " + totalEnergyUJ
+ + " and uid energies " + String.valueOf(uidEnergies));
+ }
+ if (mGlobalMeasuredEnergyStats == null) return;
+ if (!mOnBatteryInternal || mIgnoreNextExternalStats || totalEnergyUJ <= 0) return;
+
+ mGlobalMeasuredEnergyStats.updateCustomBucket(customEnergyBucket, totalEnergyUJ, true);
+
+ if (uidEnergies == null) return;
+ final int numUids = uidEnergies.size();
+ for (int i = 0; i < numUids; i++) {
+ final int uidInt = mapUid(uidEnergies.keyAt(i));
+ final long uidEnergyUJ = uidEnergies.valueAt(i);
+ if (uidEnergyUJ == 0) continue;
+ // TODO(b/180030409): Worry about dead Uids (no longer in BSI) being revived by this,
+ // or converse problem of not creating a new Uid if its first blame is recorded here.
+ final Uid uidObj = getUidStatsLocked(uidInt);
+ uidObj.addEnergyToCustomBucketLocked(uidEnergyUJ, customEnergyBucket, true);
+ }
+ }
+
+ /**
* Read and record Rail Energy data.
*/
public void updateRailStatsLocked() {
@@ -12624,12 +12712,12 @@ public class BatteryStatsImpl extends BatteryStats {
// When the battery is not on, we don't attribute the cpu times to any timers but we still
// need to take the snapshots.
if (!onBattery) {
- mCpuUidUserSysTimeReader.readDelta(null);
- mCpuUidFreqTimeReader.readDelta(null);
+ mCpuUidUserSysTimeReader.readDelta(false, null);
+ mCpuUidFreqTimeReader.readDelta(false, null);
mNumAllUidCpuTimeReads += 2;
if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
- mCpuUidActiveTimeReader.readDelta(null);
- mCpuUidClusterTimeReader.readDelta(null);
+ mCpuUidActiveTimeReader.readDelta(false, null);
+ mCpuUidClusterTimeReader.readDelta(false, null);
mNumAllUidCpuTimeReads += 2;
}
for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
@@ -12815,7 +12903,7 @@ public class BatteryStatsImpl extends BatteryStats {
final long startTimeMs = mClocks.uptimeMillis();
final long elapsedRealtimeMs = mClocks.elapsedRealtime();
- mCpuUidUserSysTimeReader.readDelta((uid, timesUs) -> {
+ mCpuUidUserSysTimeReader.readDelta(false, (uid, timesUs) -> {
long userTimeUs = timesUs[0], systemTimeUs = timesUs[1];
uid = mapUid(uid);
@@ -12928,16 +13016,17 @@ public class BatteryStatsImpl extends BatteryStats {
mWakeLockAllocationsUs = null;
final long startTimeMs = mClocks.uptimeMillis();
final long elapsedRealtimeMs = mClocks.elapsedRealtime();
- mCpuUidFreqTimeReader.readDelta((uid, cpuFreqTimeMs) -> {
+ final List<Integer> uidsToRemove = new ArrayList<>();
+ mCpuUidFreqTimeReader.readDelta(false, (uid, cpuFreqTimeMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
- mCpuUidFreqTimeReader.removeUid(uid);
+ uidsToRemove.add(uid);
if (DEBUG) Slog.d(TAG, "Got freq readings for an isolated uid: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
if (DEBUG) Slog.d(TAG, "Got freq readings for an invalid user's uid " + uid);
- mCpuUidFreqTimeReader.removeUid(uid);
+ uidsToRemove.add(uid);
return;
}
final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
@@ -12996,6 +13085,9 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
});
+ for (int uid : uidsToRemove) {
+ mCpuUidFreqTimeReader.removeUid(uid);
+ }
final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
@@ -13042,21 +13134,25 @@ public class BatteryStatsImpl extends BatteryStats {
public void readKernelUidCpuActiveTimesLocked(boolean onBattery) {
final long startTimeMs = mClocks.uptimeMillis();
final long elapsedRealtimeMs = mClocks.elapsedRealtime();
- mCpuUidActiveTimeReader.readDelta((uid, cpuActiveTimesMs) -> {
+ final List<Integer> uidsToRemove = new ArrayList<>();
+ mCpuUidActiveTimeReader.readDelta(false, (uid, cpuActiveTimesMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
- mCpuUidActiveTimeReader.removeUid(uid);
+ uidsToRemove.add(uid);
if (DEBUG) Slog.w(TAG, "Got active times for an isolated uid: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
if (DEBUG) Slog.w(TAG, "Got active times for an invalid user's uid " + uid);
- mCpuUidActiveTimeReader.removeUid(uid);
+ uidsToRemove.add(uid);
return;
}
final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesMs, onBattery);
});
+ for (int uid : uidsToRemove) {
+ mCpuUidActiveTimeReader.removeUid(uid);
+ }
final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
@@ -13072,21 +13168,25 @@ public class BatteryStatsImpl extends BatteryStats {
public void readKernelUidCpuClusterTimesLocked(boolean onBattery) {
final long startTimeMs = mClocks.uptimeMillis();
final long elapsedRealtimeMs = mClocks.elapsedRealtime();
- mCpuUidClusterTimeReader.readDelta((uid, cpuClusterTimesMs) -> {
+ final List<Integer> uidsToRemove = new ArrayList<>();
+ mCpuUidClusterTimeReader.readDelta(false, (uid, cpuClusterTimesMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
- mCpuUidClusterTimeReader.removeUid(uid);
+ uidsToRemove.add(uid);
if (DEBUG) Slog.w(TAG, "Got cluster times for an isolated uid: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
if (DEBUG) Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid);
- mCpuUidClusterTimeReader.removeUid(uid);
+ uidsToRemove.add(uid);
return;
}
final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesMs, onBattery);
});
+ for (int uid : uidsToRemove) {
+ mCpuUidClusterTimeReader.removeUid(uid);
+ }
final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
@@ -13118,7 +13218,7 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
- final boolean onBattery, final int oldStatus, final int level, final int chargeUAh) {
+ final boolean onBattery, final int oldStatus, final int level, final int chargeUah) {
boolean doWrite = false;
Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
m.arg1 = onBattery ? 1 : 0;
@@ -13174,10 +13274,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
doWrite = true;
resetAllStatsLocked(mSecUptime, mSecRealtime);
- if (chargeUAh > 0 && level > 0) {
- mBatteryCharge = chargeUAh;
+ if (chargeUah > 0 && level > 0) {
+ mBatteryChargeUah = chargeUah;
// Only use the reported coulomb charge value if it is supported and reported.
- mEstimatedBatteryCapacity = (int) ((chargeUAh / 1000) / (level / 100.0));
+ mEstimatedBatteryCapacityMah = (int) ((chargeUah / 1000) / (level / 100.0));
}
mDischargeStartLevel = level;
reset = true;
@@ -13292,16 +13392,16 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void setBatteryStateLocked(final int status, final int health, final int plugType,
- final int level, /* not final */ int temp, final int volt, final int chargeUAh,
- final int chargeFullUAh, final long chargeTimeToFullSeconds) {
- setBatteryStateLocked(status, health, plugType, level, temp, volt, chargeUAh,
- chargeFullUAh, chargeTimeToFullSeconds,
+ final int level, /* not final */ int temp, final int voltageMv, final int chargeUah,
+ final int chargeFullUah, final long chargeTimeToFullSeconds) {
+ setBatteryStateLocked(status, health, plugType, level, temp, voltageMv, chargeUah,
+ chargeFullUah, chargeTimeToFullSeconds,
mClocks.elapsedRealtime(), mClocks.uptimeMillis(), System.currentTimeMillis());
}
public void setBatteryStateLocked(final int status, final int health, final int plugType,
- final int level, /* not final */ int temp, final int volt, final int chargeUAh,
- final int chargeFullUAh, final long chargeTimeToFullSeconds,
+ final int level, /* not final */ int temp, final int voltageMv, final int chargeUah,
+ final int chargeFullUah, final long chargeTimeToFullSeconds,
final long elapsedRealtimeMs, final long uptimeMs, final long currentTimeMs) {
// Temperature is encoded without the signed bit, so clamp any negative temperatures to 0.
temp = Math.max(0, temp);
@@ -13327,7 +13427,7 @@ public class BatteryStatsImpl extends BatteryStats {
mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG;
mHistoryCur.batteryStatus = (byte)status;
mHistoryCur.batteryLevel = (byte)level;
- mHistoryCur.batteryChargeUAh = chargeUAh;
+ mHistoryCur.batteryChargeUah = chargeUah;
mMaxChargeStepLevel = mMinDischargeStepLevel =
mLastChargeStepLevel = mLastDischargeStepLevel = level;
mLastChargingStateLevel = level;
@@ -13350,10 +13450,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (ENABLE_FOREGROUND_STATS_COLLECTION) {
- mBatteryVolt = volt;
+ mBatteryVoltageMv = voltageMv;
if (onBattery) {
- final long energyNwh = (volt * (long) chargeUAh);
- final long energyDelta = mLastBatteryEnergyCapacityNWh - energyNwh;
+ final long energyNwh = (voltageMv * (long) chargeUah);
+ final long energyDelta = mLastBatteryEnergyCapacityNwh - energyNwh;
for (int i = 0; i < mForegroundUids.size(); i++) {
final int uid = mForegroundUids.get(i);
if (uid == INVALID_UID) {
@@ -13364,7 +13464,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (pfu.baseTimeMs <= 0) {
pfu.baseTimeMs = currentTimeMs;
} else {
- // Check if mLastBatteryEnergyCapacityNWh > energyNwh,
+ // Check if mLastBatteryEnergyCapacityNwh > energyNwh,
// to make sure we only count discharges
if (energyDelta > 0) {
pfu.energyNwh += energyDelta;
@@ -13382,7 +13482,7 @@ public class BatteryStatsImpl extends BatteryStats {
pfu.baseTimeMs = currentTimeMs;
}
}
- mLastBatteryEnergyCapacityNWh = energyNwh;
+ mLastBatteryEnergyCapacityNwh = energyNwh;
} else if (onBattery != mOnBattery) {
// Transition to onBattery = false
mUidToPower.values().forEach(v -> v.baseTimeMs = 0);
@@ -13400,10 +13500,10 @@ public class BatteryStatsImpl extends BatteryStats {
mHistoryCur.batteryHealth = (byte)health;
mHistoryCur.batteryPlugType = (byte)plugType;
mHistoryCur.batteryTemperature = (short)temp;
- mHistoryCur.batteryVoltage = (char)volt;
- if (chargeUAh < mHistoryCur.batteryChargeUAh) {
+ mHistoryCur.batteryVoltage = (char) voltageMv;
+ if (chargeUah < mHistoryCur.batteryChargeUah) {
// Only record discharges
- final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh;
+ final long chargeDiff = mHistoryCur.batteryChargeUah - chargeUah;
mDischargeCounter.addCountLocked(chargeDiff);
mDischargeScreenOffCounter.addCountLocked(chargeDiff);
if (Display.isDozeState(mScreenState)) {
@@ -13415,8 +13515,8 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeDeepDozeCounter.addCountLocked(chargeDiff);
}
}
- mHistoryCur.batteryChargeUAh = chargeUAh;
- setOnBatteryLocked(elapsedRealtimeMs, uptimeMs, onBattery, oldStatus, level, chargeUAh);
+ mHistoryCur.batteryChargeUah = chargeUah;
+ setOnBatteryLocked(elapsedRealtimeMs, uptimeMs, onBattery, oldStatus, level, chargeUah);
} else {
boolean changed = false;
if (mHistoryCur.batteryLevel != level) {
@@ -13445,16 +13545,16 @@ public class BatteryStatsImpl extends BatteryStats {
mHistoryCur.batteryTemperature = (short)temp;
changed = true;
}
- if (volt > (mHistoryCur.batteryVoltage+20)
- || volt < (mHistoryCur.batteryVoltage-20)) {
- mHistoryCur.batteryVoltage = (char)volt;
+ if (voltageMv > (mHistoryCur.batteryVoltage + 20)
+ || voltageMv < (mHistoryCur.batteryVoltage - 20)) {
+ mHistoryCur.batteryVoltage = (char) voltageMv;
changed = true;
}
- if (chargeUAh >= (mHistoryCur.batteryChargeUAh+10)
- || chargeUAh <= (mHistoryCur.batteryChargeUAh-10)) {
- if (chargeUAh < mHistoryCur.batteryChargeUAh) {
+ if (chargeUah >= (mHistoryCur.batteryChargeUah + 10)
+ || chargeUah <= (mHistoryCur.batteryChargeUah - 10)) {
+ if (chargeUah < mHistoryCur.batteryChargeUah) {
// Only record discharges
- final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh;
+ final long chargeDiff = mHistoryCur.batteryChargeUah - chargeUah;
mDischargeCounter.addCountLocked(chargeDiff);
mDischargeScreenOffCounter.addCountLocked(chargeDiff);
if (Display.isDozeState(mScreenState)) {
@@ -13466,7 +13566,7 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeDeepDozeCounter.addCountLocked(chargeDiff);
}
}
- mHistoryCur.batteryChargeUAh = chargeUAh;
+ mHistoryCur.batteryChargeUah = chargeUah;
changed = true;
}
long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)
@@ -13538,12 +13638,12 @@ public class BatteryStatsImpl extends BatteryStats {
mRecordingHistory = DEBUG;
}
- if (mMinLearnedBatteryCapacity == -1) {
- mMinLearnedBatteryCapacity = chargeFullUAh;
+ if (mMinLearnedBatteryCapacityUah == -1) {
+ mMinLearnedBatteryCapacityUah = chargeFullUah;
} else {
- mMinLearnedBatteryCapacity = Math.min(mMinLearnedBatteryCapacity, chargeFullUAh);
+ mMinLearnedBatteryCapacityUah = Math.min(mMinLearnedBatteryCapacityUah, chargeFullUah);
}
- mMaxLearnedBatteryCapacity = Math.max(mMaxLearnedBatteryCapacity, chargeFullUAh);
+ mMaxLearnedBatteryCapacityUah = Math.max(mMaxLearnedBatteryCapacityUah, chargeFullUah);
mBatteryTimeToFullSeconds = chargeTimeToFullSeconds;
}
@@ -14449,8 +14549,13 @@ public class BatteryStatsImpl extends BatteryStats {
*/
@GuardedBy("this")
public void dumpMeasuredEnergyStatsLocked(PrintWriter pw) {
- if (mGlobalMeasuredEnergyStats == null) return;
- dumpMeasuredEnergyStatsLocked(pw, "non-uid usage", mGlobalMeasuredEnergyStats);
+ pw.printf("On battery measured energy stats (microjoules) \n");
+ if (mGlobalMeasuredEnergyStats == null) {
+ pw.printf(" Not supported on this device.\n");
+ return;
+ }
+
+ dumpMeasuredEnergyStatsLocked(pw, "global usage", mGlobalMeasuredEnergyStats);
int size = mUidStats.size();
for (int i = 0; i < size; i++) {
@@ -14467,7 +14572,8 @@ public class BatteryStatsImpl extends BatteryStats {
MeasuredEnergyStats stats) {
if (stats == null) return;
final IndentingPrintWriter iPw = new IndentingPrintWriter(pw, " ");
- iPw.printf("On battery measured energy stats for %s:\n", name);
+ iPw.increaseIndent();
+ iPw.printf("%s:\n", name);
iPw.increaseIndent();
stats.dump(iPw);
iPw.decreaseIndent();
@@ -14812,9 +14918,9 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargePlugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
mCurrentBatteryLevel = in.readInt();
- mEstimatedBatteryCapacity = in.readInt();
- mMinLearnedBatteryCapacity = in.readInt();
- mMaxLearnedBatteryCapacity = in.readInt();
+ mEstimatedBatteryCapacityMah = in.readInt();
+ mMinLearnedBatteryCapacityUah = in.readInt();
+ mMaxLearnedBatteryCapacityUah = in.readInt();
mLowDischargeAmountSinceCharge = in.readInt();
mHighDischargeAmountSinceCharge = in.readInt();
mDischargeAmountScreenOnSinceCharge = in.readInt();
@@ -15316,9 +15422,9 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeInt(mDischargePlugLevel);
out.writeInt(mDischargeCurrentLevel);
out.writeInt(mCurrentBatteryLevel);
- out.writeInt(mEstimatedBatteryCapacity);
- out.writeInt(mMinLearnedBatteryCapacity);
- out.writeInt(mMaxLearnedBatteryCapacity);
+ out.writeInt(mEstimatedBatteryCapacityMah);
+ out.writeInt(mMinLearnedBatteryCapacityUah);
+ out.writeInt(mMaxLearnedBatteryCapacityUah);
out.writeInt(getLowDischargeAmountSinceCharge());
out.writeInt(getHighDischargeAmountSinceCharge());
out.writeInt(getDischargeAmountScreenOnSinceCharge());
@@ -15817,9 +15923,9 @@ public class BatteryStatsImpl extends BatteryStats {
mRealtimeUs = in.readLong();
mRealtimeStartUs = in.readLong();
mOnBattery = in.readInt() != 0;
- mEstimatedBatteryCapacity = in.readInt();
- mMinLearnedBatteryCapacity = in.readInt();
- mMaxLearnedBatteryCapacity = in.readInt();
+ mEstimatedBatteryCapacityMah = in.readInt();
+ mMinLearnedBatteryCapacityUah = in.readInt();
+ mMaxLearnedBatteryCapacityUah = in.readInt();
mOnBatteryInternal = false; // we are no longer really running.
mOnBatteryTimeBase.readFromParcel(in);
mOnBatteryScreenOffTimeBase.readFromParcel(in);
@@ -16056,9 +16162,9 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeLong(mRealtimeUs);
out.writeLong(mRealtimeStartUs);
out.writeInt(mOnBattery ? 1 : 0);
- out.writeInt(mEstimatedBatteryCapacity);
- out.writeInt(mMinLearnedBatteryCapacity);
- out.writeInt(mMaxLearnedBatteryCapacity);
+ out.writeInt(mEstimatedBatteryCapacityMah);
+ out.writeInt(mMinLearnedBatteryCapacityUah);
+ out.writeInt(mMaxLearnedBatteryCapacityUah);
mOnBatteryTimeBase.writeToParcel(out, uSecUptime, uSecRealtime);
mOnBatteryScreenOffTimeBase.writeToParcel(out, uSecUptime, uSecRealtime);
@@ -16314,8 +16420,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
if (ENABLE_FOREGROUND_STATS_COLLECTION) {
- long actualCharge = -1;
- long actualEnergy = -1;
+ long actualChargeUah = -1;
+ long actualEnergyNwh = -1;
try {
IBatteryPropertiesRegistrar registrar =
IBatteryPropertiesRegistrar.Stub.asInterface(
@@ -16324,27 +16430,27 @@ public class BatteryStatsImpl extends BatteryStats {
BatteryProperty prop = new BatteryProperty();
if (registrar.getProperty(
BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER, prop) == 0) {
- actualCharge = prop.getLong();
+ actualChargeUah = prop.getLong();
}
prop = new BatteryProperty();
if (registrar.getProperty(
BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER, prop) == 0) {
- actualEnergy = prop.getLong();
+ actualEnergyNwh = prop.getLong();
}
}
} catch (RemoteException e) {
// Ignore.
}
- pw.printf("ActualCharge (uAh): %d\n", (int) actualCharge);
- pw.printf("ActualEnergy (nWh): %d\n", actualEnergy);
- pw.printf("mBatteryCharge (uAh): %d\n", mBatteryCharge);
- pw.printf("mBatteryVolts (mV): %d\n", mBatteryVolt);
- pw.printf("est energy (nWh): %d\n", mBatteryVolt * (long) mBatteryCharge);
- pw.printf("mEstimatedBatteryCapacity (mAh): %d\n", mEstimatedBatteryCapacity);
- pw.printf("mMinLearnedBatteryCapacity (uAh): %d\n", mMinLearnedBatteryCapacity);
- pw.printf("mMaxLearnedBatteryCapacity (uAh): %d\n", mMaxLearnedBatteryCapacity);
+ pw.printf("ActualCharge (uAh): %d\n", (int) actualChargeUah);
+ pw.printf("ActualEnergy (nWh): %d\n", actualEnergyNwh);
+ pw.printf("mBatteryCharge (uAh): %d\n", mBatteryChargeUah);
+ pw.printf("mBatteryVolts (mV): %d\n", mBatteryVoltageMv);
+ pw.printf("est energy (nWh): %d\n", mBatteryVoltageMv * (long) mBatteryChargeUah);
+ pw.printf("mEstimatedBatteryCapacity (mAh): %d\n", mEstimatedBatteryCapacityMah);
+ pw.printf("mMinLearnedBatteryCapacity (uAh): %d\n", mMinLearnedBatteryCapacityUah);
+ pw.printf("mMaxLearnedBatteryCapacity (uAh): %d\n", mMaxLearnedBatteryCapacityUah);
pw.printf("est. capacity: %f\n",
- (float) actualCharge / (mEstimatedBatteryCapacity * 1000));
+ (float) actualChargeUah / (mEstimatedBatteryCapacityMah * 1000));
pw.printf("mCurrentBatteryLevel: %d\n", mCurrentBatteryLevel);
pw.println("Total Power per app:");
mUidToPower.entrySet().forEach(e ->
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index e76e34f840ba..eef9fa74e83a 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -24,7 +24,6 @@ import android.os.BatteryUsageStatsQuery;
import android.os.Bundle;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.os.UserManager;
import android.util.SparseArray;
import java.util.ArrayList;
@@ -53,6 +52,7 @@ public class BatteryUsageStatsProvider {
mPowerCalculators = new ArrayList<>();
// Power calculators are applied in the order of registration
+ mPowerCalculators.add(new DischargedPowerCalculator(mPowerProfile));
mPowerCalculators.add(new CpuPowerCalculator(mPowerProfile));
mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
@@ -71,10 +71,14 @@ public class BatteryUsageStatsProvider {
mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile));
mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile));
mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new SystemServicePowerCalculator(mPowerProfile));
mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
-
+ mPowerCalculators.add(new CustomMeasuredPowerCalculator(mPowerProfile));
mPowerCalculators.add(new UserPowerCalculator());
+
+ // It is important that SystemServicePowerCalculator be applied last,
+ // because it re-attributes some of the power estimated by the other
+ // calculators.
+ mPowerCalculators.add(new SystemServicePowerCalculator(mPowerProfile));
}
}
return mPowerCalculators;
@@ -89,34 +93,38 @@ public class BatteryUsageStatsProvider {
final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(mContext,
false /* collectBatteryBroadcast */);
batteryStatsHelper.create((Bundle) null);
- final UserManager userManager = mContext.getSystemService(UserManager.class);
- final List<UserHandle> asUsers = userManager.getUserProfiles();
- final int n = asUsers.size();
- SparseArray<UserHandle> users = new SparseArray<>(n);
- for (int i = 0; i < n; ++i) {
- UserHandle userHandle = asUsers.get(i);
- users.put(userHandle.getIdentifier(), userHandle);
+ final List<UserHandle> users = new ArrayList<>();
+ for (int i = 0; i < queries.size(); i++) {
+ BatteryUsageStatsQuery query = queries.get(i);
+ for (int userId : query.getUserIds()) {
+ UserHandle userHandle = UserHandle.of(userId);
+ if (!users.contains(userHandle)) {
+ users.add(userHandle);
+ }
+ }
}
-
batteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, users);
ArrayList<BatteryUsageStats> results = new ArrayList<>(queries.size());
for (int i = 0; i < queries.size(); i++) {
- results.add(getBatteryUsageStats(queries.get(i), batteryStatsHelper, users));
+ results.add(getBatteryUsageStats(queries.get(i)));
}
return results;
}
- private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query,
- BatteryStatsHelper batteryStatsHelper, SparseArray<UserHandle> users) {
- // TODO(b/174186358): read extra power component number from configuration
- final int customPowerComponentCount = 0;
+ private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
+ final long[] customMeasuredEnergiesMicroJoules =
+ mStats.getCustomMeasuredEnergiesMicroJoules();
+ final int customPowerComponentCount = customMeasuredEnergiesMicroJoules != null
+ ? customMeasuredEnergiesMicroJoules.length
+ : 0;
+
+ // TODO(b/174186358): read extra time component number from configuration
final int customTimeComponentCount = 0;
final BatteryUsageStats.Builder batteryUsageStatsBuilder =
new BatteryUsageStats.Builder(customPowerComponentCount, customTimeComponentCount)
- .setDischargePercentage(batteryStatsHelper.getStats().getDischargeAmount(0))
- .setConsumedPower(batteryStatsHelper.getTotalPower());
+ .setStatsStartRealtime(mStats.getStatsStartRealtime() / 1000);
SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats();
for (int i = uidStats.size() - 1; i >= 0; i--) {
@@ -127,9 +135,10 @@ public class BatteryUsageStatsProvider {
final long uptimeUs = SystemClock.uptimeMillis() * 1000;
final List<PowerCalculator> powerCalculators = getPowerCalculators();
- for (PowerCalculator powerCalculator : powerCalculators) {
- powerCalculator.calculate(batteryUsageStatsBuilder, mStats, realtimeUs, uptimeUs, query,
- users);
+ for (int i = 0, count = powerCalculators.size(); i < count; i++) {
+ PowerCalculator powerCalculator = powerCalculators.get(i);
+ powerCalculator.calculate(batteryUsageStatsBuilder, mStats, realtimeUs, uptimeUs,
+ query);
}
return batteryUsageStatsBuilder.build();
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
index 4c3b950ff715..7d42de4486a4 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -50,8 +50,7 @@ public class BluetoothPowerCalculator extends PowerCalculator {
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
if (!mHasBluetoothPowerController || !batteryStats.hasBluetoothActivityReporting()) {
return;
}
@@ -68,7 +67,7 @@ public class BluetoothPowerCalculator extends PowerCalculator {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
calculateApp(app, total);
if (app.getUid() == Process.BLUETOOTH_UID) {
- app.setSystemComponent(true);
+ app.excludeFromBatteryUsageStats();
systemBatteryConsumerBuilder.addUidBatteryConsumer(app);
}
}
diff --git a/core/java/com/android/internal/os/CPU_OWNERS b/core/java/com/android/internal/os/CPU_OWNERS
new file mode 100644
index 000000000000..5d3473cc0c9e
--- /dev/null
+++ b/core/java/com/android/internal/os/CPU_OWNERS
@@ -0,0 +1,3 @@
+connoro@google.com
+dplotnikov@google.com
+rslawik@google.com # Android Telemetry
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 11c87618762e..97f727ba72c5 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -17,81 +17,166 @@ package com.android.internal.os;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.List;
public class CpuPowerCalculator extends PowerCalculator {
private static final String TAG = "CpuPowerCalculator";
private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
- private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000;
- private final PowerProfile mProfile;
+ private final int mNumCpuClusters;
+
+ // Time-in-state based CPU power estimation model computes the estimated power
+ // by adding up three components:
+ // - CPU Active power: the constant amount of charge consumed by the CPU when it is on
+ // - Per Cluster power: the additional amount of charge consumed by a CPU cluster
+ // when it is running
+ // - Per frequency power: the additional amount of charge caused by dynamic frequency scaling
+
+ private final UsageBasedPowerEstimator mCpuActivePowerEstimator;
+ // One estimator per cluster
+ private final UsageBasedPowerEstimator[] mPerClusterPowerEstimators;
+ // Multiple estimators per cluster: one per available scaling frequency. Note that different
+ // clusters have different sets of frequencies and corresponding power consumption averages.
+ private final UsageBasedPowerEstimator[][] mPerCpuFreqPowerEstimators;
+
+ private static class Result {
+ public long durationMs;
+ public double powerMah;
+ public long durationFgMs;
+ public String packageWithHighestDrain;
+ }
public CpuPowerCalculator(PowerProfile profile) {
- mProfile = profile;
+ mNumCpuClusters = profile.getNumCpuClusters();
+
+ mCpuActivePowerEstimator = new UsageBasedPowerEstimator(
+ profile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE));
+
+ mPerClusterPowerEstimators = new UsageBasedPowerEstimator[mNumCpuClusters];
+ for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
+ mPerClusterPowerEstimators[cluster] = new UsageBasedPowerEstimator(
+ profile.getAveragePowerForCpuCluster(cluster));
+ }
+
+ mPerCpuFreqPowerEstimators = new UsageBasedPowerEstimator[mNumCpuClusters][];
+ for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
+ final int speedsForCluster = profile.getNumSpeedStepsInCpuCluster(cluster);
+ mPerCpuFreqPowerEstimators[cluster] = new UsageBasedPowerEstimator[speedsForCluster];
+ for (int speed = 0; speed < speedsForCluster; speed++) {
+ mPerCpuFreqPowerEstimators[cluster][speed] =
+ new UsageBasedPowerEstimator(
+ profile.getAveragePowerForCpuCore(cluster, speed));
+ }
+ }
}
@Override
- protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
- final int statsType = BatteryStats.STATS_SINCE_CHARGED;
+ Result result = new Result();
+ final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+ builder.getUidBatteryConsumerBuilders();
+ for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+ final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+ calculateApp(app, app.getBatteryStatsUid(), result);
+ }
+ }
- long cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
- final int numClusters = mProfile.getNumCpuClusters();
+ private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, Result result) {
+ calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED, result);
- double cpuPowerMaUs = 0;
- for (int cluster = 0; cluster < numClusters; cluster++) {
- final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
- for (int speed = 0; speed < speedsForCluster; speed++) {
- final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
- final double cpuSpeedStepPower = timeUs *
- mProfile.getAveragePowerForCpuCore(cluster, speed);
- if (DEBUG) {
- Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
- + speed + " timeUs=" + timeUs + " power="
- + formatCharge(cpuSpeedStepPower / MICROSEC_IN_HR));
- }
- cpuPowerMaUs += cpuSpeedStepPower;
+ app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, result.powerMah)
+ .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, result.durationMs)
+ .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND,
+ result.durationFgMs)
+ .setPackageWithHighestDrain(result.packageWithHighestDrain);
+ }
+
+ @Override
+ public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
+ long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
+ Result result = new Result();
+ for (int i = sippers.size() - 1; i >= 0; i--) {
+ final BatterySipper app = sippers.get(i);
+ if (app.drainType == BatterySipper.DrainType.APP) {
+ calculateApp(app, app.uidObj, statsType, result);
}
}
- cpuPowerMaUs += u.getCpuActiveTime() * 1000 * mProfile.getAveragePower(
- PowerProfile.POWER_CPU_ACTIVE);
+ }
+
+ private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, Result result) {
+ calculatePowerAndDuration(u, statsType, result);
+
+ app.cpuPowerMah = result.powerMah;
+ app.cpuTimeMs = result.durationMs;
+ app.cpuFgTimeMs = result.durationFgMs;
+ app.packageWithHighestDrain = result.packageWithHighestDrain;
+ }
+
+ private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType, 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 == numClusters) {
- for (int i = 0; i < numClusters; i++) {
- double power =
- cpuClusterTimes[i] * 1000 * mProfile.getAveragePowerForCpuCluster(i);
- cpuPowerMaUs += power;
+ 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 #" + i + " clusterTimeUs="
- + cpuClusterTimes[i] + " power="
- + formatCharge(power / MICROSEC_IN_HR));
+ 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 # "
- + numClusters + " actual # " + cpuClusterTimes.length);
+ + mNumCpuClusters + " actual # " + cpuClusterTimes.length);
}
}
- final double cpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR;
- if (DEBUG && (cpuTimeMs != 0 || cpuPowerMah != 0)) {
- Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + cpuTimeMs + " ms power="
- + formatCharge(cpuPowerMah));
+ // 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;
+ }
+ }
+
+ if (DEBUG && (durationMs != 0 || powerMah != 0)) {
+ Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + durationMs + " ms power="
+ + formatCharge(powerMah));
}
// Keep track of the package with highest drain.
double highestDrain = 0;
String packageWithHighestDrain = null;
- long cpuFgTimeMs = 0;
+ long durationFgMs = 0;
final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
final int processStatsCount = processStats.size();
for (int i = 0; i < processStatsCount; i++) {
final BatteryStats.Uid.Proc ps = processStats.valueAt(i);
final String processName = processStats.keyAt(i);
- cpuFgTimeMs += ps.getForegroundTime(statsType);
+ durationFgMs += ps.getForegroundTime(statsType);
final long costValue = ps.getUserTime(statsType) + ps.getSystemTime(statsType)
+ ps.getForegroundTime(statsType);
@@ -107,20 +192,54 @@ public class CpuPowerCalculator extends PowerCalculator {
}
}
-
// Ensure that the CPU times make sense.
- if (cpuFgTimeMs > cpuTimeMs) {
- if (DEBUG && cpuFgTimeMs > cpuTimeMs + 10000) {
+ if (durationFgMs > durationMs) {
+ if (DEBUG && durationFgMs > durationMs + 10000) {
Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
}
// Statistics may not have been gathered yet.
- cpuTimeMs = cpuFgTimeMs;
+ durationMs = durationFgMs;
}
- app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, cpuPowerMah)
- .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, cpuTimeMs)
- .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, cpuFgTimeMs)
- .setPackageWithHighestDrain(packageWithHighestDrain);
+ result.durationMs = durationMs;
+ result.durationFgMs = durationFgMs;
+ result.powerMah = powerMah;
+ result.packageWithHighestDrain = packageWithHighestDrain;
+ }
+
+ /**
+ * Calculates active CPU power consumption.
+ *
+ * @param durationsMs duration of CPU usage.
+ * @return a double in milliamp-hours of estimated active CPU power consumption.
+ */
+ public double calculateActiveCpuPowerMah(long durationsMs) {
+ return mCpuActivePowerEstimator.calculatePower(durationsMs);
+ }
+
+ /**
+ * Calculates CPU cluster power consumption.
+ *
+ * @param cluster CPU cluster used.
+ * @param clusterDurationMs duration of CPU cluster usage.
+ * @return a double in milliamp-hours of estimated CPU cluster power consumption.
+ */
+ public double calculatePerCpuClusterPowerMah(int cluster, long clusterDurationMs) {
+ return mPerClusterPowerEstimators[cluster].calculatePower(clusterDurationMs);
+ }
+
+ /**
+ * Calculates CPU cluster power consumption at a specific speedstep.
+ *
+ * @param cluster CPU cluster used.
+ * @param speedStep which speedstep used.
+ * @param clusterSpeedDurationsMs duration of CPU cluster usage at the specified speed step.
+ * @return a double in milliamp-hours of estimated CPU cluster-speed power consumption.
+ */
+ public double calculatePerCpuFreqPowerMah(int cluster, int speedStep,
+ long clusterSpeedDurationsMs) {
+ return mPerCpuFreqPowerEstimators[cluster][speedStep].calculatePower(
+ clusterSpeedDurationsMs);
}
}
diff --git a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
new file mode 100644
index 000000000000..2606d80eaa09
--- /dev/null
+++ b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
@@ -0,0 +1,85 @@
+/*
+ * 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.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
+
+/**
+ * Calculates the amount of power consumed by custom energy consumers (i.e. consumers of type
+ * {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
+ */
+public class CustomMeasuredPowerCalculator extends PowerCalculator {
+ public CustomMeasuredPowerCalculator(PowerProfile powerProfile) {
+ }
+
+ @Override
+ public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ super.calculate(builder, batteryStats, rawRealtimeUs, rawUptimeUs, query);
+ final double[] customMeasuredPowerMah = calculateMeasuredEnergiesMah(
+ batteryStats.getCustomMeasuredEnergiesMicroJoules());
+ if (customMeasuredPowerMah != null) {
+ final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder =
+ builder.getOrCreateSystemBatteryConsumerBuilder(
+ SystemBatteryConsumer.DRAIN_TYPE_CUSTOM);
+ for (int i = 0; i < customMeasuredPowerMah.length; i++) {
+ systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i,
+ customMeasuredPowerMah[i]);
+ }
+ }
+ }
+
+ @Override
+ protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ final double[] customMeasuredPowerMah = calculateMeasuredEnergiesMah(
+ u.getCustomMeasuredEnergiesMicroJoules());
+ if (customMeasuredPowerMah != null) {
+ for (int i = 0; i < customMeasuredPowerMah.length; i++) {
+ app.setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i,
+ customMeasuredPowerMah[i]);
+ }
+ }
+ }
+
+ @Override
+ protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ updateCustomMeasuredPowerMah(app, u.getCustomMeasuredEnergiesMicroJoules());
+ }
+
+ private void updateCustomMeasuredPowerMah(BatterySipper sipper, long[] measuredEnergiesUJ) {
+ sipper.customMeasuredPowerMah = calculateMeasuredEnergiesMah(measuredEnergiesUJ);
+ }
+
+ private double[] calculateMeasuredEnergiesMah(long[] measuredEnergiesUJ) {
+ if (measuredEnergiesUJ == null) {
+ return null;
+ }
+ final double[] measuredEnergiesMah = new double[measuredEnergiesUJ.length];
+ for (int i = 0; i < measuredEnergiesUJ.length; i++) {
+ measuredEnergiesMah[i] = uJtoMah(measuredEnergiesUJ[i]);
+ }
+ return measuredEnergiesMah;
+ }
+}
diff --git a/core/java/com/android/internal/os/DischargedPowerCalculator.java b/core/java/com/android/internal/os/DischargedPowerCalculator.java
new file mode 100644
index 000000000000..e94020cc4f18
--- /dev/null
+++ b/core/java/com/android/internal/os/DischargedPowerCalculator.java
@@ -0,0 +1,52 @@
+/*
+ * 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.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.UserHandle;
+import android.util.SparseArray;
+
+import java.util.List;
+
+/**
+ * Estimates the battery discharge amounts.
+ */
+public class DischargedPowerCalculator extends PowerCalculator {
+ private final double mBatteryCapacity;
+
+ public DischargedPowerCalculator(PowerProfile powerProfile) {
+ mBatteryCapacity = powerProfile.getBatteryCapacity();
+ }
+
+ @Override
+ public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ builder.setDischargePercentage(
+ batteryStats.getDischargeAmount(BatteryStats.STATS_SINCE_CHARGED))
+ .setDischargedPowerRange(
+ batteryStats.getLowDischargeAmountSinceCharge() * mBatteryCapacity / 100,
+ batteryStats.getHighDischargeAmountSinceCharge() * mBatteryCapacity / 100);
+ }
+
+ @Override
+ public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
+ long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
+ // Not implemented. The computation is done by BatteryStatsHelper
+ }
+}
diff --git a/core/java/com/android/internal/os/GnssPowerCalculator.java b/core/java/com/android/internal/os/GnssPowerCalculator.java
index 9ea934af642a..df25cdaee17f 100644
--- a/core/java/com/android/internal/os/GnssPowerCalculator.java
+++ b/core/java/com/android/internal/os/GnssPowerCalculator.java
@@ -45,8 +45,7 @@ public class GnssPowerCalculator extends PowerCalculator {
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
final double averageGnssPowerMa = getAverageGnssPower(batteryStats, rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED);
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
diff --git a/core/java/com/android/internal/os/IdlePowerCalculator.java b/core/java/com/android/internal/os/IdlePowerCalculator.java
index dcc8a15b2f50..4a4991bd3966 100644
--- a/core/java/com/android/internal/os/IdlePowerCalculator.java
+++ b/core/java/com/android/internal/os/IdlePowerCalculator.java
@@ -49,8 +49,7 @@ public class IdlePowerCalculator extends PowerCalculator {
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
calculatePowerAndDuration(batteryStats, rawRealtimeUs, rawUptimeUs,
BatteryStats.STATS_SINCE_CHARGED);
if (mPowerMah != 0) {
diff --git a/core/java/com/android/internal/os/KernelCpuBpfTracking.java b/core/java/com/android/internal/os/KernelCpuBpfTracking.java
new file mode 100644
index 000000000000..28525478be05
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelCpuBpfTracking.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/** CPU tracking using eBPF. */
+public final class KernelCpuBpfTracking {
+ private KernelCpuBpfTracking() {
+ }
+
+ /** Returns whether CPU tracking using eBPF is supported. */
+ public static native boolean isSupported();
+}
diff --git a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java
index fa552e3603c6..06760e140de1 100644
--- a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java
+++ b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java
@@ -23,12 +23,6 @@ public final class KernelCpuTotalBpfMapReader {
private KernelCpuTotalBpfMapReader() {
}
- /** Returns whether total CPU time is measured. */
- public static boolean isSupported() {
- // TODO(b/174245730): Implement this check.
- return true;
- }
-
/** Reads total CPU time from bpf map. */
public static native boolean read(Callback callback);
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
index f7fad2c5bbaa..4299f0936dae 100644
--- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -91,15 +91,24 @@ public abstract class KernelCpuUidTimeReader<T> {
* Reads the proc file, calling into the callback with a delta of time for each UID.
*
* @param cb The callback to invoke for each line of the proc file. If null,the data is
- * consumed and subsequent calls to readDelta will provide a fresh delta.
*/
public void readDelta(@Nullable Callback<T> cb) {
+ readDelta(false, cb);
+ }
+
+ /**
+ * Reads the proc file, calling into the callback with a delta of time for each UID.
+ *
+ * @param force Ignore the throttling and force read the delta.
+ * @param cb The callback to invoke for each line of the proc file. If null,the data is
+ */
+ public void readDelta(boolean force, @Nullable Callback<T> cb) {
if (!mThrottle) {
readDeltaImpl(cb);
return;
}
final long currTimeMs = SystemClock.elapsedRealtime();
- if (currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
+ if (!force && currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
if (DEBUG) {
Slog.d(mTag, "Throttle readDelta");
}
diff --git a/core/java/com/android/internal/os/MemoryPowerCalculator.java b/core/java/com/android/internal/os/MemoryPowerCalculator.java
index df4605838b28..21dcce9fb64f 100644
--- a/core/java/com/android/internal/os/MemoryPowerCalculator.java
+++ b/core/java/com/android/internal/os/MemoryPowerCalculator.java
@@ -26,8 +26,7 @@ public class MemoryPowerCalculator extends PowerCalculator {
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
final long durationMs = calculateDuration(batteryStats, rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED);
final double powerMah = calculatePower(batteryStats, rawRealtimeUs,
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
index e3bd64d77e9b..22001d4ffbb7 100644
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -85,8 +85,7 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
PowerAndDuration total = new PowerAndDuration();
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 1b07aa0cf0b7..0aa54f556b92 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -1,5 +1,6 @@
per-file *Power* = file:/services/core/java/com/android/server/power/OWNERS
per-file *Zygote* = file:/ZYGOTE_OWNERS
+per-file *Cpu* = file:CPU_OWNERS
# BatteryStats
per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/os/PhonePowerCalculator.java b/core/java/com/android/internal/os/PhonePowerCalculator.java
index 6ab8c90d21d9..362ca0761b2c 100644
--- a/core/java/com/android/internal/os/PhonePowerCalculator.java
+++ b/core/java/com/android/internal/os/PhonePowerCalculator.java
@@ -39,8 +39,7 @@ public class PhonePowerCalculator extends PowerCalculator {
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
final long phoneOnTimeMs = batteryStats.getPhoneOnTime(rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED) / 1000;
final double phoneOnPower = mPowerEstimator.calculatePower(phoneOnTimeMs);
diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java
index 05fcc705e734..fe4fb7afa1f5 100644
--- a/core/java/com/android/internal/os/PowerCalculator.java
+++ b/core/java/com/android/internal/os/PowerCalculator.java
@@ -67,17 +67,10 @@ public abstract class PowerCalculator {
* @param batteryStats The recorded battery stats.
* @param rawRealtimeUs The raw system realtime in microseconds.
* @param rawUptimeUs The raw system uptime in microseconds.
- * @param statsType The type of stats. As of {@link android.os.Build.VERSION_CODES#Q}, this
- * can only be {@link BatteryStats#STATS_SINCE_CHARGED}, since
- * {@link BatteryStats#STATS_CURRENT} and
- * {@link BatteryStats#STATS_SINCE_UNPLUGGED} are deprecated.
- * @param asUsers An array of users for which the attribution is requested. It may
- * contain {@link UserHandle#USER_ALL} to indicate that the attribution
- * should be performed for all users.
+ * @param query The query parameters for the calculator.
*/
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
builder.getUidBatteryConsumerBuilders();
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
@@ -99,19 +92,6 @@ public abstract class PowerCalculator {
*/
protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
long rawUptimeUs, int statsType) {
-
- // TODO(b/175156498): Temporary code during the transition from BatterySippers to
- // BatteryConsumers.
- UidBatteryConsumer.Builder builder = new UidBatteryConsumer.Builder(0, 0, u);
- calculateApp(builder, u, rawRealtimeUs, rawUptimeUs, BatteryUsageStatsQuery.DEFAULT);
- final UidBatteryConsumer uidBatteryConsumer = builder.build();
- app.cpuPowerMah = uidBatteryConsumer.getConsumedPower(
- UidBatteryConsumer.POWER_COMPONENT_CPU);
- app.cpuTimeMs = uidBatteryConsumer.getUsageDurationMillis(
- UidBatteryConsumer.TIME_COMPONENT_CPU);
- app.cpuFgTimeMs = uidBatteryConsumer.getUsageDurationMillis(
- UidBatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND);
- app.packageWithHighestDrain = uidBatteryConsumer.getPackageWithHighestDrain();
}
/**
@@ -133,6 +113,19 @@ public abstract class PowerCalculator {
}
/**
+ * Returns either the measured energy converted to mAh or a usage-based estimate.
+ */
+ protected static double getMeasuredOrEstimatedPower(long measuredEnergyUj,
+ UsageBasedPowerEstimator powerEstimator, long durationMs,
+ boolean forceUsePowerProfileModel) {
+ if (measuredEnergyUj != BatteryStats.ENERGY_DATA_UNAVAILABLE
+ && !forceUsePowerProfileModel) {
+ return uJtoMah(measuredEnergyUj);
+ }
+ return powerEstimator.calculatePower(durationMs);
+ }
+
+ /**
* Converts charge in mAh to string.
*/
public static String formatCharge(double power) {
@@ -163,7 +156,11 @@ public abstract class PowerCalculator {
return String.format(Locale.ENGLISH, format, power);
}
- static double mAhToUJ(long energyUJ) {
+ static double uJtoMah(long energyUJ) {
+ if (energyUJ == 0) {
+ return 0;
+ }
+
// TODO(b/173765509): Convert properly. This is mJ / V * (h/3600s) = mAh with V = 3.7 fixed.
// Leaving for later since desired units of energy have yet to be decided
return energyUJ / 1000.0 / 3.7 / 3600;
diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java
index c86c795c965a..5dca8d5e70fa 100644
--- a/core/java/com/android/internal/os/ScreenPowerCalculator.java
+++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java
@@ -21,7 +21,7 @@ import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.SystemBatteryConsumer;
-import android.os.SystemClock;
+import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.text.format.DateUtils;
import android.util.Slog;
@@ -39,9 +39,17 @@ public class ScreenPowerCalculator extends PowerCalculator {
private static final String TAG = "ScreenPowerCalculator";
private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+ // Minimum amount of time the screen should be on to start smearing drain to apps
+ public static final long MIN_ACTIVE_TIME_FOR_SMEARING = 10 * DateUtils.MINUTE_IN_MILLIS;
+
private final UsageBasedPowerEstimator mScreenOnPowerEstimator;
private final UsageBasedPowerEstimator mScreenFullPowerEstimator;
+ private static class PowerAndDuration {
+ public long durationMs;
+ public double powerMah;
+ }
+
public ScreenPowerCalculator(PowerProfile powerProfile) {
mScreenOnPowerEstimator = new UsageBasedPowerEstimator(
powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON));
@@ -51,19 +59,39 @@ public class ScreenPowerCalculator extends PowerCalculator {
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
- final long durationMs = computeDuration(batteryStats, rawRealtimeUs,
- BatteryStats.STATS_SINCE_CHARGED);
- final double powerMah = computePower(batteryStats, rawRealtimeUs,
- BatteryStats.STATS_SINCE_CHARGED, durationMs);
- if (powerMah != 0) {
- builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_SCREEN)
- .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah);
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ final PowerAndDuration totalPowerAndDuration = new PowerAndDuration();
+
+ final boolean useEnergyData = calculateTotalDurationAndPower(totalPowerAndDuration,
+ batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED,
+ query.shouldForceUsePowerProfileModel());
+
+ builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_SCREEN)
+ .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE,
+ totalPowerAndDuration.durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE,
+ totalPowerAndDuration.powerMah);
+
+ // Now deal with each app's UidBatteryConsumer. The results are stored in the
+ // BatteryConsumer.POWER_COMPONENT_SCREEN power component, which is considered smeared,
+ // but the method depends on the data source.
+ final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+ builder.getUidBatteryConsumerBuilders();
+ if (useEnergyData) {
+ final PowerAndDuration appPowerAndDuration = new PowerAndDuration();
+ for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+ final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+ calculateAppUsingMeasuredEnergy(appPowerAndDuration, app.getBatteryStatsUid(),
+ rawRealtimeUs);
+ app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN,
+ appPowerAndDuration.durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN,
+ appPowerAndDuration.powerMah);
+ }
+ } else {
+ smearScreenBatteryDrain(uidBatteryConsumerBuilders, totalPowerAndDuration,
+ rawRealtimeUs);
}
- // TODO(b/178140704): Attribute *measured* total usage for BatteryUsageStats.
- // TODO(b/178140704): Attribute (measured/smeared) usage *per app* for BatteryUsageStats.
}
/**
@@ -72,51 +100,79 @@ public class ScreenPowerCalculator extends PowerCalculator {
@Override
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
-
- final long energyUJ = batteryStats.getScreenOnEnergy();
- final boolean isMeasuredDataAvailable = energyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE;
-
- final long durationMs = computeDuration(batteryStats, rawRealtimeUs, statsType);
- final double powerMah = getMeasuredOrComputedPower(
- energyUJ, batteryStats, rawRealtimeUs, statsType, durationMs);
- if (powerMah == 0) {
+ final PowerAndDuration totalPowerAndDuration = new PowerAndDuration();
+ final boolean useEnergyData = calculateTotalDurationAndPower(totalPowerAndDuration,
+ batteryStats, rawRealtimeUs, statsType, false);
+ if (totalPowerAndDuration.powerMah == 0) {
return;
}
// First deal with the SCREEN BatterySipper (since we need this for smearing over apps).
final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.SCREEN, null, 0);
- bs.usagePowerMah = powerMah;
- bs.usageTimeMs = durationMs;
+ bs.usagePowerMah = totalPowerAndDuration.powerMah;
+ bs.usageTimeMs = totalPowerAndDuration.durationMs;
bs.sumPower();
sippers.add(bs);
// Now deal with each app's BatterySipper. The results are stored in the screenPowerMah
// field, which is considered smeared, but the method depends on the data source.
- if (isMeasuredDataAvailable) {
- super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers);
+ if (useEnergyData) {
+ final PowerAndDuration appPowerAndDuration = new PowerAndDuration();
+ for (int i = sippers.size() - 1; i >= 0; i--) {
+ final BatterySipper app = sippers.get(i);
+ if (app.drainType == BatterySipper.DrainType.APP) {
+ calculateAppUsingMeasuredEnergy(appPowerAndDuration, app.uidObj, rawRealtimeUs);
+ app.screenPowerMah = appPowerAndDuration.powerMah;
+ }
+ }
} else {
- smearScreenBatterySipper(sippers, bs);
+ smearScreenBatterySipper(sippers, bs, rawRealtimeUs);
}
}
- @Override
- protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
+ /**
+ * Stores duration and power information in totalPowerAndDuration and returns true if measured
+ * energy data is available and should be used by the model.
+ */
+ private boolean calculateTotalDurationAndPower(PowerAndDuration totalPowerAndDuration,
+ BatteryStats batteryStats, long rawRealtimeUs, int statsType,
+ boolean forceUsePowerProfileModel) {
+ totalPowerAndDuration.durationMs = calculateDuration(batteryStats, rawRealtimeUs,
+ statsType);
+
+ if (!forceUsePowerProfileModel) {
+ final long energyUJ = batteryStats.getScreenOnEnergy();
+ if (energyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) {
+ totalPowerAndDuration.powerMah = uJtoMah(energyUJ);
+ return true;
+ }
+ }
+
+ totalPowerAndDuration.powerMah = calculateTotalPowerFromBrightness(batteryStats,
+ rawRealtimeUs, statsType, totalPowerAndDuration.durationMs);
+ return false;
+ }
+
+ private void calculateAppUsingMeasuredEnergy(PowerAndDuration appPowerAndDuration,
+ BatteryStats.Uid u, long rawRealtimeUs) {
+ appPowerAndDuration.durationMs = getProcessForegroundTimeMs(u, rawRealtimeUs);
+
final long energyUJ = u.getScreenOnEnergy();
if (energyUJ < 0) {
Slog.wtf(TAG, "Screen energy not supported, so calculateApp shouldn't de called");
+ appPowerAndDuration.powerMah = 0;
return;
}
- if (energyUJ == 0) return;
- app.screenPowerMah = mAhToUJ(u.getScreenOnEnergy());
+
+ appPowerAndDuration.powerMah = uJtoMah(energyUJ);
}
- private long computeDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) {
+ private long calculateDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) {
return batteryStats.getScreenOnTime(rawRealtimeUs, statsType) / 1000;
}
- private double computePower(BatteryStats batteryStats, long rawRealtimeUs, int statsType,
- long durationMs) {
+ private double calculateTotalPowerFromBrightness(BatteryStats batteryStats, long rawRealtimeUs,
+ int statsType, long durationMs) {
double power = mScreenOnPowerEstimator.calculatePower(durationMs);
for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {
final long brightnessTime =
@@ -132,35 +188,25 @@ public class ScreenPowerCalculator extends PowerCalculator {
return power;
}
- private double getMeasuredOrComputedPower(long measuredEnergyUJ,
- BatteryStats batteryStats, long rawRealtimeUs, int statsType, long durationMs) {
-
- if (measuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) {
- return mAhToUJ(measuredEnergyUJ);
- } else {
- return computePower(batteryStats, rawRealtimeUs, statsType, durationMs);
- }
- }
-
/**
* Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
* time, and store this in the {@link BatterySipper#screenPowerMah} field.
*/
@VisibleForTesting
- public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) {
-
+ public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper,
+ long rawRealtimeUs) {
long totalActivityTimeMs = 0;
final SparseLongArray activityTimeArray = new SparseLongArray();
for (int i = sippers.size() - 1; i >= 0; i--) {
final BatteryStats.Uid uid = sippers.get(i).uidObj;
if (uid != null) {
- final long timeMs = getProcessForegroundTimeMs(uid);
+ final long timeMs = getProcessForegroundTimeMs(uid, rawRealtimeUs);
activityTimeArray.put(uid.getUid(), timeMs);
totalActivityTimeMs += timeMs;
}
}
- if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
+ if (screenSipper != null && totalActivityTimeMs >= MIN_ACTIVE_TIME_FOR_SMEARING) {
final double totalScreenPowerMah = screenSipper.totalPowerMah;
for (int i = sippers.size() - 1; i >= 0; i--) {
final BatterySipper sipper = sippers.get(i);
@@ -171,10 +217,37 @@ public class ScreenPowerCalculator extends PowerCalculator {
}
}
+ /**
+ * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
+ * time, and store this in the {@link BatterySipper#screenPowerMah} field.
+ */
+ private void smearScreenBatteryDrain(
+ SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders,
+ PowerAndDuration totalPowerAndDuration, long rawRealtimeUs) {
+ long totalActivityTimeMs = 0;
+ final SparseLongArray activityTimeArray = new SparseLongArray();
+ for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+ final BatteryStats.Uid uid = uidBatteryConsumerBuilders.valueAt(i).getBatteryStatsUid();
+ final long timeMs = getProcessForegroundTimeMs(uid, rawRealtimeUs);
+ activityTimeArray.put(uid.getUid(), timeMs);
+ totalActivityTimeMs += timeMs;
+ }
+
+ if (totalActivityTimeMs >= MIN_ACTIVE_TIME_FOR_SMEARING) {
+ final double totalScreenPowerMah = totalPowerAndDuration.powerMah;
+ for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+ final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+ final long durationMs = activityTimeArray.get(app.getUid(), 0);
+ final double powerMah = totalScreenPowerMah * durationMs / totalActivityTimeMs;
+ app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN, durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, powerMah);
+ }
+ }
+ }
+
/** Get the minimum of the uid's ForegroundActivity time and its TOP time. */
@VisibleForTesting
- public long getProcessForegroundTimeMs(BatteryStats.Uid uid) {
- final long rawRealTimeUs = SystemClock.elapsedRealtime() * 1000;
+ public long getProcessForegroundTimeMs(BatteryStats.Uid uid, long rawRealTimeUs) {
final int[] foregroundTypes = {BatteryStats.Uid.PROCESS_STATE_TOP};
long timeUs = 0;
diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
index fbad75e93a17..3ed59f1d4370 100644
--- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
+++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
@@ -110,4 +110,24 @@ public class SystemServerCpuThreadReader {
return mDeltaCpuThreadTimes;
}
+
+ /** Returns CPU times, per thread group, since tracking started. */
+ @Nullable
+ public SystemServiceCpuThreadTimes readAbsolute() {
+ final int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount();
+ final KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
+ mKernelCpuThreadReader.getProcessCpuUsage();
+ if (processCpuUsage == null) {
+ return null;
+ }
+ final SystemServiceCpuThreadTimes result = new SystemServiceCpuThreadTimes();
+ result.threadCpuTimesUs = new long[numCpuFrequencies];
+ result.binderThreadCpuTimesUs = new long[numCpuFrequencies];
+ for (int i = 0; i < numCpuFrequencies; ++i) {
+ result.threadCpuTimesUs[i] = processCpuUsage.threadCpuTimesMillis[i] * 1_000;
+ result.binderThreadCpuTimesUs[i] =
+ processCpuUsage.selectedThreadCpuTimesMillis[i] * 1_000;
+ }
+ return result;
+ }
}
diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
index 55fc1bb607a9..5c0eeb2f54f9 100644
--- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java
+++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
@@ -20,12 +20,12 @@ import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.Process;
import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
-import java.util.Arrays;
import java.util.List;
/**
@@ -36,88 +36,112 @@ public class SystemServicePowerCalculator extends PowerCalculator {
private static final boolean DEBUG = false;
private static final String TAG = "SystemServicePowerCalc";
- private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000;
-
- private final PowerProfile mPowerProfile;
-
- // Tracks system server CPU [cluster][speed] power in milliAmp-microseconds
- // Data organized like this:
+ // Power estimators per CPU cluster, per CPU frequency. The array is flattened according
+ // to this layout:
// {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...}
- private double[] mSystemServicePowerMaUs;
+ private final UsageBasedPowerEstimator[] mPowerEstimators;
public SystemServicePowerCalculator(PowerProfile powerProfile) {
- mPowerProfile = powerProfile;
- }
+ int numFreqs = 0;
+ final int numCpuClusters = powerProfile.getNumCpuClusters();
+ for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+ numFreqs += powerProfile.getNumSpeedStepsInCpuCluster(cluster);
+ }
- @Override
- public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
- SparseArray<UserHandle> asUsers) {
- calculateSystemServicePower(batteryStats);
- super.calculate(builder, batteryStats, rawRealtimeUs, rawUptimeUs, query, asUsers);
+ mPowerEstimators = new UsageBasedPowerEstimator[numFreqs];
+ int index = 0;
+ for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+ final int numSpeeds = powerProfile.getNumSpeedStepsInCpuCluster(cluster);
+ for (int speed = 0; speed < numSpeeds; speed++) {
+ mPowerEstimators[index++] = new UsageBasedPowerEstimator(
+ powerProfile.getAveragePowerForCpuCore(cluster, speed));
+ }
+ }
}
@Override
- protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
- app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
- calculateSystemServerCpuPowerMah(u));
+ double systemServicePowerMah = calculateSystemServicePower(batteryStats);
+ final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+ builder.getUidBatteryConsumerBuilders();
+ final UidBatteryConsumer.Builder systemServerConsumer = uidBatteryConsumerBuilders.get(
+ Process.SYSTEM_UID);
+
+ if (systemServerConsumer != null) {
+ systemServicePowerMah = Math.min(systemServicePowerMah,
+ systemServerConsumer.getTotalPower());
+
+ // The system server power needs to be adjusted because part of it got
+ // distributed to applications
+ systemServerConsumer.setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS,
+ -systemServicePowerMah);
+ }
+
+ for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+ final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+ if (app != systemServerConsumer) {
+ final BatteryStats.Uid uid = app.getBatteryStatsUid();
+ app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
+ systemServicePowerMah * uid.getProportionalSystemServiceUsage());
+ }
+ }
}
@Override
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType,
SparseArray<UserHandle> asUsers) {
- calculateSystemServicePower(batteryStats);
- super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers);
- }
+ double systemServicePowerMah = calculateSystemServicePower(batteryStats);
+ BatterySipper systemServerSipper = null;
+ for (int i = sippers.size() - 1; i >= 0; i--) {
+ final BatterySipper app = sippers.get(i);
+ if (app.drainType == BatterySipper.DrainType.APP) {
+ if (app.getUid() == Process.SYSTEM_UID) {
+ systemServerSipper = app;
+ break;
+ }
+ }
+ }
- @Override
- protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
- app.systemServiceCpuPowerMah = calculateSystemServerCpuPowerMah(u);
+ if (systemServerSipper != null) {
+ systemServicePowerMah = Math.min(systemServicePowerMah, systemServerSipper.sumPower());
+
+ // The system server power needs to be adjusted because part of it got
+ // distributed to applications
+ systemServerSipper.powerReattributedToOtherSippersMah = -systemServicePowerMah;
+ }
+
+ for (int i = sippers.size() - 1; i >= 0; i--) {
+ final BatterySipper app = sippers.get(i);
+ if (app.drainType == BatterySipper.DrainType.APP) {
+ if (app != systemServerSipper) {
+ final BatteryStats.Uid uid = app.uidObj;
+ app.systemServiceCpuPowerMah =
+ systemServicePowerMah * uid.getProportionalSystemServiceUsage();
+ }
+ }
+ }
}
- private void calculateSystemServicePower(BatteryStats batteryStats) {
+ private double calculateSystemServicePower(BatteryStats batteryStats) {
final long[] systemServiceTimeAtCpuSpeeds = batteryStats.getSystemServiceTimeAtCpuSpeeds();
if (systemServiceTimeAtCpuSpeeds == null) {
- return;
+ return 0;
}
- if (mSystemServicePowerMaUs == null) {
- mSystemServicePowerMaUs = new double[systemServiceTimeAtCpuSpeeds.length];
- }
- int index = 0;
- final int numCpuClusters = mPowerProfile.getNumCpuClusters();
- for (int cluster = 0; cluster < numCpuClusters; cluster++) {
- final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
- for (int speed = 0; speed < numSpeeds; speed++) {
- mSystemServicePowerMaUs[index] =
- systemServiceTimeAtCpuSpeeds[index]
- * mPowerProfile.getAveragePowerForCpuCore(cluster, speed);
- index++;
- }
- }
+ // TODO(179210707): additionally account for CPU active and per cluster battery use
- if (DEBUG) {
- Log.d(TAG, "System service power per CPU cluster and frequency:"
- + Arrays.toString(mSystemServicePowerMaUs));
+ double powerMah = 0;
+ for (int i = 0; i < mPowerEstimators.length; i++) {
+ powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i]);
}
- }
- private double calculateSystemServerCpuPowerMah(BatteryStats.Uid u) {
- double cpuPowerMaUs = 0;
- final double proportionalUsage = u.getProportionalSystemServiceUsage();
- if (proportionalUsage > 0 && mSystemServicePowerMaUs != null) {
- for (int i = 0; i < mSystemServicePowerMaUs.length; i++) {
- cpuPowerMaUs += mSystemServicePowerMaUs[i] * proportionalUsage;
- }
+ if (DEBUG) {
+ Log.d(TAG, "System service power:" + powerMah);
}
- return cpuPowerMaUs / MICROSEC_IN_HR;
- }
- @Override
- public void reset() {
- mSystemServicePowerMaUs = null;
+ return powerMah;
}
}
diff --git a/core/java/com/android/internal/os/UserPowerCalculator.java b/core/java/com/android/internal/os/UserPowerCalculator.java
index 53f85154330c..8e802869e2fc 100644
--- a/core/java/com/android/internal/os/UserPowerCalculator.java
+++ b/core/java/com/android/internal/os/UserPowerCalculator.java
@@ -17,10 +17,15 @@
package com.android.internal.os;
import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
import android.os.Process;
+import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.util.SparseArray;
+import com.android.internal.util.ArrayUtils;
+
import java.util.List;
/**
@@ -29,6 +34,33 @@ import java.util.List;
public class UserPowerCalculator extends PowerCalculator {
@Override
+ public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ final int[] userIds = query.getUserIds();
+ if (ArrayUtils.contains(userIds, UserHandle.USER_ALL)) {
+ return;
+ }
+
+ SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+ builder.getUidBatteryConsumerBuilders();
+
+ for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+ UidBatteryConsumer.Builder uidBuilder = uidBatteryConsumerBuilders.valueAt(i);
+ final int uid = uidBuilder.getUid();
+ if (UserHandle.getAppId(uid) < Process.FIRST_APPLICATION_UID) {
+ continue;
+ }
+
+ final int userId = UserHandle.getUserId(uid);
+ if (!ArrayUtils.contains(userIds, userId)) {
+ uidBuilder.excludeFromBatteryUsageStats();
+ builder.getOrCreateUserBatteryConsumerBuilder(userId)
+ .addUidBatteryConsumer(uidBuilder);
+ }
+ }
+ }
+
+ @Override
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java
index 3f68597dc1cc..0f4767b859a3 100644
--- a/core/java/com/android/internal/os/WakelockPowerCalculator.java
+++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java
@@ -15,7 +15,12 @@
*/
package com.android.internal.os;
+import android.os.BatteryConsumer;
import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Process;
+import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
@@ -26,39 +31,93 @@ import java.util.List;
public class WakelockPowerCalculator extends PowerCalculator {
private static final String TAG = "WakelockPowerCalculator";
private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
- private final double mPowerWakelock;
- private long mTotalAppWakelockTimeMs = 0;
+ private final UsageBasedPowerEstimator mPowerEstimator;
+
+ private static class PowerAndDuration {
+ public long durationMs;
+ public double powerMah;
+ }
public WakelockPowerCalculator(PowerProfile profile) {
- mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_IDLE);
+ mPowerEstimator = new UsageBasedPowerEstimator(
+ profile.getAveragePower(PowerProfile.POWER_CPU_IDLE));
}
@Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers);
+ public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ final PowerAndDuration result = new PowerAndDuration();
+ UidBatteryConsumer.Builder osBatteryConsumer = null;
+ double osPowerMah = 0;
+ long osDurationMs = 0;
+ long totalAppDurationMs = 0;
+ final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+ builder.getUidBatteryConsumerBuilders();
+ for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+ final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+ calculateApp(result, app.getBatteryStatsUid(), rawRealtimeUs,
+ BatteryStats.STATS_SINCE_CHARGED);
+ app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK, result.durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah);
+ totalAppDurationMs += result.durationMs;
+
+ if (app.getUid() == Process.ROOT_UID) {
+ osBatteryConsumer = app;
+ osDurationMs = result.durationMs;
+ osPowerMah = result.powerMah;
+ }
+ }
// The device has probably been awake for longer than the screen on
// time and application wake lock time would account for. Assign
// this remainder to the OS, if possible.
+ if (osBatteryConsumer != null) {
+ calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs,
+ BatteryStats.STATS_SINCE_CHARGED, osPowerMah, osDurationMs, totalAppDurationMs);
+ osBatteryConsumer.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK,
+ result.durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah);
+ }
+ }
+
+ @Override
+ public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
+ long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
+ final PowerAndDuration result = new PowerAndDuration();
BatterySipper osSipper = null;
+ double osPowerMah = 0;
+ long osDurationMs = 0;
+ long totalAppDurationMs = 0;
for (int i = sippers.size() - 1; i >= 0; i--) {
- BatterySipper app = sippers.get(i);
- if (app.getUid() == 0) {
- osSipper = app;
- break;
+ final BatterySipper app = sippers.get(i);
+ if (app.drainType == BatterySipper.DrainType.APP) {
+ calculateApp(result, app.uidObj, rawRealtimeUs, statsType);
+ app.wakeLockTimeMs = result.durationMs;
+ app.wakeLockPowerMah = result.powerMah;
+ totalAppDurationMs += result.durationMs;
+
+ if (app.getUid() == Process.ROOT_UID) {
+ osSipper = app;
+ osPowerMah = result.powerMah;
+ osDurationMs = result.durationMs;
+ }
}
}
+ // The device has probably been awake for longer than the screen on
+ // time and application wake lock time would account for. Assign
+ // this remainder to the OS, if possible.
if (osSipper != null) {
- calculateRemaining(osSipper, batteryStats, rawRealtimeUs, rawUptimeUs, statsType);
+ calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs, statsType,
+ osPowerMah, osDurationMs, totalAppDurationMs);
+ osSipper.wakeLockTimeMs = result.durationMs;
+ osSipper.wakeLockPowerMah = result.powerMah;
osSipper.sumPower();
}
}
- @Override
- protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
+ private void calculateApp(PowerAndDuration result, BatteryStats.Uid u, long rawRealtimeUs,
+ int statsType) {
long wakeLockTimeUs = 0;
final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
u.getWakelockStats();
@@ -73,34 +132,29 @@ public class WakelockPowerCalculator extends PowerCalculator {
wakeLockTimeUs += timer.getTotalTimeLocked(rawRealtimeUs, statsType);
}
}
- app.wakeLockTimeMs = wakeLockTimeUs / 1000; // convert to millis
- mTotalAppWakelockTimeMs += app.wakeLockTimeMs;
+ result.durationMs = wakeLockTimeUs / 1000; // convert to millis
// Add cost of holding a wake lock.
- app.wakeLockPowerMah = (app.wakeLockTimeMs * mPowerWakelock) / (1000 * 60 * 60);
- if (DEBUG && app.wakeLockPowerMah != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": wake " + app.wakeLockTimeMs
- + " power=" + formatCharge(app.wakeLockPowerMah));
+ result.powerMah = mPowerEstimator.calculatePower(result.durationMs);
+ if (DEBUG && result.powerMah != 0) {
+ Log.d(TAG, "UID " + u.getUid() + ": wake " + result.durationMs
+ + " power=" + formatCharge(result.powerMah));
}
}
- private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
- long wakeTimeMillis = stats.getBatteryUptime(rawUptimeUs) / 1000;
- wakeTimeMillis -= mTotalAppWakelockTimeMs
- + (stats.getScreenOnTime(rawRealtimeUs, statsType) / 1000);
+ private void calculateRemaining(PowerAndDuration result, BatteryStats stats, long rawRealtimeUs,
+ long rawUptimeUs, int statsType, double osPowerMah, long osDurationMs,
+ long totalAppDurationMs) {
+ final long wakeTimeMillis = stats.getBatteryUptime(rawUptimeUs) / 1000
+ - stats.getScreenOnTime(rawRealtimeUs, statsType) / 1000
+ - totalAppDurationMs;
if (wakeTimeMillis > 0) {
- final double power = (wakeTimeMillis * mPowerWakelock) / (1000 * 60 * 60);
+ final double power = mPowerEstimator.calculatePower(wakeTimeMillis);
if (DEBUG) {
Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power " + formatCharge(power));
}
- app.wakeLockTimeMs += wakeTimeMillis;
- app.wakeLockPowerMah += power;
+ result.durationMs = osDurationMs + wakeTimeMillis;
+ result.powerMah = osPowerMah + power;
}
}
-
- @Override
- public void reset() {
- mTotalAppWakelockTimeMs = 0;
- }
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index d196d4af2e42..c8afea9b0982 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -1091,4 +1091,11 @@ public final class Zygote {
* fully-feature Memory Tagging, rather than the static Tagged Pointers.
*/
public static native boolean nativeSupportsTaggedPointers();
+
+ /**
+ * Returns the current native tagging level, as one of the
+ * MEMORY_TAG_LEVEL_* constants. Returns zero if no tagging is present, or
+ * we failed to determine the level.
+ */
+ public static native int nativeCurrentTaggingLevel();
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 1fce64dc68fd..c289dbeb1e54 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -792,9 +792,19 @@ public class ZygoteInit {
Zygote.applyInvokeWithSystemProperty(parsedArgs);
if (Zygote.nativeSupportsMemoryTagging()) {
- /* The system server is more privileged than regular app processes, so it has async
- * tag checks enabled on hardware that supports memory tagging. */
- parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC;
+ /* The system server has ASYNC MTE by default, in order to allow
+ * system services to specify their own MTE level later, as you
+ * can't re-enable MTE once it's disabled. */
+ String mode = SystemProperties.get("arm64.memtag.process.system_server", "async");
+ if (mode.equals("async")) {
+ parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC;
+ } else if (mode.equals("sync")) {
+ parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_SYNC;
+ } else if (!mode.equals("off")) {
+ /* When we have an invalid memory tag level, keep the current level. */
+ parsedArgs.mRuntimeFlags |= Zygote.nativeCurrentTaggingLevel();
+ Slog.e(TAG, "Unknown memory tag level for the system server: \"" + mode + "\"");
+ }
} else if (Zygote.nativeSupportsTaggedPointers()) {
/* Enable pointer tagging in the system server. Hardware support for this is present
* in all ARMv8 CPUs. */
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index a4ce027501b8..585ddf6ddf98 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -492,10 +492,12 @@ class ZygoteServer {
long elapsedTimeMs = System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp;
if (elapsedTimeMs >= mUsapPoolRefillDelayMs) {
- // Normalize the poll timeout value when the time between one poll event and the
- // next pushes us over the delay value. This prevents poll receiving a 0
- // timeout value, which would result in it returning immediately.
- pollTimeoutMs = -1;
+ // The refill delay has elapsed during the period between poll invocations.
+ // We will now check for any currently ready file descriptors before refilling
+ // the USAP pool.
+ pollTimeoutMs = 0;
+ mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
+ mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
} else if (elapsedTimeMs <= 0) {
// This can occur if the clock used by currentTimeMillis is reset, which is
@@ -517,9 +519,11 @@ class ZygoteServer {
}
if (pollReturnValue == 0) {
- // The poll timeout has been exceeded. This only occurs when we have finished the
- // USAP pool refill delay period.
-
+ // The poll returned zero results either when the timeout value has been exceeded
+ // or when a non-blocking poll is issued and no FDs are ready. In either case it
+ // is time to refill the pool. This will result in a duplicate assignment when
+ // the non-blocking poll returns zero results, but it avoids an additional
+ // conditional in the else branch.
mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index adebde016bb3..9840013935f8 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -110,6 +110,7 @@ import android.widget.FrameLayout;
import android.widget.PopupWindow;
import com.android.internal.R;
+import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
import com.android.internal.policy.PhoneWindow.PanelFeatureState;
import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback;
import com.android.internal.view.FloatingActionMode;
@@ -255,6 +256,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
private Drawable mOriginalBackgroundDrawable;
private Drawable mLastOriginalBackgroundDrawable;
private Drawable mResizingBackgroundDrawable;
+ private BackgroundBlurDrawable mBackgroundBlurDrawable;
/**
* Temporary holder for a window background when it is set before {@link #mWindow} is
@@ -280,9 +282,14 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
private final Paint mLegacyNavigationBarBackgroundPaint = new Paint();
private Insets mBackgroundInsets = Insets.NONE;
private Insets mLastBackgroundInsets = Insets.NONE;
+ private int mLastBackgroundBlurRadius = 0;
private boolean mDrawLegacyNavigationBarBackground;
private PendingInsetsController mPendingInsetsController = new PendingInsetsController();
+ private final ViewTreeObserver.OnPreDrawListener mBackgroundBlurOnPreDrawListener = () -> {
+ updateBackgroundBlur();
+ return true;
+ };
DecorView(Context context, int featureId, PhoneWindow window,
WindowManager.LayoutParams params) {
@@ -1263,18 +1270,27 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
if (mBackgroundInsets == null) {
mBackgroundInsets = Insets.NONE;
}
+
if (mBackgroundInsets.equals(mLastBackgroundInsets)
+ && mWindow.mBackgroundBlurRadius == mLastBackgroundBlurRadius
&& mLastOriginalBackgroundDrawable == mOriginalBackgroundDrawable) {
return;
}
- if (mOriginalBackgroundDrawable == null || mBackgroundInsets.equals(Insets.NONE)) {
- // Call super since we are intercepting setBackground on this class.
- super.setBackgroundDrawable(mOriginalBackgroundDrawable);
- } else {
+ Drawable destDrawable = mOriginalBackgroundDrawable;
+ if (mWindow.mBackgroundBlurRadius > 0 && getViewRootImpl() != null
+ && mWindow.isTranslucent()) {
+ if (mBackgroundBlurDrawable == null) {
+ mBackgroundBlurDrawable = getViewRootImpl().createBackgroundBlurDrawable();
+ }
+ destDrawable = new LayerDrawable(new Drawable[] {mBackgroundBlurDrawable,
+ mOriginalBackgroundDrawable});
+ mLastBackgroundBlurRadius = mWindow.mBackgroundBlurRadius;
+ }
- // Call super since we are intercepting setBackground on this class.
- super.setBackgroundDrawable(new InsetDrawable(mOriginalBackgroundDrawable,
+
+ if (destDrawable != null && !mBackgroundInsets.equals(Insets.NONE)) {
+ destDrawable = new InsetDrawable(destDrawable,
mBackgroundInsets.left, mBackgroundInsets.top,
mBackgroundInsets.right, mBackgroundInsets.bottom) {
@@ -1286,12 +1302,32 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
public boolean getPadding(Rect padding) {
return getDrawable().getPadding(padding);
}
- });
+ };
}
+
+ // Call super since we are intercepting setBackground on this class.
+ super.setBackgroundDrawable(destDrawable);
+
mLastBackgroundInsets = mBackgroundInsets;
mLastOriginalBackgroundDrawable = mOriginalBackgroundDrawable;
}
+ private void updateBackgroundBlur() {
+ if (mBackgroundBlurDrawable == null) return;
+
+ // If the blur radius is 0, the blur region won't be sent to surface flinger, so we don't
+ // need to calculate the corner radius.
+ if (mWindow.mBackgroundBlurRadius > 0) {
+ if (mOriginalBackgroundDrawable != null) {
+ final Outline outline = new Outline();
+ mOriginalBackgroundDrawable.getOutline(outline);
+ mBackgroundBlurDrawable.setCornerRadius(outline.mMode == Outline.MODE_ROUND_RECT
+ ? outline.getRadius() : 0);
+ }
+ }
+ mBackgroundBlurDrawable.setBlurRadius(mWindow.mBackgroundBlurRadius);
+ }
+
@Override
public Drawable getBackground() {
return mOriginalBackgroundDrawable;
@@ -1722,6 +1758,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
cb.onAttachedToWindow();
}
+ getViewTreeObserver().addOnPreDrawListener(mBackgroundBlurOnPreDrawListener);
+ updateBackgroundDrawable();
+
if (mFeatureId == -1) {
/*
* The main window has been attached, try to restore any panels
@@ -1755,6 +1794,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
cb.onDetachedFromWindow();
}
+ getViewTreeObserver().removeOnPreDrawListener(mBackgroundBlurOnPreDrawListener);
+
if (mWindow.mDecorContentParent != null) {
mWindow.mDecorContentParent.dismissPopups();
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 5df175e8aee5..d06413c3ca15 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -258,6 +258,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
Drawable mBackgroundDrawable = null;
Drawable mBackgroundFallbackDrawable = null;
+ int mBackgroundBlurRadius = 0;
+
private boolean mLoadElevation = true;
private float mElevation;
@@ -1523,6 +1525,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
@Override
+ public final void setBackgroundBlurRadius(int blurRadius) {
+ super.setBackgroundBlurRadius(blurRadius);
+ if (getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_CROSS_LAYER_BLUR)) {
+ mBackgroundBlurRadius = Math.max(blurRadius, 0);
+ }
+ }
+
+ @Override
public final void setFeatureDrawableResource(int featureId, int resId) {
if (resId != 0) {
DrawableFeatureState st = getDrawableState(featureId, true);
@@ -2549,6 +2560,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
android.R.styleable.Window_windowBlurBehindRadius, 0);
}
+ setBackgroundBlurRadius(a.getDimensionPixelSize(
+ R.styleable.Window_windowBackgroundBlurRadius, 0));
+
if (params.windowAnimations == 0) {
params.windowAnimations = a.getResourceId(
diff --git a/core/java/com/android/internal/power/MeasuredEnergyArray.java b/core/java/com/android/internal/power/MeasuredEnergyArray.java
deleted file mode 100644
index 1f6dc260a197..000000000000
--- a/core/java/com/android/internal/power/MeasuredEnergyArray.java
+++ /dev/null
@@ -1,66 +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.power;
-
-
-import android.annotation.IntDef;
-
-import com.android.internal.os.RailStats;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Interface to provide subsystem energy data.
- * TODO: replace this and {@link RailStats} once b/173077356 is done
- */
-public interface MeasuredEnergyArray {
- int SUBSYSTEM_UNKNOWN = -1;
- int SUBSYSTEM_DISPLAY = 0;
- int NUMBER_SUBSYSTEMS = 1;
- String[] SUBSYSTEM_NAMES = {"display"};
-
-
- @IntDef(prefix = { "SUBSYSTEM_" }, value = {
- SUBSYSTEM_UNKNOWN,
- SUBSYSTEM_DISPLAY,
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface MeasuredEnergySubsystem {}
-
- /**
- * Get the subsystem at an index in array.
- *
- * @param index into the array.
- * @return subsystem.
- */
- @MeasuredEnergySubsystem
- int getSubsystem(int index);
-
- /**
- * Get the energy (in microjoules) consumed since boot of the subsystem at an index.
- *
- * @param index into the array.
- * @return energy (in microjoules) consumed since boot.
- */
- long getEnergy(int index);
-
- /**
- * Return number of subsystems in the array.
- */
- int size();
-}
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index e310f8d9b36e..d7b4d78c56cf 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -28,7 +28,6 @@ import android.util.Slog;
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.power.MeasuredEnergyArray.MeasuredEnergySubsystem;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -239,6 +238,7 @@ public class MeasuredEnergyStats {
* Return accumulated energy (in microjoules) for the a custom energy bucket since last reset.
* Returns {@link android.os.BatteryStats#ENERGY_DATA_UNAVAILABLE} if this data is unavailable.
*/
+ @VisibleForTesting
public long getAccumulatedCustomBucketEnergy(int customBucket) {
if (!isValidCustomBucket(customBucket)) {
return ENERGY_DATA_UNAVAILABLE;
@@ -247,7 +247,18 @@ public class MeasuredEnergyStats {
}
/**
- * Map {@link MeasuredEnergySubsystem} and device state to Display {@link StandardEnergyBucket}.
+ * Return accumulated energies (in microjoules) for all custom energy buckets since last reset.
+ */
+ public @NonNull long[] getAccumulatedCustomBucketEnergies() {
+ final long[] energies = new long[getNumberCustomEnergyBuckets()];
+ for (int bucket = 0; bucket < energies.length; bucket++) {
+ energies[bucket] = mAccumulatedEnergiesMicroJoules[customBucketToIndex(bucket)];
+ }
+ return energies;
+ }
+
+ /**
+ * Map {@link android.view.Display} STATE_ to corresponding {@link StandardEnergyBucket}.
*/
public static @StandardEnergyBucket int getDisplayEnergyBucket(int screenState) {
if (Display.isOnState(screenState)) {
@@ -405,7 +416,6 @@ public class MeasuredEnergyStats {
/** Dump debug data. */
public void dump(PrintWriter pw) {
- pw.println("Accumulated energy since last reset (microjoules):");
pw.print(" ");
for (int index = 0; index < mAccumulatedEnergiesMicroJoules.length; index++) {
pw.print(getBucketName(index));
@@ -432,6 +442,11 @@ public class MeasuredEnergyStats {
return "CUSTOM_" + indexToCustomBucket(index);
}
+ /** Get the number of custom energy buckets on this device. */
+ public int getNumberCustomEnergyBuckets() {
+ return mAccumulatedEnergiesMicroJoules.length - NUMBER_STANDARD_ENERGY_BUCKETS;
+ }
+
private static int customBucketToIndex(int customBucket) {
return customBucket + NUMBER_STANDARD_ENERGY_BUCKETS;
}
@@ -450,7 +465,9 @@ public class MeasuredEnergyStats {
return bucket >= 0 && bucket < NUMBER_STANDARD_ENERGY_BUCKETS;
}
- private boolean isValidCustomBucket(int customBucket) {
+ /** Returns whether the given custom bucket is valid (exists) on this device. */
+ @VisibleForTesting
+ public boolean isValidCustomBucket(int customBucket) {
return customBucket >= 0
&& customBucketToIndex(customBucket) < mAccumulatedEnergiesMicroJoules.length;
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index bb0fd7fb8c23..7edc6c855ec2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -22,6 +22,7 @@ import android.graphics.Rect;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
@@ -82,6 +83,7 @@ interface IStatusBarService
void hideCurrentInputMethodForBubbles();
void grantInlineReplyUriPermission(String key, in Uri uri, in UserHandle user, String packageName);
void clearInlineReplyUriPermissions(String key);
+ void onNotificationFeedbackReceived(String key, in Bundle feedback);
void onGlobalActionsShown();
void onGlobalActionsHidden();
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 488b18db66d6..c6a040c162ab 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -47,6 +47,13 @@ public class CollectionUtils {
private CollectionUtils() { /* cannot be instantiated */ }
/**
+ * @see Collection#contains(Object)
+ */
+ public static <T> boolean contains(@Nullable Collection<T> collection, T element) {
+ return collection != null && collection.contains(element);
+ }
+
+ /**
* Returns a list of items from the provided list that match the given condition.
*
* This is similar to {@link Stream#filter} but without the overhead of creating an intermediate
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index f42f468aefa0..dc6880e4f997 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -219,7 +219,7 @@ public class LatencyTracker {
}
private static String getTraceTriggerNameForAction(@Action int action) {
- return "latency-tracker-" + getNameOfAction(STATSD_ACTION[action]);
+ return "com.android.telemetry.latency-tracker-" + getNameOfAction(STATSD_ACTION[action]);
}
public static boolean isEnabled(Context ctx) {
diff --git a/core/java/com/android/internal/util/PerfettoTrigger.java b/core/java/com/android/internal/util/PerfettoTrigger.java
index 9c87c697a737..c7585046cf9c 100644
--- a/core/java/com/android/internal/util/PerfettoTrigger.java
+++ b/core/java/com/android/internal/util/PerfettoTrigger.java
@@ -16,6 +16,7 @@
package com.android.internal.util;
+import android.os.SystemClock;
import android.util.Log;
import java.io.IOException;
@@ -27,16 +28,28 @@ import java.io.IOException;
public class PerfettoTrigger {
private static final String TAG = "PerfettoTrigger";
private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto";
+ private static final long THROTTLE_MILLIS = 60000;
+ private static volatile long sLastTriggerTime = -THROTTLE_MILLIS;
/**
* @param triggerName The name of the trigger. Must match the value defined in the AOT
* Perfetto config.
*/
public static void trigger(String triggerName) {
+ // Trace triggering has a non-negligible cost (fork+exec).
+ // To mitigate potential excessive triggering by the API client we ignore calls that happen
+ // too quickl after the most recent trigger.
+ long sinceLastTrigger = SystemClock.elapsedRealtime() - sLastTriggerTime;
+ if (sinceLastTrigger < THROTTLE_MILLIS) {
+ Log.v(TAG, "Not triggering " + triggerName + " - not enough time since last trigger");
+ return;
+ }
+
try {
ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, triggerName);
Log.v(TAG, "Triggering " + String.join(" ", pb.command()));
- Process process = pb.start();
+ pb.start();
+ sLastTriggerTime = SystemClock.elapsedRealtime();
} catch (IOException e) {
Log.w(TAG, "Failed to trigger " + triggerName, e);
}
diff --git a/core/java/com/android/internal/widget/CallLayout.java b/core/java/com/android/internal/widget/CallLayout.java
new file mode 100644
index 000000000000..6cc5a4aacda5
--- /dev/null
+++ b/core/java/com/android/internal/widget/CallLayout.java
@@ -0,0 +1,137 @@
+/*
+ * 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.widget;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
+import android.app.Notification;
+import android.app.Person;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.RemotableViewMethod;
+import android.widget.FrameLayout;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+/**
+ * A custom-built layout for the Notification.CallStyle.
+ */
+@RemoteViews.RemoteView
+public class CallLayout extends FrameLayout {
+ private final PeopleHelper mPeopleHelper = new PeopleHelper();
+
+ private int mLayoutColor;
+ private Icon mLargeIcon;
+ private Person mUser;
+
+ private CachingIconView mConversationIconView;
+ private CachingIconView mIcon;
+ private CachingIconView mConversationIconBadgeBg;
+ private TextView mConversationText;
+
+ public CallLayout(@NonNull Context context) {
+ super(context);
+ }
+
+ public CallLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CallLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public CallLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mPeopleHelper.init(getContext());
+ mConversationText = findViewById(R.id.conversation_text);
+ mConversationIconView = findViewById(R.id.conversation_icon);
+ mIcon = findViewById(R.id.icon);
+ mConversationIconBadgeBg = findViewById(R.id.conversation_icon_badge_bg);
+
+ // When the small icon is gone, hide the rest of the badge
+ mIcon.setOnForceHiddenChangedListener((forceHidden) -> {
+ mPeopleHelper.animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
+ });
+ }
+
+ private void updateCallLayout() {
+ CharSequence callerName = "";
+ String symbol = "";
+ Icon icon = null;
+ if (mUser != null) {
+ icon = mUser.getIcon();
+ callerName = mUser.getName();
+ symbol = mPeopleHelper.findNamePrefix(callerName, "");
+ }
+ if (icon == null) {
+ icon = mLargeIcon;
+ }
+ if (icon == null) {
+ icon = mPeopleHelper.createAvatarSymbol(callerName, symbol, mLayoutColor);
+ }
+ // TODO(b/179178086): crop/clip the icon to a circle?
+ mConversationIconView.setImageIcon(icon);
+ mConversationText.setText(callerName);
+ }
+
+ @RemotableViewMethod
+ public void setLayoutColor(int color) {
+ mLayoutColor = color;
+ }
+
+ /**
+ * @param color the color of the notification background
+ */
+ @RemotableViewMethod
+ public void setNotificationBackgroundColor(int color) {
+ mConversationIconBadgeBg.setImageTintList(ColorStateList.valueOf(color));
+ }
+
+ @RemotableViewMethod
+ public void setLargeIcon(Icon largeIcon) {
+ mLargeIcon = largeIcon;
+ }
+
+ /**
+ * Set the notification extras so that this layout has access
+ */
+ @RemotableViewMethod
+ public void setData(Bundle extras) {
+ setUser(extras.getParcelable(Notification.EXTRA_CALL_PERSON));
+ updateCallLayout();
+ }
+
+ private void setUser(Person user) {
+ mUser = user;
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 40e671ffd27c..1b1e0bfb3a58 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -18,8 +18,6 @@ package com.android.internal.widget;
import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_EXTERNAL;
import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_INLINE;
-import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_IN;
-import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_OUT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -34,10 +32,7 @@ import android.app.Person;
import android.app.RemoteInputHistoryItem;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
@@ -68,14 +63,12 @@ import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.graphics.ColorUtils;
-import com.android.internal.util.ContrastColorUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.function.Consumer;
-import java.util.regex.Pattern;
/**
* A custom-built layout for the Notification.MessagingStyle allows dynamic addition and removal
@@ -85,16 +78,6 @@ import java.util.regex.Pattern;
public class ConversationLayout extends FrameLayout
implements ImageMessageConsumer, IMessagingLayout {
- private static final float COLOR_SHIFT_AMOUNT = 60;
- /**
- * Pattern for filter some ignorable characters.
- * p{Z} for any kind of whitespace or invisible separator.
- * p{C} for any kind of punctuation character.
- */
- private static final Pattern IGNORABLE_CHAR_PATTERN
- = Pattern.compile("[\\p{C}\\p{Z}]");
- private static final Pattern SPECIAL_CHAR_PATTERN
- = Pattern.compile ("[!@#$%&*()_+=|<>?{}\\[\\]~-]");
private static final Consumer<MessagingMessage> REMOVE_MESSAGE
= MessagingMessage::removeMessage;
public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
@@ -106,6 +89,7 @@ public class ConversationLayout extends FrameLayout
public static final int IMPORTANCE_ANIM_GROW_DURATION = 250;
public static final int IMPORTANCE_ANIM_SHRINK_DURATION = 200;
public static final int IMPORTANCE_ANIM_SHRINK_DELAY = 25;
+ private final PeopleHelper mPeopleHelper = new PeopleHelper();
private List<MessagingMessage> mMessages = new ArrayList<>();
private List<MessagingMessage> mHistoricMessages = new ArrayList<>();
private MessagingLinearLayout mMessagingLinearLayout;
@@ -114,9 +98,6 @@ public class ConversationLayout extends FrameLayout
private int mLayoutColor;
private int mSenderTextColor;
private int mMessageTextColor;
- private int mAvatarSize;
- private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private Paint mTextPaint = new Paint();
private Icon mAvatarReplacement;
private boolean mIsOneToOne;
private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>();
@@ -196,6 +177,7 @@ public class ConversationLayout extends FrameLayout
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mPeopleHelper.init(getContext());
mMessagingLinearLayout = findViewById(R.id.notification_messaging);
mActions = findViewById(R.id.actions);
mImageMessageContainer = findViewById(R.id.conversation_image_message_container);
@@ -205,9 +187,6 @@ public class ConversationLayout extends FrameLayout
int size = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels);
mMessagingClipRect = new Rect(0, 0, size, size);
setMessagingClippingDisabled(false);
- mAvatarSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size);
- mTextPaint.setTextAlign(Paint.Align.CENTER);
- mTextPaint.setAntiAlias(true);
mConversationIconView = findViewById(R.id.conversation_icon);
mConversationIconContainer = findViewById(R.id.conversation_icon_container);
mIcon = findViewById(R.id.icon);
@@ -250,15 +229,15 @@ public class ConversationLayout extends FrameLayout
});
// When the small icon is gone, hide the rest of the badge
mIcon.setOnForceHiddenChangedListener((forceHidden) -> {
- animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
- animateViewForceHidden(mImportanceRingView, forceHidden);
+ mPeopleHelper.animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
+ mPeopleHelper.animateViewForceHidden(mImportanceRingView, forceHidden);
});
// When the conversation icon is gone, hide the whole badge
mConversationIconView.setOnForceHiddenChangedListener((forceHidden) -> {
- animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
- animateViewForceHidden(mImportanceRingView, forceHidden);
- animateViewForceHidden(mIcon, forceHidden);
+ mPeopleHelper.animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
+ mPeopleHelper.animateViewForceHidden(mImportanceRingView, forceHidden);
+ mPeopleHelper.animateViewForceHidden(mIcon, forceHidden);
});
mConversationText = findViewById(R.id.conversation_text);
mExpandButtonContainer = findViewById(R.id.expand_button_container);
@@ -317,28 +296,6 @@ public class ConversationLayout extends FrameLayout
R.dimen.notification_header_separating_margin);
}
- private void animateViewForceHidden(CachingIconView view, boolean forceHidden) {
- boolean nowForceHidden = view.willBeForceHidden() || view.isForceHidden();
- if (forceHidden == nowForceHidden) {
- // We are either already forceHidden or will be
- return;
- }
- view.animate().cancel();
- view.setWillBeForceHidden(forceHidden);
- view.animate()
- .scaleX(forceHidden ? 0.5f : 1.0f)
- .scaleY(forceHidden ? 0.5f : 1.0f)
- .alpha(forceHidden ? 0.0f : 1.0f)
- .setInterpolator(forceHidden ? ALPHA_OUT : ALPHA_IN)
- .setDuration(160);
- if (view.getVisibility() != VISIBLE) {
- view.setForceHidden(forceHidden);
- } else {
- view.animate().withEndAction(() -> view.setForceHidden(forceHidden));
- }
- view.animate().start();
- }
-
@RemotableViewMethod
public void setAvatarReplacement(Icon icon) {
mAvatarReplacement = icon;
@@ -561,7 +518,8 @@ public class ConversationLayout extends FrameLayout
if (mConversationIcon == null) {
Icon avatarIcon = messagingGroup.getAvatarIcon();
if (avatarIcon == null) {
- avatarIcon = createAvatarSymbol(conversationText, "", mLayoutColor);
+ avatarIcon = mPeopleHelper.createAvatarSymbol(conversationText, "",
+ mLayoutColor);
}
mConversationIcon = avatarIcon;
}
@@ -674,11 +632,11 @@ public class ConversationLayout extends FrameLayout
}
}
if (lastIcon == null) {
- lastIcon = createAvatarSymbol(" ", "", mLayoutColor);
+ lastIcon = mPeopleHelper.createAvatarSymbol(" ", "", mLayoutColor);
}
bottomView.setImageIcon(lastIcon);
if (secondLastIcon == null) {
- secondLastIcon = createAvatarSymbol("", "", mLayoutColor);
+ secondLastIcon = mPeopleHelper.createAvatarSymbol("", "", mLayoutColor);
}
topView.setImageIcon(secondLastIcon);
}
@@ -838,8 +796,10 @@ public class ConversationLayout extends FrameLayout
}
private void updateTitleAndNamesDisplay() {
+ // Map of unique names to their prefix
ArrayMap<CharSequence, String> uniqueNames = new ArrayMap<>();
- ArrayMap<Character, CharSequence> uniqueCharacters = new ArrayMap<>();
+ // Map of single-character string prefix to the only name which uses it, or null if multiple
+ ArrayMap<String, CharSequence> uniqueCharacters = new ArrayMap<>();
for (int i = 0; i < mGroups.size(); i++) {
MessagingGroup group = mGroups.get(i);
CharSequence senderName = group.getSenderName();
@@ -847,22 +807,22 @@ public class ConversationLayout extends FrameLayout
continue;
}
if (!uniqueNames.containsKey(senderName)) {
- // Only use visible characters to get uniqueNames
- String pureSenderName = IGNORABLE_CHAR_PATTERN
- .matcher(senderName).replaceAll("" /* replacement */);
- char c = pureSenderName.charAt(0);
- if (uniqueCharacters.containsKey(c)) {
+ String charPrefix = mPeopleHelper.findNamePrefix(senderName, null);
+ if (charPrefix == null) {
+ continue;
+ }
+ if (uniqueCharacters.containsKey(charPrefix)) {
// this character was already used, lets make it more unique. We first need to
// resolve the existing character if it exists
- CharSequence existingName = uniqueCharacters.get(c);
+ CharSequence existingName = uniqueCharacters.get(charPrefix);
if (existingName != null) {
- uniqueNames.put(existingName, findNameSplit((String) existingName));
- uniqueCharacters.put(c, null);
+ uniqueNames.put(existingName, mPeopleHelper.findNameSplit(existingName));
+ uniqueCharacters.put(charPrefix, null);
}
- uniqueNames.put(senderName, findNameSplit((String) senderName));
+ uniqueNames.put(senderName, mPeopleHelper.findNameSplit(senderName));
} else {
- uniqueNames.put(senderName, Character.toString(c));
- uniqueCharacters.put(c, pureSenderName);
+ uniqueNames.put(senderName, charPrefix);
+ uniqueCharacters.put(charPrefix, senderName);
}
}
}
@@ -898,8 +858,8 @@ public class ConversationLayout extends FrameLayout
} else {
Icon cachedIcon = cachedAvatars.get(senderName);
if (cachedIcon == null) {
- cachedIcon = createAvatarSymbol(senderName, uniqueNames.get(senderName),
- mLayoutColor);
+ cachedIcon = mPeopleHelper.createAvatarSymbol(senderName,
+ uniqueNames.get(senderName), mLayoutColor);
cachedAvatars.put(senderName, cachedIcon);
}
group.setCreatedAvatar(cachedIcon, senderName, uniqueNames.get(senderName),
@@ -908,49 +868,6 @@ public class ConversationLayout extends FrameLayout
}
}
- private Icon createAvatarSymbol(CharSequence senderName, String symbol, int layoutColor) {
- if (symbol.isEmpty() || TextUtils.isDigitsOnly(symbol) ||
- SPECIAL_CHAR_PATTERN.matcher(symbol).find()) {
- Icon avatarIcon = Icon.createWithResource(getContext(),
- R.drawable.messaging_user);
- avatarIcon.setTint(findColor(senderName, layoutColor));
- return avatarIcon;
- } else {
- Bitmap bitmap = Bitmap.createBitmap(mAvatarSize, mAvatarSize, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- float radius = mAvatarSize / 2.0f;
- int color = findColor(senderName, layoutColor);
- mPaint.setColor(color);
- canvas.drawCircle(radius, radius, radius, mPaint);
- boolean needDarkText = ColorUtils.calculateLuminance(color) > 0.5f;
- mTextPaint.setColor(needDarkText ? Color.BLACK : Color.WHITE);
- mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.5f : mAvatarSize * 0.3f);
- int yPos = (int) (radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));
- canvas.drawText(symbol, radius, yPos, mTextPaint);
- return Icon.createWithBitmap(bitmap);
- }
- }
-
- private int findColor(CharSequence senderName, int layoutColor) {
- double luminance = ContrastColorUtil.calculateLuminance(layoutColor);
- float shift = Math.abs(senderName.hashCode()) % 5 / 4.0f - 0.5f;
-
- // we need to offset the range if the luminance is too close to the borders
- shift += Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - luminance, 0);
- shift -= Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - (1.0f - luminance), 0);
- return ContrastColorUtil.getShiftedColor(layoutColor,
- (int) (shift * COLOR_SHIFT_AMOUNT));
- }
-
- private String findNameSplit(String existingName) {
- String[] split = existingName.split(" ");
- if (split.length > 1) {
- return Character.toString(split[0].charAt(0))
- + Character.toString(split[1].charAt(0));
- }
- return existingName.substring(0, 1);
- }
-
@RemotableViewMethod
public void setLayoutColor(int color) {
mLayoutColor = color;
diff --git a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
index 5213746e5a12..058a9218def4 100644
--- a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
+++ b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
@@ -16,17 +16,24 @@
package com.android.internal.widget;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.graphics.BlendMode;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.DrawableWrapper;
import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.Icon;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.view.RemotableViewMethod;
+import android.view.ViewGroup;
import android.widget.Button;
+import android.widget.LinearLayout;
import android.widget.RemoteViews;
+import com.android.internal.R;
+
/**
* A button implementation for the emphasized notification style.
*
@@ -37,6 +44,7 @@ public class EmphasizedNotificationButton extends Button {
private final RippleDrawable mRipple;
private final int mStrokeWidth;
private final int mStrokeColor;
+ private boolean mPriority;
public EmphasizedNotificationButton(Context context) {
this(context, null);
@@ -80,4 +88,57 @@ public class EmphasizedNotificationButton extends Button {
inner.setStroke(hasStroke ? mStrokeWidth : 0, mStrokeColor);
invalidate();
}
+
+ /**
+ * Sets an image icon which will have its size constrained and will be set to the same color as
+ * the text. Must be called after {@link #setTextColor(int)} for the latter to work.
+ */
+ @RemotableViewMethod(asyncImpl = "setImageIconAsync")
+ public void setImageIcon(@Nullable Icon icon) {
+ final Drawable drawable = icon == null ? null : icon.loadDrawable(mContext);
+ setImageDrawable(drawable);
+ }
+
+ /**
+ * @hide
+ */
+ @RemotableViewMethod
+ public Runnable setImageIconAsync(@Nullable Icon icon) {
+ final Drawable drawable = icon == null ? null : icon.loadDrawable(mContext);
+ return () -> setImageDrawable(drawable);
+ }
+
+ private void setImageDrawable(Drawable drawable) {
+ if (drawable != null) {
+ drawable.mutate();
+ drawable.setTintList(getTextColors());
+ drawable.setTintBlendMode(BlendMode.SRC_IN);
+ int iconSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.notification_actions_icon_drawable_size);
+ drawable.setBounds(0, 0, iconSize, iconSize);
+ }
+ setCompoundDrawablesRelative(drawable, null, null, null);
+ }
+
+ /**
+ * Changes the LayoutParams.width to WRAP_CONTENT, with the argument representing if this view
+ * is a priority over its peers (which affects weight).
+ */
+ @RemotableViewMethod
+ public void setWrapModePriority(boolean priority) {
+ mPriority = priority;
+ ViewGroup.LayoutParams layoutParams = getLayoutParams();
+ layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+ if (layoutParams instanceof LinearLayout.LayoutParams) {
+ ((LinearLayout.LayoutParams) layoutParams).weight = 0;
+ }
+ setLayoutParams(layoutParams);
+ }
+
+ /**
+ * Sizing this button is a priority compared with its peers.
+ */
+ public boolean isPriority() {
+ return mPriority;
+ }
}
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index c7ea781b2793..8e6497b204c7 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -16,6 +16,7 @@
package com.android.internal.widget;
+import android.annotation.DimenRes;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.RippleDrawable;
@@ -41,13 +42,16 @@ public class NotificationActionListLayout extends LinearLayout {
private final int mGravity;
private int mTotalWidth = 0;
+ private int mExtraStartPadding = 0;
private ArrayList<Pair<Integer, TextView>> mMeasureOrderTextViews = new ArrayList<>();
private ArrayList<View> mMeasureOrderOther = new ArrayList<>();
private boolean mEmphasizedMode;
+ private boolean mPrioritizedWrapMode;
private int mDefaultPaddingBottom;
private int mDefaultPaddingTop;
private int mEmphasizedHeight;
private int mRegularHeight;
+ @DimenRes private int mCollapsibleIndentDimen;
public NotificationActionListLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -68,7 +72,7 @@ public class NotificationActionListLayout extends LinearLayout {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mEmphasizedMode) {
+ if (mEmphasizedMode && !mPrioritizedWrapMode) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
@@ -151,7 +155,15 @@ public class NotificationActionListLayout extends LinearLayout {
measuredChildren++;
}
- mTotalWidth = usedWidth + mPaddingRight + mPaddingLeft;
+ int collapsibleIndent = mCollapsibleIndentDimen == 0 ? 0
+ : getResources().getDimensionPixelOffset(mCollapsibleIndentDimen);
+ if (innerWidth - usedWidth > collapsibleIndent) {
+ mExtraStartPadding = collapsibleIndent;
+ } else {
+ mExtraStartPadding = 0;
+ }
+
+ mTotalWidth = usedWidth + mPaddingRight + mPaddingLeft + mExtraStartPadding;
setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec),
resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
@@ -163,7 +175,11 @@ public class NotificationActionListLayout extends LinearLayout {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View c = getChildAt(i);
- if (c instanceof TextView && ((TextView) c).getText().length() > 0) {
+ if (c instanceof EmphasizedNotificationButton
+ && ((EmphasizedNotificationButton) c).isPriority()) {
+ // add with 0 length to ensure that this view is measured before others.
+ mMeasureOrderTextViews.add(Pair.create(0, (TextView) c));
+ } else if (c instanceof TextView && ((TextView) c).getText().length() > 0) {
mMeasureOrderTextViews.add(Pair.create(((TextView) c).getText().length(),
(TextView)c));
} else {
@@ -197,7 +213,7 @@ public class NotificationActionListLayout extends LinearLayout {
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (mEmphasizedMode) {
+ if (mEmphasizedMode && !mPrioritizedWrapMode) {
super.onLayout(changed, left, top, right, bottom);
return;
}
@@ -214,6 +230,9 @@ public class NotificationActionListLayout extends LinearLayout {
int absoluteGravity = Gravity.getAbsoluteGravity(Gravity.START, getLayoutDirection());
if (absoluteGravity == Gravity.RIGHT) {
childLeft += right - left - mTotalWidth;
+ } else {
+ // Put the extra start padding (if any) on the left when LTR
+ childLeft += mExtraStartPadding;
}
}
@@ -274,6 +293,26 @@ public class NotificationActionListLayout extends LinearLayout {
}
/**
+ * When used with emphasizedMode, changes the button sizing behavior to prioritize certain
+ * buttons (which are system generated) to not scrunch, and leave the remaining space for
+ * custom actions.
+ */
+ @RemotableViewMethod
+ public void setPrioritizedWrapMode(boolean prioritizedWrapMode) {
+ mPrioritizedWrapMode = prioritizedWrapMode;
+ }
+
+ /**
+ * When buttons are in wrap mode, this is a padding that will be applied at the start of the
+ * layout of the actions, but only when those actions would fit with the entire padding
+ * visible. Otherwise, this padding will be omitted entirely.
+ */
+ @RemotableViewMethod
+ public void setCollapsibleIndentDimen(@DimenRes int collapsibleIndentDimen) {
+ mCollapsibleIndentDimen = collapsibleIndentDimen;
+ }
+
+ /**
* Set whether the list is in a mode where some actions are emphasized. This will trigger an
* equal measuring where all actions are full height and change a few parameters like
* the padding.
diff --git a/core/java/com/android/internal/widget/PeopleHelper.java b/core/java/com/android/internal/widget/PeopleHelper.java
new file mode 100644
index 000000000000..77f4c8f6bede
--- /dev/null
+++ b/core/java/com/android/internal/widget/PeopleHelper.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 com.android.internal.widget;
+
+import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_IN;
+import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_OUT;
+
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.drawable.Icon;
+import android.text.TextUtils;
+import android.view.View;
+
+import com.android.internal.R;
+import com.android.internal.graphics.ColorUtils;
+import com.android.internal.util.ContrastColorUtil;
+
+import java.util.regex.Pattern;
+
+/**
+ * This class provides some methods used by both the {@link ConversationLayout} and
+ * {@link CallLayout} which both use the visual design originally created for conversations in R.
+ */
+public class PeopleHelper {
+
+ private static final float COLOR_SHIFT_AMOUNT = 60;
+ /**
+ * Pattern for filter some ignorable characters.
+ * p{Z} for any kind of whitespace or invisible separator.
+ * p{C} for any kind of punctuation character.
+ */
+ private static final Pattern IGNORABLE_CHAR_PATTERN = Pattern.compile("[\\p{C}\\p{Z}]");
+ private static final Pattern SPECIAL_CHAR_PATTERN =
+ Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]");
+
+ private Context mContext;
+ private int mAvatarSize;
+ private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private Paint mTextPaint = new Paint();
+
+ /**
+ * Call this when the view is inflated to provide a context and initialize the helper
+ */
+ public void init(Context context) {
+ mContext = context;
+ mAvatarSize = context.getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size);
+ mTextPaint.setTextAlign(Paint.Align.CENTER);
+ mTextPaint.setAntiAlias(true);
+ }
+
+ /**
+ * A utility for animating CachingIconViews away when hidden.
+ */
+ public void animateViewForceHidden(CachingIconView view, boolean forceHidden) {
+ boolean nowForceHidden = view.willBeForceHidden() || view.isForceHidden();
+ if (forceHidden == nowForceHidden) {
+ // We are either already forceHidden or will be
+ return;
+ }
+ view.animate().cancel();
+ view.setWillBeForceHidden(forceHidden);
+ view.animate()
+ .scaleX(forceHidden ? 0.5f : 1.0f)
+ .scaleY(forceHidden ? 0.5f : 1.0f)
+ .alpha(forceHidden ? 0.0f : 1.0f)
+ .setInterpolator(forceHidden ? ALPHA_OUT : ALPHA_IN)
+ .setDuration(160);
+ if (view.getVisibility() != View.VISIBLE) {
+ view.setForceHidden(forceHidden);
+ } else {
+ view.animate().withEndAction(() -> view.setForceHidden(forceHidden));
+ }
+ view.animate().start();
+ }
+
+ /**
+ * This creates an avatar symbol for the given person or group
+ *
+ * @param name the name of the person or group
+ * @param symbol a pre-chosen symbol for the person or group. See
+ * {@link #findNamePrefix(CharSequence, String)} or
+ * {@link #findNameSplit(CharSequence)}
+ * @param layoutColor the background color of the layout
+ */
+ @NonNull
+ public Icon createAvatarSymbol(@NonNull CharSequence name, @NonNull String symbol,
+ @ColorInt int layoutColor) {
+ if (symbol.isEmpty() || TextUtils.isDigitsOnly(symbol)
+ || SPECIAL_CHAR_PATTERN.matcher(symbol).find()) {
+ Icon avatarIcon = Icon.createWithResource(mContext, R.drawable.messaging_user);
+ avatarIcon.setTint(findColor(name, layoutColor));
+ return avatarIcon;
+ } else {
+ Bitmap bitmap = Bitmap.createBitmap(mAvatarSize, mAvatarSize, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ float radius = mAvatarSize / 2.0f;
+ int color = findColor(name, layoutColor);
+ mPaint.setColor(color);
+ canvas.drawCircle(radius, radius, radius, mPaint);
+ boolean needDarkText = ColorUtils.calculateLuminance(color) > 0.5f;
+ mTextPaint.setColor(needDarkText ? Color.BLACK : Color.WHITE);
+ mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.5f : mAvatarSize * 0.3f);
+ int yPos = (int) (radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));
+ canvas.drawText(symbol, radius, yPos, mTextPaint);
+ return Icon.createWithBitmap(bitmap);
+ }
+ }
+
+ private int findColor(@NonNull CharSequence senderName, int layoutColor) {
+ double luminance = ContrastColorUtil.calculateLuminance(layoutColor);
+ float shift = Math.abs(senderName.hashCode()) % 5 / 4.0f - 0.5f;
+
+ // we need to offset the range if the luminance is too close to the borders
+ shift += Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - luminance, 0);
+ shift -= Math.max(COLOR_SHIFT_AMOUNT / 2.0f / 100 - (1.0f - luminance), 0);
+ return ContrastColorUtil.getShiftedColor(layoutColor,
+ (int) (shift * COLOR_SHIFT_AMOUNT));
+ }
+
+ /**
+ * Get the name with whitespace and punctuation characters removed
+ */
+ private String getPureName(@NonNull CharSequence name) {
+ return IGNORABLE_CHAR_PATTERN.matcher(name).replaceAll("" /* replacement */);
+ }
+
+ /**
+ * Gets a single character string prefix name for the person or group
+ *
+ * @param name the name of the person or group
+ * @param fallback the string to return if the name has no usable characters
+ */
+ public String findNamePrefix(@NonNull CharSequence name, String fallback) {
+ String pureName = getPureName(name);
+ if (pureName.isEmpty()) {
+ return fallback;
+ }
+ try {
+ return new String(Character.toChars(pureName.codePointAt(0)));
+ } catch (RuntimeException ignore) {
+ return fallback;
+ }
+ }
+
+ /**
+ * Find a 1 or 2 character prefix name for the person or group
+ */
+ public String findNameSplit(@NonNull CharSequence name) {
+ String nameString = name instanceof String ? ((String) name) : name.toString();
+ String[] split = nameString.trim().split("[ ]+");
+ if (split.length > 1) {
+ String first = findNamePrefix(split[0], null);
+ String second = findNamePrefix(split[1], null);
+ if (first != null && second != null) {
+ return first + second;
+ }
+ }
+ return findNamePrefix(name, "");
+ }
+}
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index a2de0aff5dfa..143017cba57d 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -36,6 +36,7 @@ import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
+import android.view.Surface;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
@@ -59,6 +60,9 @@ public class PointerLocationView extends View implements InputDeviceListener,
*/
private static final String GESTURE_EXCLUSION_PROP = "debug.pointerlocation.showexclusion";
+ private static final boolean ENABLE_PER_WINDOW_INPUT_ROTATION =
+ SystemProperties.getBoolean("persist.debug.per_window_input_rotation", false);
+
public static class PointerState {
// Trace of previous points.
private float[] mTraceX = new float[32];
@@ -352,6 +356,21 @@ public class PointerLocationView extends View implements InputDeviceListener,
.toString(), 1 + itemW * 6, base, mTextPaint);
}
+ int saveId = canvas.save();
+ if (ENABLE_PER_WINDOW_INPUT_ROTATION) {
+ // Rotate negative (since we're rotating the drawing canvas vs the output).
+ canvas.rotate(-90.0f * mContext.getDisplay().getRotation());
+ switch (mContext.getDisplay().getRotation()) {
+ case Surface.ROTATION_90:
+ canvas.translate(-canvas.getHeight(), 0);
+ break;
+ case Surface.ROTATION_180:
+ canvas.translate(-canvas.getWidth(), -canvas.getHeight());
+ break;
+ case Surface.ROTATION_270:
+ canvas.translate(0, -canvas.getWidth());
+ }
+ }
// Pointer trace.
for (int p = 0; p < NP; p++) {
final PointerState ps = mPointers.get(p);
@@ -399,7 +418,10 @@ public class PointerLocationView extends View implements InputDeviceListener,
if (mCurDown && ps.mCurDown) {
// Draw crosshairs.
canvas.drawLine(0, ps.mCoords.y, getWidth(), ps.mCoords.y, mTargetPaint);
- canvas.drawLine(ps.mCoords.x, 0, ps.mCoords.x, getHeight(), mTargetPaint);
+ // Extend crosshairs to cover screen regardless of rotation (ie. since the rotated
+ // canvas can "expose" content past 0 and up-to the largest screen dimension).
+ canvas.drawLine(ps.mCoords.x, -getHeight(), ps.mCoords.x,
+ Math.max(getHeight(), getWidth()), mTargetPaint);
// Draw current point.
int pressureLevel = (int)(ps.mCoords.pressure * 255);
@@ -458,6 +480,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
}
}
}
+ canvas.restoreToCount(saveId);
}
private void logMotionEvent(String type, MotionEvent event) {
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 95999a716707..fe3042d9da54 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -74,6 +74,7 @@ public class BootReceiver extends BroadcastReceiver {
private static final int GMSCORE_LASTK_LOG_SIZE = 196608;
private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE";
+ private static final String TAG_TOMBSTONE_PROTO = "SYSTEM_TOMBSTONE_PROTO";
// The pre-froyo package and class of the system updater, which
// ran in the system process. We need to remove its packages here
@@ -251,14 +252,14 @@ public class BootReceiver extends BroadcastReceiver {
* @param ctx Context
* @param tombstone path to the tombstone
*/
- public static void addTombstoneToDropBox(Context ctx, File tombstone) {
+ public static void addTombstoneToDropBox(Context ctx, File tombstone, boolean proto) {
final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
HashMap<String, Long> timestamps = readTimestamps();
try {
final String headers = getBootHeadersToLogAndUpdate();
addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
- TAG_TOMBSTONE);
+ proto ? TAG_TOMBSTONE_PROTO : TAG_TOMBSTONE);
} catch (IOException e) {
Slog.e(TAG, "Can't log tombstone", e);
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index cb586d660634..8982519eff96 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -1236,6 +1236,8 @@ public class SystemConfig {
if (IncrementalManager.isFeatureEnabled()) {
addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY, 0);
+ addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY_VERSION,
+ IncrementalManager.isV2Available() ? 2 : 1);
}
if (PackageManager.APP_ENUMERATION_ENABLED_BY_DEFAULT) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index c6948d9bde70..61518437e8fe 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -1,3 +1,21 @@
+
+package {
+ default_applicable_licenses: ["frameworks_base_core_jni_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_core_jni_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
cc_library_shared {
name: "libandroid_runtime",
host_supported: true,
@@ -51,11 +69,12 @@ cc_library_shared {
"libhwui",
"liblog",
"libminikin",
- "libnativehelper",
"libz",
"libziparchive",
],
+ static_libs: ["libnativehelper_lazy"],
+
export_include_dirs: [
".",
"include",
@@ -186,6 +205,7 @@ cc_library_shared {
"com_android_internal_net_NetworkUtilsInternal.cpp",
"com_android_internal_os_ClassLoaderFactory.cpp",
"com_android_internal_os_FuseAppLoop.cpp",
+ "com_android_internal_os_KernelCpuBpfTracking.cpp",
"com_android_internal_os_KernelCpuTotalBpfMapReader.cpp",
"com_android_internal_os_KernelCpuUidBpfMapReader.cpp",
"com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp",
@@ -209,6 +229,7 @@ cc_library_shared {
],
shared_libs: [
+ "android.hardware.memtrack-V1-ndk_platform",
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
"av-types-aidl-cpp",
@@ -272,12 +293,13 @@ cc_library_shared {
"libstatspull",
],
export_shared_lib_headers: [
- // AndroidRuntime.h depends on nativehelper/jni.h
- "libnativehelper",
-
// our headers include libnativewindow's public headers
"libnativewindow",
],
+ export_static_lib_headers: [
+ // AndroidRuntime.h depends on nativehelper/jni.h
+ "libnativehelper_lazy",
+ ],
header_libs: [
"bionic_libc_platform_headers",
"dnsproxyd_protocol_headers",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 1c4115ba9d65..a4b1f88deaf5 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -191,6 +191,7 @@ extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env);
extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env);
extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
+extern int register_com_android_internal_os_KernelCpuBpfTracking(JNIEnv* env);
extern int register_com_android_internal_os_KernelCpuTotalBpfMapReader(JNIEnv* env);
extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env);
extern int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv* env);
@@ -1589,6 +1590,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_security_Scrypt),
REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
REG_JNI(register_com_android_internal_os_FuseAppLoop),
+ REG_JNI(register_com_android_internal_os_KernelCpuBpfTracking),
REG_JNI(register_com_android_internal_os_KernelCpuTotalBpfMapReader),
REG_JNI(register_com_android_internal_os_KernelCpuUidBpfMapReader),
REG_JNI(register_com_android_internal_os_KernelSingleUidTimeReader),
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 2279d5796c02..35d1d7bd7946 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -5,6 +5,9 @@ per-file *Camera*,*camera* = shuzhenwang@google.com, yinchiayeh@google.com, zhij
# Connectivity
per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
+# CPU
+per-file *Cpu* = file:/core/java/com/android/internal/os/CPU_OWNERS
+
# Display
per-file android_hardware_display_* = file:/services/core/java/com/android/server/display/OWNERS
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index f1998a583a21..c7439f1b32d4 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -188,7 +188,7 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t forma
ATRACE_NAME(base::StringPrintf("LoadApkAssets(%s)", path.c_str()).c_str());
auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
- std::unique_ptr<const ApkAssets> apk_assets;
+ std::unique_ptr<ApkAssets> apk_assets;
switch (format) {
case FORMAT_APK: {
auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
@@ -354,7 +354,7 @@ static void NativeDestroy(void* ptr) {
}
static jlong NativeGetFinalizer(JNIEnv* /*env*/, jclass /*clazz*/) {
- return reinterpret_cast<jlong>(&NativeDestroy);
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&NativeDestroy));
}
static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index bb51c57c74b2..8dcb2100b5fb 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -33,6 +33,7 @@
#include <string>
#include <vector>
+#include <aidl/android/hardware/memtrack/DeviceInfo.h>
#include <android-base/logging.h>
#include <bionic/malloc.h>
#include <debuggerd/client.h>
@@ -43,7 +44,9 @@
#include <nativehelper/JNIPlatformHelp.h>
#include <nativehelper/ScopedUtfChars.h>
#include "jni.h"
+#include <dmabufinfo/dmabuf_sysfs_stats.h>
#include <dmabufinfo/dmabufinfo.h>
+#include <dmabufinfo/dmabuf_sysfs_stats.h>
#include <meminfo/procmeminfo.h>
#include <meminfo/sysmeminfo.h>
#include <memtrack/memtrack.h>
@@ -519,14 +522,15 @@ static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid,
}
if (outUssSwapPssRss != NULL) {
- if (env->GetArrayLength(outUssSwapPssRss) >= 1) {
+ int outLen = env->GetArrayLength(outUssSwapPssRss);
+ if (outLen >= 1) {
jlong* outUssSwapPssRssArray = env->GetLongArrayElements(outUssSwapPssRss, 0);
if (outUssSwapPssRssArray != NULL) {
outUssSwapPssRssArray[0] = uss;
- if (env->GetArrayLength(outUssSwapPssRss) >= 2) {
+ if (outLen >= 2) {
outUssSwapPssRssArray[1] = swapPss;
}
- if (env->GetArrayLength(outUssSwapPssRss) >= 3) {
+ if (outLen >= 3) {
outUssSwapPssRssArray[2] = rss;
}
}
@@ -535,10 +539,20 @@ static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid,
}
if (outMemtrack != NULL) {
- if (env->GetArrayLength(outMemtrack) >= 1) {
+ int outLen = env->GetArrayLength(outMemtrack);
+ if (outLen >= 1) {
jlong* outMemtrackArray = env->GetLongArrayElements(outMemtrack, 0);
if (outMemtrackArray != NULL) {
outMemtrackArray[0] = memtrack;
+ if (outLen >= 2) {
+ outMemtrackArray[1] = graphics_mem.graphics;
+ }
+ if (outLen >= 3) {
+ outMemtrackArray[2] = graphics_mem.gl;
+ }
+ if (outLen >= 4) {
+ outMemtrackArray[3] = graphics_mem.other;
+ }
}
env->ReleaseLongArrayElements(outMemtrack, outMemtrackArray, 0);
}
@@ -805,6 +819,26 @@ static jlong android_os_Debug_getIonHeapsSizeKb(JNIEnv* env, jobject clazz) {
return heapsSizeKb;
}
+static jlong android_os_Debug_getDmabufTotalExportedKb(JNIEnv* env, jobject clazz) {
+ jlong dmabufTotalSizeKb = -1;
+ uint64_t size;
+
+ if (dmabufinfo::GetDmabufTotalExportedKb(&size)) {
+ dmabufTotalSizeKb = size;
+ }
+ return dmabufTotalSizeKb;
+}
+
+static jlong android_os_Debug_getDmabufHeapTotalExportedKb(JNIEnv* env, jobject clazz) {
+ jlong dmabufHeapTotalSizeKb = -1;
+ uint64_t size;
+
+ if (meminfo::ReadDmabufHeapTotalExportedKb(&size)) {
+ dmabufHeapTotalSizeKb = size;
+ }
+ return dmabufHeapTotalSizeKb;
+}
+
static jlong android_os_Debug_getIonPoolsSizeKb(JNIEnv* env, jobject clazz) {
jlong poolsSizeKb = -1;
uint64_t size;
@@ -816,8 +850,44 @@ static jlong android_os_Debug_getIonPoolsSizeKb(JNIEnv* env, jobject clazz) {
return poolsSizeKb;
}
-static jlong android_os_Debug_getIonMappedSizeKb(JNIEnv* env, jobject clazz) {
- jlong ionPss = 0;
+static jlong android_os_Debug_getDmabufHeapPoolsSizeKb(JNIEnv* env, jobject clazz) {
+ jlong poolsSizeKb = -1;
+ uint64_t size;
+
+ if (meminfo::ReadDmabufHeapPoolsSizeKb(&size)) {
+ poolsSizeKb = size;
+ }
+
+ return poolsSizeKb;
+}
+
+static jlong android_os_Debug_getGpuDmaBufUsageKb(JNIEnv* env, jobject clazz) {
+ std::vector<aidl::android::hardware::memtrack::DeviceInfo> gpu_device_info;
+ if (!memtrack_gpu_device_info(&gpu_device_info)) {
+ return -1;
+ }
+
+ dmabufinfo::DmabufSysfsStats stats;
+ if (!GetDmabufSysfsStats(&stats)) {
+ return -1;
+ }
+
+ jlong sizeKb = 0;
+ const auto& importer_stats = stats.importer_info();
+ for (const auto& dev_info : gpu_device_info) {
+ const auto& importer_info = importer_stats.find(dev_info.name);
+ if (importer_info == importer_stats.end()) {
+ continue;
+ }
+
+ sizeKb += importer_info->second.size;
+ }
+
+ return sizeKb;
+}
+
+static jlong android_os_Debug_getDmabufMappedSizeKb(JNIEnv* env, jobject clazz) {
+ jlong dmabufPss = 0;
std::vector<dmabufinfo::DmaBuffer> dmabufs;
std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir("/proc"), closedir);
@@ -841,10 +911,10 @@ static jlong android_os_Debug_getIonMappedSizeKb(JNIEnv* env, jobject clazz) {
}
for (const dmabufinfo::DmaBuffer& buf : dmabufs) {
- ionPss += buf.size() / 1024;
+ dmabufPss += buf.size() / 1024;
}
- return ionPss;
+ return dmabufPss;
}
static jlong android_os_Debug_getGpuTotalUsageKb(JNIEnv* env, jobject clazz) {
@@ -922,10 +992,18 @@ static const JNINativeMethod gMethods[] = {
(void*)android_os_Debug_getFreeZramKb },
{ "getIonHeapsSizeKb", "()J",
(void*)android_os_Debug_getIonHeapsSizeKb },
+ { "getDmabufTotalExportedKb", "()J",
+ (void*)android_os_Debug_getDmabufTotalExportedKb },
+ { "getGpuDmaBufUsageKb", "()J",
+ (void*)android_os_Debug_getGpuDmaBufUsageKb },
+ { "getDmabufHeapTotalExportedKb", "()J",
+ (void*)android_os_Debug_getDmabufHeapTotalExportedKb },
{ "getIonPoolsSizeKb", "()J",
(void*)android_os_Debug_getIonPoolsSizeKb },
- { "getIonMappedSizeKb", "()J",
- (void*)android_os_Debug_getIonMappedSizeKb },
+ { "getDmabufMappedSizeKb", "()J",
+ (void*)android_os_Debug_getDmabufMappedSizeKb },
+ { "getDmabufHeapPoolsSizeKb", "()J",
+ (void*)android_os_Debug_getDmabufHeapPoolsSizeKb },
{ "getGpuTotalUsageKb", "()J",
(void*)android_os_Debug_getGpuTotalUsageKb },
{ "isVmapStack", "()Z",
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 0f7611a8ead1..f67007cda209 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -83,7 +83,7 @@ static void android_os_Trace_nativeAsyncTraceEnd(JNIEnv* env, jclass,
}
static void android_os_Trace_nativeSetAppTracingAllowed(JNIEnv*, jclass, jboolean allowed) {
- atrace_set_debuggable(allowed);
+ atrace_update_tags();
}
static void android_os_Trace_nativeSetTracingEnabled(JNIEnv*, jclass, jboolean enabled) {
diff --git a/core/jni/android_os_incremental_IncrementalManager.cpp b/core/jni/android_os_incremental_IncrementalManager.cpp
index 44bff0188544..2384efaf1a54 100644
--- a/core/jni/android_os_incremental_IncrementalManager.cpp
+++ b/core/jni/android_os_incremental_IncrementalManager.cpp
@@ -30,6 +30,10 @@ static jboolean nativeIsEnabled(JNIEnv* env, jobject clazz) {
return IncFs_IsEnabled();
}
+static jboolean nativeIsV2Available(JNIEnv* env, jobject clazz) {
+ return !!(IncFs_Features() & INCFS_FEATURE_V2);
+}
+
static jboolean nativeIsIncrementalPath(JNIEnv* env,
jobject clazz,
jstring javaPath) {
@@ -53,12 +57,12 @@ static jbyteArray nativeUnsafeGetFileSignature(JNIEnv* env, jobject clazz, jstri
return result;
}
-static const JNINativeMethod method_table[] = {{"nativeIsEnabled", "()Z", (void*)nativeIsEnabled},
- {"nativeIsIncrementalPath", "(Ljava/lang/String;)Z",
- (void*)nativeIsIncrementalPath},
- {"nativeUnsafeGetFileSignature",
- "(Ljava/lang/String;)[B",
- (void*)nativeUnsafeGetFileSignature}};
+static const JNINativeMethod method_table[] =
+ {{"nativeIsEnabled", "()Z", (void*)nativeIsEnabled},
+ {"nativeIsV2Available", "()Z", (void*)nativeIsV2Available},
+ {"nativeIsIncrementalPath", "(Ljava/lang/String;)Z", (void*)nativeIsIncrementalPath},
+ {"nativeUnsafeGetFileSignature", "(Ljava/lang/String;)[B",
+ (void*)nativeUnsafeGetFileSignature}};
int register_android_os_incremental_IncrementalManager(JNIEnv* env) {
return jniRegisterNativeMethods(env, "android/os/incremental/IncrementalManager",
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 175b6f793742..c6ecd7bfa07c 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -447,6 +447,10 @@ jint android_os_Process_getProcessGroup(JNIEnv* env, jobject clazz, jint pid)
return (int) sp;
}
+jint android_os_Process_createProcessGroup(JNIEnv* env, jobject clazz, jint uid, jint pid) {
+ return createProcessGroup(uid, pid);
+}
+
/** Sample CPUset list format:
* 0-3,4,6-8
*/
@@ -1442,6 +1446,7 @@ static const JNINativeMethod methods[] = {
{"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup},
{"setCgroupProcsProcessGroup", "(IIIZ)V", (void*)android_os_Process_setCgroupProcsProcessGroup},
{"getProcessGroup", "(I)I", (void*)android_os_Process_getProcessGroup},
+ {"createProcessGroup", "(II)I", (void*)android_os_Process_createProcessGroup},
{"getExclusiveCores", "()[I", (void*)android_os_Process_getExclusiveCores},
{"setSwappiness", "(IZ)Z", (void*)android_os_Process_setSwappiness},
{"setArgV0", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0},
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 6337680147a5..8977b97fc541 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -40,7 +40,7 @@ static struct {
jmethodID dispatchVsync;
jmethodID dispatchHotplug;
- jmethodID dispatchConfigChanged;
+ jmethodID dispatchModeChanged;
jmethodID dispatchFrameRateOverrides;
struct {
@@ -69,8 +69,8 @@ private:
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
VsyncEventData vsyncEventData) override;
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
- void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
- int32_t configId, nsecs_t vsyncPeriod) override;
+ void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
+ nsecs_t vsyncPeriod) override;
void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) override;
void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) override {}
@@ -129,20 +129,19 @@ void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, PhysicalDisp
mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
}
-void NativeDisplayEventReceiver::dispatchConfigChanged(
- nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId, nsecs_t) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+void NativeDisplayEventReceiver::dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+ int32_t modeId, nsecs_t) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
- ScopedLocalRef<jobject> receiverObj(env,
- jniGetReferent(env, mReceiverWeakGlobal));
- if (receiverObj.get()) {
- ALOGV("receiver %p ~ Invoking config changed handler.", this);
- env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchConfigChanged,
- timestamp, displayId.value, configId);
- ALOGV("receiver %p ~ Returned from config changed handler.", this);
- }
+ ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
+ if (receiverObj.get()) {
+ ALOGV("receiver %p ~ Invoking mode changed handler.", this);
+ env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchModeChanged,
+ timestamp, displayId.value, modeId);
+ ALOGV("receiver %p ~ Returned from mode changed handler.", this);
+ }
- mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged");
+ mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
}
void NativeDisplayEventReceiver::dispatchFrameRateOverrides(
@@ -173,7 +172,7 @@ void NativeDisplayEventReceiver::dispatchFrameRateOverrides(
ALOGV("receiver %p ~ Returned from FrameRateOverride handler.", this);
}
- mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged");
+ mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
}
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj,
@@ -243,8 +242,9 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) {
"(JJIJJ)V");
gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
- gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env,
- gDisplayEventReceiverClassInfo.clazz, "dispatchConfigChanged", "(JJI)V");
+ gDisplayEventReceiverClassInfo.dispatchModeChanged =
+ GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchModeChanged",
+ "(JJI)V");
gDisplayEventReceiverClassInfo.dispatchFrameRateOverrides =
GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz,
"dispatchFrameRateOverrides",
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index ac680f6e2611..9746a07a1b77 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -199,7 +199,13 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) {
for (;;) {
uint32_t publishedSeq;
bool handled;
- status_t status = mInputPublisher.receiveFinishedSignal(&publishedSeq, &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) {
if (status == WOULD_BLOCK) {
return OK;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 859730872e8c..7a3366acce27 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -44,8 +44,8 @@
#include <ui/BlurRegion.h>
#include <ui/ConfigStoreTypes.h>
#include <ui/DeviceProductInfo.h>
-#include <ui/DisplayConfig.h>
#include <ui/DisplayInfo.h>
+#include <ui/DisplayMode.h>
#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
#include <ui/GraphicTypes.h>
@@ -98,8 +98,8 @@ static struct {
jfieldID refreshRate;
jfieldID appVsyncOffsetNanos;
jfieldID presentationDeadlineNanos;
- jfieldID configGroup;
-} gDisplayConfigClassInfo;
+ jfieldID group;
+} gDisplayModeClassInfo;
static struct {
jfieldID bottom;
@@ -202,13 +202,13 @@ static struct {
static struct {
jclass clazz;
jmethodID ctor;
- jfieldID defaultConfig;
+ jfieldID defaultMode;
jfieldID allowGroupSwitching;
jfieldID primaryRefreshRateMin;
jfieldID primaryRefreshRateMax;
jfieldID appRequestRefreshRateMin;
jfieldID appRequestRefreshRateMax;
-} gDesiredDisplayConfigSpecsClassInfo;
+} gDesiredDisplayModeSpecsClassInfo;
static struct {
jclass clazz;
@@ -1028,101 +1028,97 @@ static jobject nativeGetDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj)
return object;
}
-static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz, jobject tokenObj) {
- Vector<DisplayConfig> configs;
+static jobjectArray nativeGetDisplayModes(JNIEnv* env, jclass clazz, jobject tokenObj) {
+ Vector<ui::DisplayMode> modes;
if (const auto token = ibinderForJavaObject(env, tokenObj); !token ||
- SurfaceComposerClient::getDisplayConfigs(token, &configs) != NO_ERROR ||
- configs.isEmpty()) {
+ SurfaceComposerClient::getDisplayModes(token, &modes) != NO_ERROR || modes.isEmpty()) {
return nullptr;
}
- jobjectArray configArray =
- env->NewObjectArray(configs.size(), gDisplayConfigClassInfo.clazz, nullptr);
-
- for (size_t c = 0; c < configs.size(); ++c) {
- const DisplayConfig& config = configs[c];
- jobject object =
- env->NewObject(gDisplayConfigClassInfo.clazz, gDisplayConfigClassInfo.ctor);
- env->SetIntField(object, gDisplayConfigClassInfo.width, config.resolution.getWidth());
- env->SetIntField(object, gDisplayConfigClassInfo.height, config.resolution.getHeight());
- env->SetFloatField(object, gDisplayConfigClassInfo.xDpi, config.xDpi);
- env->SetFloatField(object, gDisplayConfigClassInfo.yDpi, config.yDpi);
-
- env->SetFloatField(object, gDisplayConfigClassInfo.refreshRate, config.refreshRate);
- env->SetLongField(object, gDisplayConfigClassInfo.appVsyncOffsetNanos,
- config.appVsyncOffset);
- env->SetLongField(object, gDisplayConfigClassInfo.presentationDeadlineNanos,
- config.presentationDeadline);
- env->SetIntField(object, gDisplayConfigClassInfo.configGroup, config.configGroup);
- env->SetObjectArrayElement(configArray, static_cast<jsize>(c), object);
+ jobjectArray modesArray =
+ env->NewObjectArray(modes.size(), gDisplayModeClassInfo.clazz, nullptr);
+
+ for (size_t c = 0; c < modes.size(); ++c) {
+ const ui::DisplayMode& mode = modes[c];
+ jobject object = env->NewObject(gDisplayModeClassInfo.clazz, gDisplayModeClassInfo.ctor);
+ env->SetIntField(object, gDisplayModeClassInfo.width, mode.resolution.getWidth());
+ env->SetIntField(object, gDisplayModeClassInfo.height, mode.resolution.getHeight());
+ env->SetFloatField(object, gDisplayModeClassInfo.xDpi, mode.xDpi);
+ env->SetFloatField(object, gDisplayModeClassInfo.yDpi, mode.yDpi);
+
+ env->SetFloatField(object, gDisplayModeClassInfo.refreshRate, mode.refreshRate);
+ env->SetLongField(object, gDisplayModeClassInfo.appVsyncOffsetNanos, mode.appVsyncOffset);
+ env->SetLongField(object, gDisplayModeClassInfo.presentationDeadlineNanos,
+ mode.presentationDeadline);
+ env->SetIntField(object, gDisplayModeClassInfo.group, mode.group);
+ env->SetObjectArrayElement(modesArray, static_cast<jsize>(c), object);
env->DeleteLocalRef(object);
}
- return configArray;
+ return modesArray;
}
-static jboolean nativeSetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jobject tokenObj,
- jobject desiredDisplayConfigSpecs) {
+static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj,
+ jobject DesiredDisplayModeSpecs) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == nullptr) return JNI_FALSE;
- jint defaultConfig = env->GetIntField(desiredDisplayConfigSpecs,
- gDesiredDisplayConfigSpecsClassInfo.defaultConfig);
+ size_t defaultMode =
+ static_cast<size_t>(env->GetIntField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.defaultMode));
jboolean allowGroupSwitching =
- env->GetBooleanField(desiredDisplayConfigSpecs,
- gDesiredDisplayConfigSpecsClassInfo.allowGroupSwitching);
+ env->GetBooleanField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching);
jfloat primaryRefreshRateMin =
- env->GetFloatField(desiredDisplayConfigSpecs,
- gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin);
+ env->GetFloatField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMin);
jfloat primaryRefreshRateMax =
- env->GetFloatField(desiredDisplayConfigSpecs,
- gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMax);
+ env->GetFloatField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMax);
jfloat appRequestRefreshRateMin =
- env->GetFloatField(desiredDisplayConfigSpecs,
- gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMin);
+ env->GetFloatField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMin);
jfloat appRequestRefreshRateMax =
- env->GetFloatField(desiredDisplayConfigSpecs,
- gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMax);
-
- size_t result = SurfaceComposerClient::setDesiredDisplayConfigSpecs(token, defaultConfig,
- allowGroupSwitching,
- primaryRefreshRateMin,
- primaryRefreshRateMax,
- appRequestRefreshRateMin,
- appRequestRefreshRateMax);
+ env->GetFloatField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMax);
+
+ size_t result = SurfaceComposerClient::setDesiredDisplayModeSpecs(token, defaultMode,
+ allowGroupSwitching,
+ primaryRefreshRateMin,
+ primaryRefreshRateMax,
+ appRequestRefreshRateMin,
+ appRequestRefreshRateMax);
return result == NO_ERROR ? JNI_TRUE : JNI_FALSE;
}
-static jobject nativeGetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jobject tokenObj) {
+static jobject nativeGetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == nullptr) return nullptr;
- int32_t defaultConfig;
+ size_t defaultMode;
bool allowGroupSwitching;
float primaryRefreshRateMin;
float primaryRefreshRateMax;
float appRequestRefreshRateMin;
float appRequestRefreshRateMax;
- if (SurfaceComposerClient::getDesiredDisplayConfigSpecs(token, &defaultConfig,
- &allowGroupSwitching,
- &primaryRefreshRateMin,
- &primaryRefreshRateMax,
- &appRequestRefreshRateMin,
- &appRequestRefreshRateMax) !=
- NO_ERROR) {
+ if (SurfaceComposerClient::getDesiredDisplayModeSpecs(token, &defaultMode, &allowGroupSwitching,
+ &primaryRefreshRateMin,
+ &primaryRefreshRateMax,
+ &appRequestRefreshRateMin,
+ &appRequestRefreshRateMax) != NO_ERROR) {
return nullptr;
}
- return env->NewObject(gDesiredDisplayConfigSpecsClassInfo.clazz,
- gDesiredDisplayConfigSpecsClassInfo.ctor, defaultConfig,
- allowGroupSwitching, primaryRefreshRateMin, primaryRefreshRateMax,
- appRequestRefreshRateMin, appRequestRefreshRateMax);
+ return env->NewObject(gDesiredDisplayModeSpecsClassInfo.clazz,
+ gDesiredDisplayModeSpecsClassInfo.ctor, defaultMode, allowGroupSwitching,
+ primaryRefreshRateMin, primaryRefreshRateMax, appRequestRefreshRateMin,
+ appRequestRefreshRateMax);
}
-static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) {
+static jint nativeGetActiveDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObj) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == NULL) return -1;
- return static_cast<jint>(SurfaceComposerClient::getActiveConfig(token));
+ return static_cast<jint>(SurfaceComposerClient::getActiveDisplayModeId(token));
}
static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) {
@@ -1784,16 +1780,16 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetDisplaySize },
{"nativeGetDisplayInfo", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayInfo;",
(void*)nativeGetDisplayInfo },
- {"nativeGetDisplayConfigs", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$DisplayConfig;",
- (void*)nativeGetDisplayConfigs },
- {"nativeGetActiveConfig", "(Landroid/os/IBinder;)I",
- (void*)nativeGetActiveConfig },
- {"nativeSetDesiredDisplayConfigSpecs",
- "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;)Z",
- (void*)nativeSetDesiredDisplayConfigSpecs },
- {"nativeGetDesiredDisplayConfigSpecs",
- "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;",
- (void*)nativeGetDesiredDisplayConfigSpecs },
+ {"nativeGetDisplayModes", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$DisplayMode;",
+ (void*)nativeGetDisplayModes },
+ {"nativeGetActiveDisplayMode", "(Landroid/os/IBinder;)I",
+ (void*)nativeGetActiveDisplayMode },
+ {"nativeSetDesiredDisplayModeSpecs",
+ "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;)Z",
+ (void*)nativeSetDesiredDisplayModeSpecs },
+ {"nativeGetDesiredDisplayModeSpecs",
+ "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;",
+ (void*)nativeGetDesiredDisplayModeSpecs },
{"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I",
(void*)nativeGetDisplayColorModes},
{"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;",
@@ -1902,19 +1898,19 @@ int register_android_view_SurfaceControl(JNIEnv* env)
GetFieldIDOrDie(env, infoClazz, "deviceProductInfo",
"Landroid/hardware/display/DeviceProductInfo;");
- jclass configClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayConfig");
- gDisplayConfigClassInfo.clazz = MakeGlobalRefOrDie(env, configClazz);
- gDisplayConfigClassInfo.ctor = GetMethodIDOrDie(env, configClazz, "<init>", "()V");
- gDisplayConfigClassInfo.width = GetFieldIDOrDie(env, configClazz, "width", "I");
- gDisplayConfigClassInfo.height = GetFieldIDOrDie(env, configClazz, "height", "I");
- gDisplayConfigClassInfo.xDpi = GetFieldIDOrDie(env, configClazz, "xDpi", "F");
- gDisplayConfigClassInfo.yDpi = GetFieldIDOrDie(env, configClazz, "yDpi", "F");
- gDisplayConfigClassInfo.refreshRate = GetFieldIDOrDie(env, configClazz, "refreshRate", "F");
- gDisplayConfigClassInfo.appVsyncOffsetNanos =
- GetFieldIDOrDie(env, configClazz, "appVsyncOffsetNanos", "J");
- gDisplayConfigClassInfo.presentationDeadlineNanos =
- GetFieldIDOrDie(env, configClazz, "presentationDeadlineNanos", "J");
- gDisplayConfigClassInfo.configGroup = GetFieldIDOrDie(env, configClazz, "configGroup", "I");
+ jclass modeClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayMode");
+ gDisplayModeClassInfo.clazz = MakeGlobalRefOrDie(env, modeClazz);
+ gDisplayModeClassInfo.ctor = GetMethodIDOrDie(env, modeClazz, "<init>", "()V");
+ gDisplayModeClassInfo.width = GetFieldIDOrDie(env, modeClazz, "width", "I");
+ gDisplayModeClassInfo.height = GetFieldIDOrDie(env, modeClazz, "height", "I");
+ gDisplayModeClassInfo.xDpi = GetFieldIDOrDie(env, modeClazz, "xDpi", "F");
+ gDisplayModeClassInfo.yDpi = GetFieldIDOrDie(env, modeClazz, "yDpi", "F");
+ gDisplayModeClassInfo.refreshRate = GetFieldIDOrDie(env, modeClazz, "refreshRate", "F");
+ gDisplayModeClassInfo.appVsyncOffsetNanos =
+ GetFieldIDOrDie(env, modeClazz, "appVsyncOffsetNanos", "J");
+ gDisplayModeClassInfo.presentationDeadlineNanos =
+ GetFieldIDOrDie(env, modeClazz, "presentationDeadlineNanos", "J");
+ gDisplayModeClassInfo.group = GetFieldIDOrDie(env, modeClazz, "group", "I");
jclass rectClazz = FindClassOrDie(env, "android/graphics/Rect");
gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClazz, "bottom", "I");
@@ -2005,24 +2001,23 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gDisplayPrimariesClassInfo.white = GetFieldIDOrDie(env, displayPrimariesClazz, "white",
"Landroid/view/SurfaceControl$CieXyz;");
- jclass desiredDisplayConfigSpecsClazz =
- FindClassOrDie(env, "android/view/SurfaceControl$DesiredDisplayConfigSpecs");
- gDesiredDisplayConfigSpecsClassInfo.clazz =
- MakeGlobalRefOrDie(env, desiredDisplayConfigSpecsClazz);
- gDesiredDisplayConfigSpecsClassInfo.ctor =
- GetMethodIDOrDie(env, gDesiredDisplayConfigSpecsClassInfo.clazz, "<init>", "(IZFFFF)V");
- gDesiredDisplayConfigSpecsClassInfo.defaultConfig =
- GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "defaultConfig", "I");
- gDesiredDisplayConfigSpecsClassInfo.allowGroupSwitching =
- GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "allowGroupSwitching", "Z");
- gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin =
- GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "primaryRefreshRateMin", "F");
- gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMax =
- GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "primaryRefreshRateMax", "F");
- gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMin =
- GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "appRequestRefreshRateMin", "F");
- gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMax =
- GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "appRequestRefreshRateMax", "F");
+ jclass DesiredDisplayModeSpecsClazz =
+ FindClassOrDie(env, "android/view/SurfaceControl$DesiredDisplayModeSpecs");
+ gDesiredDisplayModeSpecsClassInfo.clazz = MakeGlobalRefOrDie(env, DesiredDisplayModeSpecsClazz);
+ gDesiredDisplayModeSpecsClassInfo.ctor =
+ GetMethodIDOrDie(env, gDesiredDisplayModeSpecsClassInfo.clazz, "<init>", "(IZFFFF)V");
+ gDesiredDisplayModeSpecsClassInfo.defaultMode =
+ GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "defaultMode", "I");
+ gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching =
+ GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "allowGroupSwitching", "Z");
+ gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMin =
+ GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "primaryRefreshRateMin", "F");
+ gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMax =
+ GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "primaryRefreshRateMax", "F");
+ gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMin =
+ GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "appRequestRefreshRateMin", "F");
+ gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMax =
+ GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "appRequestRefreshRateMax", "F");
jclass captureArgsClazz = FindClassOrDie(env, "android/view/SurfaceControl$CaptureArgs");
gCaptureArgsClassInfo.pixelFormat = GetFieldIDOrDie(env, captureArgsClazz, "mPixelFormat", "I");
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 191f748d80bf..0aac07d17cdc 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -51,19 +51,12 @@ static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
client->decStrong((void*)nativeCreate);
}
-static void nativeKill(JNIEnv* env, jclass clazz, jlong ptr) {
- SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
- client->dispose();
-}
-
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "nativeCreate", "()J",
(void*)nativeCreate },
{ "nativeDestroy", "(J)V",
(void*)nativeDestroy },
- { "nativeKill", "(J)V",
- (void*)nativeKill }
};
int register_android_view_SurfaceSession(JNIEnv* env) {
diff --git a/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp b/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp
new file mode 100644
index 000000000000..e6a82f6d0cb5
--- /dev/null
+++ b/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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 "core_jni_helpers.h"
+
+#include <cputimeinstate.h>
+
+namespace android {
+
+static jboolean KernelCpuBpfTracking_isSupported(JNIEnv *, jobject) {
+ return android::bpf::isTrackingUidTimesSupported() ? JNI_TRUE : JNI_FALSE;
+}
+
+static const JNINativeMethod methods[] = {
+ {"isSupported", "()Z", (void *)KernelCpuBpfTracking_isSupported},
+};
+
+int register_com_android_internal_os_KernelCpuBpfTracking(JNIEnv *env) {
+ return RegisterMethodsOrDie(env, "com/android/internal/os/KernelCpuBpfTracking", methods,
+ NELEM(methods));
+}
+
+} // namespace android
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 97e77f81e4a2..17c8368ca080 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -2530,6 +2530,36 @@ static jboolean com_android_internal_os_Zygote_nativeSupportsTaggedPointers(JNIE
#endif
}
+static jint com_android_internal_os_Zygote_nativeCurrentTaggingLevel(JNIEnv* env, jclass) {
+#if defined(__aarch64__)
+ int level = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ if (level < 0) {
+ ALOGE("Failed to get memory tag level: %s", strerror(errno));
+ return 0;
+ } else if (!(level & PR_TAGGED_ADDR_ENABLE)) {
+ return 0;
+ }
+ // TBI is only possible on non-MTE hardware.
+ if (!mte_supported()) {
+ return MEMORY_TAG_LEVEL_TBI;
+ }
+
+ switch (level & PR_MTE_TCF_MASK) {
+ case PR_MTE_TCF_NONE:
+ return 0;
+ case PR_MTE_TCF_SYNC:
+ return MEMORY_TAG_LEVEL_SYNC;
+ case PR_MTE_TCF_ASYNC:
+ return MEMORY_TAG_LEVEL_ASYNC;
+ default:
+ ALOGE("Unknown memory tagging level: %i", level);
+ return 0;
+ }
+#else // defined(__aarch64__)
+ return 0;
+#endif // defined(__aarch64__)
+}
+
static const JNINativeMethod gMethods[] = {
{"nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/"
@@ -2569,6 +2599,8 @@ static const JNINativeMethod gMethods[] = {
(void*)com_android_internal_os_Zygote_nativeSupportsMemoryTagging},
{"nativeSupportsTaggedPointers", "()Z",
(void*)com_android_internal_os_Zygote_nativeSupportsTaggedPointers},
+ {"nativeCurrentTaggingLevel", "()I",
+ (void*)com_android_internal_os_Zygote_nativeCurrentTaggingLevel},
};
int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
index bb39ea810add..5c6116a09b77 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -114,4 +114,5 @@ message ApplicationInfoProto {
optional bool native_heap_zero_init = 21;
}
optional Detail detail = 17;
+ repeated string overlay_paths = 18;
}
diff --git a/core/proto/android/net/networkrequest.proto b/core/proto/android/net/networkrequest.proto
index 6794c8cd8acb..0041f199b448 100644
--- a/core/proto/android/net/networkrequest.proto
+++ b/core/proto/android/net/networkrequest.proto
@@ -63,6 +63,9 @@ message NetworkRequestProto {
// higher-scoring network will not go into the background immediately,
// but will linger and go into the background after the linger timeout.
TYPE_BACKGROUND_REQUEST = 5;
+ // Like TRACK_DEFAULT, but tracks the system default network, instead of
+ // the default network of the calling application.
+ TYPE_TRACK_SYSTEM_DEFAULT = 6;
}
// The type of the request. This is only used by the system and is always
// NONE elsewhere.
diff --git a/core/proto/android/server/vibratorservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto
index 9e42e9edfd27..aab054f4bf73 100644
--- a/core/proto/android/server/vibratorservice.proto
+++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto
@@ -15,7 +15,7 @@
*/
syntax = "proto2";
-package com.android.server;
+package com.android.server.vibrator;
option java_multiple_files = true;
@@ -51,12 +51,25 @@ message ComposedProto {
// A com.android.os.VibrationEffect object.
message VibrationEffectProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional OneShotProto oneshot = 3;
- optional WaveformProto waveform = 1;
- optional PrebakedProto prebaked = 2;
+ optional OneShotProto oneshot = 1;
+ optional WaveformProto waveform = 2;
+ optional PrebakedProto prebaked = 3;
optional ComposedProto composed = 4;
}
+message SyncVibrationEffectProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ repeated VibrationEffectProto effects = 1;
+ repeated int32 vibrator_ids = 2;
+}
+
+// A com.android.os.CombinedVibrationEffect object.
+message CombinedVibrationEffectProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ repeated SyncVibrationEffectProto effects = 1;
+ repeated int32 delays = 2;
+}
+
message VibrationAttributesProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional int32 usage = 1;
@@ -68,30 +81,31 @@ message VibrationAttributesProto {
message VibrationProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional int64 start_time = 1;
- optional int64 end_time = 4;
- optional VibrationEffectProto effect = 2;
- optional VibrationEffectProto original_effect = 3;
+ optional int64 end_time = 2;
+ optional CombinedVibrationEffectProto effect = 3;
+ optional CombinedVibrationEffectProto original_effect = 4;
optional VibrationAttributesProto attributes = 5;
optional int32 status = 6;
}
-// Next id: 17
-message VibratorServiceDumpProto {
+// Next id: 18
+message VibratorManagerServiceDumpProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional VibrationProto current_vibration = 1;
- optional bool is_vibrating = 2;
- optional VibrationProto current_external_vibration = 3;
- optional bool vibrator_under_external_control = 4;
- optional bool low_power_mode = 5;
- optional int32 haptic_feedback_intensity = 6;
- optional int32 haptic_feedback_default_intensity = 14;
- optional int32 notification_intensity = 7;
- optional int32 notification_default_intensity = 15;
- optional int32 ring_intensity = 8;
- optional int32 ring_default_intensity = 16;
- repeated VibrationProto previous_ring_vibrations = 9;
- repeated VibrationProto previous_notification_vibrations = 10;
- repeated VibrationProto previous_alarm_vibrations = 11;
- repeated VibrationProto previous_vibrations = 12;
- repeated VibrationProto previous_external_vibrations = 13;
+ repeated int32 vibrator_ids = 1;
+ optional VibrationProto current_vibration = 2;
+ optional bool is_vibrating = 3;
+ optional VibrationProto current_external_vibration = 4;
+ optional bool vibrator_under_external_control = 5;
+ optional bool low_power_mode = 6;
+ optional int32 haptic_feedback_intensity = 7;
+ optional int32 haptic_feedback_default_intensity = 8;
+ optional int32 notification_intensity = 9;
+ optional int32 notification_default_intensity = 10;
+ optional int32 ring_intensity = 11;
+ optional int32 ring_default_intensity = 12;
+ repeated VibrationProto previous_ring_vibrations = 13;
+ repeated VibrationProto previous_notification_vibrations = 14;
+ repeated VibrationProto previous_alarm_vibrations = 15;
+ repeated VibrationProto previous_vibrations = 16;
+ repeated VibrationProto previous_external_vibrations = 17;
} \ No newline at end of file
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 9ee5e5e18185..4cf9cfb7120a 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -14,6 +14,36 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["frameworks_base_core_res_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_core_res_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ "SPDX-license-identifier-GPL",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
android_app {
name: "framework-res",
sdk_version: "core_platform",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 53694c5b6e17..f3df6c2e3890 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -723,6 +723,9 @@
<!-- Made protected in S (was added in R) -->
<protected-broadcast android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" />
+ <!-- Added in S -->
+ <protected-broadcast android:name="android.intent.action.REBOOT_READY" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -1088,6 +1091,14 @@
android:description="@string/permdesc_accessImsCallService"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi @hide Allows an application to perform IMS Single Registration related actions.
+ Only granted if the application is a system app AND is in the Default SMS Role.
+ The permission is revoked when the app is taken out of the Default SMS Role.
+ <p>Protection level: internal|role
+ -->
+ <permission android:name="android.permission.PERFORM_IMS_SINGLE_REGISTRATION"
+ android:protectionLevel="internal|role" />
+
<!-- Allows an application to read the user's call log.
<p class="note"><strong>Note:</strong> If your app uses the
{@link #READ_CONTACTS} permission and <em>both</em> your <a
@@ -1338,11 +1349,13 @@
android:protectionLevel="dangerous|instant" />
<!-- ====================================================================== -->
- <!-- Permissions for accessing the UCE Service -->
+ <!-- Permissions for accessing the vendor UCE Service -->
<!-- ====================================================================== -->
<!-- @hide Allows an application to Access UCE-Presence.
<p>Protection level: signature|privileged
+ @deprecated Framework should no longer use this permission to access the vendor UCE service
+ using AIDL, it is instead implemented by RcsCapabilityExchangeImplBase
-->
<permission android:name="android.permission.ACCESS_UCE_PRESENCE_SERVICE"
android:permissionGroup="android.permission-group.PHONE"
@@ -1350,6 +1363,8 @@
<!-- @hide Allows an application to Access UCE-OPTIONS.
<p>Protection level: signature|privileged
+ @deprecated Framework should no longer use this permission to access the vendor UCE service
+ using AIDL, it is instead implemented by RcsCapabilityExchangeImplBase
-->
<permission android:name="android.permission.ACCESS_UCE_OPTIONS_SERVICE"
android:permissionGroup="android.permission-group.PHONE"
@@ -1428,6 +1443,15 @@
android:description="@string/permgroupdesc_sensors"
android:priority="800" />
+ <!-- Allows an app to access sensor data with a sampling rate greater than 200 Hz.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS"
+ android:permissionGroup="android.permission-group.SENSORS"
+ android:label="@string/permlab_highSamplingRateSensors"
+ android:description="@string/permdesc_highSamplingRateSensors"
+ android:protectionLevel="normal" />
+
<!-- Allows an application to access data from sensors that the user uses to
measure what is happening inside their body, such as heart rate.
<p>Protection level: dangerous -->
@@ -1900,6 +1924,12 @@
<permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi @hide Allows an application to manage an automotive device's application network
+ preference as it relates to OEM_PAID and OEM_PRIVATE capable networks.
+ <p>Not for use by third-party or privileged applications. -->
+ <permission android:name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE"
+ android:protectionLevel="signature" />
+
<!-- ======================================= -->
<!-- Permissions for short range, peripheral networks -->
<!-- ======================================= -->
@@ -2026,6 +2056,12 @@
<permission android:name="android.permission.ENABLE_TEST_HARNESS_MODE"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows access to ultra wideband device.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.UWB_PRIVILEGED"
+ android:protectionLevel="signature|privileged" />
+
<!-- ================================== -->
<!-- Permissions for accessing accounts -->
<!-- ================================== -->
@@ -2287,6 +2323,11 @@
<permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows read access to privileged network state in the device config.
+ @hide Used internally. -->
+ <permission android:name="android.permission.READ_NETWORK_DEVICE_CONFIG"
+ android:protectionLevel="signature|privileged" />
+
<!-- Allows to read device identifiers and use ICC based authentication like EAP-AKA.
Often required in authentication to access the carrier's server and manage services
of the subscriber.
@@ -2472,6 +2513,15 @@
<permission android:name="android.permission.BIND_GBA_SERVICE"
android:protectionLevel="signature" />
+ <!-- Required for an Application to access APIs related to RCS User Capability Exchange.
+ <p> This permission is only granted to system applications fulfilling the SMS, Dialer, and
+ Contacts app roles.
+ <p>Protection level: internal|role
+ @SystemApi
+ @hide -->
+ <permission android:name="android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE"
+ android:protectionLevel="internal|role" />
+
<!-- ================================== -->
<!-- Permissions for sdcard interaction -->
<!-- ================================== -->
@@ -2736,11 +2786,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|recents|appPredictor|pre23|development -->
+ <p>Protection level: signature|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|recents|appPredictor|pre23|development" />
+ android:protectionLevel="signature|appop|installer|appPredictor|pre23|development" />
<!-- @SystemApi @hide Allows an application to create windows using the type
{@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY},
@@ -3896,6 +3946,12 @@
<permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
android:protectionLevel="signature" />
+ <!-- Allows an app to use exact alarm scheduling APIs to perform timing
+ sensitive background work.
+ -->
+ <permission android:name="android.permission.SCHEDULE_EXACT_ALARM"
+ android:protectionLevel="normal|appop"/>
+
<!-- Allows an application to query tablet mode state and monitor changes
in it.
<p>Not for use by third-party applications.
@@ -4032,6 +4088,11 @@
<permission android:name="android.permission.MOVE_PACKAGE"
android:protectionLevel="signature|privileged" />
+ <!-- @TestApi Allows an application to keep uninstalled packages as apks.
+ @hide -->
+ <permission android:name="android.permission.KEEP_UNINSTALLED_PACKAGES"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to change whether an application component (other than its own) is
enabled or not.
<p>Not for use by third-party applications. -->
@@ -4391,6 +4452,12 @@
<permission android:name="android.permission.POWER_SAVER"
android:protectionLevel="signature|privileged" />
+ <!-- Allows providing the system with battery predictions.
+ Superseded by DEVICE_POWER permission. @hide @SystemApi
+ -->
+ <permission android:name="android.permission.BATTERY_PREDICTION"
+ android:protectionLevel="signature|privileged" />
+
<!-- Allows access to the PowerManager.userActivity function.
<p>Not for use by third-party applications. @hide @SystemApi -->
<permission android:name="android.permission.USER_ACTIVITY"
@@ -5472,11 +5539,11 @@
android:protectionLevel="signature" />
<!-- Must be required by a
- {@link android.service.screenshot.ScreenshotHasherService}
+ {@link android.service.displayhash.DisplayHasherService}
to ensure that only the system can bind to it.
@hide This is not a third-party API (intended for OEMs and system apps).
-->
- <permission android:name="android.permission.BIND_SCREENSHOT_HASHER_SERVICE"
+ <permission android:name="android.permission.BIND_DISPLAY_HASHER_SERVICE"
android:protectionLevel="signature" />
<!-- @hide @TestApi Allows an application to enable/disable toast rate limiting.
@@ -5485,6 +5552,27 @@
<permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING"
android:protectionLevel="signature" />
+ <!-- Allows managing the Game Mode
+ @hide Used internally. -->
+ <permission android:name="android.permission.MANAGE_GAME_MODE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows the holder to register callbacks to inform the RebootReadinessManager
+ when they are performing reboot-blocking work.
+ @hide -->
+ <permission android:name="android.permission.SIGNAL_REBOOT_READINESS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide Allows an application to get a People Tile preview for a given shortcut. -->
+ <permission android:name="android.permission.GET_PEOPLE_TILE_PREVIEW"
+ android:protectionLevel="signature|recents" />
+
+ <!-- @hide @SystemApi Allows an application to retrieve whether shortcut is backed by a
+ Conversation.
+ TODO(b/180412052): STOPSHIP: Define a role so it can be granted to Shell and AiAi. -->
+ <permission android:name="android.permission.READ_PEOPLE_DATA"
+ android:protectionLevel="signature|appPredictor|recents"/>
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/color/btn_leanback_color.xml b/core/res/res/color/btn_leanback_color.xml
new file mode 100644
index 000000000000..012df6069e9c
--- /dev/null
+++ b/core/res/res/color/btn_leanback_color.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.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"
+ android:color="@color/btn_leanback_focused" />
+ <item android:state_enabled="false"
+ android:color="@android:color/transparent" />
+ <item android:color="@color/btn_leanback_unfocused" />
+</selector> \ No newline at end of file
diff --git a/core/res/res/color/btn_leanback_text_color.xml b/core/res/res/color/btn_leanback_text_color.xml
new file mode 100644
index 000000000000..df0df948063a
--- /dev/null
+++ b/core/res/res/color/btn_leanback_text_color.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@color/btn_text_leanback_focused" android:state_focused="true"/>
+ <item android:color="@color/btn_text_leanback_unfocused"/>
+</selector>
diff --git a/core/res/res/drawable/btn_borderless_material.xml b/core/res/res/drawable/btn_borderless_material.xml
index 08e1060ebad3..1a0912ee47dc 100644
--- a/core/res/res/drawable/btn_borderless_material.xml
+++ b/core/res/res/drawable/btn_borderless_material.xml
@@ -15,7 +15,8 @@
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?attr/colorControlHighlight">
+ android:color="?attr/colorControlHighlight"
+ android:rippleStyle="?attr/rippleStyle">
<item android:id="@id/mask"
android:drawable="@drawable/btn_default_mtrl_shape" />
</ripple>
diff --git a/core/res/res/drawable/btn_colored_material.xml b/core/res/res/drawable/btn_colored_material.xml
index 7ba21e840a59..5274ef2f6dce 100644
--- a/core/res/res/drawable/btn_colored_material.xml
+++ b/core/res/res/drawable/btn_colored_material.xml
@@ -19,7 +19,8 @@
android:insetTop="@dimen/button_inset_vertical_material"
android:insetRight="@dimen/button_inset_horizontal_material"
android:insetBottom="@dimen/button_inset_vertical_material">
- <ripple android:color="?attr/colorControlHighlight">
+ <ripple android:color="?attr/colorControlHighlight"
+ android:rippleStyle="?attr/rippleStyle">
<item>
<shape android:shape="rectangle"
android:tint="@color/btn_colored_background_material">
diff --git a/core/res/res/drawable/btn_default_material.xml b/core/res/res/drawable/btn_default_material.xml
index ed2b5aacb236..6a9e62110ba4 100644
--- a/core/res/res/drawable/btn_default_material.xml
+++ b/core/res/res/drawable/btn_default_material.xml
@@ -15,6 +15,7 @@
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?attr/colorControlHighlight">
+ android:color="?attr/colorControlHighlight"
+ android:rippleStyle="?attr/rippleStyle">
<item android:drawable="@drawable/btn_default_mtrl_shape" />
</ripple>
diff --git a/core/res/res/drawable/btn_leanback.xml b/core/res/res/drawable/btn_leanback.xml
new file mode 100644
index 000000000000..9a63986b69d4
--- /dev/null
+++ b/core/res/res/drawable/btn_leanback.xml
@@ -0,0 +1,20 @@
+<?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" android:shape="rectangle">
+ <solid android:color="@color/btn_leanback_color"/>
+ <corners android:radius="@dimen/leanback_button_radius"/>
+</shape>
diff --git a/core/res/res/drawable/btn_notification_emphasized.xml b/core/res/res/drawable/btn_notification_emphasized.xml
index 1a574fe39e6e..ad680549aaa1 100644
--- a/core/res/res/drawable/btn_notification_emphasized.xml
+++ b/core/res/res/drawable/btn_notification_emphasized.xml
@@ -23,7 +23,7 @@
<ripple android:color="?attr/colorControlHighlight">
<item>
<shape android:shape="rectangle">
- <corners android:radius="?attr/buttonCornerRadius" />
+ <corners android:radius="@dimen/notification_action_button_radius" />
<padding android:left="@dimen/button_padding_horizontal_material"
android:top="@dimen/button_padding_vertical_material"
android:right="@dimen/button_padding_horizontal_material"
diff --git a/core/res/res/drawable/btn_toggle_material.xml b/core/res/res/drawable/btn_toggle_material.xml
index 8b19e4ae3561..7cee3820172b 100644
--- a/core/res/res/drawable/btn_toggle_material.xml
+++ b/core/res/res/drawable/btn_toggle_material.xml
@@ -21,7 +21,8 @@
android:insetBottom="@dimen/button_inset_vertical_material">
<layer-list android:paddingMode="stack">
<item>
- <ripple android:color="?attr/colorControlHighlight">
+ <ripple android:color="?attr/colorControlHighlight"
+ android:rippleStyle="?attr/rippleStyle">
<item>
<shape android:shape="rectangle"
android:tint="?attr/colorButtonNormal">
diff --git a/core/res/res/drawable/ic_call_answer.xml b/core/res/res/drawable/ic_call_answer.xml
new file mode 100644
index 000000000000..77c0ad1a585f
--- /dev/null
+++ b/core/res/res/drawable/ic_call_answer.xml
@@ -0,0 +1,34 @@
+<!--
+ 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="20"
+ android:viewportHeight="20"
+ android:tint="?android:attr/colorControlNormal"
+ android:autoMirrored="true">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6.0168,3.3333L6.5751,5.575L4.6668,7.4916C3.8751,5.7166 3.6001,4.1916
+ 3.4834,3.3333H6.0168ZM14.4251,13.375L16.6668,13.9416V16.5166C15.8084,16.4 14.2668,16.125
+ 12.4834,15.325L14.4251,13.375ZM6.3418,1.6666H2.5668C2.0918,1.6666 1.7001,2.0666
+ 1.7334,2.5416C2.4834,12.875 11.7668,18.275 17.5168,18.275C17.9668,18.275 18.3334,17.9
+ 18.3334,17.4416V13.6166C18.3334,13.0416 17.9418,12.5416
+ 17.3834,12.4083L14.5918,11.7083C14.2251,11.6166 13.7584,11.6833
+ 13.4084,12.0333L10.9251,14.5166C8.6751,13.1833 6.7918,11.3
+ 5.4668,9.0416L7.9168,6.5916C8.2251,6.2833 8.3501,5.8333
+ 8.2418,5.4083L7.5584,2.6166C7.4168,2.0583 6.9168,1.6666 6.3418,1.6666Z"/>
+</vector> \ No newline at end of file
diff --git a/core/res/res/drawable/ic_call_decline.xml b/core/res/res/drawable/ic_call_decline.xml
new file mode 100644
index 000000000000..a5ee8f4e6e72
--- /dev/null
+++ b/core/res/res/drawable/ic_call_decline.xml
@@ -0,0 +1,36 @@
+<!--
+ 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="20"
+ android:viewportHeight="20"
+ android:tint="?android:attr/colorControlNormal"
+ android:autoMirrored="true">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5.2834,9.3083V10.7833L4.1084,11.4916L3.1334,10.5166C3.8084,10.0416
+ 4.5251,9.6333 5.2834,9.3083ZM14.75,9.325C15.4917,9.65 16.2084,10.05
+ 16.875,10.525L15.925,11.475L14.75,10.7666V9.325ZM9.975,6.6666C6.8584,6.6666 3.725,7.7333
+ 1.1751,9.9416C0.8084,10.2583 0.9667,10.7166 1.1501,10.9L3.2917,13.0416C3.4917,13.2333
+ 3.7417,13.3333 4.0001,13.3333C4.175,13.3333 4.35,13.2833
+ 4.5084,13.1916L6.4667,12.0166C6.725,11.8583 6.95,11.5583 6.95,11.1666V8.3833C7.95,8.125
+ 8.975,8 10.0084,8C11.0417,8 12.075,8.1333 13.0834,8.3916V11.1416C13.0834,11.4916
+ 13.2667,11.8166 13.5667,11.9916L15.525,13.1666C15.6834,13.2583 15.8584,13.3083
+ 16.0334,13.3083C16.2917,13.3083 16.5417,13.2083
+ 16.7334,13.0166L18.85,10.9C19.1167,10.6333 19.1167,10.1916 18.825,9.9416C16.3334,7.7833
+ 13.1667,6.6666 9.975,6.6666Z"/>
+</vector>
diff --git a/core/res/res/layout/alert_dialog_button_bar_leanback.xml b/core/res/res/layout/alert_dialog_button_bar_leanback.xml
new file mode 100644
index 000000000000..ea94af662dcf
--- /dev/null
+++ b/core/res/res/layout/alert_dialog_button_bar_leanback.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.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/buttonPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbarAlwaysDrawVerticalTrack="true"
+ android:scrollIndicators="top|bottom"
+ android:fillViewport="true"
+ style="?attr/buttonBarStyle">
+ <com.android.internal.widget.ButtonBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layoutDirection="locale"
+ android:orientation="horizontal"
+ android:gravity="start">
+
+ <Button
+ android:id="@+id/button1"
+ style="?attr/buttonBarPositiveButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Button
+ android:id="@+id/button2"
+ style="?attr/buttonBarNegativeButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Space
+ android:id="@+id/spacer"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:visibility="invisible" />
+
+ <Button
+ android:id="@+id/button3"
+ style="?attr/buttonBarNeutralButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ </com.android.internal.widget.ButtonBarLayout>
+</ScrollView>
diff --git a/core/res/res/layout/alert_dialog_leanback.xml b/core/res/res/layout/alert_dialog_leanback.xml
index 848015c28f4a..091613fd9b56 100644
--- a/core/res/res/layout/alert_dialog_leanback.xml
+++ b/core/res/res/layout/alert_dialog_leanback.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 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,108 +15,70 @@
limitations under the License.
-->
-<LinearLayout
+<com.android.internal.widget.AlertDialogLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/parentPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@drawable/dialog_background_material"
- android:translationZ="@dimen/floating_window_z"
- android:layout_marginLeft="@dimen/leanback_alert_dialog_horizontal_margin"
- android:layout_marginTop="@dimen/leanback_alert_dialog_vertical_margin"
- android:layout_marginRight="@dimen/leanback_alert_dialog_horizontal_margin"
- android:layout_marginBottom="@dimen/leanback_alert_dialog_vertical_margin">
+ android:gravity="start|top"
+ android:orientation="vertical">
- <LinearLayout android:id="@+id/topPanel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <LinearLayout android:id="@+id/title_template"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="center_vertical|start"
- android:paddingStart="16dip"
- android:paddingEnd="16dip"
- android:paddingTop="16dip">
- <ImageView android:id="@+id/icon"
- android:layout_width="32dip"
- android:layout_height="32dip"
- android:layout_marginEnd="8dip"
- android:scaleType="fitCenter"
- android:src="@null" />
- <TextView android:id="@+id/alertTitle"
- style="?attr/windowTitleStyle"
- android:singleLine="true"
- android:ellipsize="end"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAlignment="viewStart" />
- </LinearLayout>
- <!-- If the client uses a customTitle, it will be added here. -->
- </LinearLayout>
+ <include layout="@layout/alert_dialog_title_material" />
- <LinearLayout android:id="@+id/contentPanel"
+ <FrameLayout
+ android:id="@+id/contentPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical"
- android:minHeight="64dp">
- <ScrollView android:id="@+id/scrollView"
+ android:minHeight="48dp">
+
+ <ScrollView
+ android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false">
- <TextView android:id="@+id/message"
- style="?attr/textAppearanceMedium"
+
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingStart="16dip"
- android:paddingEnd="16dip"
- android:paddingTop="16dip" />
+ android:orientation="vertical">
+
+ <Space
+ android:id="@+id/textSpacerNoTitle"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dialog_padding_top_material" />
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingEnd="?attr/dialogPreferredPadding"
+ android:paddingStart="?attr/dialogPreferredPadding"
+ style="@style/TextAppearance.Material.Subhead" />
+
+ <Space
+ android:id="@+id/textSpacerNoButtons"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dialog_padding_top_material" />
+ </LinearLayout>
</ScrollView>
- </LinearLayout>
+ </FrameLayout>
- <FrameLayout android:id="@+id/customPanel"
+ <FrameLayout
+ android:id="@+id/customPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:minHeight="64dp">
- <FrameLayout android:id="@+android:id/custom"
+ android:minHeight="48dp">
+
+ <FrameLayout
+ android:id="@+id/custom"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
- <LinearLayout android:id="@+id/buttonPanel"
+ <include
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- android:orientation="vertical"
- android:gravity="end"
- android:padding="16dip">
- <LinearLayout
- style="?attr/buttonBarStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layoutDirection="locale">
- <Button android:id="@+id/button3"
- style="?attr/buttonBarButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height" />
- <Button android:id="@+id/button2"
- style="?attr/buttonBarButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height" />
- <Button android:id="@+id/button1"
- style="?attr/buttonBarButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height" />
- </LinearLayout>
- </LinearLayout>
-</LinearLayout>
+ layout="@layout/alert_dialog_button_bar_leanback" />
+</com.android.internal.widget.AlertDialogLayout>
diff --git a/core/res/res/layout/notification_material_action_emphasized.xml b/core/res/res/layout/notification_material_action_emphasized.xml
index a6b7b380eaa4..cd1f1ab88c96 100644
--- a/core/res/res/layout/notification_material_action_emphasized.xml
+++ b/core/res/res/layout/notification_material_action_emphasized.xml
@@ -22,6 +22,7 @@
android:layout_height="match_parent"
android:layout_marginStart="12dp"
android:layout_weight="1"
+ android:drawablePadding="6dp"
android:gravity="center"
android:textColor="@color/notification_default_color"
android:singleLine="true"
diff --git a/core/res/res/layout/notification_template_conversation_header.xml b/core/res/res/layout/notification_template_conversation_header.xml
new file mode 100644
index 000000000000..302a388cf1b2
--- /dev/null
+++ b/core/res/res/layout/notification_template_conversation_header.xml
@@ -0,0 +1,179 @@
+<?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:id="@+id/conversation_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="16dp"
+ >
+
+ <TextView
+ android:id="@+id/conversation_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ android:textSize="16sp"
+ android:singleLine="true"
+ android:layout_weight="1"
+ />
+
+ <TextView
+ android:id="@+id/app_name_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
+ android:text="@string/notification_header_divider_symbol"
+ android:layout_gravity="center"
+ android:paddingTop="1sp"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <!-- App Name -->
+ <com.android.internal.widget.ObservableTextView
+ android:id="@+id/app_name_text"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
+ android:paddingTop="1sp"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <TextView
+ android:id="@+id/time_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
+ android:text="@string/notification_header_divider_symbol"
+ android:layout_gravity="center"
+ android:paddingTop="1sp"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <DateTimeView
+ android:id="@+id/time"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:paddingTop="1sp"
+ android:showRelative="true"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <ViewStub
+ android:id="@+id/chronometer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:layout="@layout/notification_template_part_chronometer"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/verification_icon"
+ android:layout_width="@dimen/notification_verification_icon_size"
+ android:layout_height="@dimen/notification_verification_icon_size"
+ android:layout_gravity="center"
+ android:layout_marginStart="4dp"
+ android:contentDescription="@string/notification_alerted_content_description"
+ android:paddingTop="2dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_notifications_alerted"
+ android:visibility="gone"
+ />
+
+ <TextView
+ android:id="@+id/verification_text"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
+ android:paddingTop="1sp"
+ android:showRelative="true"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <ImageButton
+ android:id="@+id/feedback"
+ android:layout_width="@dimen/notification_feedback_size"
+ android:layout_height="@dimen/notification_feedback_size"
+ android:layout_gravity="center"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:contentDescription="@string/notification_feedback_indicator"
+ android:paddingTop="2dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_feedback_indicator"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/phishing_alert"
+ android:layout_width="@dimen/notification_phishing_alert_size"
+ android:layout_height="@dimen/notification_phishing_alert_size"
+ android:layout_marginStart="4dp"
+ android:paddingTop="2dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_dialog_alert_material"
+ android:visibility="gone"
+ android:contentDescription="@string/notification_phishing_alert_content_description"
+ />
+
+
+ <ImageView
+ android:id="@+id/profile_badge"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
+ android:layout_gravity="center"
+ android:layout_marginStart="4dp"
+ android:paddingTop="2dp"
+ android:scaleType="fitCenter"
+ android:visibility="gone"
+ android:contentDescription="@string/notification_work_profile_content_description"
+ />
+
+ <ImageView
+ android:id="@+id/alerted_icon"
+ android:layout_width="@dimen/notification_alerted_size"
+ android:layout_height="@dimen/notification_alerted_size"
+ android:layout_gravity="center"
+ android:layout_marginStart="4dp"
+ android:contentDescription="@string/notification_alerted_content_description"
+ android:paddingTop="2dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_notifications_alerted"
+ android:visibility="gone"
+ />
+</LinearLayout>
diff --git a/core/res/res/layout/notification_template_conversation_icon_container.xml b/core/res/res/layout/notification_template_conversation_icon_container.xml
new file mode 100644
index 000000000000..e9ec7ce77deb
--- /dev/null
+++ b/core/res/res/layout/notification_template_conversation_icon_container.xml
@@ -0,0 +1,97 @@
+<?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:id="@+id/conversation_icon_container"
+ android:layout_width="@dimen/conversation_content_start"
+ android:layout_height="wrap_content"
+ android:gravity="start|top"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:importantForAccessibility="no"
+ >
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layout_gravity="top|center_horizontal"
+ >
+
+ <!-- Big icon: 52x52, 12dp padding left + top, 16dp padding right -->
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/conversation_icon"
+ android:layout_width="@dimen/conversation_avatar_size"
+ android:layout_height="@dimen/conversation_avatar_size"
+ android:scaleType="centerCrop"
+ android:importantForAccessibility="no"
+ />
+
+ <ViewStub
+ android:layout="@layout/conversation_face_pile_layout"
+ android:layout_width="@dimen/conversation_avatar_size"
+ android:layout_height="@dimen/conversation_avatar_size"
+ android:id="@+id/conversation_face_pile"
+ />
+
+ <FrameLayout
+ android:id="@+id/conversation_icon_badge"
+ android:layout_width="@dimen/conversation_icon_size_badged"
+ android:layout_height="@dimen/conversation_icon_size_badged"
+ android:layout_marginLeft="@dimen/conversation_badge_side_margin"
+ android:layout_marginTop="@dimen/conversation_badge_side_margin"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ >
+
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/conversation_icon_badge_bg"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:src="@drawable/conversation_badge_background"
+ android:forceHasOverlappingRendering="false"
+ android:scaleType="center"
+ />
+
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/icon"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="4dp"
+ android:layout_gravity="center"
+ android:forceHasOverlappingRendering="false"
+ />
+
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/conversation_icon_badge_ring"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:src="@drawable/conversation_badge_ring"
+ android:visibility="gone"
+ android:forceHasOverlappingRendering="false"
+ android:clipToPadding="false"
+ android:scaleType="center"
+ />
+ </FrameLayout>
+ </FrameLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 88998f2167a8..62114630a328 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -33,6 +33,7 @@
android:layout_gravity="center_vertical|start"
android:layout_marginStart="@dimen/notification_left_icon_start"
android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:visibility="gone"
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 41be36bc2b04..d79cb74a3d53 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -30,6 +30,7 @@
android:layout_gravity="center_vertical|start"
android:layout_marginStart="@dimen/notification_left_icon_start"
android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:visibility="gone"
@@ -54,6 +55,7 @@
android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
android:layout_marginEnd="@dimen/notification_header_expand_icon_size"
android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
/>
diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml
index 25d396f94b2c..e1b7bc4d7bca 100644
--- a/core/res/res/layout/notification_template_material_big_picture.xml
+++ b/core/res/res/layout/notification_template_material_big_picture.xml
@@ -72,6 +72,7 @@
android:layout_marginStart="@dimen/notification_content_margin_start"
android:layout_marginEnd="@dimen/notification_content_margin_end"
android:background="@drawable/notification_big_picture_outline"
+ android:clipToOutline="true"
android:scaleType="centerCrop"
/>
diff --git a/core/res/res/layout/notification_template_material_call.xml b/core/res/res/layout/notification_template_material_call.xml
new file mode 100644
index 000000000000..471d874c59f5
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_call.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
+ -->
+<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:orientation="vertical"
+ 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_action_list_height"
+ android:orientation="vertical"
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="top"
+ 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: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
+ 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>
+
+ <!-- TODO(b/179178086): remove padding from main column when this is visible -->
+ <com.android.internal.widget.NotificationExpandButton
+ android:id="@+id/expand_button"
+ android:layout_width="@dimen/notification_header_expand_icon_size"
+ android:layout_height="@dimen/notification_header_expand_icon_size"
+ android:layout_gravity="top|end"
+ android:contentDescription="@string/expand_button_content_description_collapsed"
+ android:paddingTop="@dimen/notification_expand_button_padding_top"
+ android:scaleType="center"
+ android:visibility="gone"
+ />
+
+ </LinearLayout>
+
+ <include layout="@layout/notification_material_action_list" />
+
+ </LinearLayout>
+
+</com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index f9364d565f3b..f3aa54066c92 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -24,82 +24,7 @@
android:theme="@style/Theme.DeviceDefault.Notification"
>
- <FrameLayout
- android:id="@+id/conversation_icon_container"
- android:layout_width="@dimen/conversation_content_start"
- android:layout_height="wrap_content"
- android:gravity="start|top"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:paddingTop="12dp"
- android:paddingBottom="12dp"
- android:importantForAccessibility="no"
- >
-
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_gravity="top|center_horizontal"
- >
-
- <!-- Big icon: 52x52, 12dp padding left + top, 16dp padding right -->
- <com.android.internal.widget.CachingIconView
- android:id="@+id/conversation_icon"
- android:layout_width="@dimen/conversation_avatar_size"
- android:layout_height="@dimen/conversation_avatar_size"
- android:scaleType="centerCrop"
- android:importantForAccessibility="no"
- />
-
- <ViewStub
- android:layout="@layout/conversation_face_pile_layout"
- android:layout_width="@dimen/conversation_avatar_size"
- android:layout_height="@dimen/conversation_avatar_size"
- android:id="@+id/conversation_face_pile"
- />
-
- <FrameLayout
- android:id="@+id/conversation_icon_badge"
- android:layout_width="@dimen/conversation_icon_size_badged"
- android:layout_height="@dimen/conversation_icon_size_badged"
- android:layout_marginLeft="@dimen/conversation_badge_side_margin"
- android:layout_marginTop="@dimen/conversation_badge_side_margin"
- android:clipChildren="false"
- android:clipToPadding="false"
- >
- <com.android.internal.widget.CachingIconView
- android:id="@+id/conversation_icon_badge_bg"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:src="@drawable/conversation_badge_background"
- android:forceHasOverlappingRendering="false"
- android:scaleType="center"
- />
- <com.android.internal.widget.CachingIconView
- android:id="@+id/icon"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_margin="4dp"
- android:layout_gravity="center"
- android:forceHasOverlappingRendering="false"
- />
- <com.android.internal.widget.CachingIconView
- android:id="@+id/conversation_icon_badge_ring"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:src="@drawable/conversation_badge_ring"
- android:visibility="gone"
- android:forceHasOverlappingRendering="false"
- android:clipToPadding="false"
- android:scaleType="center"
- />
- </FrameLayout>
- </FrameLayout>
- </FrameLayout>
+ <include layout="@layout/notification_template_conversation_icon_container" />
<!-- Wraps entire "expandable" notification -->
<com.android.internal.widget.RemeasuringLinearLayout
@@ -132,161 +57,14 @@
<!-- Use layout_marginStart instead of paddingStart to work around strange
measurement behavior on lower display densities. -->
- <LinearLayout
- android:id="@+id/conversation_header"
+ <include
+ layout="@layout/notification_template_conversation_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:paddingTop="16dp"
android:layout_marginBottom="2dp"
android:layout_marginStart="@dimen/conversation_content_start"
- >
- <TextView
- android:id="@+id/conversation_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
- android:textSize="16sp"
- android:singleLine="true"
- android:layout_weight="1"
- />
-
- <TextView
- android:id="@+id/app_name_divider"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
- android:text="@string/notification_header_divider_symbol"
- android:layout_gravity="center"
- android:paddingTop="1sp"
- android:singleLine="true"
- android:visibility="gone"
/>
- <!-- App Name -->
- <com.android.internal.widget.ObservableTextView
- android:id="@+id/app_name_text"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
- android:paddingTop="1sp"
- android:singleLine="true"
- android:visibility="gone"
- />
-
- <TextView
- android:id="@+id/time_divider"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
- android:text="@string/notification_header_divider_symbol"
- android:layout_gravity="center"
- android:paddingTop="1sp"
- android:singleLine="true"
- android:visibility="gone"
- />
-
- <DateTimeView
- android:id="@+id/time"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:paddingTop="1sp"
- android:showRelative="true"
- android:singleLine="true"
- android:visibility="gone"
- />
-
- <ImageButton
- android:id="@+id/feedback"
- android:layout_width="@dimen/notification_feedback_size"
- android:layout_height="@dimen/notification_feedback_size"
- android:layout_gravity="center"
- android:layout_marginStart="@dimen/notification_header_separating_margin"
- android:background="?android:selectableItemBackgroundBorderless"
- android:contentDescription="@string/notification_feedback_indicator"
- android:paddingTop="2dp"
- android:scaleType="fitCenter"
- android:src="@drawable/ic_feedback_indicator"
- android:visibility="gone"
- />
-
- <ImageView
- android:id="@+id/profile_badge"
- android:layout_width="@dimen/notification_badge_size"
- android:layout_height="@dimen/notification_badge_size"
- android:layout_gravity="center"
- android:layout_marginStart="4dp"
- android:paddingTop="2dp"
- android:scaleType="fitCenter"
- android:visibility="gone"
- android:contentDescription="@string/notification_work_profile_content_description"
- />
-
- <ImageView
- android:id="@+id/alerted_icon"
- android:layout_width="@dimen/notification_alerted_size"
- android:layout_height="@dimen/notification_alerted_size"
- android:layout_gravity="center"
- android:layout_marginStart="4dp"
- android:contentDescription="@string/notification_alerted_content_description"
- android:paddingTop="2dp"
- android:scaleType="fitCenter"
- android:src="@drawable/ic_notifications_alerted"
- android:visibility="gone"
- />
-
- <LinearLayout
- android:id="@+id/app_ops"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:paddingTop="3dp"
- android:layout_marginStart="2dp"
- android:background="?android:selectableItemBackgroundBorderless"
- android:orientation="horizontal" >
- <ImageView
- android:layout_marginStart="4dp"
- android:id="@+id/camera"
- android:layout_width="?attr/notificationHeaderIconSize"
- android:layout_height="?attr/notificationHeaderIconSize"
- android:src="@drawable/ic_camera"
- android:visibility="gone"
- android:focusable="false"
- android:contentDescription="@string/notification_appops_camera_active"
- />
- <ImageView
- android:id="@+id/mic"
- android:layout_width="?attr/notificationHeaderIconSize"
- android:layout_height="?attr/notificationHeaderIconSize"
- android:src="@drawable/ic_mic"
- android:layout_marginStart="4dp"
- android:visibility="gone"
- android:focusable="false"
- android:contentDescription="@string/notification_appops_microphone_active"
- />
- <ImageView
- android:id="@+id/overlay"
- android:layout_width="?attr/notificationHeaderIconSize"
- android:layout_height="?attr/notificationHeaderIconSize"
- android:src="@drawable/ic_alert_window_layer"
- android:layout_marginStart="4dp"
- android:visibility="gone"
- android:focusable="false"
- android:contentDescription="@string/notification_appops_overlay_active"
- />
- </LinearLayout>
- </LinearLayout>
-
<!-- Messages -->
<com.android.internal.widget.MessagingLinearLayout
android:id="@+id/notification_messaging"
diff --git a/core/res/res/layout/notification_template_part_chronometer.xml b/core/res/res/layout/notification_template_part_chronometer.xml
index c5ffbeadc525..e4ebc76c113a 100644
--- a/core/res/res/layout/notification_template_part_chronometer.xml
+++ b/core/res/res/layout/notification_template_part_chronometer.xml
@@ -15,7 +15,7 @@
-->
<Chronometer android:id="@+id/chronometer" xmlns:android="http://schemas.android.com/apk/res/android"
- android:textAppearance="@style/TextAppearance.Material.Notification.Time"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
diff --git a/core/res/res/layout/notification_template_right_icon.xml b/core/res/res/layout/notification_template_right_icon.xml
index d22d4c20dad2..f163ed5f955a 100644
--- a/core/res/res/layout/notification_template_right_icon.xml
+++ b/core/res/res/layout/notification_template_right_icon.xml
@@ -22,6 +22,7 @@
android:layout_marginEnd="@dimen/notification_header_expand_icon_size"
android:layout_marginTop="@dimen/notification_right_icon_big_margin_top"
android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
/>
diff --git a/core/res/res/layout/notification_top_line_views.xml b/core/res/res/layout/notification_top_line_views.xml
index 7cda03f8fc4f..88bcc4dc9e39 100644
--- a/core/res/res/layout/notification_top_line_views.xml
+++ b/core/res/res/layout/notification_top_line_views.xml
@@ -26,7 +26,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
android:singleLine="true"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:visibility="?attr/notificationHeaderAppNameVisibility"
/>
@@ -34,7 +34,7 @@
android:id="@+id/header_text_secondary_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:layout_marginStart="@dimen/notification_header_separating_margin"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
android:text="@string/notification_header_divider_symbol"
@@ -45,7 +45,7 @@
android:id="@+id/header_text_secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:layout_marginStart="@dimen/notification_header_separating_margin"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
android:visibility="gone"
@@ -56,7 +56,7 @@
android:id="@+id/header_text_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:layout_marginStart="@dimen/notification_header_separating_margin"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
android:text="@string/notification_header_divider_symbol"
@@ -67,7 +67,7 @@
android:id="@+id/header_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:layout_marginStart="@dimen/notification_header_separating_margin"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
android:visibility="gone"
@@ -78,7 +78,7 @@
android:id="@+id/time_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:layout_marginStart="@dimen/notification_header_separating_margin"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
android:text="@string/notification_header_divider_symbol"
@@ -123,6 +123,18 @@
/>
<ImageView
+ android:id="@+id/phishing_alert"
+ android:layout_width="@dimen/notification_phishing_alert_size"
+ android:layout_height="@dimen/notification_phishing_alert_size"
+ android:layout_marginStart="4dp"
+ android:baseline="10dp"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_dialog_alert_material"
+ android:visibility="gone"
+ android:contentDescription="@string/notification_phishing_alert_content_description"
+ />
+
+ <ImageView
android:id="@+id/profile_badge"
android:layout_width="@dimen/notification_badge_size"
android:layout_height="@dimen/notification_badge_size"
diff --git a/core/res/res/layout/splash_screen_view.xml b/core/res/res/layout/splash_screen_view.xml
new file mode 100644
index 000000000000..513da5e431e5
--- /dev/null
+++ b/core/res/res/layout/splash_screen_view.xml
@@ -0,0 +1,34 @@
+<?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.
+ -->
+<android.window.SplashScreenView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="vertical">
+
+ <View android:id="@+id/splashscreen_icon_view"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center"/>
+
+ <View android:id="@+id/splashscreen_branding_view"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_marginBottom="60dp"/>
+
+</android.window.SplashScreenView> \ No newline at end of file
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 55eaaf648f84..d7474d050399 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Gebruik jou vingerafdruk om voort te gaan"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Vingerafdrukikoon"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Laat die program toe om Moenie Steur Nie-opstelling te lees en skryf."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"begin kyk van toestemminggebruik"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Laat die houer toe om die toestemminggebruik vir \'n program te begin. Behoort nooit vir normale programme nodig te wees nie."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Stel wagwoordreëls"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Beheer die lengte en die karakters wat in skermslotwagwoorde en -PIN\'e toegelaat word."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor pogings om skerm te ontsluit"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gebruik kortpad"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Kleuromkering"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Kleurkorreksie"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Verminder helderheid"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aangeskakel."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> is afgeskakel"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Druk en hou albei volumesleutels drie sekondes lank om <xliff:g id="SERVICE_NAME">%1$s</xliff:g> te gebruik"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maksimeer"</string>
<string name="close_button_text" msgid="10603510034455258">"Maak toe"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Antwoord"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Wys af"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Lui af"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Inkomende oproep"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Oproep aan die gang"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Keur tans \'n inkomende oproep"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> gekies</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> gekies</item>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 316122669352..8283584758cc 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ለመቀጠል የእርስዎን የጣት አሻራ ይጠቀሙ"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"የጣት አሻራ አዶ"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"መተግበሪያው የአትረብሽ ውቅረትን እንዲያነብብ እና እንዲጸፍ ይፈቅዳል።"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"የእይታ ፈቃድ መጠቀምን መጀመር"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ያዢው ለአንድ መተግበሪያ የፈቃድ አጠቃቀሙን እንዲያስጀምር ያስችለዋል። ለመደበኛ መተግበሪያዎች በጭራሽ ሊያስፈልግ አይገባም።"</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"የይለፍ ቃል ደንቦች አዘጋጅ"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"በማያ ገጽ መቆለፊያ የይለፍ ቃሎች እና ፒኖች ውስጥ የሚፈቀዱ ቁምፊዎችን እና ርዝመታቸውን ተቆጣጠር።"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"የማሳያ-ክፈት ሙከራዎችን ክትትል ያድርጉባቸው"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"አቋራጭ ይጠቀሙ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ተቃራኒ ቀለም"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"የቀለም ማስተካከያ"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"ብሩህነትን ይቀንሱ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"የድምፅ ቁልፎችን ይዟል። <xliff:g id="SERVICE_NAME">%1$s</xliff:g> በርቷል።"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"የድምፅ ቁልፎችን ይዟል። <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ጠፍተዋል።"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ን ለመጠቀም ለሦስት ሰከንዶች ሁለቱንም የድምፅ ቁልፎች ተጭነው ይያዙ"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"አስፋ"</string>
<string name="close_button_text" msgid="10603510034455258">"ዝጋ"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>፦ <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"መልስ"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"አትቀበል"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ስልኩን ዝጋ"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"ገቢ ጥሪ"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"እየተካሄደ ያለ ጥሪ"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"ገቢ ጥሪ ማጣራት"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ተመርጧል</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ተመርጠዋል</item>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index a2d3671d667c..d0dd5fb52ecf 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -152,8 +152,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"‏Wi-Fi فقط"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> الاتصال الاحتياطي"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: لم تتم إعادة التوجيه"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> بعد <xliff:g id="TIME_DELAY">{2}</xliff:g> ثانية"</string>
@@ -592,6 +591,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"يمكنك استخدام بصمة الإصبع للمتابعة."</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"رمز بصمة الإصبع"</string>
@@ -697,6 +697,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"للسماح للتطبيق بقراءة إعداد \"عدم الإزعاج\" وكتابتها."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"بدء استخدام إذن العرض"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"للسماح للمالك ببدء استخدام الإذن لأحد التطبيقات. ولن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"تعيين قواعد كلمة المرور"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"للتحكم في الطول والأحرف المسموح بها في كلمات المرور وأرقام التعريف الشخصي في قفل الشاشة."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"مراقبة محاولات فتح قفل الشاشة"</string>
@@ -1753,6 +1757,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"استخدام الاختصار"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"قلب الألوان"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"تصحيح الألوان"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"تقليل السطوع"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم تفعيل <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم إيقاف <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"اضغط مع الاستمرار على مفتاحي مستوى الصوت لمدة 3 ثوانٍ لاستخدام <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
@@ -2005,6 +2010,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"تكبير"</string>
<string name="close_button_text" msgid="10603510034455258">"إغلاق"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"ردّ"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"رفض"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"قطع الاتصال"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"مكالمة واردة"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"مكالمة جارية"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"رصد مكالمة واردة"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="zero">تم اختيار <xliff:g id="COUNT_1">%1$d</xliff:g> عنصر</item>
<item quantity="two">تم اختيار عنصرين (<xliff:g id="COUNT_1">%1$d</xliff:g>)</item>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 0a9ef97c2ad8..f6fc09f8d5e7 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"কোৱল ৱাই-ফাই"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> বেকআপ কলিং"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ফৰৱাৰ্ড কৰা নহ\'ল"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> ছেকেণ্ডৰ পাছত"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"অব্যাহত ৰাখিবলৈ আপোনাৰ ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ফিংগাৰপ্ৰিণ্ট আইকন"</string>
@@ -685,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"অসুবিধা নিদিবৰ কনফিগাৰেশ্বনক পঢ়িবলৈ আৰু সালসলনি কৰিবলৈ এপটোক অনুমতি দিয়ে।"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"চোৱাৰ অনুমতিৰ ব্যৱহাৰ আৰম্ভ কৰক"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ধাৰকক কোনো এপৰ বাবে অনুমতিৰ ব্যৱহাৰ আৰম্ভ কৰিবলৈ দিয়ে। সাধাৰণ এপ্‌সমূহৰ বাবে কেতিয়াও প্ৰয়োজন হ’ব নালাগে।"</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"পাছৱর্ডৰ নিয়ম ছেট কৰক"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"স্ক্ৰীণ লক পাছৱৰ্ড আৰু পিনৰ দৈর্ঘ্য আৰু কি কি আখৰ ব্যৱহাৰ কৰিব পাৰে তাক নিয়ন্ত্ৰণ কৰক।"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"স্ক্ৰীণ আনলক কৰা প্ৰয়াসবোৰ পৰ্যবেক্ষণ কৰিব পাৰে"</string>
@@ -1665,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"শ্বৰ্টকাট ব্যৱহাৰ কৰক"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ৰং বিপৰীতকৰণ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ৰং শুধৰণী"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"উজ্জ্বলতা কমাওক"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ভলিউম কীসমূহ ধৰি ৰাখক। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> অন কৰা হ\'ল।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ভলিউম কী ধৰি ৰাখিছিল। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> অফ কৰা হ\'ল।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ব্যৱহাৰ কৰিবলৈ দুয়োটা ভলিউম বুটাম তিনি ছেকেণ্ডৰ বাবে হেঁচি ৰাখক"</string>
@@ -1881,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"সৰ্বাধিক মাত্ৰালৈ বঢ়াওক"</string>
<string name="close_button_text" msgid="10603510034455258">"বন্ধ কৰক"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"উত্তৰ দিয়ক"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"প্ৰত্যাখ্যান কৰক"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"কল কাটি দিয়ক"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"অন্তৰ্গামী কল"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"চলি থকা কল"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"এটা অন্তৰ্গামী কলৰ পৰীক্ষা কৰি থকা হৈছে"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টা বাছনি কৰা হ’ল</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টা বাছনি কৰা হ’ল</item>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index e91e2b740543..58d225d4d854 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -325,7 +325,7 @@
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"Həyati əlamətlər haqqında sensor dataya daxil olun"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Pəncərənin məzmununu əldə edin"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Əlaqədə olduğunuz pəncərənin məzmununu nəzərdən keçirin."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Toxunaraq Kəşf et funksiyasını yandırın"</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Toxunuşla öyrənmə funksiyasını aktiv edin"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Tıklanan hissələr səsləndiriləcək və ekran jestlərlə idarə oluna biləcək."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Yazdığınız mətni izləyin"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Kredit kartı nömrələri və parollar kimi şəxsi məlumatlar daxildir."</string>
@@ -579,6 +579,7 @@
<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>
+ <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>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Barmaq izi ikonası"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Tətbiqə \"Narahat Etməyin\" konfiqurasiyasını oxumağa və yazmağa icazə verin."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Baxış icazəsinin istifadəsinə başlayın"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Sahibinə tətbiqin icazədən istifadəsinə başlamağa imkan verir. Adi tətbiqlər üçün heç vaxt tələb edilmir."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Parol qaydalarını təyin edin"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Ekran kilidinin parolu və PINlərində icazə verilən uzunluq və simvollara nəzarət edin."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekranı kiliddən çıxarmaq üçün edilən cəhdlərə nəzarət edin"</string>
@@ -1629,7 +1634,7 @@
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"Android TV cihazını kiliddən çıxarmaq üçün <xliff:g id="NUMBER">%d</xliff:g> dəfə yanlış cəhd etdiniz. Android TV cihazınız defolt fabrik dəyərlərinə sıfırlanacaq."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"Siz telefonun kilidini açmaq üçün <xliff:g id="NUMBER">%d</xliff:g> yanlış cəhd etmisiniz. Telefon artıq defolt zavod halına sıfırlanacaq."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"Siz kilidi açmaq üçün şablonu <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə səhv çəkdiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> daha uğursuz cəhddən sonra planşetinizin kilidini e-poçt hesabınızla açmaq tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə ərzində bir daha yoxlayın."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Kiliddən çıxarma modelini <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış çəkdiniz. Daha <xliff:g id="NUMBER_1">%2$d</xliff:g> yanlış cəhddən sonra Android TV cihazını e-poçt hesabınızla kiliddən çıxarmağınız tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə sonra yenidən cəhd edin."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Kiliddən çıxarma modelini <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış çəkdiniz. Daha <xliff:g id="NUMBER_1">%2$d</xliff:g> yanlış cəhddən sonra Android TV cihazını e-poçt hesabınızla kiliddən çıxarmağınız tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə sonra cəhd edin."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Siz artıq modeli <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış daxil etmisiniz.<xliff:g id="NUMBER_1">%2$d</xliff:g> dəfə də yanlış daxil etsəniz, telefonun kilidinin açılması üçün elektron poçt ünvanınız tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə ərzində yenidən cəhd edin."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" - "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Yığışdır"</string>
@@ -1637,10 +1642,10 @@
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Əlçatımlılıq Qısayolu istifadə edilsin?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Qısayol aktiv olduqda, hər iki səs düyməsinə 3 saniyə basıb saxlamaqla əlçatımlılıq funksiyası başladılacaq."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Əlçatımlılıq funksiyaları üçün qısayol aktiv edilsin?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hər iki səs səviyyəsi düyməsinə bir neçə saniyə basıb saxladıqda əlçatımlılıq funksiyaları aktiv olur. Bu, cihazınızın işləmə qaydasını dəyişə bilər.\n\nCari funksiyalar:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAyarlar və Əlçatımlılıq bölməsində seçilmiş funksiyaları dəyişə bilərsiniz."</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hər iki səs səviyyəsi düyməsinə bir neçə saniyə basıb saxladıqda əlçatımlılıq funksiyaları aktiv olur. Cihazınızın işləmə qaydasını dəyişə bilər.\n\nCari funksiyalar:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAyarlar və Əlçatımlılıq bölməsində seçilmiş funksiyaları dəyişə bilərsiniz."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"<xliff:g id="SERVICE">%1$s</xliff:g> qısayolu aktiv edilsin?"</string>
- <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hər iki səs səviyyəsi düyməsinə bir neçə saniyə basıb saxladıqda əlçatımlılıq funksiyası olan <xliff:g id="SERVICE">%1$s</xliff:g> aktiv olur. Bu, cihazınızın işləmə qaydasını dəyişə bilər.\n\nAyarlar və Əlçatımlılıq bölməsində bu qısayolu başqa bir funksiyata dəyişə bilərsiniz."</string>
+ <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hər iki səs səviyyəsi düyməsinə bir neçə saniyə basıb saxladıqda əlçatımlılıq funksiyası olan <xliff:g id="SERVICE">%1$s</xliff:g> aktiv olur. Cihazınızın işləmə qaydasını dəyişə bilər.\n\nAyarlar və Əlçatımlılıq bölməsində bu qısayolu başqa bir funksiyaya dəyişə bilərsiniz."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktiv edin"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Aktiv etməyin"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"AKTİV"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Qısayol İstifadə edin"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Rəng İnversiyası"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Rəng korreksiyası"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Parlaqlığı Azaldın"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Səs səviyyəsi düymələrinə basıb saxlayın. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktiv edildi."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Səs səviyyəsi düymələrinə basılaraq saxlanıb. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> deaktiv edilib."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> istifadə etmək üçün hər iki səs düyməsini üç saniyə basıb saxlayın"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Böyüdün"</string>
<string name="close_button_text" msgid="10603510034455258">"Qapadın"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Cavab verin"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"İmtina edin"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Dəstəyi asın"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Gələn zəng"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Davam edən zəng"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Gələn zəng göstərilir"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> seçilib</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> seçilib</item>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index a490a6ce2b7d..1569bd13797c 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -582,6 +582,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Nastavite pomoću otiska prsta"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona otiska prsta"</string>
@@ -687,6 +688,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Dozvoljava aplikaciji da čita i upisuje konfiguraciju podešavanja Ne uznemiravaj."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"početak korišćenja dozvole za pregled"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dozvoljava vlasniku da započne korišćenje dozvole za aplikaciju. Nikada ne bi trebalo da bude potrebna za uobičajene aplikacije."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Podešavanje pravila za lozinku"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontroliše dužinu i znakove dozvoljene u lozinkama i PIN-ovima za zaključavanje ekrana."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Nadgledajte pokušaje otključavanja ekrana"</string>
@@ -1686,6 +1691,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Koristi prečicu"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcija boja"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Smanjite osvetljenost"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite i zadržite oba tastera za jačinu zvuka tri sekunde da biste koristili <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1911,6 +1917,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Uvećaj"</string>
<string name="close_button_text" msgid="10603510034455258">"Zatvori"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Odgovori"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Odbij"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Prekini vezu"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Dolazni poziv"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Poziv je u toku"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Proverava se dolazni poziv"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one">Izabrana je <xliff:g id="COUNT_1">%1$d</xliff:g> stavka</item>
<item quantity="few">Izabrane su <xliff:g id="COUNT_1">%1$d</xliff:g> stavke</item>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 80f67d07a0d2..f1f7e0cc2313 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -150,8 +150,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Толькі Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Рэзервовае капіраванне выклікаў праз аператара \"<xliff:g id="SPN">%s</xliff:g>\""</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не пераадрасоўваецца"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> праз <xliff:g id="TIME_DELAY">{2}</xliff:g> с."</string>
@@ -586,6 +585,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Каб працягнуць, выкарыстоўвайце свой адбітак пальца"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Значок адбіткаў пальцаў"</string>
@@ -691,6 +691,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дазваляе праграме чытаць і выконваць запіс у канфігурацыю рэжыму «Не турбаваць»."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"запусціць выкарыстанне дазволаў на прагляд"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дазваляе трымальніку запусціць выкарыстанне дазволаў праграмай. Не патрэбна для звычайных праграм."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Устанавіць правілы паролю"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Кіраваць даўжынёй і сімваламі, дазволенымі пры ўводзе пароляў і PIN-кодаў блакіроўкі экрана."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Сачыць за спробамі разблакіроўкі экрана"</string>
@@ -1709,6 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Выкарыстоўваць камбінацыю хуткага доступу"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Інверсія колеру"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Карэкцыя колеру"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Паменшыць яркасць"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Клавішы гучнасці ўтрымліваліся націснутымі. Уключана служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Клавішы гучнасці ўтрымліваліся націснутымі. Служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" выключана."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Каб карыстацца сэрвісам \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\", націсніце і ўтрымлівайце на працягу трох секунд абедзве клавішы гучнасці"</string>
@@ -1943,6 +1948,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Разгарнуць"</string>
<string name="close_button_text" msgid="10603510034455258">"Закрыць"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Адказаць"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Адхіліць"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Завяршыць"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Уваходны выклік"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Бягучы выклік"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Фільтраванне ўваходнага выкліку"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> выбраны</item>
<item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> выбрана</item>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 20342e07953d..076df22cbe7b 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Използвайте отпечатъка си, за да продължите"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Икона за отпечатък"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Предоставя на приложението достъп за четене и запис до конфигурацията на „Не безпокойте“."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"стартиране на прегледа на използваните разрешения"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Разрешава на притежателя да стартира прегледа на използваните разрешения за дадено приложение. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Задаване на правила за паролата"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Контролира дължината и разрешените знаци за паролите и ПИН кодовете за заключване на екрана."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Наблюдаване на опитите за отключване на екрана"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Използване на пряк път"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Инвертиране на цветовете"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Коригиране на цветовете"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Намаляване на яркостта"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Задържахте бутоните за силата на звука. Услугата <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е включена."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Задържахте бутоните за силата на звука. Услугата <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е изключена."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"За да използвате <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, натиснете двата бутона за силата на звука и ги задръжте за 3 секунди"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Увеличаване"</string>
<string name="close_button_text" msgid="10603510034455258">"Затваряне"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"„<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>“: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Отговор"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Отхвърляне"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Затваряне"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Входящо обаждане"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Текущо обаждане"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Преглежда се входящо обаждане"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other">Избрахте <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">Избрахте <xliff:g id="COUNT_0">%1$d</xliff:g></item>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 51d09b27afe6..c7fb8bd21e33 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"শুধুমাত্র ওয়াই-ফাই"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> ব্যাক-আপ কলিং"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ফরওয়ার্ড করা হয়নি"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> সেকেন্ড পরে"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"চালিয়ে যেতে আঙ্গুলের ছাপ ব্যবহার করুন"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"আঙ্গুলের ছাপ আইকন"</string>
@@ -685,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"অ্যাপটিকে \'বিরক্ত করবে না\' কনফিগারেশন পড়া এবং লেখার অনুমতি দেয়।"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"দেখার অনুমতি কাজে লাগানো শুরু করুন"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"কোনও অ্যাপের কোনও নির্দিষ্ট অনুমতির ব্যবহার শুরু করার ক্ষেত্রে হোল্ডারকে সাহায্য করে। সাধারণ অ্যাপের জন্য এটির পরিবর্তন হওয়ার কথা নয়।"</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"পাসওয়ার্ড নিয়মগুলি সেট করে"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"স্ক্রিন লক করার পাসওয়ার্ডগুলিতে অনুমতিপ্রাপ্ত অক্ষর এবং দৈর্ঘ্য নিয়ন্ত্রণ করে৷"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"স্ক্রিন আনলক করার প্রচেষ্টাগুলির উপরে নজর রাখুন"</string>
@@ -1665,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"শর্টকাট ব্যবহার করুন"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"রঙ উল্টানো"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"রঙ সংশোধন"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"উজ্জ্বলতা কমান"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> চালু করা হয়েছে।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> বন্ধ করা হয়েছে।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ব্যবহার করতে ভলিউম কী বোতাম ৩ সেকেন্ডের জন্য চেপে ধরে রাখুন"</string>
@@ -1881,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"বড় করুন"</string>
<string name="close_button_text" msgid="10603510034455258">"বন্ধ করুন"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"উত্তর"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"বাতিল করুন"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"কল কেটে দেওয়া"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"ইনকামিং কল"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"চালু থাকা কল"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"ইনকামিং কল স্ক্রিনিং করা হচ্ছে"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টি নির্বাচন করা হয়েছে</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টি নির্বাচন করা হয়েছে</item>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 989e2bb7b598..393e70224b9c 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -582,6 +582,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Nastavite pomoću otiska prsta"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona za otisak prsta"</string>
@@ -687,6 +688,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Omogućava aplikaciji da čita i upisuje konfiguraciju načina rada Ne ometaj."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"pokrenuti korištenje odobrenja za pregled"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dozvoljava vlasniku da pokrene korištenje odobrenja za aplikaciju. Ne bi trebalo biti potrebno za obične aplikacije."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Postavljanje pravila za lozinke"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrolira dužinu i znakove koji su dozvoljeni u lozinkama za zaključavanje ekrana i PIN-ovima."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Prati pokušaje otključavanja ekrana"</string>
@@ -1686,6 +1691,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Koristi prečicu"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Ispravka boja"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Smanjenje osvjetljenja"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tipke za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tipke za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite obje tipke za podešavanje jačine zvuka i držite ih pritisnutim tri sekunde da koristite uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1911,6 +1917,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Povećaj maksimalno"</string>
<string name="close_button_text" msgid="10603510034455258">"Zatvori"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Odgovori"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Odbaci"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Prekini vezu"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Dolazni poziv"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Poziv u toku"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtriranje dolaznog poziva"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> stavka je odabrana</item>
<item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> stavke su odabrane</item>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index e9c55d76c6ac..56131e3e4386 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -538,10 +538,10 @@
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Permet que l\'aplicació conegui el nivell de complexitat del bloqueig de pantalla (alt, mitjà, baix o cap), que indica la llargària i el tipus de bloqueig de pantalla possibles. L\'aplicació també pot suggerir que els usuaris actualitzin el bloqueig de pantalla a un nivell determinat, però els usuaris poden ignorar aquestes recomanacions. Tingues en compte que el bloqueig de pantalla no s\'emmagatzema com a text sense format, de manera que l\'aplicació no coneix la contrasenya exacta."</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"utilitza maquinari biomètric"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Permet que l\'aplicació faci servir maquinari biomètric per a l\'autenticació"</string>
- <string name="permlab_manageFingerprint" msgid="7432667156322821178">"Gestionar el maquinari d\'empremtes dactilars"</string>
+ <string name="permlab_manageFingerprint" msgid="7432667156322821178">"Gestionar el maquinari d\'empremtes digitals"</string>
<string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Permet que l\'aplicació invoqui mètodes per afegir i suprimir plantilles d\'empremtes dactilars que es puguin fer servir."</string>
- <string name="permlab_useFingerprint" msgid="1001421069766751922">"Utilitzar el maquinari d\'empremtes dactilars"</string>
- <string name="permdesc_useFingerprint" msgid="412463055059323742">"Permet que l\'aplicació faci servir maquinari d\'empremtes dactilars per a l\'autenticació"</string>
+ <string name="permlab_useFingerprint" msgid="1001421069766751922">"Utilitzar el maquinari d\'empremtes digitals"</string>
+ <string name="permdesc_useFingerprint" msgid="412463055059323742">"Permet que l\'aplicació faci servir maquinari d\'empremtes digitals per a l\'autenticació"</string>
<string name="permlab_audioWrite" msgid="8501705294265669405">"modificar la teva col·lecció de música"</string>
<string name="permdesc_audioWrite" msgid="8057399517013412431">"Permet que l\'aplicació modifiqui la teva col·lecció de música."</string>
<string name="permlab_videoWrite" msgid="5940738769586451318">"modificar la teva col·lecció de vídeos"</string>
@@ -567,7 +567,7 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"L\'empremta digital s\'ha autenticat"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Cara autenticada"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Cara autenticada; prem el botó per confirmar"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"El maquinari per a empremtes dactilars no està disponible."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"El maquinari d\'empremtes digitals no està disponible."</string>
<string name="fingerprint_error_no_space" msgid="6126456006769817485">"L\'empremta digital no es pot desar. Suprimeix-ne una."</string>
<string name="fingerprint_error_timeout" msgid="2946635815726054226">"S\'ha esgotat el temps d\'espera per a l\'empremta digital. Torna-ho a provar."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"S\'ha cancel·lat l\'operació d\'empremta digital."</string>
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Fes servir l\'empremta digital per continuar"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icona d\'empremta digital"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permet que l\'aplicació llegeixi la configuració No molestis i hi escrigui."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"comença a utilitzar el permís de visualització"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permet que un propietari comenci a utilitzar el permís amb una aplicació. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Definir les normes de contrasenya"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Permet controlar la longitud i el nombre de caràcters permesos a les contrasenyes i als PIN del bloqueig de pantalla."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisar els intents de desbloqueig de la pantalla"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilitza la drecera"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversió de colors"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correcció de color"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Reducció de la brillantor"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S\'han mantingut premudes les tecles de volum. S\'ha activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S\'han mantingut premudes les tecles de volum. S\'ha desactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén premudes les dues tecles de volum durant 3 segons per fer servir <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximitza"</string>
<string name="close_button_text" msgid="10603510034455258">"Tanca"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Respon"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Rebutja"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Penja"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Trucada entrant"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Trucada en curs"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"S\'està filtrant una trucada entrant"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other">Seleccionats: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">Seleccionats: <xliff:g id="COUNT_0">%1$d</xliff:g></item>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 071bbfd69ce5..7f20d3f0511c 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -585,6 +585,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Pokračujte přiložením prstu"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona otisku prstů"</string>
@@ -690,6 +691,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Umožňuje aplikaci číst a zapisovat konfiguraci režimu Nerušit."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"zahájení zobrazení využití oprávnění"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Umožňuje přístup zahájit využití oprávnění jiné aplikace. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Nastavit pravidla pro heslo"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Ovládání délky a znaků povolených v heslech a kódech PIN zámku obrazovky."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Sledovat pokusy o odemknutí obrazovky"</string>
@@ -1708,6 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Použít zkratku"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Převrácení barev"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Oprava barev"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Snížit jas"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> byla vypnuta."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Chcete-li používat službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, tři sekundy podržte stisknutá obě tlačítka hlasitosti"</string>
@@ -1942,6 +1948,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximalizovat"</string>
<string name="close_button_text" msgid="10603510034455258">"Zavřít"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Přijmout"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Odmítnout"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Zavěsit"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Příchozí hovor"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Probíhající hovor"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Prověřování příchozího hovoru"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> položky</item>
<item quantity="many"><xliff:g id="COUNT_1">%1$d</xliff:g> položky</item>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index b760f5b1c16e..d0effa6628e5 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Kun Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Alternativ løsning til opkald leveret af <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g>-opkald via alternativt SIM"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke viderestillet"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> efter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string>
@@ -581,6 +581,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Brug dit fingeraftryk for at fortsætte"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon for fingeraftryk"</string>
@@ -686,6 +687,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Giver appen tilladelse til at læse og redigere konfigurationen af Forstyr ikke."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start brugen at tilladelsesvisning"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Tillader, at brugeren kan bruge en tilladelse for en app. Dette bør aldrig være nødvendigt for almindelige apps."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Angiv regler for adgangskoder"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Tjek længden samt tilladte tegn i adgangskoder og pinkoder til skærmlåsen."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Overvåg forsøg på oplåsning af skærm"</string>
@@ -1666,6 +1671,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Brug genvej"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Ombytning af farver"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korriger farve"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Reducer lysstyrken"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er aktiveret."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er deaktiveret."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Hold begge lydstyrkeknapper nede i tre sekunder for at bruge <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1882,6 +1888,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maksimér"</string>
<string name="close_button_text" msgid="10603510034455258">"Luk"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Besvar"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Afvis"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Læg på"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Indgående opkald"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Igangværende opkald"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Et indgående opkald screenes"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>valgt</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> valgt</item>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index f8066e2bfedf..70459411bf1d 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Nur WLAN"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g>-Anruf über Ersatz-SIM"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nicht weitergeleitet"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> nach <xliff:g id="TIME_DELAY">{2}</xliff:g> Sekunden."</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Mithilfe deines Fingerabdrucks fortfahren"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerabdruck-Symbol"</string>
@@ -685,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ermöglicht der App Lese- und Schreibzugriff auf die \"Bitte nicht stören\"-Konfiguration"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Mit der Verwendung der Anzeigeberechtigung beginnen"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ermöglicht dem Inhaber, die Berechtigungsnutzung für eine App zu beginnen. Sollte für normale Apps nie benötigt werden."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Passwortregeln festlegen"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Zulässige Länge und Zeichen für Passwörter für die Displaysperre festlegen"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Versuche zum Entsperren des Displays überwachen"</string>
@@ -1665,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Verknüpfung verwenden"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Farbumkehr"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Farbkorrektur"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Helligkeit verringern"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lautstärketasten wurden gedrückt gehalten. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist aktiviert."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lautstärketasten wurden gedrückt gehalten. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist deaktiviert."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Halten Sie beide Lautstärketasten drei Sekunden lang gedrückt, um <xliff:g id="SERVICE_NAME">%1$s</xliff:g> zu verwenden"</string>
@@ -1881,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximieren"</string>
<string name="close_button_text" msgid="10603510034455258">"Schließen"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Annehmen"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Ablehnen"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Auflegen"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Eingehender Anruf"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Aktueller Anruf"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Screening für eingehenden Anruf"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ausgewählt</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ausgewählt</item>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 4f3c8a95a469..8d14b3bbe710 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Χρησιμοποιήστε το δακτυλικό αποτύπωμά σας για να συνεχίσετε"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Εικονίδιο δακτυλικών αποτυπωμάτων"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Επιτρέπει στην εφαρμογή την εγγραφή και τη σύνταξη διαμόρφωσης για τη λειτουργία \"Μην ενοχλείτε\"."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"έναρξη χρήσης άδειας προβολής"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Επιτρέπει στον κάτοχο να ξεκινήσει τη χρήση της άδειας για μια εφαρμογή. Δεν απαιτείται ποτέ για κανονικές εφαρμογές."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Ορισμός κανόνων κωδικού πρόσβασης"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Ελέγξτε την έκταση και τους επιτρεπόμενους χαρακτήρες σε κωδικούς πρόσβασης κλειδώματος οθόνης και PIN."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Παρακολούθηση προσπαθειών ξεκλειδώματος οθόνης"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Χρήση συντόμευσης"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Αντιστροφή χρωμάτων"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Διόρθωση χρωμάτων"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Μείωση φωτεινότητας"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Τα πλήκτρα έντασης είναι πατημένα. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ενεργοποιήθηκε."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Τα πλήκτρα έντασης είναι πατημένα. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>: απενεργοποιημένο"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Πατήστε παρατεταμένα και τα δύο κουμπιά έντασης ήχου για τρία δευτερόλεπτα, ώστε να χρησιμοποιήσετε την υπηρεσία <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Μεγιστοποίηση"</string>
<string name="close_button_text" msgid="10603510034455258">"Κλείσιμο"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Απάντηση"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Απόρριψη"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Τερματισμός"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Εισερχόμενη κλήση"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Κλήση σε εξέλιξη"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Διαλογή εισερχόμενης κλήσης"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other">Επιλέχτηκαν <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">Επιλέχτηκε <xliff:g id="COUNT_0">%1$d</xliff:g></item>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 9f3bc7de2385..51e4ef79ddba 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use your fingerprint to continue"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Allows the app to sample sensor data at a rate greater than 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Set password rules"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Control the length and the characters allowed in screen lock passwords and PINs."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string>
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Reduce Brightness"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximise"</string>
<string name="close_button_text" msgid="10603510034455258">"Close"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Answer"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Decline"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Hang up"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Incoming call"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"On-going call"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Screening an incoming call"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selected</item>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 766e372927d6..3e99738a8900 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use your fingerprint to continue"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Allows the app to sample sensor data at a rate greater than 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Set password rules"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Control the length and the characters allowed in screen lock passwords and PINs."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string>
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour inversion"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Reduce Brightness"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximise"</string>
<string name="close_button_text" msgid="10603510034455258">"Close"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Answer"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Decline"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Hang up"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Incoming call"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"On-going call"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Screening an incoming call"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selected</item>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 16da2116ed40..464a44ba3df4 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use your fingerprint to continue"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Allows the app to sample sensor data at a rate greater than 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Set password rules"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Control the length and the characters allowed in screen lock passwords and PINs."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string>
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Reduce Brightness"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximise"</string>
<string name="close_button_text" msgid="10603510034455258">"Close"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Answer"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Decline"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Hang up"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Incoming call"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"On-going call"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Screening an incoming call"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selected</item>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 150830ef027d..2b8cd8681a57 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use your fingerprint to continue"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Allows the app to sample sensor data at a rate greater than 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Set password rules"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Control the length and the characters allowed in screen lock passwords and PINs."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string>
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Color correction"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Reduce Brightness"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximise"</string>
<string name="close_button_text" msgid="10603510034455258">"Close"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Answer"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Decline"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Hang up"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Incoming call"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"On-going call"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Screening an incoming call"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selected</item>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index e952cac93ca6..9d073b9edf86 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‎‏‏‏‏‎‎‏‎‎‎‏‎‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‏‎Use your fingerprint to continue‎‏‎‎‏‎"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‏‎‎‎‎‎‎‏‎‎‏‏‏‎‏‎‎‏‎‎‎‏‎Fingerprint icon‎‏‎‎‏‎"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‏‎‏‏‎‎‏‎‎‏‏‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‏‎‏‎Allows the app to read and write Do Not Disturb configuration.‎‏‎‎‏‎"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‏‎‏‎start view permission usage‎‏‎‎‏‎"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‏‏‎‏‎‎‏‎‏‏‏‎‏‎‏‎‎Allows the holder to start the permission usage for an app. Should never be needed for normal apps.‎‏‎‎‏‎"</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‎‏‎‏‏‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‎‎‎‏‏‏‎‎access sensor data at a high sampling rate‎‏‎‎‏‎"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‎‏‏‎‎‎‏‎‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‎‏‏‎Allows the app to sample sensor data at a rate greater than 200 Hz‎‏‎‎‏‎"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‏‎‏‎‏‎‎‏‎‏‎‎‏‏‎‎‏‏‎‏‎‏‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‏‎‏‏‏‎Set password rules‎‏‎‎‏‎"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‎‏‏‏‏‎‏‏‎‎‎‏‎‏‎‎‏‏‎‏‎‎‎‎‏‏‎‎‎‎‎‎‏‎Control the length and the characters allowed in screen lock passwords and PINs.‎‏‎‎‏‎"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎Monitor screen unlock attempts‎‏‎‎‏‎"</string>
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‏‏‏‎‎Use Shortcut‎‏‎‎‏‎"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‏‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎Color Inversion‎‏‎‎‏‎"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎‎‏‎‏‏‏‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‎‏‏‎‎‎‎‎‎‎‎‏‎‏‎Color Correction‎‏‎‎‏‎"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‎‏‏‎‏‎‎Reduce Brightness‎‏‎‎‏‎"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‏‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎Held volume keys. ‎‏‎‎‏‏‎<xliff:g id="SERVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ turned on.‎‏‎‎‏‎"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‎‏‏‎Held volume keys. ‎‏‎‎‏‏‎<xliff:g id="SERVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ turned off.‎‏‎‎‏‎"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‎‏‎‏‎‎‏‏‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‎‏‎‎Press and hold both volume keys for three seconds to use ‎‏‎‎‏‏‎<xliff:g id="SERVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‏‏‎‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‏‎Maximize‎‏‎‎‏‎"</string>
<string name="close_button_text" msgid="10603510034455258">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‏‎‎Close‎‏‎‎‏‎"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‏‎‏‎‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>‎‏‎‎‏‏‏‎: ‎‏‎‎‏‏‎<xliff:g id="SENDER_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‏‎‎‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎Answer‎‏‎‎‏‎"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‏‏‏‏‎‎‏‎‏‎‏‏‎‎Decline‎‏‎‎‏‎"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‎‎‎‎‎‎‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‎‏‏‎Hang Up‎‏‎‎‏‎"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎Incoming call‎‏‎‎‏‎"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‏‎‏‏‎Ongoing call‎‏‎‎‏‎"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‎‎‎‎‏‏‎‏‎‎‏‏‏‎‏‏‎‎‎‎‎Screening an incoming call‎‏‎‎‏‎"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‎‎‎‏‏‏‏‏‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="COUNT_1">%1$d</xliff:g>‎‏‎‎‏‏‏‎ selected‎‏‎‎‏‎</item>
<item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‎‎‎‏‏‏‏‏‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="COUNT_0">%1$d</xliff:g>‎‏‎‎‏‏‏‎ selected‎‏‎‎‏‎</item>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index c5ff193bc6a2..333bdc804369 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -579,6 +579,7 @@
<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">"Se inhabilitó temporalmente el sensor."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utiliza tu huella digital para continuar"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícono de huella digital"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite que la aplicación lea y modifique la configuración de la función No interrumpir."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso de permiso de vista"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que el propietario inicie el uso de permisos para una app. No debería requerirse para apps normales."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Acceder a los datos del sensor a una tasa de muestreo alta"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que la app tome una muestra de los datos del sensor a una tasa superior a 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Establecer reglas de contraseña"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Controlar la longitud y los caracteres permitidos en las contraseñas y los PIN para el bloqueo de pantalla."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisa los intentos para desbloquear la pantalla"</string>
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar acceso directo"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de color"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de color"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Reducir el brillo"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Como mantuviste presionadas las teclas de volumen, se activó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se presionaron las teclas de volumen. Se desactivó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén presionadas ambas teclas de volumen durante tres segundos para usar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximizar"</string>
<string name="close_button_text" msgid="10603510034455258">"Cerrar"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Responder"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Rechazar"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Colgar"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Llamada entrante"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Llamada en curso"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando una llamada entrante"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> elementos seleccionados</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> elemento seleccionado</item>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 108c3dbeb954..476193e3102f 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Usa tu huella digital para continuar"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icono de huella digital"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite que la aplicación lea y modifique la configuración de No molestar."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso de permiso de visualización"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que el titular inicie el uso de permisos de una aplicación. Las aplicaciones normales no deberían necesitar nunca este permiso."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Establecimiento de reglas de contraseña"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Controla la longitud y los caracteres permitidos en los PIN y en las contraseñas de bloqueo de pantalla."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisar los intentos de desbloqueo de pantalla"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar acceso directo"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de color"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de color"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Reducir brillo"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Al mantener pulsadas las teclas de volumen, se ha activado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se han mantenido pulsadas las teclas de volumen. Se ha desactivado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Para utilizar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, mantén pulsadas ambas teclas de volumen durante 3 segundos"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximizar"</string>
<string name="close_button_text" msgid="10603510034455258">"Cerrar"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Responder"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Rechazar"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Colgar"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Llamada entrante"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Llamada en curso"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando una llamada entrante"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> seleccionados</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> seleccionado</item>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 70862d481b65..c32cf73bf2ca 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Jätkamiseks kasutage sõrmejälge"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Sõrmejälje ikoon"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Võimaldab rakendusel lugeda ja kirjutada funktsiooni Mitte segada seadistusi."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"vaatamisloa kasutamise alustamine"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Võimaldab omanikul rakenduse puhul alustada loa kasutamist. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Parooli reeglite määramine"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Juhitakse ekraaniluku paroolide ja PIN-koodide pikkusi ning lubatud tähemärkide seadeid."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekraani avamiskatsete jälgimine"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kasuta otseteed"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Värvide ümberpööramine"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Värvide korrigeerimine"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Ereduse vähendamine"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati sisse."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati välja."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kasutamiseks hoidke kolm sekundit all mõlemat helitugevuse klahvi"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maksimeeri"</string>
<string name="close_button_text" msgid="10603510034455258">"Sule"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Vasta"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Keeldu"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Lõpeta kõne"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Sissetulev kõne"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Käimasolev kõne"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Sissetuleva kõne filtreerimine"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> on valitud</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> on valitud</item>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index bc691a5c37e2..86546e0baac0 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -93,7 +93,7 @@
<string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Datu-konexioaren egoera"</string>
<string name="notification_channel_sms" msgid="1243384981025535724">"SMS mezuak"</string>
<string name="notification_channel_voice_mail" msgid="8457433203106654172">"Erantzungailuko mezuak"</string>
- <string name="notification_channel_wfc" msgid="9048240466765169038">"Wi-Fi bidezko deiak"</string>
+ <string name="notification_channel_wfc" msgid="9048240466765169038">"Wifi bidezko deiak"</string>
<string name="notification_channel_sim" msgid="5098802350325677490">"SIMaren egoera"</string>
<string name="notification_channel_sim_high_prio" msgid="642361929452850928">"SIM txartelaren lehentasun handiko jakinarazpenak"</string>
<string name="peerTtyModeFull" msgid="337553730440832160">"Beste gailuak TTY osagarria FULL moduan erabiltzea eskatu du"</string>
@@ -122,23 +122,23 @@
<string name="roamingText11" msgid="5245687407203281407">"Ibiltaritzari buruzko jakinarazpena aktibatuta"</string>
<string name="roamingText12" msgid="673537506362152640">"Ibiltaritzari buruzko jakinarazpena desaktibatuta"</string>
<string name="roamingTextSearching" msgid="5323235489657753486">"Zerbitzu bila"</string>
- <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Ezin izan dira konfiguratu Wi‑Fi bidezko deiak"</string>
+ <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Ezin izan dira konfiguratu wifi bidezko deiak"</string>
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="468830943567116703">"Wi-Fi bidez deiak egiteko eta mezuak bidaltzeko, eskatu operadoreari zerbitzu hori gaitzeko. Ondoren, aktibatu Wi-Fi bidezko deiak Ezarpenak atalean. (Errore-kodea: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+ <item msgid="468830943567116703">"Wifi bidez deiak egiteko eta mezuak bidaltzeko, eskatu operadoreari zerbitzu hori gaitzeko. Ondoren, aktibatu Wifi bidezko deiak Ezarpenak atalean. (Errore-kodea: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
- <item msgid="4795145070505729156">"Arazo bat izan da Wi‑Fi bidezko deiak zure operadorearekin erregistratzean: <xliff:g id="CODE">%1$s</xliff:g>"</item>
+ <item msgid="4795145070505729156">"Arazo bat izan da wifi bidezko deiak zure operadorearekin erregistratzean: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
<!-- no translation found for wfcSpnFormat_spn (2982505428519096311) -->
<skip />
- <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi bidezko deiak"</string>
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> operadorearen wifi bidezko deiak"</string>
<string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> operadorearen wifi bidezko deiak"</string>
<string name="wfcSpnFormat_wlan_call" msgid="4895315549916165700">"WLAN bidezko deia"</string>
<string name="wfcSpnFormat_spn_wlan_call" msgid="255919245825481510">"<xliff:g id="SPN">%s</xliff:g> WLAN bidezko deia"</string>
<string name="wfcSpnFormat_spn_wifi" msgid="7232899594327126970">"<xliff:g id="SPN">%s</xliff:g> wifia"</string>
<string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Wi-Fi bidezko deiak | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="6865214948822061486">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Wi-Fi bidezko deiak"</string>
+ <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Wifi bidezko deiak"</string>
<string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"Wifia"</string>
<string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Wi-Fi bidezko deiak"</string>
<string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string>
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wifi-sarea soilik"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> operadorearen deietarako ordezko aukera"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ez da desbideratu"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> zenbakira <xliff:g id="TIME_DELAY">{2}</xliff:g> segundotan"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Aurrera egiteko, erabili hatz-marka"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Hatz-markaren ikonoa"</string>
@@ -685,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ez molestatzeko moduaren konfigurazioa irakurtzeko eta bertan idazteko baimena ematen die aplikazioei."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"hasi ikusteko baimena erabiltzen"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Aplikazioaren baimena erabiltzen hasteko baimena ematen die titularrei. Aplikazio normalek ez lukete beharko."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Ezarri pasahitzen arauak"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrolatu pantaila blokeoaren pasahitzen eta PINen luzera eta onartutako karaktereak."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Gainbegiratu pantaila desblokeatzeko saiakerak"</string>
@@ -1665,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Erabili lasterbidea"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Koloreen alderantzikatzea"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Koloreen zuzenketa"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Murriztu distira"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bolumen-botoiak sakatuta eduki direnez, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktibatu egin da."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bolumen-botoiak sakatuta eduki direnez, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desaktibatu egin da."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> erabiltzeko, eduki sakatuta bi bolumen-botoiak hiru segundoz"</string>
@@ -1881,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximizatu"</string>
<string name="close_button_text" msgid="10603510034455258">"Itxi"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Erantzun"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Baztertu"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Amaitu deia"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Jasotako deia"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Deia abian da"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Jasotako dei bat bistaratzen"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> hautatuta</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> hautatuta</item>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 6a0e45446341..b1ec1f6e7483 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"برای ادامه، از اثر انگشتتان استفاده کنید"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"نماد اثر انگشت"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"به برنامه امکان می‌دهد پیکربندی «مزاحم نشوید» را بخواند و بنویسد."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"شروع مشاهده استفاده از مجوز"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"به دارنده اجازه شروع استفاده از مجوز را برای برنامه می‌دهد. هرگز برای برنامه‌های معمول نیاز نیست."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"تنظیم قوانین گذرواژه"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"کنترل طول و نوع نویسه‌هایی که در گذرواژه و پین قفل صفحه مجاز است."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"پایش تلاش‌های باز کردن قفل صفحه"</string>
@@ -1440,7 +1445,7 @@
<string name="notification_listener_binding_label" msgid="2702165274471499713">"شنونده اعلان"</string>
<string name="vr_listener_binding_label" msgid="8013112996671206429">"‏شنونده VR"</string>
<string name="condition_provider_service_binding_label" msgid="8490641013951857673">"ارائه‌دهنده وضعیت"</string>
- <string name="notification_ranker_binding_label" msgid="432708245635563763">"سرویس رتبه‌بندی اعلان"</string>
+ <string name="notification_ranker_binding_label" msgid="432708245635563763">"سرویس رده‌بندی اعلان"</string>
<string name="vpn_title" msgid="5906991595291514182">"‏VPN فعال شد"</string>
<string name="vpn_title_long" msgid="6834144390504619998">"‏VPN را <xliff:g id="APP">%s</xliff:g> فعال کرده است"</string>
<string name="vpn_text" msgid="2275388920267251078">"برای مدیریت شبکه ضربه بزنید."</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"استفاده از میان‌بر"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"وارونگی رنگ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"تصحیح رنگ"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"کاهش روشنایی"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> روشن شد."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> خاموش شد."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"برای استفاده از <xliff:g id="SERVICE_NAME">%1$s</xliff:g>، هر دو کلید صدا را فشار دهید و سه ثانیه نگه دارید"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"بزرگ کردن"</string>
<string name="close_button_text" msgid="10603510034455258">"بستن"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>:‏ <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"پاسخ"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"رد کردن"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"قطع تماس"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"تماس ورودی"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"تماس درحال انجام"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"درحال غربال کردن تماس ورودی"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one">‏<xliff:g id="COUNT_1">%1$d</xliff:g> انتخاب شد</item>
<item quantity="other">‏<xliff:g id="COUNT_1">%1$d</xliff:g> انتخاب شد</item>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 8e5d9b44759a..55c6d92f1136 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Jatka sormenjäljen avulla"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Sormenjälkikuvake"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Sallii sovelluksen lukea ja muokata Älä häiritse -tilan asetuksia."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"aloita katseluoikeuksien käyttö"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Antaa luvanhaltijan käynnistää sovelluksen käyttöoikeuksien käytön. Ei tavallisten sovelluksien käyttöön."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Asentaa salasanasäännöt"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Hallinnoida ruudun lukituksen salasanoissa ja PIN-koodeissa sallittuja merkkejä ja niiden pituutta."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Tarkkailla näytön avaamisyrityksiä"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Käytä pikanäppäintä"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Käänteiset värit"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Värinkorjaus"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Vähennä kirkkautta"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin päälle."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin pois päältä."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Voit käyttää palvelua <xliff:g id="SERVICE_NAME">%1$s</xliff:g> painamalla molempia äänenvoimakkuuspainikkeita kolmen sekunnin ajan"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Suurenna"</string>
<string name="close_button_text" msgid="10603510034455258">"Sulje"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Vastaa"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Hylkää"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Lopeta puhelu"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Saapuva puhelu"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Käynnissä oleva puhelu"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Seulotaan saapuvaa puhelua"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> valittu</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> valittu</item>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 849f9594729f..15351bcef7ba 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi seulement"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Méthode d\'appel secondaire avec <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g> au bout de <xliff:g id="TIME_DELAY">{2}</xliff:g> secondes"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utilisez votre empreinte digitale pour continuer"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icône d\'empreinte digitale"</string>
@@ -685,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permet à l\'application de consulter et de modifier la configuration du mode Ne pas déranger."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"démarrer l\'affichage de l\'usage des autorisations"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permet au détenteur de démarrer l\'usage des autorisations pour une application. Cette fonctionnalité ne devrait pas être nécessaire pour les applications standards."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"accéder aux données des capteurs à un taux d’échantillonnage élevé"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permet à l’application d’échantillonner les données des capteurs à une fréquence supérieure à 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Définir les règles du mot de passe"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Gérer le nombre et le type de caractères autorisés dans les mots de passe et les NIP de verrouillage de l\'écran."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Gérer les tentatives de déverrouillage de l\'écran"</string>
@@ -1665,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utiliser le raccourci"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversion des couleurs"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correction des couleurs"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Réduire la luminosité"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume maintenues enfoncées. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume maintenues enfoncées. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Maintenez enfoncées les deux touches de volume pendant trois secondes pour utiliser <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1881,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Agrandir"</string>
<string name="close_button_text" msgid="10603510034455258">"Fermer"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g> : <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Répondre"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Refuser"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Raccrocher"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Appel entrant"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Appel en cours"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrer un appel entrant"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> élément sélectionné</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> éléments sélectionnés</item>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 60138c47d40e..8cb86143b5f5 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi uniquement"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Appels de secours via <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g> au bout de <xliff:g id="TIME_DELAY">{2}</xliff:g> secondes"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utilisez votre empreinte digitale pour continuer"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icône d\'empreinte digitale"</string>
@@ -685,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permet à l\'application de consulter et de modifier la configuration du mode Ne pas déranger."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"activer l\'utilisation de l\'autorisation d\'affichage"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permet à l\'application autorisée d\'activer l\'utilisation de l\'autorisation pour une application. Cette fonctionnalité ne devrait pas être nécessaire pour les applications standards."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Définir les règles du mot de passe"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Gérer le nombre et le type de caractères autorisés dans les mots de passe et les codes d\'accès de verrouillage de l\'écran"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Gérer les tentatives de déverrouillage de l\'écran"</string>
@@ -1665,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utiliser le raccourci"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversion des couleurs"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correction des couleurs"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Réduire la luminosité"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Appuyez de manière prolongée sur les deux touches de volume pendant trois secondes pour utiliser <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1881,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Agrandir"</string>
<string name="close_button_text" msgid="10603510034455258">"Fermer"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g> : <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Répondre"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Refuser"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Raccrocher"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Appel entrant"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Appel en cours"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrer un appel entrant"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> élément sélectionné</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> éléments sélectionnés</item>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index eadac9edad74..f99d02c01996 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <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>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icona de impresión dixital"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite á aplicación ler e escribir a configuración do modo Non molestar."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso de permiso de vista"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite ao propietario iniciar o uso de permisos dunha aplicación. As aplicacións normais non deberían precisalo nunca."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Establecer as normas de contrasinal"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Controla a lonxitude e os caracteres permitidos nos contrasinais e nos PIN de bloqueo da pantalla."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Controlar os intentos de desbloqueo da pantalla"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar atallo"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de cor"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de cor"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Reducir brillo"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume premidas. Activouse o servizo <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume premidas. Desactivouse <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén premidas as teclas do volume durante tres segudos para usar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximizar"</string>
<string name="close_button_text" msgid="10603510034455258">"Pechar"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Resposta"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Rexeitar"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Colgar"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Chamada entrante"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chamada en curso"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando chamada entrante"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other">Seleccionáronse <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">Seleccionouse <xliff:g id="COUNT_0">%1$d</xliff:g></item>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 86416bce3f48..84ee732e0d16 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"ફક્ત વાઇ-ફાઇ"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> બૅકઅપ કૉલિંગ"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ફોરવર્ડ કર્યો નથી"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="TIME_DELAY">{2}</xliff:g> સેકન્ડ પછી <xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ચાલુ રાખવા માટે તમારી ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ફિંગરપ્રિન્ટ આયકન"</string>
@@ -685,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"એપ્લિકેશનને ખલેલ પાડશો નહીં ગોઠવણી વાંચવા અને લખવાની મંજૂરી આપે છે."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"પરવાનગી વપરાશ જુઓને શરૂ કરો"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"કોઈ ઍપ માટે પરવાનગી વપરાશ શરૂ કરવાની ધારકને મંજૂરી આપે છે. સામાન્ય ઍપ માટે ક્યારેય જરૂર પડી ન શકે."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"પાસવર્ડ નિયમો સેટ કરો"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"સ્ક્રીન લૉક પાસવર્ડ અને પિનમાં મંજૂર લંબાઈ અને અક્ષરોને નિયંત્રિત કરો."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"સ્ક્રીનને અનલૉક કરવાના પ્રયત્નોનું નિયમન કરો"</string>
@@ -1665,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"શૉર્ટકટનો ઉપયોગ કરો"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"વિપરીત રંગમાં બદલવું"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"રંગ સુધારણા"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"બ્રાઇટનેસ ઘટાડો"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ચાલુ કરી."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> બંધ કરી."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>નો ઉપયોગ કરવા માટે બન્ને વૉલ્યૂમ કીને ત્રણ સેકન્ડ સુધી દબાવી રાખો"</string>
@@ -1881,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"મહત્તમ કરો"</string>
<string name="close_button_text" msgid="10603510034455258">"બંધ કરો"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"જવાબ"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"નકારો"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"સમાપ્ત કરો"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"ઇનકમિંગ કૉલ"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"ચાલુ કૉલ"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"ઇનકમિંગ કૉલનું સ્ક્રીનિંગ થાય છે"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> પસંદ કરી</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> પસંદ કરી</item>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 5f0a8f89b926..93408be6a5e3 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -303,7 +303,7 @@
<string name="managed_profile_label" msgid="7316778766973512382">"प्रोफ़ाइल बदलकर वर्क प्रोफ़ाइल पर जाएं"</string>
<string name="permgrouplab_contacts" msgid="4254143639307316920">"संपर्क"</string>
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"अपने संपर्कों को ऐक्सेस करने की"</string>
- <string name="permgrouplab_location" msgid="1858277002233964394">"जगह"</string>
+ <string name="permgrouplab_location" msgid="1858277002233964394">"जगह की जानकारी"</string>
<string name="permgroupdesc_location" msgid="1995955142118450685">"इस डिवाइस की जगह तक पहुंचने दें"</string>
<string name="permgrouplab_calendar" msgid="6426860926123033230">"कैलेंडर"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"अपने कैलेंडर को ऐक्सेस करने"</string>
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"जारी रखने के लिए फ़िंगरप्रिंट का इस्तेमाल करें"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"फ़िंगरप्रिंट आइकॉन"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ऐप को परेशान न करें कॉन्फ़िगरेशन पढ़ने और लिखने देती है."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"देखने की अनुमतियां चालू करें"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"इस्तेमाल करने वाले को किसी ऐप्लिकेशन के लिए अनुमतियों का इस्तेमाल शुरू करने देता है. सामान्य ऐप्लिकेशन के लिए इसकी ज़रूरत कभी नहीं पड़ती."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"पासवर्ड नियम सेट करना"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"स्‍क्रीन लॉक पासवर्ड और पिन की लंबाई और उनमें स्वीकृत वर्णों को नियंत्रित करना."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"स्‍क्रीन अनलॉक करने के की कोशिशों पर नज़र रखना"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"शॉर्टकट का उपयोग करें"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"रंग बदलने की सुविधा"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"रंग में सुधार करने की सुविधा"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"स्क्रीन की चमक कम करें"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"आवाज़ कम-ज़्यादा करने वाले दोनों बटन दबाकर रखें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को चालू कर दिया गया."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"आवाज़ कम-ज़्यादा करने वाले दोनों बटन दबाकर रखें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को बंद कर दिया गया."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> इस्तेमाल करने के लिए आवाज़ वाले दोनों बटन तीन सेकंड तक दबाकर रखें"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"बड़ा करें"</string>
<string name="close_button_text" msgid="10603510034455258">"बंद करें"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"जवाब दें"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"अस्वीकार करें"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"कॉल काटें"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"आने वाला (इनकमिंग) कॉल"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"पहले से जारी कॉल"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"इनकमिंग कॉल को स्क्रीन किया जा रहा है"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> चयनित</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> चयनित</item>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index e1ae1e24e0a1..7fa4fe32ede5 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -582,6 +582,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Nastavite pomoću otiska prsta"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona otiska prsta"</string>
@@ -687,6 +688,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Omogućuje aplikaciji čitanje i pisanje konfiguracije opcije Ne ometaj."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"pokrenuti upotrebu dopuštenja za pregled"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dopušta nositelju pokretanje upotrebe dopuštenja za aplikaciju. Ne bi smjelo biti potrebno za uobičajene aplikacije."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Postavi pravila zaporke"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Upravlja duljinom i znakovima koji su dopušteni u zaporkama i PIN-ovima zaključavanja zaslona."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Nadziri pokušaje otključavanja zaslona"</string>
@@ -1686,6 +1691,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Upotrijebi prečac"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcija boje"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Smanjenje svjetline"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tipke za glasnoću. Uključila se usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tipke za glasnoću. Isključila se usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite i zadržite tipke za glasnoću na tri sekunde da biste koristili uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1911,6 +1917,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maksimiziraj"</string>
<string name="close_button_text" msgid="10603510034455258">"Zatvori"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Odgovori"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Odbij"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Prekini"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Dolazni poziv"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Poziv u tijeku"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtriranje dolaznog poziva"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> odabrana</item>
<item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> odabrane</item>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 3c04bec04151..d71c5620d44d 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"A folytatáshoz használja ujjlenyomatát"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ujjlenyomat ikon"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Az alkalmazás olvashatja és szerkesztheti a „Ne zavarjanak” funkció beállításait."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"engedélyhasználat megtekintésének elindítása"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Lehetővé teszi a felhasználó számára, hogy elindítsa az alkalmazás engedélyhasználatát. A normál alkalmazásoknak erre soha nincs szükségük."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Jelszavakkal kapcsolatos szabályok beállítása"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"A képernyőzár jelszavaiban és PIN kódjaiban engedélyezett karakterek és hosszúság vezérlése."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Képernyőzár-feloldási kísérletek figyelése"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Billentyűparancs használata"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Színek invertálása"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Színkorrekció"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Fényerő csökkentése"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> bekapcsolva."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kikapcsolva."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"A(z) <xliff:g id="SERVICE_NAME">%1$s</xliff:g> használatához tartsa lenyomva három másodpercig a két hangerőgombot"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Teljes méret"</string>
<string name="close_button_text" msgid="10603510034455258">"Bezárás"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Fogadás"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Elutasítás"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Befejezés"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Bejövő hívás"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Hívás folyamatban"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Bejövő hívás szűrése"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> kiválasztva</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> kiválasztva</item>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index bdd238757bfb..e5267d95a959 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Շարունակելու համար անհրաժեշտ է ձեր մատնահետքը"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Մատնահետքի պատկերակ"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Թույլ է տալիս հավելվածին փոփոխել «Չանհանգստացնել» գործառույթի կազմաձևումը:"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"թույլտվությունների մասին տվյալների հասանելիություն"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Հավելվածին հասանելի կդառնան թույլտվությունների մասին տվյալները։ Այս թույլտվությունն անհրաժեշտ չէ սովորական հավելվածներին։"</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Սահմանել գաղտնաբառի կանոնները"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Կառավարել էկրանի ապակողպման գաղտնաբառերի և PIN կոդերի թույլատրելի երկարությունն ու գրանշանները:"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Վերահսկել էկրանի ապակողպման փորձերը"</string>
@@ -1438,7 +1443,7 @@
<string name="wallpaper_binding_label" msgid="1197440498000786738">"Պաստառ"</string>
<string name="chooser_wallpaper" msgid="3082405680079923708">"Փոխել պաստառը"</string>
<string name="notification_listener_binding_label" msgid="2702165274471499713">"Ծանուցման ունկնդիր"</string>
- <string name="vr_listener_binding_label" msgid="8013112996671206429">"VR ունկնդրիչ"</string>
+ <string name="vr_listener_binding_label" msgid="8013112996671206429">"VR ունկնիր"</string>
<string name="condition_provider_service_binding_label" msgid="8490641013951857673">"Պայմանների մատակարար"</string>
<string name="notification_ranker_binding_label" msgid="432708245635563763">"Ծանուցումների դասակարգման ծառայություն"</string>
<string name="vpn_title" msgid="5906991595291514182">"VPN-ը ակտիվացված է"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Օգտագործել դյուրանցումը"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Գունաշրջում"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Գունաշտկում"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Նվազեցնել պայծառությունը"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը միացավ։"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունն անջատվեց։"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"«<xliff:g id="SERVICE_NAME">%1$s</xliff:g>» ծառայությունն օգտագործելու համար սեղմեք և 3 վայրկյան պահեք ձայնի ուժգնության երկու կոճակները"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Մեծացնել"</string>
<string name="close_button_text" msgid="10603510034455258">"Փակել"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>՝ <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Պատասխանել"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Մերժել"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Ավարտել"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Մուտքային զանգ"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Ընթացիկ զանգ"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Մուտքային զանգի զտում"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one">Ընտրված է՝ <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="other">Ընտրված է՝ <xliff:g id="COUNT_1">%1$d</xliff:g></item>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index c84bc1318caf..636bb04c3473 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Gunakan sidik jari untuk melanjutkan"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon sidik jari"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Mengizinkan aplikasi membaca dan menulis konfigurasi status Jangan Ganggu."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"mulai melihat penggunaan izin"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Memungkinkan pemegang memulai penggunaan izin untuk aplikasi. Tidak diperlukan untuk aplikasi normal."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Setel aturan sandi"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Mengontrol panjang dan karakter yang diizinkan dalam sandi dan PIN kunci layar."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Pantau upaya pembukaan kunci layar"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gunakan Pintasan"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversi Warna"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Koreksi Warna"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Kurangi Kecerahan"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tombol volume ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> diaktifkan."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tombol volume ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dinonaktifkan."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tekan dan tahan kedua tombol volume selama tiga detik untuk menggunakan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maksimalkan"</string>
<string name="close_button_text" msgid="10603510034455258">"Tutup"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Jawab"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Tolak"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Tutup"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Panggilan masuk"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Panggilan sedang berlangsung"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Menyaring panggilan masuk"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> dipilih</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> dipilih</item>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 84611a2d44a9..b359e7d3a28e 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <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>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingrafaratákn"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Leyfir forriti að lesa og skrifa í grunnstillingu „Ónáðið ekki“."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"heimildanotkun upphafsyfirlits"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Leyfir handhafa að byrja heimildanotkun fyrir forrit. Ætti aldrei að þurfa fyrir venjuleg forrit."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Setja reglur um aðgangsorð"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Stjórna lengd og fjölda stafa í aðgangsorðum og PIN-númerum skjáláss."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Fylgjast með tilraunum til að taka skjáinn úr lás"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Nota flýtileið"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Umsnúningur lita"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Litaleiðrétting"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Draga úr birtu"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Hljóðstyrkstökkum haldið inni. Kveikt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Hljóðstyrkstökkum haldið inni. Slökkt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Haltu báðum hljóðstyrkstökkunum inni í þrjár sekúndur til að nota <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Stækka"</string>
<string name="close_button_text" msgid="10603510034455258">"Loka"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Svara"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Hafna"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Leggja á"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Símtal berst"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Símtal í gangi"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Síar símtal sem berst"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> valið</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> valin</item>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 2fcc273880f7..d4c6ae0540c9 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Solo Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Chiamate di backup <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: inoltro non effettuato"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> dopo <xliff:g id="TIME_DELAY">{2}</xliff:g> secondi"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utilizza la tua impronta per continuare"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icona dell\'impronta"</string>
@@ -685,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Consente all\'app di leggere e modificare la configurazione della funzione Non disturbare."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"avvio dell\'uso dell\'autorizzazione di visualizzazione"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Consente al titolare di avviare l\'uso delle autorizzazioni per un\'app. Non dovrebbe essere mai necessaria per le normali applicazioni."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Impostare regole per le password"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Controlla la lunghezza e i caratteri ammessi nelle password e nei PIN del blocco schermo."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorare tentativi di sblocco dello schermo"</string>
@@ -1665,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usa scorciatoia"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversione dei colori"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correzione del colore"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Riduci la luminosità"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> attivato."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> disattivato."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tieni premuti entrambi i tasti del volume per tre secondi per utilizzare <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1881,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Ingrandisci"</string>
<string name="close_button_text" msgid="10603510034455258">"Chiudi"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Rispondi"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Rifiuta"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Riaggancia"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Chiamata in arrivo"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chiamata in corso"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Applicazione filtro a chiamata in arrivo"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> file selezionati</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> file selezionato</item>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index f104d442d154..33f43af259e8 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -585,6 +585,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"יש להשתמש בטביעת האצבע כדי להמשיך"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"סמל טביעת אצבע"</string>
@@ -690,6 +691,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"מאפשר לאפליקציה לקרוא ולכתוב את התצורה של \'נא לא להפריע\'."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"התחלת צפייה בהרשאות השימוש"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"מאפשרת לבעלים להפעיל את השימוש בהרשאות עבור אפליקציה מסוימת. הרשאה זו אף פעם לא נדרשת עבור אפליקציות רגילות."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"הגדר כללי סיסמה"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"קביעת האורך הנדרש והתווים המותרים בסיסמאות ובקודי הגישה של מסך הנעילה."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"מעקב אחר ניסיונות לביטול של נעילת המסך"</string>
@@ -1708,6 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"השתמש בקיצור הדרך"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"היפוך צבעים"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"תיקון צבעים"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"הפחתה של עוצמת הבהירות"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הופעל."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הושבת."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"יש ללחוץ לחיצה ארוכה על שני לחצני עוצמת הקול למשך שלוש שניות כדי להשתמש בשירות <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1942,6 +1948,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"הגדל"</string>
<string name="close_button_text" msgid="10603510034455258">"סגירה"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"תשובה"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"דחייה"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ניתוק"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"שיחה נכנסת"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"שיחה פעילה"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"סינון שיחה נכנסת"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="two">בחרת <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="many">בחרת <xliff:g id="COUNT_1">%1$d</xliff:g></item>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 69822ac67fb5..2482c7e8a5e2 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -203,7 +203,7 @@
<string name="sensor_notification_service" msgid="7474531979178682676">"センサー通知サービス"</string>
<string name="twilight_service" msgid="8964898045693187224">"トワイライト サービス"</string>
<string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Time Zone Detector(未接続)"</string>
- <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS Time Update Service"</string>
+ <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS 時間アップデートサービス"</string>
<string name="factory_reset_warning" msgid="6858705527798047809">"デバイスのデータが消去されます"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"管理アプリを使用できません。デバイスのデータはこれから消去されます。\n\nご不明な点がある場合は、組織の管理者にお問い合わせください。"</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"「<xliff:g id="OWNER_APP">%s</xliff:g>」により印刷は無効にされています。"</string>
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"続行するには指紋認証を使用してください"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指紋アイコン"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"サイレント モード設定の読み取りと書き込みをアプリに許可します。"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"表示権限の使用の開始"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"アプリの権限使用の開始を所有者に許可します。通常のアプリでは不要です。"</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"高サンプリング レートでセンサーデータにアクセスする"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz を超えるレートでセンサーデータをサンプリングすることをアプリに許可します"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"パスワードルールの設定"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"画面ロックのパスワードとPINの長さと使用できる文字を制御します。"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"画面ロック解除試行の監視"</string>
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ショートカットを使用"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"色反転"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"色補正"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"明るさを下げる"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が ON になりました。"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が OFF になりました。"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> を使用するには、音量大と音量小の両方のボタンを 3 秒間長押ししてください"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"最大化"</string>
<string name="close_button_text" msgid="10603510034455258">"閉じる"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"応答"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"拒否"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"通話終了"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"着信"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"通話中"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"着信をスクリーニング中"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>件選択済み</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g>件選択済み</item>
@@ -2060,9 +2070,9 @@
<string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"電源ダイアログ"</string>
<string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"ロック画面"</string>
<string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"スクリーンショット"</string>
- <string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"画面上のユーザー補助のショートカット"</string>
- <string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"画面上のユーザー補助のショートカットの選択メニュー"</string>
- <string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ユーザー補助のショートカット"</string>
+ <string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"画面上のユーザー補助機能のショートカット"</string>
+ <string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"画面上のユーザー補助機能のショートカットの選択メニュー"</string>
+ <string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ユーザー補助機能のショートカット"</string>
<string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> のキャプション バーです。"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> は RESTRICTED バケットに移動しました。"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 6a7d563c2dc7..e45b18a6170b 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"გასაგრძელებლად გამოიყენეთ თქვენი თითის ანაბეჭდი"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"თითის ანაბეჭდის ხატულა"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"საშუალებას აძლევს აპს, წაიკითხოს და დაწეროს კონფიგურაცია „არ შემაწუხოთ“."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ნახვის ნებართვის გამოყენების დაწყება"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"მფლობელს საშუალებას აძლევს, დაიწყოს აპის ნებართვის გამოყენება. ჩვეულებრივი აპებისთვის არასოდეს უნდა იყოს საჭირო."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"სენსორის მონაცემებზე წვდომა სემპლინგის მაღალი სიხშირით"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"საშუალებას აძლევს აპს, მიიღოს სენსორის მონაცემების ნიმუშები 200 ჰც-ზე მეტი სიხშირით"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"პაროლის წესების დაყენება"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"აკონტროლეთ ეკრანის ბლოკირების პაროლებისა და PIN-ების სიმბოლოების სიგრძე."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"ეკრანის განბლოკვის მცდელობების მონიტორინგი"</string>
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"მალსახმობის გამოყენება"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ფერთა ინვერსია"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ფერთა კორექცია"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"სიკაშკაშის შემცირება"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ხანგრძლივად დააჭირეთ ხმის ღილაკებს. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ჩართულია."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ხანგრძლივად დააჭირეთ ხმის ღილაკებს. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> გამორთულია."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> რომ გამოიყენოთ, დააჭირეთ ხმის ორივე ღილაკზე 3 წამის განმავლობაში"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"მაქსიმალური ზომა"</string>
<string name="close_button_text" msgid="10603510034455258">"დახურვა"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"პასუხი"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"უარყოფა"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"გათიშვა"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"შემომავალი ზარი"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"მიმდინარე ზარი"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"შემომავალი ზარების გაცხრილვა"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> შერჩეული</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> შერჩეული</item>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index aec2312c0a21..08a2e6773c90 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Тек Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Қосалқы қоңырау шалу"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Басқа нөмірге бағытталмады"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> секундтан кейін"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Жалғастыру үшін саусақ ізін пайдаланыңыз."</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Саусақ ізі белгішесі"</string>
@@ -685,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Қолданбаға «Мазаламау» конфигурациясын оқу және жазу мүмкіндігін береді."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"рұқсаттарды пайдалану туралы деректерді көру"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Пайдаланушы қолданбаға берілетін рұқсаттарды басқара алады. Ондай рұқсаттар әдеттегі қолданбаларға керек емес."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Құпия сөз ережелерін тағайындау"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Экран бекітпесінің құпия сөздерінің және PIN кодтарының ұзындығын және оларда рұқсат етілген таңбаларды басқару."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Экран құлпын ашу әркеттерін бақылау"</string>
@@ -1665,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Төте жолды пайдалану"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Түстер инверсиясы"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Түсті түзету"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Жарықтығын азайту"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Пайдаланушы дыбыс деңгейі пернелерін басып ұстап тұрды. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қосулы."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дыбыс деңгейі пернелерін басып тұрған соң, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өшірілді."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> қызметін пайдалану үшін дыбыс деңгейін реттейтін екі түймені де 3 секунд басып тұрыңыз"</string>
@@ -1881,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Жазу"</string>
<string name="close_button_text" msgid="10603510034455258">"Жабу"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Жауап"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Қабылдамау"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Тұтқаны қою"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Кіріс қоңырау"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Қоңырау"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Келген қоңырауды сүзу"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> таңдалды</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> таңдалды</item>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index d1400b9d17ff..34e249e550ee 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ប្រើ​ស្នាមម្រាមដៃ​របស់អ្នក ដើម្បីបន្ត"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"រូបស្នាមម្រាមដៃ"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"អនុញ្ញាតឲ្យកម្មវិធីអាន និងសរសេរការកំណត់រចនាសម្ព័ន្ធមុខងារ កុំរំខាន។"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ចាប់ផ្ដើម​មើល​ការប្រើប្រាស់​ការអនុញ្ញាត"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"អនុញ្ញាត​ឱ្យម្ចាស់​ចាប់ផ្ដើម​ការប្រើប្រាស់​ការអនុញ្ញាត​សម្រាប់កម្មវិធី។ មិនគួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"កំណត់​ក្បួន​ពាក្យ​សម្ងាត់"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"គ្រប់គ្រងប្រវែង និងតួអក្សរដែលអនុញ្ញាតឲ្យប្រើក្នុងពាក្យសម្ងាត់ និងលេខសម្ងាត់ចាក់សោអេក្រង់។"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"តាមដាន​ការ​ព្យាយាម​ដោះ​សោ​អេក្រង់"</string>
@@ -1108,7 +1113,7 @@
<string name="cut" msgid="2561199725874745819">"កាត់"</string>
<string name="copy" msgid="5472512047143665218">"ចម្លង"</string>
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"មិនអាច​ចម្លងទៅ​អង្គចងចាំទេ"</string>
- <string name="paste" msgid="461843306215520225">"បិទ​ភ្ជាប់"</string>
+ <string name="paste" msgid="461843306215520225">"ដាក់ចូល"</string>
<string name="paste_as_plain_text" msgid="7664800665823182587">"បិទភ្ជាប់ជាអត្ថបទធម្មតា"</string>
<string name="replace" msgid="7842675434546657444">"ជំនួស..."</string>
<string name="delete" msgid="1514113991712129054">"លុប"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ប្រើប្រាស់​ផ្លូវកាត់"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"បញ្ច្រាស​ពណ៌"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ការ​កែ​ពណ៌"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"បន្ថយពន្លឺ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"បានសង្កត់​គ្រាប់ចុច​កម្រិតសំឡេង​ជាប់។ បាន​បើក <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"បានសង្កត់​គ្រាប់ចុច​កម្រិតសំឡេង​ជាប់។ បាន​បិទ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"ចុចគ្រាប់ចុច​កម្រិត​សំឡេងទាំងពីរ​ឱ្យជាប់រយៈពេលបីវិនាទី ដើម្បីប្រើ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"ពង្រីក"</string>
<string name="close_button_text" msgid="10603510034455258">"បិទ"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>៖ <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"ឆ្លើយ"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"បដិសេធ"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ដាក់​ចុះ"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"ការ​ហៅ​ចូល"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"ការ​ហៅដែលកំពុងដំណើរការ"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"កំពុងពិនិត្យការ​ហៅ​ចូល"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other">បានជ្រើស <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">បានជ្រើស <xliff:g id="COUNT_0">%1$d</xliff:g></item>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 32e3c4d98578..9a33f06b61fc 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ಮುಂದುವರಿಸಲು ನಿಮ್ಮ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಬಳಸಿ"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಐಕಾನ್"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಕಾನ್ಫಿಗರೇಶನ್ ಅನ್ನು ಓದಲು ಮತ್ತು ಬರೆಯಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ವೀಕ್ಷಣಾ ಅನುಮತಿಯ ಬಳಕೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿ"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ಆ್ಯಪ್‌ಗಾಗಿ ಅನುಮತಿ ಬಳಕೆಯನ್ನು ಪ್ರಾರಂಭಿಸಲು ಹೊಂದಿರುವವರಿಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಸಾಮಾನ್ಯ ಆ್ಯಪ್‌ಗಳಿಗೆ ಎಂದಿಗೂ ಅಗತ್ಯವಿರುವುದಿಲ್ಲ."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"ಪಾಸ್‌ವರ್ಡ್ ನಿಮಯಗಳನ್ನು ಹೊಂದಿಸಿ"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"ಪರದೆ ಲಾಕ್‌ನಲ್ಲಿನ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು ಮತ್ತು ಪಿನ್‌ಗಳ ಅನುಮತಿಸಲಾದ ಅಕ್ಷರಗಳ ಪ್ರಮಾಣವನ್ನು ನಿಯಂತ್ರಿಸಿ."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"ಪರದೆಯ ಅನ್‌ಲಾಕ್ ಪ್ರಯತ್ನಗಳನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ಶಾರ್ಟ್‌ಕಟ್ ಬಳಸಿ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ಬಣ್ಣ ವಿಲೋಮ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"ಪ್ರಖರತೆಯನ್ನು ಕಡಿಮೆ ಮಾಡಿ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದುಕೊಳ್ಳಿ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಲಾಗಿದೆ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಬಳಸಲು ಎರಡೂ ಧ್ವನಿ ಕೀಗಳನ್ನು ಮೂರು ಸೆಕೆಂಡ್‌ಗಳ ಕಾಲ ಒತ್ತಿ ಹಿಡಿದುಕೊಳ್ಳಿ"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"ಹಿಗ್ಗಿಸು"</string>
<string name="close_button_text" msgid="10603510034455258">"ಮುಚ್ಚು"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"ಉತ್ತರಿಸಿ"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"ನಿರಾಕರಿಸಿ"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ಹ್ಯಾಂಗ್ ಅಪ್"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"ಒಳಬರುವ ಕರೆ"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಕರೆ"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"ಒಳಬರುವ ಕರೆಯನ್ನು ಸ್ಕ್ರೀನ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ</item>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 34074312e70d..a395c892dcfa 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"계속하려면 지문을 사용하세요."</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"지문 아이콘"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"앱에서 방해 금지 모드 설정을 읽고 작성하도록 허용합니다."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"권한 사용 보기 시작"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"앱의 권한 사용을 시작하려면 보유자를 허용하세요. 일반 앱에는 필요하지 않습니다."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"비밀번호 규칙 설정"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"화면 잠금 비밀번호와 PIN에 허용되는 길이와 문자 수를 제어합니다."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"화면 잠금 해제 시도 모니터링"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"단축키 사용"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"색상 반전"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"색상 보정"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"밝기 낮추기"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 설정되었습니다."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 중지되었습니다."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 서비스를 사용하려면 두 볼륨 키를 3초 동안 길게 누르세요"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"최대화"</string>
<string name="close_button_text" msgid="10603510034455258">"닫기"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"답변"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"거절"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"전화 끊기"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"수신 전화"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"진행 중인 통화"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"수신 전화 검사 중"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>개 선택됨</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g>개 선택됨</item>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 9a84d0a985e6..f8a800db2ba2 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -298,7 +298,7 @@
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Батареянын кубаты жана трафиктин көлөмү жөнүндө билүү үчүн таптап коюңуз"</string>
<string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="8974401416068943888">"Коопсуз режим"</string>
- <string name="android_system_label" msgid="5974767339591067210">"Android тутуму"</string>
+ <string name="android_system_label" msgid="5974767339591067210">"Android системасы"</string>
<string name="user_owner_label" msgid="8628726904184471211">"Жеке профилге которулуу"</string>
<string name="managed_profile_label" msgid="7316778766973512382">"Жумуш профилине которулуу"</string>
<string name="permgrouplab_contacts" msgid="4254143639307316920">"Байланыштар"</string>
@@ -314,7 +314,7 @@
<string name="permgrouplab_microphone" msgid="2480597427667420076">"Микрофон"</string>
<string name="permgroupdesc_microphone" msgid="1047786732792487722">"аудио жаздыруу"</string>
<string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Кыймыл-аракет"</string>
- <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"кыймыл-аракетиңизге мүмкүнчүлүк алат"</string>
+ <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"кыймыл-аракеттериңизге көз салып турганга мүмкүнчүлүк алат"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"Камера"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"сүрөт жана видео тартууга"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"Чалуулар тизмеси"</string>
@@ -332,7 +332,7 @@
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Көрүнүштү чоңойтуп кичирейтет"</string>
<string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Экрандагы сүрөттү тууралап жайгаштырып, өлчөмүн өзгөртөт."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Жаңсоолорду аткаруу"</string>
- <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Таптап, серпип, чымчып жана башка жаңсоолорду аткара алат."</string>
+ <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Таптап, сүрүп, чымчып жана башка жаңсоолорду аткара алат."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Манжа изинин жаңсоолору"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Түзмөктөгү манжа изинин сенсорунда жасалган жаңсоолорду жаздырып алат."</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Скриншот тартып алуу"</string>
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Улантуу үчүн манжаңыздын изин колдонуңуз"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Манжа изинин сүрөтчөсү"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Колдонмого \"Тынчымды алба\" режиминин конфигурациясын окуу жана жазуу мүмкүнчүлүгүн берет."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"уруксаттын колдонулушун көрүп баштоо"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Колдонмонун пайдаланылышына уруксат берүүгө мүмкүнчүлүк берет. Кадимки колдонмолорго эч качан талап кылынбашы керек."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Сырсөз эрежелерин коюу"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Экран кулпусунун сырсөздөрү менен PIN\'дерине уруксат берилген узундук менен белгилерди көзөмөлдөө."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Экран кулпусун ачуу аракеттерин көзөмөлдөө"</string>
@@ -886,7 +891,7 @@
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"Сиз телефонду бөгөттөн чыгарууга <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес аракет кылдыңыз. Телефон баштапкы абалына келтирилет."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"<xliff:g id="NUMBER">%d</xliff:g> секунддан кийин кайталаңыз."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Сүрөт үлгүсүн унутуп калдыңызбы?"</string>
- <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Каттоо эсеби менен кулпусун ачуу"</string>
+ <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Аккаунт менен кулпусун ачуу"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"Өтө көп үлгү киргизүү аракети болду"</string>
<string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Бөгөттөн чыгарыш үчүн, Google эсебиңиз менен кириңиз."</string>
<string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"Колдонуучунун аты (электрондук почта)"</string>
@@ -1664,12 +1669,13 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Кыска жолду колдонуу"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Түстү инверсиялоо"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Түсүн тууралоо"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Экрандын жарыктыгын төмөндөтүү"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> күйгүзүлдү."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өчүрүлдү."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> кызматын колдонуу үчүн үнүн чоңойтуп/кичирейтүү баскычтарын үч секунд коё бербей басып туруңуз"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Атайын мүмкүнчүлүктөр баскычын таптаганыңызда иштей турган функцияны тандаңыз:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Атайын мүмкүнчүлүктөр жаңсоосу үчүн функцияны тандаңыз (эки манжаңыз менен экрандын ылдый жагынан өйдө карай сүрүңүз):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Атайын мүмкүнчүлүктөр жаңсоосу аркылуу иштетиле турган функцияны тандаңыз (үч манжаңыз менен экрандын ылдый жагынан өйдө карай сүрүңүз):"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Атайын мүмкүнчүлүктөр жаңсоосу үчүн функцияны тандаңыз (эки манжаңыз менен экранды ылдыйдан өйдө сүрүңүз):"</string>
+ <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Атайын мүмкүнчүлүктөр жаңсоосу аркылуу иштетиле турган функцияны тандаңыз (үч манжаңыз менен экранды ылдыйдан өйдө сүрүңүз):"</string>
<string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Функцияларды которуштуруу үчүн, Атайын мүмкүнчүлүктөр баскычын басып, кармап туруңуз."</string>
<string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Функцияларды которуштуруу үчүн, эки манжаңыз менен өйдө сүрүп, кармап туруңуз."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Башка функцияга которулуу үчүн үч манжаңыз менен экранды өйдө сүрүп, кармап туруңуз."</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Чоңойтуу"</string>
<string name="close_button_text" msgid="10603510034455258">"Жабуу"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Жооп берүү"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Четке кагуу"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Чалууну бүтүрүү"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Кирүүчү чалуу"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Учурдагы чалуу"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Кирүүчү чалууну иргөө"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> тандалды</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> тандалды</item>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 9977f9fca122..93f38890326f 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi​-Fi ເທົ່າ​ນັ້ນ"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> ການໂທສຳຮອງ"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ບໍ່ຖືກສົ່ງຕໍ່"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> ຫຼັງຈາກ <xliff:g id="TIME_DELAY">{2}</xliff:g> ວິນາທີ"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ໃຊ້​ລາຍ​ນີ້ວ​ມື​ຂອງ​ທ່ານ​ເພື່ອ​ສືບ​ຕໍ່"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ໄອຄອນລາຍນິ້ວມື"</string>
@@ -685,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ອະນຸຍາດ​​ໃຫ້​ແອັບ​ອ່ານ​ ​ແລະ​ຂຽນການກນຳ​ດຄ່າ ບໍ່​ລົບ​ກວນ."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ເລີ່ມການໃຊ້ສິດອະນຸຍາດການເບິ່ງ"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ອະນຸຍາດໃຫ້ຜູ້ຖືເລີ່ມການໃຊ້ສິດອະນຸຍາດສຳລັບແອັບໃດໜຶ່ງໄດ້. ແອັບປົກກະຕິບໍ່ຄວນຕ້ອງໃຊ້."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ເຂົ້າເຖິງຂໍ້ມູນເຊັນເຊີໃນອັດຕາຕົວຢ່າງສູງ"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"ອະນຸຍາດໃຫ້ແອັບສຸ່ມຕົວຢ່າງຂໍ້ມູນເຊັນເຊີໃນອັດຕາທີ່ຫຼາຍກວ່າ 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"ຕັ້ງຄ່າກົດຂອງລະຫັດຜ່ານ"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"ຄວບຄຸມຄວາມຍາວ ແລະຕົວອັກສອນທີ່ອະ​ນຸ​ຍາດ​ໃຫ້​ຢູ່​ໃນລະ​ຫັດລັອກໜ້າຈໍ ແລະ PIN."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"ຕິດຕາມການພະຍາຍາມປົດລັອກໜ້າຈໍ"</string>
@@ -1665,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ໃຊ້ປຸ່ມລັດ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ການປີ້ນສີ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ການແກ້ໄຂຄ່າສີ"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"ຫຼຸດຄວາມສະຫວ່າງ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ກົດປຸ່ມລະດັບສຽງຄ້າງໄວ້. ເປີດໃຊ້ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ແລ້ວ."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ກົດປຸ່ມລະດັບສຽງຄ້າງໄວ້. ປິດ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ໄວ້ແລ້ວ."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"ກົດປຸ່ມສຽງທັງສອງພ້ອມກັນຄ້າງໄວ້ສາມວິນາທີເພື່ອໃຊ້ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1881,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"ຂະຫຍາຍອອກ"</string>
<string name="close_button_text" msgid="10603510034455258">"ປິດ"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"ຮັບສາຍ"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"ປະຕິເສດ"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ວາງສາຍ"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"ສາຍໂທເຂົ້າ"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"ສາຍໂທອອກ"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"ກຳລັງກວດສອບສາຍໂທເຂົ້າ"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ຖືກເລືອກ​ແລ້ວ</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ຖືກເລືອກ​ແລ້ວ</item>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 5f72e07a6959..9c1e09de02b6 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -585,6 +585,7 @@
<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>
+ <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>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Piršto antspaudo piktograma"</string>
@@ -690,6 +691,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Leidžiama programai skaityti ir rašyti „Do Not Disturb“ konfigūraciją."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"pradėti peržiūrėti leidimo naudojimą"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Leidžia savininkui pradėti naudoti programos leidimą. Įprastoms programoms to neturėtų prireikti."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Nustatyti slaptažodžio taisykles"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Valdykite, kokio ilgio ekrano užrakto slaptažodžius ir PIN kodus galima naudoti."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Stebėti bandymus atrakinti ekraną"</string>
@@ -1708,6 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Naudoti spartųjį klavišą"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Spalvų inversija"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Spalvų taisymas"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Šviesumo mažinimas"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ įjungta."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ išjungta."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Jei norite naudoti „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“, paspauskite abu garsumo klavišus ir palaikykite tris sekundes"</string>
@@ -1942,6 +1948,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Padidinti"</string>
<string name="close_button_text" msgid="10603510034455258">"Uždaryti"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Atsakyti"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Atmesti"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Baigti pok."</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Gaunamasis skambutis"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Vykstantis skambutis"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Gaunamojo skambučio tikrinimas"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one">Pasir. <xliff:g id="COUNT_1">%1$d</xliff:g> elem.</item>
<item quantity="few">Pasir. <xliff:g id="COUNT_1">%1$d</xliff:g> elem.</item>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 37e417fa6a56..d77cfd4c5bad 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -582,6 +582,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Lai turpinātu, izmantojiet pirksta nospiedumu"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Pirksta nospieduma ikona"</string>
@@ -687,6 +688,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ļauj lietotnei lasīt un rakstīt režīma “Netraucēt” konfigurāciju."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Datu skatīšana par izmantojamajām atļaujām"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ļauj atļaujas īpašniekam sākt lietotnes atļauju izmantošanu. Parastām lietotnēm tas nekad nav nepieciešams."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Paroles kārtulu iestatīšana"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrolēt ekrāna bloķēšanas paroļu un PIN garumu un tajos atļautās rakstzīmes."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekrāna atbloķēšanas mēģinājumu pārraudzīšana"</string>
@@ -1686,6 +1691,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Izmantot saīsni"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Krāsu inversija"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Krāsu korekcija"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Spilgtuma samazināšana"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika ieslēgts."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika izslēgts."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Lai izmantotu pakalpojumu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, nospiediet abus skaļuma taustiņus un turiet tos trīs sekundes."</string>
@@ -1911,6 +1917,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maksimizēt"</string>
<string name="close_button_text" msgid="10603510034455258">"Aizvērt"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Atbildēt"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Noraidīt"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Pārtraukt"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Ienākošais zvans"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Pašreizējais zvans"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Ienākošā zvana filtrēšana"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="zero"><xliff:g id="COUNT_1">%1$d</xliff:g> atlasīti</item>
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> atlasīts</item>
diff --git a/core/res/res/values-mcc310-mnc950-si/strings.xml b/core/res/res/values-mcc310-mnc950-si/strings.xml
new file mode 100644
index 000000000000..26fe4ac265a3
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc950-si/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="615419724607901560">"SIM MM#2 ප්‍රතිපාදනය නොකරයි"</string>
+ <string name="mmcc_illegal_ms" msgid="7801541624846497489">"SIM MM#3 ඉඩ නොදේ"</string>
+ <string name="mmcc_illegal_me" msgid="7066936962628406316">"දුරකථනය MM#6 ඉඩ නොදේ"</string>
+</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 5666cc23f2be..54ac67b0c3b6 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Користете го отпечатокот за да продолжите"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Икона за отпечатоци"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дозволува апликацијата да чита и пишува конфигурација Не вознемирувај."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"започнете со користење на дозволата за приказ"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дозволува сопственикот да почне со користење на дозволата за апликација. Не треба да се користи за стандардни апликации."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Постави правила за лозинката"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Контролирај ги должината и знаците што се дозволени за лозинки и PIN-броеви за отклучување екран."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Следи ги обидите за отклучување на екранот"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Користи кратенка"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Инверзија на бои"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Корекција на бои"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Намалете ја осветленоста"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е вклучена."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е исклучена."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Притиснете ги и задржете ги двете копчиња за јачина на звукот во траење од три секунди за да користите <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Зголеми"</string>
<string name="close_button_text" msgid="10603510034455258">"Затвори"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Одговори"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Одбиј"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Спушти"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Дојдовен повик"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Тековен повик"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Проверка на дојдовен повик"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> е избрана</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> се избрани</item>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index e0d4b0179624..1329fb096836 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"വൈഫൈ മാത്രം"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> ബാക്കപ്പ് കോളിംഗ്"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: കൈമാറിയില്ല"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> നിമിഷത്തിനുശേഷം <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"തുടരുന്നതിന് നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ഫിംഗർപ്രിന്റ് ഐക്കൺ"</string>
@@ -685,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"\'ശല്യപ്പെടുത്തരുത്\' കോൺഫിഗറേഷൻ വായിക്കുന്നതിനും എഴുതുന്നതിനും ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"അനുമതി ഉപയോഗം കാണാൻ ആരംഭിക്കുക"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ഒരു ആപ്പിനുള്ള അനുമതി ഉപയോഗം ആരംഭിക്കാൻ ഹോൾഡറിനെ അനുവദിക്കുന്നു. സാധാരണ ആപ്പുകൾക്ക് ഒരിക്കലും ആവശ്യമില്ല."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"പാസ്‌വേഡ് നിയമങ്ങൾ സജ്ജീകരിക്കുക"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"സ്‌ക്രീൻ ലോക്ക് പാസ്‌വേഡുകളിലും PIN-കളിലും അനുവദിച്ചിരിക്കുന്ന ദൈർഘ്യവും പ്രതീകങ്ങളും നിയന്ത്രിക്കുക."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"സ്‌ക്രീൻ അൺലോക്ക് ശ്രമങ്ങൾ നിരീക്ഷിക്കുക"</string>
@@ -1665,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"കുറുക്കുവഴി ഉപയോഗിക്കുക"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"വർണ്ണ വിപര്യയം"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"നിറം ക്രമീകരിക്കൽ"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"തെളിച്ചം കുറയ്ക്കുക"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"വോളിയം കീകൾ പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓണാക്കി."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"വോളിയം കീകൾ അമർത്തിപ്പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓഫാക്കി."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഉപയോഗിക്കാൻ, രണ്ട് വോളിയം കീകളും മൂന്ന് സെക്കൻഡ് അമർത്തിപ്പിടിക്കുക"</string>
@@ -1881,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"വലുതാക്കുക"</string>
<string name="close_button_text" msgid="10603510034455258">"അവസാനിപ്പിക്കുക"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"മറുപടി നൽകുക"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"നിരസിക്കുക"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"കോൾ നിർത്തുക"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"ഇൻകമിംഗ് കോൾ"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"സജീവമായ കോൾ"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"ഇൻകമിംഗ് കോൾ സ്‌ക്രീൻ ചെയ്യൽ"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> എണ്ണം തിരഞ്ഞെടുത്തു</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> എണ്ണം തിരഞ്ഞെടുത്തു</item>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 891ac47191e1..4b1dbb358adf 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -325,7 +325,7 @@
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"таны биеийн байдлын талаарх мэдрэгч бүхий өгөгдөлд нэвтрэх"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Цонхны агуулгыг авах"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Таны харилцан үйлчлэх цонхны контентоос шалгах."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Хүрч танихыг асаах"</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Хүрэлтээр сонсохыг асаах"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Товшсон зүйлсийг чангаар хэлэх ба дэлгэцийг дохио ашиглан таних боломжтой."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Бичсэн текстээ ажиглах"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Кредит картын дугаар болон нууц үг зэрэг хувийн датаг агуулж байна."</string>
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Үргэлжлүүлэхийн тулд хурууны хээгээ ашиглаарай"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Хурууны хээний дүрс"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Апп-д Бүү саад бол тохируулгыг уншиж, бичихийг зөвшөөрөх"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"зөвшөөрлийн ашиглалтыг харж эхлэх"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Эзэмшигчид аппын зөвшөөрлөө ашиглаж эхлэхийг зөвшөөрдөг. Энгийн аппуудад шаардлагагүй."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"түүврийн өндөр хувиар мэдрэгчийн өгөгдөлд хандах"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Аппад 200 Гц-ээс их хувиар мэдрэгчийн өгөгдлийг түүвэрлэх боломжийг олгодог"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Нууц үгний дүрмийг тохируулах"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Дэлгэц түгжих нууц үг болон ПИН кодны урт болон нийт тэмдэгтийн уртыг хянах."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Дэлгэцийн түгжээг тайлах оролдлогыг хянах"</string>
@@ -1002,9 +1005,9 @@
<string name="searchview_description_clear" msgid="1989371719192982900">"Асуулгыг цэвэрлэх"</string>
<string name="searchview_description_submit" msgid="6771060386117334686">"Асуулгыг илгээх"</string>
<string name="searchview_description_voice" msgid="42360159504884679">"Дуут хайлт"</string>
- <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Хүрч хайх функцийг идэвхтэй болгох уу?"</string>
- <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> нь Хүрч танихыг идэвхжүүлэхийг шаардаж байна. Хүрч таних идэвхжсэн үед та хуруун доороо юу байгааг сонсох, тайлбарыг харах боломжтой ба таблеттайгаа дохиогоор харилцах боломжтой."</string>
- <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> нь Хүрч танихыг идэвхжүүлэхийг шаардаж байна. Хүрч таних идэвхжсэн тохиолдолд та хуруун доороо юу байгааг сонсох, тайлбарыг харах боломжтой ба утастайгаа дохиогоор харилцах боломжтой."</string>
+ <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Хүрэлтээр сонсохыг идэвхжүүлэх үү?"</string>
+ <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> нь Хүрэлтээр сонсохыг идэвхжүүлэхийг шаардаж байна. Хүрэлтээр сонсох идэвхжсэн үед та хуруун доороо юу байгааг сонсох, тайлбарыг харах боломжтой ба таблеттайгаа дохиогоор харилцах боломжтой."</string>
+ <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> нь Хүрэлтээр сонсохыг идэвхжүүлэхийг шаардаж байна. Хүрэлтээр сонсох идэвхжсэн тохиолдолд та хуруун доороо юу байгааг сонсох, тайлбарыг харах боломжтой ба утастайгаа дохиогоор харилцах боломжтой."</string>
<string name="oneMonthDurationPast" msgid="4538030857114635777">"1 сарын өмнө"</string>
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"1 сарын өмнө"</string>
<plurals name="last_num_days" formatted="false" msgid="687443109145393632">
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Товчлол ашиглах"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Өнгө хувиргалт"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Өнгөний засвар"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Гэрэлтүүлгийг багасгах"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г асаалаа."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г унтраалаа."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г ашиглахын тулд дууны түвшнийг ихэсгэх, багасгах түлхүүрийг 3 секундийн турш зэрэг дарна уу"</string>
@@ -1871,7 +1875,7 @@
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Мэдэгдсэн"</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>
+ <string name="expand_action_accessibility" msgid="1947657036871746627">"асаах/унтраах өргөтгөл"</string>
<string name="usb_midi_peripheral_name" msgid="490523464968655741">"Андройд USB Peripheral Port"</string>
<string name="usb_midi_peripheral_manufacturer_name" msgid="7557148557088787741">"Android"</string>
<string name="usb_midi_peripheral_product_name" msgid="2836276258480904434">"USB Peripheral Port"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Томруулах"</string>
<string name="close_button_text" msgid="10603510034455258">"Хаах"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Хариулах"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Татгалзах"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Таслах"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Ирсэн дуудлага"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Дуудлага хийгдэж байна"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Ирсэн дуудлагыг харуулж байна"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> сонгосон</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> сонгосон</item>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index d57c4c1d0d05..ea222907eee8 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"केवळ वाय-फाय"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> बॅकअप कॉलिंग"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: फॉरवर्ड केला नाही"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकंदांनंतर <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"पुढे सुरू ठेवण्‍यासाठी तुमची फिंगरप्रिंट वापरा"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"फिंगरप्रिंट आयकन"</string>
@@ -685,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"व्यत्यय आणू नका कॉंफिगरेशन वाचण्यासाठी आणि लिहिण्यासाठी ॲपला अनुमती देते."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"व्ह्यू परवानगी वापर सुरू करा"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"धारकास अ‍ॅपसाठी परवानगी वापरणे सुरू करण्याची अनुमती देते. सामान्य अ‍ॅप्ससाठी कधीही आवश्यकता नसते."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"पासवर्ड नियम सेट करा"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"स्क्रीन लॉक पासवर्ड आणि पिन मध्ये अनुमती दिलेले लांबी आणि वर्ण नियंत्रित करा."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"स्क्रीन अनलॉक प्रयत्नांचे परीक्षण करा"</string>
@@ -1665,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"शॉर्टकट वापरा"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"रंगांची उलटापालट"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"रंग सुधारणा"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"ब्राइटनेस कमी करा"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> सुरू केला आहे."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> बंद केले आहे."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> वापरण्यासाठी दोन्ही व्हॉल्युम की तीन सेकंद दाबा आणि धरून ठेवा"</string>
@@ -1881,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"मोठे करा"</string>
<string name="close_button_text" msgid="10603510034455258">"बंद करा"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"उत्तर द्या"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"नकार द्या"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"कॉल बंद करा"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"इनकमिंग कॉल"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"सुरू असलेला कॉल"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"इनकमिंग कॉल स्क्रीन करत आहे"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> निवडले</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> निवडला</item>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index ec0aa5d6fae6..0a1205fe608b 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -325,7 +325,7 @@
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"akses data penderia tentang tanda vital anda"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Dapatkan kembali kandungan tetingkap"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Periksa kandungan tetingkap yang berinteraksi dengan anda."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Hidupkan Jelajah melalui Sentuhan"</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Hidupkan Teroka melalui Sentuhan"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Item yang diketik akan dituturkan dengan lantang dan skrin boleh dijelajah menggunakan gerak isyarat."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Perhatikan teks yang anda taip"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Termasuk data peribadi seperti nombor kad kredit dan kata laluan."</string>
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Gunakan cap jari anda untuk teruskan"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon cap jari"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Membenarkan apl membaca dan menulis konfigurasi Jangan Ganggu."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"mulakan lihat penggunaan kebenaran"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Membenarkan pemegang memulakan penggunaan kebenaran untuk apl. Tidak sekali-kali diperlukan untuk apl biasa."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Tetapkan peraturan kata laluan"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Mengawal panjang dan aksara yang dibenarkan dalam kata laluan dan PIN kunci skrin."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Pantau percubaan buka kunci skrin"</string>
@@ -1002,9 +1007,9 @@
<string name="searchview_description_clear" msgid="1989371719192982900">"Pertanyaan jelas"</string>
<string name="searchview_description_submit" msgid="6771060386117334686">"Serah pertanyaan"</string>
<string name="searchview_description_voice" msgid="42360159504884679">"Carian suara"</string>
- <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Dayakan Jelajah melalui Sentuhan?"</string>
- <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> ingin mendayakan Jelajah melalui Sentuhan. Apabila Jelajah melalui Sentuhan didayakan, anda boleh mendengar atau melihat penerangan tentang apa di bawah jari anda atau melakukan gerak isyarat untuk berinteraksi dengan tablet."</string>
- <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> ingin mendayakan Jelajah melalui Sentuhan. Apabila Jelajah melalui Sentuhan didayakan, anda boleh mendengar atau melihat penerangan tentang apa di bawah jari anda atau melakukan gerak isyarat untuk berinteraksi dengan telefon."</string>
+ <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Dayakan Teroka melalui Sentuhan?"</string>
+ <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> ingin mendayakan Teroka melalui Sentuhan. Apabila Teroka melalui Sentuhan didayakan, anda boleh mendengar atau melihat penerangan tentang apa-apa di bawah jari anda atau melakukan gerak isyarat untuk berinteraksi dengan tablet."</string>
+ <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> ingin mendayakan Teroka melalui Sentuhan. Apabila Teroka melalui Sentuhan didayakan, anda boleh mendengar atau melihat penerangan tentang apa-apa di bawah jari anda atau melakukan gerak isyarat untuk berinteraksi dengan telefon."</string>
<string name="oneMonthDurationPast" msgid="4538030857114635777">"1 bulan yang lalu"</string>
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Sebelum 1 bulan yang lalu"</string>
<plurals name="last_num_days" formatted="false" msgid="687443109145393632">
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gunakan Pintasan"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Penyongsangan Warna"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Pembetulan Warna"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Kurangkan Kecerahan"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Kekunci kelantangan ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dihidupkan."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Kekunci kelantangan ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dimatikan."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tekan dan tahan kedua-dua kekunci kelantangan selama tiga saat untuk menggunakan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maksimumkan"</string>
<string name="close_button_text" msgid="10603510034455258">"Tutup"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Jawapan"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Tolak"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Tamatkan Panggilan"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Panggilan masuk"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Panggilan sedang berlangsung"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Menyaring panggilan masuk"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> dipilih</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> dipilih</item>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 7b4a430fc1bb..845c24f97ce2 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ရှေ့ဆက်ရန် သင့်လက်ဗွေကို သုံးပါ"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"လက်ဗွေ သင်္ကေတ"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"မနှောင့်ယှက်ရန် ချိန်ညှိမှုကို အပ်ဖ်များ ဖတ်ခြင်း ပြင်ခြင်းပြုလုပ်နိုင်ရန် ခွင့်ပြုမည်။"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"အစမြင်ကွင်း ခွင့်ပြုချက် အသုံးပြုမှု"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"အက်ပ်တစ်ခုအတွက် ခွင့်ပြုချက်စတင်အသုံးပြုမှုကို ကိုင်ဆောင်သူအား ခွင့်ပြုသည်။ ပုံမှန်အက်ပ်များအတွက် ဘယ်သောအခါမျှ မလိုအပ်ပါ။"</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"စကားဝှက်စည်းမျဥ်းကိုသတ်မှတ်ရန်"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"မျက်နှာပြင်သော့ခတ်သည့် စကားဝှက်များနှင့် PINများရှိ ခွင့်ပြုထားသည့် စာလုံးအရေအတွက်နှင့် အက္ခရာများအား ထိန်းချုပ်ရန်။"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"မျက်နှာပြင်လော့ခ်ဖွင့်ရန် ကြိုးပမ်းမှုများကို စောင့်ကြည့်ပါ"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ဖြတ်လမ်းလင့်ခ်ကို သုံးရန်"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"အရောင် ပြောင်းပြန်လှန်ခြင်း"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"အရောင်ပြင်ဆင်ခြင်း"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"တောက်ပမှုကို လျှော့ခြင်း"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"အသံခလုတ်များကို ဖိထားသည်။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ဖွင့်လိုက်သည်။"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"အသံခလုတ်များကို ဖိထားသည်။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ပိတ်လိုက်သည်။"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ကို သုံးရန် အသံအတိုးအလျှော့ ခလုတ်နှစ်ခုလုံးကို သုံးစက္ကန့်ကြာ ဖိထားပါ"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"အများဆုံး လုပ်ပေးရန်"</string>
<string name="close_button_text" msgid="10603510034455258">"ပိတ်ရန်"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>− <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"ဖုန်းကိုင်ရန်"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"ငြင်းပယ်ရန်"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ဖုန်းချရန်"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"အဝင်ခေါ်ဆိုမှု"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"လက်ရှိခေါ်ဆိုမှု"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"အဝင်ခေါ်ဆိုမှုကို စစ်ဆေးနေသည်"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ရွေးချယ်ပြီးပါပြီ</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ရွေးချယ်ပြီးပါပြီ</item>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 496818426714..4e05659a0732 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Bruk fingeravtrykket ditt for å fortsette"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon for fingeravtrykk"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Lar appen lese og skrive konfigurasjon av Ikke forstyrr."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start visning av bruk av tillatelser"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Lar innehaveren starte bruk av tillatelser for en app. Dette skal aldri være nødvendig for vanlige apper."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Angi passordregler"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrollerer tillatt lengde og tillatte tegn i passord og PIN-koder for opplåsing av skjermen."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Overvåk forsøk på å låse opp skjermen"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Bruk snarveien"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Fargeinvertering"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Fargekorrigering"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Reduser lysstyrken"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volumtastene holdes inne. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er slått på."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volumtastene holdes inne. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er slått av."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Trykk og hold inne begge volumtastene i tre sekunder for å bruke <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maksimer"</string>
<string name="close_button_text" msgid="10603510034455258">"Lukk"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g><xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Svar"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Avvis"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Legg på"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Innkommende anrop"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Pågående samtale"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrerer et innkommende anrop"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> er valgt</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> er valgt</item>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 541ace80518c..f76011ae439c 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi मात्र"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> ब्याकअप कलिङ"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अगाडि पठाइएको छैन"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> पछि <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकेन्ड"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"जारी राख्न आफ्नो फिंगरप्रिन्ट प्रयोग गर्नुहोस्"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"फिंगरप्रिन्ट आइकन"</string>
@@ -685,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"बाधा नपुर्याउँनुहोस् कन्फिगरेसन पढ्न र लेख्‍नको लागि एपलाई अनुमति दिनुहोस्।"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"हेर्ने अनुमतिको प्रयोग सुरु गर्नुहोस्"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"वाहकलाई कुनै एपसम्बन्धी अनुमतिको प्रयोग सुरु गर्न दिन्छ। साधारण एपहरूलाई कहिल्यै आवश्यक नपर्नु पर्ने हो।"</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"पासवर्ड नियमहरू मिलाउनुहोस्"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"स्क्रिन लक पासवर्ड र PIN हरूमा अनुमति दिइएको लम्बाइ र वर्णहरूको नियन्त्रण गर्नुहोस्।"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"मनिटरको स्क्रिन अनलक गर्ने प्रयासहरू"</string>
@@ -1665,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"सर्टकट प्रयोग गर्नुहोस्"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"रङ्ग उल्टाउने सुविधा"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"रङ्ग सच्याउने सुविधा"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"स्क्रिनको चमक घटाउनुहोस्"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अन भयो।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अफ भयो।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> प्रयोग गर्न दुवै भोल्युम कुञ्जीहरूलाई तीन सेकेन्डसम्म थिचिराख्नुहोस्"</string>
@@ -1881,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"ठुलो बनाउनुहोस्"</string>
<string name="close_button_text" msgid="10603510034455258">"बन्द गर्नुहोस्"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"कलको जवाफ दिनु…"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"अस्वीकार गर्नुहोस्"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"फोन राख्नुहोस्"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"आगमन कल"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"भइरहेको कल"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"आगमन कल जाँचिँदै छ"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> चयन गरियो</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> चयन गरियो</item>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 7f23cbbbb6dd..01d4a70bd958 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -515,9 +515,9 @@
<string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Hiermee kan de app pakketten ontvangen die via multicastadressen naar alle apparaten in een wifi-netwerk worden verzonden, niet alleen naar je Android TV-apparaat. Het stroomgebruik ligt hierbij hoger dan in de niet-multicastmodus."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Hiermee kan de app pakketten ontvangen die via multicastadressen naar alle apparaten in een wifi-netwerk worden verzonden, niet alleen naar je telefoon. Het stroomgebruik ligt hierbij hoger dan in de niet-multicastmodus."</string>
<string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"Bluetooth-instellingen openen"</string>
- <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Hiermee kan de app de lokale bluetooth-tablet configureren en externe apparaten zoeken en koppelen."</string>
- <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Hiermee kan de app Bluetooth op je Android TV-apparaat configureren en externe apparaten zoeken en koppelen."</string>
- <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Hiermee kan de app de lokale bluetooth-telefoon configureren en externe apparaten zoeken en koppelen."</string>
+ <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Hiermee kan de app de lokale bluetooth-tablet instellen en externe apparaten zoeken en koppelen."</string>
+ <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Hiermee kan de app Bluetooth op je Android TV-apparaat isntellen en externe apparaten zoeken en koppelen."</string>
+ <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Hiermee kan de app de lokale bluetooth-telefoon instellen en externe apparaten zoeken en koppelen."</string>
<string name="permlab_accessWimaxState" msgid="7029563339012437434">"WiMAX-verbinding maken en verbreken"</string>
<string name="permdesc_accessWimaxState" msgid="5372734776802067708">"Hiermee kan de app bepalen of WiMAX is ingeschakeld en informatie bekijken over alle WiMAX-netwerken waarmee verbinding is gemaakt."</string>
<string name="permlab_changeWimaxState" msgid="6223305780806267462">"WiMAX-status wijzigen"</string>
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Gebruik je vingerafdruk om door te gaan."</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Vingerafdruk-icoon"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Hiermee kan de app configuratie voor Niet storen lezen en schrijven."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"rechtengebruik starten"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Hiermee kan de houder het rechtengebruik voor een app starten. Nooit vereist voor normale apps."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Wachtwoordregels instellen"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"De lengte en het aantal tekens beheren die zijn toegestaan in wachtwoorden en pincodes voor schermvergrendeling."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Pogingen voor schermontgrendeling bijhouden"</string>
@@ -1346,7 +1351,7 @@
<string name="select_input_method" msgid="3971267998568587025">"Invoermethode selecteren"</string>
<string name="show_ime" msgid="6406112007347443383">"Dit op het scherm weergeven terwijl het fysieke toetsenbord actief is"</string>
<string name="hardware" msgid="1800597768237606953">"Virtueel toetsenbord tonen"</string>
- <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"Fysiek toetsenbord configureren"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"Fysiek toetsenbord instellen"</string>
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Tik om een taal en indeling te selecteren"</string>
<string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
@@ -1369,7 +1374,7 @@
<string name="ext_media_unmountable_notification_message" product="automotive" msgid="2274596120715020680">"Je moet het apparaat misschien opnieuw formatteren. Tik om het uit te werpen."</string>
<string name="ext_media_unsupported_notification_title" msgid="4358280700537030333">"<xliff:g id="NAME">%s</xliff:g> niet ondersteund"</string>
<string name="ext_media_unsupported_notification_title" product="automotive" msgid="6004193172658722381">"<xliff:g id="NAME">%s</xliff:g> werkt niet"</string>
- <string name="ext_media_unsupported_notification_message" msgid="917738524888367560">"Dit apparaat biedt geen ondersteuning voor deze <xliff:g id="NAME">%s</xliff:g>. Tik om te configureren in een ondersteunde indeling."</string>
+ <string name="ext_media_unsupported_notification_message" msgid="917738524888367560">"Dit apparaat biedt geen ondersteuning voor deze <xliff:g id="NAME">%s</xliff:g>. Tik om in te stellen in een ondersteunde indeling."</string>
<string name="ext_media_unsupported_notification_message" product="tv" msgid="7744945987775645685">"Dit apparaat biedt geen ondersteuning voor deze <xliff:g id="NAME">%s</xliff:g>. Selecteer om in te stellen in een ondersteunde indeling."</string>
<string name="ext_media_unsupported_notification_message" product="automotive" msgid="3412494732736336330">"Je moet het apparaat misschien opnieuw formatteren"</string>
<string name="ext_media_badremoval_notification_title" msgid="4114625551266196872">"<xliff:g id="NAME">%s</xliff:g> is onverwacht verwijderd"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sneltoets gebruiken"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Kleurinversie"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Kleurcorrectie"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Helderheid verlagen"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volumetoetsen ingedrukt gehouden. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> is ingeschakeld."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volumetoetsen ingedrukt gehouden. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> uitgeschakeld."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Houd beide volumetoetsen drie seconden ingedrukt om <xliff:g id="SERVICE_NAME">%1$s</xliff:g> te gebruiken"</string>
@@ -1856,7 +1862,7 @@
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Afspraken"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Slapen"</string>
- <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> dempt sommige geluiden"</string>
+ <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> zet sommige geluiden uit"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Er is een intern probleem met je apparaat. Het apparaat kan instabiel zijn totdat u het apparaat terugzet naar de fabrieksinstellingen."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Er is een intern probleem met je apparaat. Neem contact op met de fabrikant voor meer informatie."</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD-verzoek gewijzigd in normaal gesprek"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximaliseren"</string>
<string name="close_button_text" msgid="10603510034455258">"Sluiten"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Beantwoorden"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Weigeren"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Ophangen"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Inkomend gesprek"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Actief gesprek"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Een inkomend gesprek screenen"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> geselecteerd</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> geselecteerd</item>
@@ -1924,7 +1936,7 @@
<string name="demo_starting_message" msgid="6577581216125805905">"Demo starten…"</string>
<string name="demo_restarting_message" msgid="1160053183701746766">"Apparaat resetten…"</string>
<string name="suspended_widget_accessibility" msgid="6331451091851326101">"<xliff:g id="LABEL">%1$s</xliff:g> uitgeschakeld"</string>
- <string name="conference_call" msgid="5731633152336490471">"Conferencecall"</string>
+ <string name="conference_call" msgid="5731633152336490471">"Telefonische vergadering"</string>
<string name="tooltip_popup_title" msgid="7863719020269945722">"Knopinfo"</string>
<string name="app_category_game" msgid="4534216074910244790">"Games"</string>
<string name="app_category_audio" msgid="8296029904794676222">"Muziek en audio"</string>
@@ -2000,7 +2012,7 @@
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wil segmenten van <xliff:g id="APP_2">%2$s</xliff:g> weergeven"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Bewerken"</string>
<string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"Trillen bij gesprekken en meldingen"</string>
- <string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"Gesprekken en meldingen zijn gedempt"</string>
+ <string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"Telefoon- en meldingsgeluid wordt uitgezet"</string>
<string name="notification_channel_system_changes" msgid="2462010596920209678">"Systeemwijzigingen"</string>
<string name="notification_channel_do_not_disturb" msgid="7832584281883687653">"Niet storen"</string>
<string name="zen_upgrade_notification_visd_title" msgid="2001148984371968620">"Nieuw: \'Niet storen\' verbergt meldingen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index f02d1573b7b2..e14578758c49 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"କେବଳ ୱାଇ-ଫାଇ"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> ବ୍ୟାକଅପ୍ କଲିଂ"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ଫରୱାର୍ଡ କରାଯାଇନାହିଁ"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> ସେକେଣ୍ଡ ପରେ"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ଜାରି ରଖିବାକୁ ଆପଣଙ୍କ ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ଟିପଚିହ୍ନ ଆଇକନ୍"</string>
@@ -685,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" କନଫିଗରେଶନ୍‍ ପଢ଼ିବା ତଥା ଲେଖିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ଅନୁମତି ବ୍ୟବହାର ଦେଖିବା ଆରମ୍ଭ କରନ୍ତୁ"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ଏକ ଆପ୍ ପାଇଁ ଅନୁମତିର ବ୍ୟବହାର ଆରମ୍ଭ କରିବାକୁ ଧାରକକୁ ଅନୁମତି ଦେଇଥାଏ। ସାଧାରଣ ଆପ୍‌ଗୁଡ଼ିକ ପାଇଁ ଏହା ଆବଶ୍ୟକ ନୁହେଁ।"</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"ପାସ୍‌ୱର୍ଡ ନିୟମାବଳୀ ସେଟ୍ କରନ୍ତୁ"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"ଲକ୍‍ ସ୍କ୍ରୀନ୍‍ ପାସ୍‌ୱର୍ଡ ଓ PINରେ ଅନୁମୋଦିତ ଦୀର୍ଘତା ଓ ବର୍ଣ୍ଣ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ।"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"ସ୍କ୍ରୀନ୍-ଅନଲକ୍ କରିବା ଉଦ୍ୟମ ନୀରିକ୍ଷଣ କରନ୍ତୁ"</string>
@@ -1665,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ଶର୍ଟକଟ୍‍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ରଙ୍ଗ ବଦଳାଇବାର ସୁବିଧା"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"ଉଜ୍ଜ୍ୱଳତା କମାନ୍ତୁ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଚାଲୁ ହୋଇଛି।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବନ୍ଦ ହୋଇଛି।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବ୍ୟବହାର କରିବାକୁ ତିନି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ୍‍ କୀ ଦବାଇ ଧରି ରଖନ୍ତୁ"</string>
@@ -1881,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"ବଡ଼ କରନ୍ତୁ"</string>
<string name="close_button_text" msgid="10603510034455258">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"ଉତ୍ତର ଦିଅନ୍ତୁ"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"ଅଗ୍ରାହ୍ୟ କର"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ସମାପ୍ତ କରନ୍ତୁ"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"ଇନକମିଂ କଲ୍"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"ଚାଲିଥିବା କଲ୍"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"ଏକ ଇନକମିଂ କଲକୁ ସ୍କ୍ରିନ୍ କରୁଛି"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ଚୟନିତ</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ଚୟନିତ</item>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index a717af6ae89b..c51845b82563 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"ਸਿਰਫ਼ ਵਾਈ-ਫਾਈ"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> ਬੈਕਅੱਪ ਕਾਲਿੰਗ"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ਅੱਗੇ ਨਹੀਂ ਭੇਜਿਆ ਗਿਆ"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> ਸਕਿੰਟਾਂ ਬਾਅਦ"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ਜਾਰੀ ਰੱਖਣ ਲਈ ਆਪਣਾ ਫਿੰਗਰਪ੍ਰਿੰਟ ਵਰਤੋ"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਤੀਕ"</string>
@@ -685,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ਐਪ ਨੂੰ ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ ਕੌਂਫਿਗਰੇਸ਼ਨ ਨੂੰ ਪੜ੍ਹਨ ਅਤੇ ਲਿਖਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ਇਜਾਜ਼ਤ ਵਰਤੋਂ ਦੇਖਣਾ ਸ਼ੁਰੂ ਕਰੋ"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ਧਾਰਕ ਨੂੰ ਕਿਸੇ ਹੋਰ ਐਪ ਲਈ ਇਜਾਜ਼ਤ ਵਰਤੋਂ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਦਿੰਦਾ ਹੈ। ਸਧਾਰਨ ਐਪਾਂ ਲਈ ਕਦੇ ਵੀ ਲੋੜੀਂਦਾ ਨਹੀਂ ਹੋਵੇਗਾ।"</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"ਪਾਸਵਰਡ ਨਿਯਮ ਸੈੱਟ ਕਰੋ"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"ਸਕ੍ਰੀਨ ਲਾਕ ਪਾਸਵਰਡਾਂ ਅਤੇ ਪਿੰਨ ਵਿੱਚ ਆਗਿਆ ਦਿੱਤੀ ਲੰਮਾਈ ਅਤੇ ਅੱਖਰਾਂ ਤੇ ਨਿਯੰਤਰਣ ਪਾਓ।"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"ਸਕ੍ਰੀਨ ਅਣਲਾਕ ਕਰਨ ਦੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ \'ਤੇ ਨਿਗਰਾਨੀ ਰੱਖੋ"</string>
@@ -1665,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ਸ਼ਾਰਟਕੱਟ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ਰੰਗ ਪਲਟਨਾ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ਰੰਗ ਸੁਧਾਈ"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"ਚਮਕ ਘਟਾਓ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਦਬਾ ਕੇ ਰੱਖੀਆਂ ਗਈਆਂ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਦਬਾ ਕੇ ਰੱਖੀਆਂ ਗਈਆਂ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ ਕੁੰਜੀਆਂ ਨੂੰ 3 ਸਕਿੰਟਾਂ ਲਈ ਦਬਾਈ ਰੱਖੋ"</string>
@@ -1881,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"ਵੱਡਾ ਕਰੋ"</string>
<string name="close_button_text" msgid="10603510034455258">"ਬੰਦ ਕਰੋ"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"ਜਵਾਬ ਦਿਓ"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ਸਮਾਪਤ ਕਰੋ"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"ਇਨਕਮਿੰਗ ਕਾਲ"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"ਜਾਰੀ ਕਾਲ"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"ਇਨਕਮਿੰਗ ਕਾਲ ਦੀ ਸਕ੍ਰੀਨਿੰਗ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ਚੁਣਿਆ ਗਿਆ</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ਚੁਣਿਆ ਗਿਆ</item>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index f73b38246e5a..6f54b413af03 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -585,6 +585,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Użyj odcisku palca, by kontynuować"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona odcisku palca"</string>
@@ -690,6 +691,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Pozwala aplikacji na odczyt i zmianę konfiguracji trybu Nie przeszkadzać."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"rozpocząć wyświetlanie użycia uprawnień"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Umożliwia rozpoczęcie korzystania z uprawnienia dotyczącego danej aplikacji jego posiadaczowi. Zwykłe aplikacje nie powinny potrzebować tego uprawnienia."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Określ reguły hasła"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrolowanie długości haseł blokady ekranu i kodów PIN oraz dozwolonych w nich znaków."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorowanie prób odblokowania ekranu"</string>
@@ -1708,6 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Użyj skrótu"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Odwrócenie kolorów"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcja kolorów"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Zmniejsz jasność"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Przytrzymano klawisze głośności. Usługa <xliff:g id="SERVICE_NAME">%1$s</xliff:g> została włączona."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Przytrzymano klawisze głośności. Usługa <xliff:g id="SERVICE_NAME">%1$s</xliff:g> została wyłączona."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Naciśnij i przytrzymaj oba przyciski głośności przez trzy sekundy, by użyć usługi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1942,6 +1948,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maksymalizuj"</string>
<string name="close_button_text" msgid="10603510034455258">"Zamknij"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Odbierz"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Odrzuć"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Rozłącz"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Połączenie przychodzące"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Trwa połączenie"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtruję połączenie przychodzące"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="few">Wybrano <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="many">Wybrano <xliff:g id="COUNT_1">%1$d</xliff:g></item>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 693af83522e7..bfab1f39a06e 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use sua impressão digital para continuar"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícone de impressão digital"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permitir que o app leia e grave a configuração \"Não perturbe\"."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso da permissão para visualização"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que o sistema inicie o uso de permissão para um app. Não deve ser necessário para apps comuns."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acessar os dados do sensor em uma taxa de amostragem elevada"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que o app receba amostras de dados do sensor em uma taxa maior que 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Definir regras para senha"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Controla o tamanho e os caracteres permitidos nos PINs e nas senhas do bloqueio de tela."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorar tentativas de desbloqueio de tela"</string>
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar atalho"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correção de cor"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Reduzir brilho"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximizar"</string>
<string name="close_button_text" msgid="10603510034455258">"Fechar"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Atender"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Recusar"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Desligar"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Chamada recebida"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chamada em andamento"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando uma chamada recebida"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionado</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index e00034acf491..ee18a2f632b3 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <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>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícone de impressão digital"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite à app ler e alterar a configuração de Não incomodar"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar utilização da autorização de visualização"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que o titular inicie a utilização de autorizações para uma app. Nunca deverá ser necessário para aplicações normais."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Definir regras de palavra-passe"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Controlar o comprimento e os carateres permitidos nos PINs e nas palavras-passe do bloqueio de ecrã."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorizar tentativas de desbloqueio do ecrã"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar atalho"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correção da cor"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Reduzir o brilho"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas do volume premidas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume premidas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Prima sem soltar as teclas de volume durante três segundos para utilizar o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximizar"</string>
<string name="close_button_text" msgid="10603510034455258">"Fechar"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Atender"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Recusar"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Desligar"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Chamada recebida"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chamada em curso"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"A filtrar uma chamada recebida…"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selecionado</item>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 693af83522e7..bfab1f39a06e 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use sua impressão digital para continuar"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícone de impressão digital"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permitir que o app leia e grave a configuração \"Não perturbe\"."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso da permissão para visualização"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que o sistema inicie o uso de permissão para um app. Não deve ser necessário para apps comuns."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acessar os dados do sensor em uma taxa de amostragem elevada"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que o app receba amostras de dados do sensor em uma taxa maior que 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Definir regras para senha"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Controla o tamanho e os caracteres permitidos nos PINs e nas senhas do bloqueio de tela."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorar tentativas de desbloqueio de tela"</string>
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar atalho"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correção de cor"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Reduzir brilho"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximizar"</string>
<string name="close_button_text" msgid="10603510034455258">"Fechar"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Atender"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Recusar"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Desligar"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Chamada recebida"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chamada em andamento"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando uma chamada recebida"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionado</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 5fd6f1a0634b..9e6b3f9b9309 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -582,6 +582,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Folosiți amprenta pentru a continua"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Pictograma amprentă"</string>
@@ -687,6 +688,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite aplicației să citească și să scrie configurația Nu deranja."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"porniți folosirea permisiunii de vizualizare"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite proprietarului să pornească folosirea permisiunii pentru o aplicație. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Să seteze reguli pentru parolă"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Stabiliți lungimea și tipul de caractere permise pentru parolele și codurile PIN de blocare a ecranului."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Să monitorizeze încercările de deblocare a ecranului"</string>
@@ -1686,6 +1691,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizați comanda rapidă"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversarea culorilor"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corecția culorii"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Reduceți luminozitatea"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S-au apăsat lung tastele de volum. S-a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S-au apăsat lung tastele de volum. S-a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Apăsați ambele butoane de volum timp de trei secunde pentru a folosi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1911,6 +1917,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximizați"</string>
<string name="close_button_text" msgid="10603510034455258">"Închideți"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Răspundeți"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Respingeți"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Încheiați"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Apel primit"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Apel în desfășurare"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Se filtrează un apel primit"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> selectate</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selectate</item>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index d1cd374d2bc7..c2377099d409 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -585,6 +585,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Чтобы продолжить, используйте цифровой отпечаток"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Значок отпечатка пальца"</string>
@@ -690,6 +691,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Открывает приложению доступ к настройкам режима \"Не беспокоить\" и позволяет изменять их."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Просмотр данных об используемых разрешениях"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Приложение получит доступ к данным об используемых разрешениях. Это разрешение не требуется обычным приложениям."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Настройка правил для паролей"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Контролировать длину и символы при вводе пароля и PIN-кода."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Отслеживание попыток разблокировать экран"</string>
@@ -1708,6 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Использовать быстрое включение"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Инверсия цветов"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Коррекция цвета"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Уменьшение яркости"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" включена."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" отключена."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Чтобы использовать сервис \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\", нажмите и удерживайте обе клавиши громкости в течение трех секунд."</string>
@@ -1942,6 +1948,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Развернуть"</string>
<string name="close_button_text" msgid="10603510034455258">"Закрыть"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Ответить"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Отклонить"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Завершить"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Входящий вызов"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Текущий вызов"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Фильтрация входящего вызова"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one">Выбрано: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="few">Выбрано: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index d102183f25ad..5a00e9265166 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ඉදිරියට යාමට ඔබගේ ඇඟිලි සලකුණ භාවිත කරන්න"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ඇඟිලි සලකුණු නිරූපකය"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"බාධා නොකරන්න වින්‍යාස කිරීම කියවීමට සහ ලිවීමට යෙදුමට ඉඩ දෙයි."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"අවසර භාවිතය බැලීමට ආරම්භ කරන්න"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"තබා සිටින්නාට යෙදුමක් සඳහා අවසර භාවිතය ආරම්භ කිරීමට ඉඩ දෙයි. සාමාන්‍ය යෙදුම් සඳහා කිසි විටෙක අවශ්‍ය නොවිය යුතු ය."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ඉහළ නියැදි කිරීමේ වේගයකින් සංවේදක දත්ත වෙත පිවිසෙන්න"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz ට වඩා වැඩි වේගයකින් සංවේදක දත්ත නියැදි කිරීමට යෙදුමට ඉඩ දෙයි"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"මුරපද නීති සකස් කිරීම"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"තිර අගුලු මුරපද සහ PIN තුළ ඉඩ දෙන දිග සහ අනුලකුණු පාලනය කිරීම."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"තිරය අගුළු ඇරීමේ උත්සාහයන් නිරීක්ෂණය කරන්න"</string>
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"කෙටිමඟ භාවිතා කරන්න"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"වර්ණ අපවර්තනය"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"වර්ණ නිවැරදි කිරීම"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"දීප්තිය අඩු කරන්න"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්‍රියාත්මකයි."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්‍රියාවිරහිතයි."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> භාවිත කිරීමට හඬ පරිමා යතුරු දෙකම තත්පර තුනකට ඔබාගෙන සිටින්න"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"විහිදන්න"</string>
<string name="close_button_text" msgid="10603510034455258">"වසන්න"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"පිළිතුරු දෙ."</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"ප්‍රතික්ෂේප ක"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"විසන්ධි කරන්න"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"එන ඇමතුම"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"කරගෙන යන ඇමතුම"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"එන ඇමතුමක් පරීක්ෂා කරන්න"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ක් තෝරන ලදි</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ක් තෝරන ලදි</item>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index ffa4b26589d5..de7a2a4f65ac 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -585,6 +585,7 @@
<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>
+ <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>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona odtlačku prsta"</string>
@@ -690,6 +691,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Umožňuje aplikácii čítať a zapisovať konfiguráciu režimu bez vyrušení."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"spustenie používania povolenia na zobrazenie"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Umožňuje držiteľovi spustiť používanie povolenia aplikáciou. Bežné aplikácie by toto povolenie nemali nikdy potrebovať."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Nastaviť pravidlá pre heslo"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Nastavte dĺžku hesiel na odomknutie obrazovky aj kódov PIN a v nich používané znaky."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Sledovanie pokusov o odomknutie obrazovky"</string>
@@ -1708,6 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Použiť skratku"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzia farieb"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Úprava farieb"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Zníženie jasu"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vypnutá."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Ak chcete používať službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, pridržte tri sekundy oba klávesy hlasitosti"</string>
@@ -1942,6 +1948,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximalizovať"</string>
<string name="close_button_text" msgid="10603510034455258">"Zavrieť"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Prijať"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Odmietnuť"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Zložiť"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Prichádzajúci hovor"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Prebiehajúci hovor"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Preveruje sa prichádzajúci hovor"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="few">Vybrané: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="many">Vybrané: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index d09ef047c624..3575131884d3 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -150,8 +150,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Pomožno klicanje prek operaterja <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ni posredovano"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po toliko sekundah: <xliff:g id="TIME_DELAY">{2}</xliff:g>"</string>
@@ -586,6 +585,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Uporabite prstni odtis, če želite nadaljevati."</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona prstnih odtisov"</string>
@@ -691,6 +691,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Aplikaciji omogoča branje in pisanje konfiguracije načina »ne moti«."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"začetek uporabe dovoljenja za ogledovanje"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Imetniku omogoča začetek uporabe dovoljenj za aplikacijo. Nikoli ni potrebno za navadne aplikacije."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Nastavitev pravil za geslo"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Nadzor nad dolžino in znaki, ki so dovoljeni v geslih in kodah PIN za odklepanje zaslona."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Nadzor nad poskusi odklepanja zaslona"</string>
@@ -1709,6 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Uporabi bližnjico"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija barv"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Popravljanje barv"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Zmanjšanje svetlosti"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vklopljena."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je izklopljena."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Za uporabo storitve <xliff:g id="SERVICE_NAME">%1$s</xliff:g> pritisnite obe tipki za glasnost in ju pridržite tri sekunde"</string>
@@ -1943,6 +1948,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maksimiziraj"</string>
<string name="close_button_text" msgid="10603510034455258">"Zapri"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Sprejmi"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Zavrni"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Prekini klic"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Dohodni klic"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Aktivni klic"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Preverjanje dohodnega klica"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> izbran</item>
<item quantity="two"><xliff:g id="COUNT_1">%1$d</xliff:g> izbrana</item>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 592e54ddceb9..8857e08002ad 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <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>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona e gjurmës së gishtit"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Lejon aplikacionin të lexojë dhe shkruajë konfigurimin e \"Mos shqetëso\"."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"nis përdorimin e lejes për shikimin"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Lejon që mbajtësi të nisë përdorimin e lejeve për një aplikacion. Nuk duhet të nevojitet asnjëherë për aplikacionet normale."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"qasu te të dhënat e sensorit me një shpejtësi më të lartë shembulli"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Lejon aplikacionin të mbledhë shembujt e të dhënave të sensorit me shpejtësi më të lartë se 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Cakto rregullat e fjalëkalimit"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrollo gjatësinë dhe karakteret e lejuara në fjalëkalimet dhe kodet PIN të kyçjes së ekranit."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Monitoro tentativat e shkyçjes së ekranit"</string>
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Përdor shkurtoren"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Kthimi i ngjyrës"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korrigjimi i ngjyrës"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Redukto ndriçimin"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tastet e volumit të mbajtura shtypur. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> i aktivizuar."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tastet e volumit të mbajtura shtypur. U çaktivizua \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Shtyp dhe mbaj shtypur të dy butonat e volumit për tre sekonda për të përdorur <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maksimizo"</string>
<string name="close_button_text" msgid="10603510034455258">"Mbyll"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Përgjigju"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Refuzo"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Mbyll"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Telefonatë hyrëse"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Telefonatë në vazhdim"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Po filtron një telefonatë hyrëse"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> të zgjedhura</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> i zgjedhur</item>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 65037a058067..4aca2009b567 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -582,6 +582,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Наставите помоћу отиска прста"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Икона отиска прста"</string>
@@ -687,6 +688,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дозвољава апликацији да чита и уписује конфигурацију подешавања Не узнемиравај."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"почетак коришћења дозволе за преглед"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дозвољава власнику да започне коришћење дозволе за апликацију. Никада не би требало да буде потребна за уобичајене апликације."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Подешавање правила за лозинку"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Контролише дужину и знакове дозвољене у лозинкама и PIN-овима за закључавање екрана."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Надгледајте покушаје откључавања екрана"</string>
@@ -1686,6 +1691,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Користи пречицу"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Инверзија боја"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Корекција боја"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Смањите осветљеност"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је укључена."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је искључена."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Притисните и задржите оба тастера за јачину звука три секунде да бисте користили <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1911,6 +1917,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Увећај"</string>
<string name="close_button_text" msgid="10603510034455258">"Затвори"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Одговори"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Одбиј"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Прекини везу"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Долазни позив"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Позив је у току"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Проверава се долазни позив"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one">Изабрана је <xliff:g id="COUNT_1">%1$d</xliff:g> ставка</item>
<item quantity="few">Изабране су <xliff:g id="COUNT_1">%1$d</xliff:g> ставке</item>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index ef7a102fa257..fb6d4ce3828c 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <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>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon för fingeravtryck"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ger appen läs- och skrivbehörighet till konfigurationen för Stör ej."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"börja visa behörighetsanvändningen"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Gör att innehavaren kan öppna behörighetsanvändning för en app. Ska inte behövas för vanliga appar."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Ange lösenordsregler"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Styr tillåten längd och tillåtna tecken i lösenord och pinkoder för skärmlåset."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Övervaka försök att låsa upp skärmen"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Använd kortkommandot"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverterade färger"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Färgkorrigering"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Minska ljusstyrkan"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har aktiverats."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har inaktiverats."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tryck och håll båda volymknapparna i tre sekunder för att använda <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maximera"</string>
<string name="close_button_text" msgid="10603510034455258">"Stäng"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Svara"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Avvisa"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Lägg på"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Inkommande samtal"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Pågående samtal"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Ett inkommande samtal förhandsgranskas"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> har valts</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> har valts</item>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index cc82f4ec8fea..93dc3b922d3f 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Tumia alama ya kidole chako ili uendelee"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Aikoni ya alama ya kidole"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Inaruhusu programu kusoma na kuandika usanidi wa kipengee cha Usinisumbue."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"anzisha kipengele cha kuona matumizi ya ruhusa"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Huruhusu kishikiliaji kuanzisha matumizi ya ruhusa ya programu. Haipaswi kuhitajika kwa ajili ya programu za kawaida."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"fikia data ya vitambuzi kwa kasi ya juu ya sampuli"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Huruhusu programu kujaribu sampuli ya data ya vitambuzi kwa kasi inayozidi Hz 200"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Kuweka kanuni za nenosiri"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Dhibiti urefu na maandishi yanayokubalika katika nenosiri la kufunga skrini na PIN."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Kuhesabu mara ambazo skrini inajaribu kufunguliwa"</string>
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Tumia Njia ya Mkato"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Ugeuzaji rangi"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Usahihishaji wa rangi"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Punguza Ung\'aavu"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Vitufe vya sauti vilivyoshikiliwa. Umewasha <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Vitufe vya sauti vimeshikiliwa. Umezima <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Bonyeza na ushikilie vitufe vyote viwili vya sauti kwa sekunde tatu ili utumie <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Panua"</string>
<string name="close_button_text" msgid="10603510034455258">"Funga"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Jibu"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Kataa"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Kata simu"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Simu uliyopigiwa"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Simu inayoendelea"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Inachuja simu unayopigiwa"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> vimechaguliwa</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> kimechaguliwa</item>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index ca90171d50ce..e70ffecc33bb 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"தொடர்வதற்கு கைரேகையைப் பயன்படுத்துங்கள்"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"கைரேகை ஐகான்"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"தொந்தரவு செய்ய வேண்டாம் உள்ளமைவைப் படிக்கவும் எழுதவும், ஆப்ஸை அனுமதிக்கிறது."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"அனுமதி உபயோகத்தை அணுகுதல்"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ஆப்ஸிற்கான அனுமதி உபயோகத்தை ஹோல்டருக்கு வழங்கும். இயல்பான ஆப்ஸிற்கு இது எப்போதுமே தேவைப்படாது."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"கடவுச்சொல் விதிகளை அமைக்கவும்"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"திரைப் பூட்டின் கடவுச்சொற்கள் மற்றும் பின்களில் அனுமதிக்கப்படும் நீளத்தையும் எழுத்துக்குறிகளையும் கட்டுப்படுத்தும்."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"திரையைத் திறப்பதற்கான முயற்சிகளைக் கண்காணி"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ஷார்ட்கட்டைப் பயன்படுத்து"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"கலர் இன்வெர்ஷன்"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"வண்ணத் திருத்தம்"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"ஒளிர்வைக் குறைத்தல்"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆன் செய்யப்பட்டது."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆஃப் செய்யப்பட்டது."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ஐப் பயன்படுத்த 3 விநாடிகளுக்கு இரண்டு ஒலியளவு பட்டன்களையும் அழுத்திப் பிடிக்கவும்"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"பெரிதாக்கு"</string>
<string name="close_button_text" msgid="10603510034455258">"மூடு"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"பதிலளி"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"நிராகரி"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"துண்டி"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"உள்வரும் அழைப்பு"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"செயலில் இருக்கும் அழைப்பு"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"உள்வரும் அழைப்பை மதிப்பாய்வு செய்கிறது"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> தேர்ந்தெடுக்கப்பட்டன</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> தேர்ந்தெடுக்கப்பட்டது</item>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index b0726e811d47..365e6d748710 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi మాత్రమే"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> బ్యాకప్ కాలింగ్"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ఫార్వార్డ్ చేయబడలేదు"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> సెకన్ల తర్వాత <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"కొనసాగించడానికి మీ వేలిముద్రను ఉపయోగించండి"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"వేలిముద్ర చిహ్నం"</string>
@@ -685,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"అంతరాయం కలిగించవద్దు ఎంపిక కాన్ఫిగరేషన్ చదవడానికి మరియు వ్రాయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"వీక్షణ అనుమతి వినియోగాన్ని ప్రారంభించండి"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"యాప్‌నకు అనుమతి వినియోగాన్ని ప్రారంభించడానికి హోల్డర్‌‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ ఇటువంటి అనుమతి అవసరం ఉండదు."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"పాస్‌వర్డ్ నియమాలను సెట్ చేయండి"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"స్క్రీన్ లాక్ పాస్‌వర్డ్‌లు మరియు PINల్లో అనుమతించబడిన పొడవు మరియు అక్షరాలను నియంత్రిస్తుంది."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"స్క్రీన్ అన్‌లాక్ ప్రయత్నాలను పర్యవేక్షించండి"</string>
@@ -1665,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"సత్వరమార్గాన్ని ఉపయోగించు"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"కలర్ మార్పిడి"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"కలర్ సరిచేయడం"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"ప్రకాశాన్ని తగ్గించండి"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆన్ చేయబడింది"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆఫ్ చేయబడింది"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ని ఉపయోగించడానికి వాల్యూమ్ కీలు రెండింటినీ 3 సెకన్లు నొక్కి ఉంచండి"</string>
@@ -1881,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"గరిష్టీకరించు"</string>
<string name="close_button_text" msgid="10603510034455258">"మూసివేయి"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"సమాధానం ఇవ్వు"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"తిరస్కరించండి"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"కాల్ ముగించు"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"ఇన్‌కమింగ్ కాల్"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"కాల్ కొనసాగుతోంది"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"ఇన్‌కమింగ్ కాల్‌ను స్క్రీన్ చేయండి"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ఎంచుకోబడ్డాయి</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ఎంచుకోబడింది</item>
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml b/core/res/res/values-television/styles_device_defaults.xml
index e09bf7e37ed0..ef55fc68001f 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
+++ b/core/res/res/values-television/styles_device_defaults.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (C) 2021 The Android Open Source Project
~
@@ -14,7 +14,8 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
<resources>
- <bool name="can_use_one_handed_bouncer">true</bool>
+ <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog"
+ parent="Widget.Leanback.Button.ButtonBar" />
</resources>
+
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 5079f232091b..b41e892180cb 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ใช้ลายนิ้วมือของคุณเพื่อดำเนินการต่อ"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ไอคอนลายนิ้วมือ"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"อนุญาตให้แอปอ่านและเขียนการกำหนดค่าโหมดห้ามรบกวน"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"เริ่มการใช้สิทธิ์การดู"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"อนุญาตให้เจ้าของเริ่มการใช้สิทธิ์ของแอป ไม่จำเป็นสำหรับแอปทั่วไป"</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"ตั้งค่ากฎรหัสผ่าน"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"ควบคุมความยาวและอักขระที่สามารถใช้ในรหัสผ่านของการล็อกหน้าจอและ PIN"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"ตรวจสอบความพยายามในการปลดล็อกหน้าจอ"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ใช้ทางลัด"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"การกลับสี"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"การแก้สี"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"ลดความสว่าง"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"กดปุ่มปรับระดับเสียงค้างไว้แล้ว เปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"กดปุ่มปรับระดับเสียงค้างไว้แล้ว ปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"กดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้ 3 วินาทีเพื่อใช้ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"ขยายใหญ่สุด"</string>
<string name="close_button_text" msgid="10603510034455258">"ปิด"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"รับสาย"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"ปฏิเสธ"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"วางสาย"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"สายเรียกเข้า"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"สายที่สนทนาอยู่"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"กำลังสกรีนสายเรียกเข้า"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other">เลือกไว้ <xliff:g id="COUNT_1">%1$d</xliff:g> รายการ</item>
<item quantity="one">เลือกไว้ <xliff:g id="COUNT_0">%1$d</xliff:g> รายการ</item>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 81bfe5258284..c617ccf67068 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Gamitin ang iyong fingerprint para magpatuloy"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icon ng fingerprint"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Nagbibigay-daan sa app na basahin at isulat ang configuration ng Huwag Istorbohin."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"simulan ang paggamit sa pahintulot sa pagtingin"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Binibigyang-daan ang may hawak na simulan ang paggamit ng pahintulot para sa isang app. Hindi dapat kailanganin kailanman para sa mga normal na app."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"mag-access ng data ng sensor sa mataas na rate ng pag-sample"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Pinapahintulutan ang app na mag-sample ng data ng sensor sa rate na higit sa 200 Hz"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Magtakda ng mga panuntunan sa password"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrolin ang haba at ang mga character na pinapayagan sa mga password at PIN sa screen lock."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Subaybayan ang mga pagsubok sa pag-unlock ng screen"</string>
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gamitin ang Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Pag-invert ng Kulay"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Pagwawasto ng Kulay"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Bawasan ang Liwanag"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pinindot nang matagal ang volume keys. Na-on ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pinindot nang matagal ang volume keys. Na-off ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pindutin nang matagal ang parehong volume key sa loob ng tatlong segundo para magamit ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"I-maximize"</string>
<string name="close_button_text" msgid="10603510034455258">"Isara"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Sagutin"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Tanggihan"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Ibaba"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Papasok na tawag"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Kasalukuyang tawag"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Nagsi-screen ng papasok na tawag"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ang napili</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ang napili</item>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 7095bb8c5875..5724bcf2cc39 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <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>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Parmak izi simgesi"</string>
@@ -684,6 +685,8 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Uygulamaya, Rahatsız Etmeyin yapılandırmasını okuma ve yazma izni verir."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"izin kullanımı görüntülemeye başlama"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"İzin sahibinin bir uygulama için izin kullanımı başlatmasına olanak tanır. Normal uygulamalar için hiçbir zaman kullanılmamalıdır."</string>
+ <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"sensör verilerine daha yüksek örnekleme hızında eriş"</string>
+ <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Uygulamanın, sensör verilerini 200 Hz\'den daha yüksek bir hızda örneklemesine olanak tanır"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Şifre kuralları ayarla"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Ekran kilidini açma şifrelerinde ve PIN\'lerde izin verilen uzunluğu ve karakterleri denetleyin."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekran kilidini açma denemelerini izle"</string>
@@ -1664,6 +1667,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kısayolu Kullan"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Rengi Ters Çevirme"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Renk Düzeltme"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Parlaklığı Azaltma"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> açıldı."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kapatıldı."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> hizmetini kullanmak için her iki ses tuşunu basılı tutun"</string>
@@ -1880,6 +1884,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Ekranı Kapla"</string>
<string name="close_button_text" msgid="10603510034455258">"Kapat"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Yanıtla"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Reddet"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Kapat"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Gelen çağrı"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Devam eden çağrı"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Gelen arama süzülüyor"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> öğe seçildi</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> öğe seçildi</item>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 9864b4b4e4a4..ff1936f2a2f7 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -585,6 +585,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Щоб продовжити, скористайтеся своїм відбитком пальця"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Значок відбитка пальця"</string>
@@ -690,6 +691,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Додаток зможе переглядати та змінювати конфігурацію режиму \"Не турбувати\"."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"перегляньте дані про використання дозволів"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Власник зможе використовувати дозволи для цього додатка. Цей дозвіл не потрібен для звичайних додатків."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Устан. правила пароля"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Укажіть максимальну довжину та кількість символів для паролів розблокування екрана та PIN-кодів."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Відстежувати спроби розблокування екрана"</string>
@@ -1708,6 +1713,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Використовувати ярлик"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Інверсія кольорів"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Корекція кольорів"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Зменшення яскравості"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> увімкнено."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> вимкнено."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Щоб скористатися службою <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, утримуйте обидві клавіші гучності впродовж трьох секунд"</string>
@@ -1942,6 +1948,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Розгорнути"</string>
<string name="close_button_text" msgid="10603510034455258">"Закрити"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Відповісти"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Відхилити"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Завершити"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Вхідний виклик"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Активний виклик"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Фільтрування вхідного виклику"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one">Вибрано <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="few">Вибрано <xliff:g id="COUNT_1">%1$d</xliff:g></item>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 64fb2fc414e2..e1fb04cc0abe 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -148,8 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"‏صرف Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) -->
- <skip />
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> بیک اپ کالنگ"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : فارورڈ نہیں کی گئی"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> بعد از <xliff:g id="TIME_DELAY">{2}</xliff:g> سیکنڈ"</string>
@@ -580,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"جاری رکھنے کیلئے اپنا فنگر پرنٹ استعمال کریں"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"فنگر پرنٹ آئیکن"</string>
@@ -685,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ایپ کو ڈسٹرب نہ کریں کنفیگریشن لکھنے اور پڑھنے کے قابل کرتا ہے۔"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"اجازت کی استعمال کا ملاحظہ شروع کریں"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"حامل کو ایپ کی اجازت کے استعمال کو شروع کرنے کی اجازت دیتا ہے۔ عام ایپس کے لیے کبھی بھی درکار نہیں ہونا چاہیے۔"</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"پاس ورڈ کے اصول سیٹ کریں"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"‏اسکرین لاک پاس ورڈز اور PINs میں اجازت یافتہ لمبائی اور حروف کو کنٹرول کریں۔"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"اسکرین غیر مقفل کرنے کی کوششیں مانیٹر کریں"</string>
@@ -1665,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"شارٹ کٹ استعمال کریں"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"رنگوں کی تقلیب"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"رنگ کی تصحیح"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"چمک کم کریں"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آن ہے۔"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آف ہے۔"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> کا استعمال کرنے کے لیے 3 سیکنڈ تک والیوم کی دونوں کلیدوں کو چھوئیں اور دبائے رکھیں"</string>
@@ -1881,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"بڑا کریں"</string>
<string name="close_button_text" msgid="10603510034455258">"بند کریں"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"جواب"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"مسترد کریں"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"منقطع کر دیں"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"اِن کمنگ کال"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"جاری کال"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"اِن کمنگ کال کی اسکریننگ"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> منتخب کردہ</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> منتخب کردہ</item>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 6612cc9a875a..ddb5b1cc0710 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -579,6 +579,7 @@
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu qurilmada barmoq izi skaneri mavjud emas."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor vaqtincha faol emas."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Barmoq izi <xliff:g id="FINGERID">%d</xliff:g>"</string>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Davom etish uchun barmoq izingizdan foydalaning"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Barmoq izi belgisi"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"“Bezovta qilinmasin” rejimi sozlamalarini ko‘rish va o‘zgartirish."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"foydalaniladigan ruxsatlar axborotini ochish"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ilova foydalanadigan ruxsatlar axborotini ishga tushirishga ruxsat beradi. Oddiy ilovalar uchun talab qilinmaydi."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Parol qoidalarini o‘rnatish"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Ekran qulfi paroli va PIN kodlari uchun qo‘yiladigan talablarni (belgilar soni va uzunligi) nazorat qiladi."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Ekranni qulfdan chiqarishga urinishlarni nazorat qilish"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Tezkor ishga tushirishdan foydalanish"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Ranglarni akslantirish"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Rangni tuzatish"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Yorqinlikni pasaytirish"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tovush tugmalari bosib turildi. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> yoqildi."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tovush tugmalari bosib turildi. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> faolsizlantirildi."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> xizmatidan foydalanish uchun ikkala ovoz balandligi tugmalarini uzoq bosib turing"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Maksimallashtirish"</string>
<string name="close_button_text" msgid="10603510034455258">"Yopish"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Javob berish"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Rad etish"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Tugatish"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Kiruvchi chaqiruv"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Joriy chaqiruv"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Kiruvchi chaqiruvni filtrlash"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other">Belgilandi: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">Belgilandi: <xliff:g id="COUNT_0">%1$d</xliff:g></item>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 8f8e0bce3409..13ffe3021c12 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <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>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Biểu tượng vân tay"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Cho phép ứng dụng đọc và ghi cấu hình Không làm phiền."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"cấp quyền xem"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Cho phép chủ sở hữu cấp quyền cho một ứng dụng. Các ứng dụng thông thường sẽ không bao giờ cần quyền này."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Đặt quy tắc mật khẩu"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Kiểm soát độ dài và ký tự được phép trong mật khẩu khóa màn hình và mã PIN."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Giám sát những lần thử mở khóa màn hình"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sử dụng phím tắt"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Đảo màu"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Chỉnh màu"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Giảm độ sáng"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã bật."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã tắt."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Nhấn và giữ đồng thời cả hai phím âm lượng trong 3 giây để sử dụng <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Tối đa hóa"</string>
<string name="close_button_text" msgid="10603510034455258">"Đóng"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Trả lời"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Từ chối"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Kết thúc"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Cuộc gọi đến"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Cuộc gọi đang thực hiện"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Đang sàng lọc cuộc gọi đến"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other">Đã chọn <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">Đã chọn <xliff:g id="COUNT_0">%1$d</xliff:g></item>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index af5f846d368b..13fa62aec28d 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"使用指纹完成验证才能继续"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指纹图标"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"允许此应用读取和写入“勿扰”模式配置。"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"授权使用“查看权限”"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"允许该应用开始查看应用的权限使用情况(普通应用绝不需要此权限)。"</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"设置密码规则"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"控制锁屏密码和 PIN 码所允许的长度和字符。"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"监控屏幕解锁尝试次数"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"使用快捷方式"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"颜色反转"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"色彩校正"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"调低亮度"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量键。<xliff:g id="SERVICE_NAME">%1$s</xliff:g>已开启。"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量键。<xliff:g id="SERVICE_NAME">%1$s</xliff:g>已关闭。"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"同时按住两个音量键 3 秒钟即可使用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"最大化"</string>
<string name="close_button_text" msgid="10603510034455258">"关闭"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>:<xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"接听"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"拒接"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"挂断"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"来电"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"正在通话"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"正在过滤来电"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other">已选择 <xliff:g id="COUNT_1">%1$d</xliff:g> 项</item>
<item quantity="one">已选择 <xliff:g id="COUNT_0">%1$d</xliff:g> 项</item>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 65586f7f1b09..dd5eada00c37 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -148,7 +148,7 @@
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"只限 Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
<skip />
- <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"「<xliff:g id="SPN">%s</xliff:g>」備用通話網路"</string>
+ <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"「<xliff:g id="SPN">%s</xliff:g>」備用通話"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:尚未轉接"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> 於 <xliff:g id="TIME_DELAY">{2}</xliff:g> 秒後轉接"</string>
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"請使用您的指紋繼續"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指紋圖示"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"允許應用程式讀取和寫入「請勿騷擾」設定。"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"開始查看權限使用情況"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"允許應用程式開始查看應用程式的權限使用情況 (一般應用程式並不需要)。"</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"設定密碼規則"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"控制螢幕鎖定密碼和 PIN 所允許的長度和字元。"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"監控螢幕解鎖嘗試次數"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"使用快速鍵"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"色彩反轉"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"色彩校正"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"調低亮度"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量鍵。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 已開啟。"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量鍵。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 已關閉。"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"㩒住兩個音量鍵 3 秒就可以用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"最大化"</string>
<string name="close_button_text" msgid="10603510034455258">"關閉"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>:<xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"接聽"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"拒接"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"掛斷"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"來電"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"通話中"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"正在過濾來電"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other">已選取 <xliff:g id="COUNT_1">%1$d</xliff:g> 個項目</item>
<item quantity="one">已選取 <xliff:g id="COUNT_0">%1$d</xliff:g> 個項目</item>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 6facceb39df7..a2573440f9bb 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"使用指紋完成驗證才能繼續操作"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指紋圖示"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"允許應用程式讀取及寫入「零打擾」設定。"</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"啟動檢視權限用途"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"允許應用程式開始使用其他應用程式 (一般應用程式並不需要)。"</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"設定密碼規則"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"管理螢幕鎖定密碼和 PIN 碼支援的字元和長度上限。"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"監控螢幕解鎖嘗試次數"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"使用捷徑"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"色彩反轉"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"色彩校正"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"調低亮度"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量鍵。「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」已開啟。"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量鍵。「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」已關閉。"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"同時按住調低及調高音量鍵三秒即可使用「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"最大化"</string>
<string name="close_button_text" msgid="10603510034455258">"關閉"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>:<xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"接聽"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"拒接"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"掛斷"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"來電"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"通話中"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"正在過濾來電"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="other">已選取 <xliff:g id="COUNT_1">%1$d</xliff:g> 個項目</item>
<item quantity="one">已選取 <xliff:g id="COUNT_0">%1$d</xliff:g> 個項目</item>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 3b22e9435462..712e57823a38 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -579,6 +579,7 @@
<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>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Sebenzisa izigxivizo zakho zeminwe ukuze uqhubeke"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Isithonjana sezigxivizo zeminwe"</string>
@@ -684,6 +685,10 @@
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ivumela izinhlelo zokusebenza ukufunda nokubhala ukulungiswa kokuthi Ungaphazamisi."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"qala ukusetshenziswa kokubuka imvume"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ivumela umphathi ukuthi aqale ukusetshenziswa kwemvume kohlelo lokusebenza. Akumele idingelwe izinhlelo zokusebenza ezijwayelekile."</string>
+ <!-- no translation found for permlab_highSamplingRateSensors (3941068435726317070) -->
+ <skip />
+ <!-- no translation found for permdesc_highSamplingRateSensors (8430061978931155995) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4851829918814422199">"Misa imithetho yephasiwedi"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Lawula ubude nezinhlamvu ezivunyelwe kumaphasiwedi wokukhiya isikrini nama-PIN."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Qapha imizamo yokuvula isikrini sakho"</string>
@@ -1664,6 +1669,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sebenzisa isinqamuleli"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Ukuguqulwa kombala"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Ukulungiswa kombala"</string>
+ <string name="reduce_bright_colors_feature_name" msgid="6222956501418407642">"Nciphisa ukukhanya"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivuliwe."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivaliwe."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Cindezela uphinde ubambe bobabili okhiye bevolumu ngamasekhondi amathathu ukuze usebenzise i-<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
@@ -1880,6 +1886,12 @@
<string name="maximize_button_text" msgid="4258922519914732645">"Khulisa"</string>
<string name="close_button_text" msgid="10603510034455258">"Vala"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Phendula"</string>
+ <string name="call_notification_decline_action" msgid="3700345945214000726">"Yenqaba"</string>
+ <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Vala Ucingo"</string>
+ <string name="call_notification_incoming_text" msgid="6143109825406638201">"Ikholi engenayo"</string>
+ <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Ikholi eqhubekayo"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"Ukuveza ikholi engenayo"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> okukhethiwe</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> okukhethiwe</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 14df77527a08..69bb20cf9c1e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -362,6 +362,12 @@
surface when the app has not drawn any content into this area. One example is
when the user is resizing a window of an activity in multi-window mode. -->
<attr name="windowBackgroundFallback" format="reference|color" />
+ <!-- Blur the screen behind the window with the bounds of the window.
+ The radius defines the size of the neighbouring area, from which pixels will be
+ averaged to form the final color for each pixel in the region.
+ A radius of 0 means no blur. The higher the radius, the denser the blur.
+ Corresponds to {@link android.view.Window#setBackgroundBlurRadius}. -->
+ <attr name="windowBackgroundBlurRadius" format="dimension" />
<!-- Drawable to use as a frame around the window. -->
<attr name="windowFrame" format="reference" />
<!-- Flag indicating whether there should be no title on this window. -->
@@ -1140,6 +1146,15 @@
<!-- The color applied to the edge effect on scrolling containers. -->
<attr name="colorEdgeEffect" format="color" />
+ <!-- The type of the edge effect. The default is glow. -->
+ <attr name="edgeEffectType">
+ <!-- Use a colored glow at the edge. -->
+ <enum name="glow" value="0" />
+
+ <!-- Stretch the content. -->
+ <enum name="stretch" value="1" />
+ </attr>
+
<!-- =================== -->
<!-- Lighting properties -->
<!-- =================== -->
@@ -1957,6 +1972,7 @@
<declare-styleable name="Window">
<attr name="windowBackground" />
<attr name="windowBackgroundFallback" />
+ <attr name="windowBackgroundBlurRadius" />
<attr name="windowContentOverlay" />
<attr name="windowFrame" />
<attr name="windowNoTitle" />
@@ -2246,6 +2262,23 @@
-->
<enum name="always" value="3" />
</attr>
+
+ <!-- The background color for the splash screen, if not specify then system will
+ calculate from windowBackground. -->
+ <attr name="windowSplashScreenBackground" format="color"/>
+
+ <!-- Replace an icon in the center of the starting window, if the object is animated
+ and drawable(e.g. AnimationDrawable, AnimatedVectorDrawable), then it will also
+ play the animation while showing the starting window. -->
+ <attr name="windowSplashScreenAnimatedIcon" format="reference"/>
+ <!-- The duration, in milliseconds, of the window splash screen icon animation duration
+ when playing the splash screen starting window. The maximum animation duration should
+ be limited below 1000ms. -->
+ <attr name="windowSplashScreenAnimationDuration" format="integer"/>
+
+ <!-- Place an drawable image in the bottom of the starting window, it can be used to
+ represent the branding of the application. -->
+ <attr name="windowSplashScreenBrandingImage" format="reference"/>
</declare-styleable>
<!-- The set of attributes that describe a AlertDialog's theme. -->
@@ -3251,6 +3284,16 @@
a value of 'true' will not override any 'false' value in its parent chain nor will
it prevent any 'false' in any of its children. -->
<attr name="forceDarkAllowed" format="boolean" />
+
+ <!-- <p>Whether the View's Outline should be used to clip the contents of the View.
+ <p>Only a single non-rectangular clip can be applied on a View at any time. Circular
+ clips from a
+ {@link android.view.ViewAnimationUtils#createCircularReveal(View, int, int, float,
+ float)} circular reveal animation take priority over Outline clipping, and child
+ Outline clipping takes priority over Outline clipping done by a parent.
+ <p>Note that this flag will only be respected if the View's Outline returns true from
+ {@link android.graphics.Outline#canClip()}. -->
+ <attr name="clipToOutline" format="boolean" />
</declare-styleable>
<!-- Attributes that can be assigned to a tag for a particular View. -->
@@ -6346,6 +6389,13 @@
<!-- The radius of the ripple when fully expanded. By default, the
radius is computed based on the size of the ripple's container. -->
<attr name="radius" />
+ <!-- The style of the ripple drawable is solid by default -->
+ <attr name="rippleStyle">
+ <!-- Solid is the default style -->
+ <enum name="solid" value="0" />
+ <!-- Patterned style-->
+ <enum name="patterned" value="1" />
+ </attr>
</declare-styleable>
<declare-styleable name="ScaleDrawable">
@@ -7960,6 +8010,14 @@
<attr name="minResizeWidth" format="dimension"/>
<!-- Minimum height that the AppWidget can be resized to. -->
<attr name="minResizeHeight" format="dimension"/>
+ <!-- Maximum width that the AppWidget can be resized to. -->
+ <attr name="maxResizeWidth" format="dimension"/>
+ <!-- Maximum height that the AppWidget can be resized to. -->
+ <attr name="maxResizeHeight" format="dimension"/>
+ <!-- Default width of the AppWidget in units of launcher grid cells. -->
+ <attr name="targetCellWidth" format="integer"/>
+ <!-- Default height of the AppWidget in units of launcher grid cells. -->
+ <attr name="targetCellHeight" format="integer"/>
<!-- Update period in milliseconds, or 0 if the AppWidget will update itself. -->
<attr name="updatePeriodMillis" format="integer" />
<!-- A resource id of a layout. -->
@@ -7969,9 +8027,17 @@
<!-- A class name in the AppWidget's package to be launched to configure.
If not supplied, then no activity will be launched. -->
<attr name="configure" format="string" />
- <!-- A preview of what the AppWidget will look like after it's configured.
- If not supplied, the AppWidget's icon will be used. -->
+ <!-- A preview, in a drawable resource id, of what the AppWidget will look like after it's
+ configured.
+ If not supplied, the AppWidget's icon will be used. -->
<attr name="previewImage" format="reference" />
+ <!-- The layout resource id of a preview of what the AppWidget will look like after it's
+ configured.
+ Unlike previewImage, previewLayout can better showcase AppWidget in different locales,
+ system themes, display sizes & density etc.
+ If supplied, this will take precedence over the previewImage on supported widget hosts.
+ Otherwise, previewImage will be used. -->
+ <attr name="previewLayout" format="reference" />
<!-- The view id of the AppWidget subview which should be auto-advanced.
by the widget's host. -->
<attr name="autoAdvanceViewId" format="reference" />
@@ -9032,6 +9098,7 @@
<!-- Used as a filter array on the theme to pull out only the EdgeEffect-relevant bits. -->
<declare-styleable name="EdgeEffect">
<attr name="colorEdgeEffect" />
+ <attr name="edgeEffectType" />
</declare-styleable>
<!-- Use <code>tv-input</code> as the root tag of the XML resource that describes a
@@ -9225,6 +9292,7 @@
<attr name="shortcutShortLabel" format="reference" />
<attr name="shortcutLongLabel" format="reference" />
<attr name="shortcutDisabledMessage" format="reference" />
+ <attr name="splashScreenTheme" format="reference"/>
</declare-styleable>
<declare-styleable name="ShortcutCategories">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b4e580aac959..0ae6a76e2a60 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -311,6 +311,10 @@
<flag name="recents" value="0x2000000" />
<!-- Additional flag from base permission type: this permission is managed by role. -->
<flag name="role" value="0x4000000" />
+ <!-- Additional flag from base permission type: this permission can also be granted if the
+ requesting application is signed by, or has in its signing lineage, any of the
+ certificate digests declared in {@link android.R.attr#knownCerts}. -->
+ <flag name="knownSigner" value="0x8000000" />
</attr>
<!-- Flags indicating more context for a permission group. -->
@@ -364,6 +368,15 @@
{@link android.R.styleable#AndroidManifestPermissionGroup permission-group} tag. -->
<attr name="permissionGroup" format="string" />
+ <!-- A reference to an array resource containing the signing certificate digests to be granted
+ this permission when using the {@code knownSigner} protection flag. The digest should
+ be computed over the DER encoding of the trusted certificate using the SHA-256 digest
+ algorithm.
+ <p>
+ If only a single signer is declared this can also be a string resource, or the digest
+ can be declared inline as the value for this attribute. -->
+ <attr name="knownCerts" format="reference|string" />
+
<!-- Specify the name of a user ID that will be shared between multiple
packages. By default, each package gets its own unique user-id.
By setting this value on two or more packages, each of these packages
@@ -1935,6 +1948,7 @@
<attr name="request" />
<attr name="protectionLevel" />
<attr name="permissionFlags" />
+ <attr name="knownCerts" />
</declare-styleable>
<!-- The <code>permission-group</code> tag declares a logical grouping of
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 20a5d379cbcc..59c260cecfbf 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -153,6 +153,11 @@
<color name="notification_action_list_background_color">@null</color>
+ <!-- The color of the Decline and Hang Up actions on a CallStyle notification -->
+ <color name="call_notification_decline_color">#d93025</color>
+ <!-- The color of the Answer action on a CallStyle notification -->
+ <color name="call_notification_answer_color">#1e8e3e</color>
+
<!-- Keyguard colors -->
<color name="keyguard_avatar_frame_color">#ffffffff</color>
<color name="keyguard_avatar_frame_shadow_color">#80000000</color>
@@ -235,77 +240,114 @@
<color name="conversation_important_highlight">#F9AB00</color>
- <!-- Lightest shade of the main color used by the system. White.
+ <!-- Lightest shade of the primary color used by the system. White.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_primary_0">#ffffff</color>
+ <!-- Shade of the primary system color at 95% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_primary_50">#f2f2f2</color>
+ <!-- Shade of the primary system color at 90% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_primary_100">#e3e3e3</color>
+ <!-- Shade of the primary system color at 80% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_primary_200">#c7c7c7</color>
+ <!-- Shade of the primary system color at 70% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_primary_300">#ababab</color>
+ <!-- Shade of the primary system color at 60% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_primary_400">#8f8f8f</color>
+ <!-- Shade of the primary system color at 50% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_primary_500">#757575</color>
+ <!-- Shade of the primary system color at 40% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_primary_600">#5e5e5e</color>
+ <!-- Shade of the primary system color at 30% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_primary_700">#474747</color>
+ <!-- Shade of the primary system color at 20% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_primary_800">#303030</color>
+ <!-- Shade of the primary system color at 10% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_primary_900">#1f1f1f</color>
+ <!-- Darkest shade of the primary color used by the system. Black.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_primary_1000">#000000</color>
+
+ <!-- Lightest shade of the secondary color used by the system. White.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_0">#ffffff</color>
- <!-- Shade of the main system color at 95% lightness.
+ <color name="system_secondary_0">#ffffff</color>
+ <!-- Shade of the secondary system color at 95% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_50">#f0f0f0</color>
- <!-- Shade of the main system color at 90% lightness.
+ <color name="system_secondary_50">#91fff4</color>
+ <!-- Shade of the secondary system color at 90% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_100">#e2e2e2</color>
- <!-- Shade of the main system color at 80% lightness.
+ <color name="system_secondary_100">#83f6e5</color>
+ <!-- Shade of the secondary system color at 80% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_200">#c6c6c6</color>
- <!-- Shade of the main system color at 70% lightness.
+ <color name="system_secondary_200">#65d9c9</color>
+ <!-- Shade of the secondary system color at 70% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_300">#ababab</color>
- <!-- Shade of the main system color at 60% lightness.
+ <color name="system_secondary_300">#45bdae</color>
+ <!-- Shade of the secondary system color at 60% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_400">#909090</color>
- <!-- Shade of the main system color at 50% lightness.
+ <color name="system_secondary_400">#1fa293</color>
+ <!-- Shade of the secondary system color at 50% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_500">#757575</color>
- <!-- Shade of the main system color at 40% lightness.
+ <color name="system_secondary_500">#008377</color>
+ <!-- Shade of the secondary system color at 40% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_600">#5e5e5e</color>
- <!-- Shade of the main system color at 30% lightness.
+ <color name="system_secondary_600">#006d61</color>
+ <!-- Shade of the secondary system color at 30% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_700">#464646</color>
- <!-- Shade of the main system color at 20% lightness.
+ <color name="system_secondary_700">#005449</color>
+ <!-- Shade of the secondary system color at 20% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_800">#303030</color>
- <!-- Shade of the main system color at 10% lightness.
+ <color name="system_secondary_800">#003c33</color>
+ <!-- Shade of the secondary system color at 10% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_900">#1b1b1b</color>
- <!-- Darkest shade of the main color used by the system. Black.
+ <color name="system_secondary_900">#00271e</color>
+ <!-- Darkest shade of the secondary color used by the system. Black.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_1000">#000000</color>
+ <color name="system_secondary_1000">#000000</color>
- <!-- Lightest shade of the accent color used by the system. White.
+ <!-- Lightest shade of the neutral color used by the system. White.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent_0">#ffffff</color>
- <!-- Shade of the accent system color at 95% lightness.
+ <color name="system_neutral_0">#ffffff</color>
+ <!-- Shade of the neutral system color at 95% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent_50">#91fff4</color>
- <!-- Shade of the accent system color at 90% lightness.
+ <color name="system_neutral_50">#f0f0f0</color>
+ <!-- Shade of the neutral system color at 90% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent_100">#83f6e5</color>
- <!-- Shade of the accent system color at 80% lightness.
+ <color name="system_neutral_100">#e2e2e2</color>
+ <!-- Shade of the neutral system color at 80% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent_200">#65d9c9</color>
- <!-- Shade of the accent system color at 70% lightness.
+ <color name="system_neutral_200">#c6c6c6</color>
+ <!-- Shade of the neutral system color at 70% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent_300">#45bdae</color>
- <!-- Shade of the accent system color at 60% lightness.
+ <color name="system_neutral_300">#ababab</color>
+ <!-- Shade of the neutral system color at 60% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent_400">#1fa293</color>
- <!-- Shade of the accent system color at 50% lightness.
+ <color name="system_neutral_400">#909090</color>
+ <!-- Shade of the neutral system color at 50% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent_500">#008377</color>
- <!-- Shade of the accent system color at 40% lightness.
+ <color name="system_neutral_500">#757575</color>
+ <!-- Shade of the neutral system color at 40% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent_600">#006d61</color>
- <!-- Shade of the accent system color at 30% lightness.
+ <color name="system_neutral_600">#5e5e5e</color>
+ <!-- Shade of the neutral system color at 30% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent_700">#005449</color>
- <!-- Shade of the accent system color at 20% lightness.
+ <color name="system_neutral_700">#464646</color>
+ <!-- Shade of the neutral system color at 20% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent_800">#003c33</color>
- <!-- Shade of the accent system color at 10% lightness.
+ <color name="system_neutral_800">#303030</color>
+ <!-- Shade of the neutral system color at 10% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent_900">#00271e</color>
- <!-- Darkest shade of the accent color used by the system. Black.
+ <color name="system_neutral_900">#1b1b1b</color>
+ <!-- Darkest shade of the neutral color used by the system. Black.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent_1000">#000000</color>
+ <color name="system_neutral_1000">#000000</color>
</resources>
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index c8cccc45f634..1020c14ef665 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -17,9 +17,9 @@
<!-- Colors specific to DeviceDefault themes. These are mostly pass-throughs to enable
overlaying new theme colors. -->
<resources>
- <color name="primary_device_default_dark">@color/system_main_800</color>
- <color name="primary_device_default_light">@color/system_main_50</color>
- <color name="primary_device_default_settings">@color/system_main_800</color>
+ <color name="primary_device_default_dark">@color/system_primary_800</color>
+ <color name="primary_device_default_light">@color/system_primary_50</color>
+ <color name="primary_device_default_settings">@color/system_primary_800</color>
<color name="primary_device_default_settings_light">@color/primary_device_default_light</color>
<color name="primary_dark_device_default_dark">@color/primary_device_default_dark</color>
<color name="primary_dark_device_default_light">@color/primary_device_default_light</color>
@@ -33,28 +33,30 @@
<color name="tertiary_device_default_settings">@color/tertiary_material_settings</color>
<color name="quaternary_device_default_settings">@color/quaternary_material_settings</color>
- <color name="accent_device_default_light">@color/system_accent_600</color>
- <color name="accent_device_default_dark">@color/system_accent_200</color>
+ <color name="accent_device_default_light">@color/system_secondary_600</color>
+ <color name="accent_device_default_dark">@color/system_secondary_200</color>
<color name="accent_device_default">@color/accent_device_default_light</color>
- <color name="background_device_default_dark">@color/system_main_800</color>
- <color name="background_device_default_light">@color/system_main_50</color>
- <color name="background_floating_device_default_dark">@color/system_main_900</color>
- <color name="background_floating_device_default_light">@color/system_main_100</color>
+ <color name="background_device_default_dark">@color/system_primary_800</color>
+ <color name="background_device_default_light">@color/system_primary_50</color>
+ <color name="background_floating_device_default_dark">@color/system_primary_900</color>
+ <color name="background_floating_device_default_light">@color/system_primary_100</color>
- <color name="text_color_primary_device_default_light">@color/system_main_900</color>
- <color name="text_color_primary_device_default_dark">@color/system_main_50</color>
- <color name="text_color_secondary_device_default_light">@color/system_main_700</color>
- <color name="text_color_secondary_device_default_dark">@color/system_main_200</color>
- <color name="text_color_tertiary_device_default_light">@color/system_main_500</color>
- <color name="text_color_tertiary_device_default_dark">@color/system_main_400</color>
+ <color name="text_color_primary_device_default_light">@color/system_primary_900</color>
+ <color name="text_color_primary_device_default_dark">@color/system_primary_50</color>
+ <color name="text_color_secondary_device_default_light">@color/system_primary_700</color>
+ <color name="text_color_secondary_device_default_dark">@color/system_primary_200</color>
+ <color name="text_color_tertiary_device_default_light">@color/system_primary_500</color>
+ <color name="text_color_tertiary_device_default_dark">@color/system_primary_400</color>
+ <color name="foreground_device_default_light">@color/text_color_primary_device_default_light</color>
+ <color name="foreground_device_default_dark">@color/text_color_primary_device_default_dark</color>
<!-- Error color -->
<color name="error_color_device_default_dark">@color/error_color_material_dark</color>
<color name="error_color_device_default_light">@color/error_color_material_light</color>
- <color name="list_divider_color_light">@color/system_main_500</color>
- <color name="list_divider_color_dark">@color/system_main_400</color>
+ <color name="list_divider_color_light">@color/system_primary_500</color>
+ <color name="list_divider_color_dark">@color/system_primary_400</color>
<color name="list_divider_opacity_device_default_light">@android:color/white</color>
<color name="list_divider_opacity_device_default_dark">@android:color/white</color>
diff --git a/core/res/res/values/colors_leanback.xml b/core/res/res/values/colors_leanback.xml
index e52a861e2040..df0be03d6f8e 100644
--- a/core/res/res/values/colors_leanback.xml
+++ b/core/res/res/values/colors_leanback.xml
@@ -26,4 +26,9 @@
<color name="secondary_text_leanback_light">#99222222</color>
<color name="primary_text_leanback_formwizard_default_dark">#ffeeeeee</color>
+
+ <color name="btn_leanback_focused">#E8EAED</color>
+ <color name="btn_leanback_unfocused">#1AFFFFFF</color>
+ <color name="btn_text_leanback_focused">#0E0E0E</color>
+ <color name="btn_text_leanback_unfocused">#E8EAED</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9e9f263bbacc..afeb02d3b996 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1590,8 +1590,8 @@
take precedence over lower ones.
See com.android.server.timedetector.TimeDetectorStrategy for available sources. -->
<string-array name="config_autoTimeSourcesPriority">
- <item>telephony</item>
<item>network</item>
+ <item>telephony</item>
</string-array>
<!-- Enables the GnssTimeUpdate service. This is the global switch for enabling Gnss time based
@@ -1949,8 +1949,10 @@
<string name="config_systemAutomotiveProjection" translatable="false"></string>
<!-- The name of the package that will hold the system cluster service role. -->
<string name="config_systemAutomotiveCluster" translatable="false"></string>
- <!-- The name of the package that will hold the system video call role. -->
- <string name="config_systemVideoCall" translatable="false"></string>
+ <!-- The name of the package that will hold the system shell role. -->
+ <string name="config_systemShell" translatable="false">com.android.shell</string>
+ <!-- The name of the package that will hold the system contacts role. -->
+ <string name="config_systemContacts" translatable="false">com.android.contacts</string>
<!-- The name of the package that will be allowed to change its components' label/icon. -->
<string name="config_overrideComponentUiPackage" translatable="false"></string>
@@ -2512,10 +2514,9 @@
<string name="config_ethernet_tcp_buffers" translatable="false">524288,1048576,3145728,524288,1048576,2097152</string>
<!-- What source to use to estimate link upstream and downstream bandwidth capacities.
- Default is carrier_config, but it should be set to modem if the modem is returning
- predictive (instead of instantaneous) bandwidth estimate.
- Values are carrier_config and modem. -->
- <string name="config_bandwidthEstimateSource">carrier_config</string>
+ Default is bandwidth_estimator.
+ Values are bandwidth_estimator, carrier_config and modem. -->
+ <string name="config_bandwidthEstimateSource">bandwidth_estimator</string>
<!-- Whether WiFi display is supported by this device.
There are many prerequisites for this feature to work correctly.
@@ -3210,6 +3211,10 @@
panning to scaling the magnification viewport. -->
<item name="config_screen_magnification_scaling_threshold" format="float" type="dimen">0.3</item>
+ <!-- Whether to support magnification area. If not enabled, it would hide the entry in
+ magnification settings and adjust the default magnification capability. -->
+ <bool name="config_magnification_area">true</bool>
+
<!-- If true, the display will be shifted around in ambient mode. -->
<bool name="config_enableBurnInProtection">false</bool>
@@ -3483,6 +3488,11 @@
<!-- The alarm window (in milliseconds) that JobScheduler uses to enter the idle state -->
<integer name="config_jobSchedulerIdleWindowSlop">300000</integer>
+ <!-- If true, jobs from background user will be restricted -->
+ <bool name="config_jobSchedulerRestrictBackgroundUser">false</bool>
+ <!-- The length of grace period after user becomes background user -->
+ <integer name="config_jobSchedulerUserGracePeriod">60000</integer>
+
<!-- If true, all guest users created on the device will be ephemeral. -->
<bool name="config_guestUserEphemeral">false</bool>
@@ -4601,11 +4611,12 @@
<item>com.android.systemui</item>
</string-array>
- <!-- Package name of custom media key dispatcher class used by MediaSessionService. -->
+ <!-- Component name of custom media key dispatcher class used by MediaSessionService. -->
<string name="config_customMediaKeyDispatcher"></string>
- <!-- Package name of custom session policy provider class used by MediaSessionService. -->
- <string name="config_customSessionPolicyProvider"></string>
+ <!-- Component name of custom media session policy provider class used by
+ MediaSessionService. -->
+ <string name="config_customMediaSessionPolicyProvider"></string>
<!-- The max scale for the wallpaper when it's zoomed in -->
<item name="config_wallpaperMaxScale" format="float" type="dimen">1.10</item>
@@ -4729,4 +4740,7 @@
<!-- Whether to enable the one-handed keyguard on the lock screen for wide-screen devices. -->
<bool name="config_enableOneHandedKeyguard">false</bool>
+
+ <!-- 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>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index cb16af5a16b0..3c712edffa63 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -236,9 +236,20 @@
value is calculated in ConversationLayout#updateActionListPadding() -->
<dimen name="notification_actions_padding_start">36dp</dimen>
+ <!-- The start padding to optionally use (e.g. if there's extra space) for CallStyle
+ notification actions.
+ this = conversation_content_start (80dp) - button inset (4dp) - action padding (12dp) -->
+ <dimen name="call_notification_collapsible_indent">64dp</dimen>
+
<!-- The size of icons for visual actions in the notification_material_action_list -->
<dimen name="notification_actions_icon_size">48dp</dimen>
+ <!-- The size of icons for visual actions in the notification_material_action_list -->
+ <dimen name="notification_actions_icon_drawable_size">20dp</dimen>
+
+ <!-- The corner radius if the emphasized action buttons in a notification -->
+ <dimen name="notification_action_button_radius">8dp</dimen>
+
<!-- Size of the stroke with for the emphasized notification button style -->
<dimen name="emphasized_button_stroke_width">1dp</dimen>
@@ -383,10 +394,6 @@
<dimen name="alert_dialog_button_bar_width">64dp</dimen>
<!-- Dialog button bar height -->
<dimen name="alert_dialog_button_bar_height">48dip</dimen>
- <!-- Leanback dialog vertical margin -->
- <dimen name="leanback_alert_dialog_vertical_margin">27dip</dimen>
- <!-- Leanback dialog horizontal margin -->
- <dimen name="leanback_alert_dialog_horizontal_margin">54dip</dimen>
<!-- Default height of an action bar. -->
<dimen name="action_bar_default_height">48dip</dimen>
@@ -489,14 +496,20 @@
<!-- The padding on top of inbox style elements -->
<dimen name="notification_inbox_item_top_padding">5dp</dimen>
+ <!-- Size of the verification icon for call notifications -->
+ <dimen name="notification_verification_icon_size">@dimen/notification_badge_size</dimen>
+
<!-- Size of the feedback indicator for notifications -->
<dimen name="notification_feedback_size">20dp</dimen>
+ <!-- Size of the phishing alert for notifications -->
+ <dimen name="notification_phishing_alert_size">@dimen/notification_badge_size</dimen>
+
<!-- Size of the profile badge for notifications -->
<dimen name="notification_badge_size">12dp</dimen>
<!-- Size of the alerted icon for notifications -->
- <dimen name="notification_alerted_size">12dp</dimen>
+ <dimen name="notification_alerted_size">@dimen/notification_badge_size</dimen>
<!-- Keyguard dimensions -->
<!-- TEMP -->
@@ -919,4 +932,12 @@
<dimen name="controls_thumbnail_image_max_height">140dp</dimen>
<!-- The maximum width of a thumbnail in a ThumbnailTemplate. The image will be reduced to that width in case they are bigger.-->
<dimen name="controls_thumbnail_image_max_width">280dp</dimen>
+
+ <!-- System-provided radius for the background view of app widgets. The resolved value of this resource may change at runtime. -->
+ <dimen name="system_app_widget_background_radius">16dp</dimen>
+ <!-- System-provided radius for inner views on app widgets. The resolved value of this resource may change at runtime. -->
+ <dimen name="system_app_widget_inner_radius">8dp</dimen>
+ <!-- System-provided padding for inner views on app widgets. The resolved value of this resource may change at runtime. -->
+ <dimen name="system_app_widget_internal_padding">16dp</dimen>
+
</resources>
diff --git a/core/res/res/values/dimens_leanback.xml b/core/res/res/values/dimens_leanback.xml
index c824a2a79f83..3ab21963ed77 100644
--- a/core/res/res/values/dimens_leanback.xml
+++ b/core/res/res/values/dimens_leanback.xml
@@ -83,4 +83,10 @@
<dimen name="leanback_setup_translation_backward_out_content_end_v4">@integer/leanback_setup_translation_content_cliff_v4</dimen>
<integer name="leanback_setup_translation_backward_out_content_delay">0</integer>
<integer name="leanback_setup_translation_backward_out_content_duration">@integer/leanback_setup_base_animation_duration</integer>
+
+ <!-- Button dimens -->
+ <dimen name="leanback_button_height">42dp</dimen>
+ <dimen name="leanback_button_radius">55dp</dimen>
+ <dimen name="leanback_button_padding_horizontal">22dp</dimen>
+ <dimen name="leanback_button_padding_vertical">11dp</dimen>
</resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index a4c7293e48b6..0f0ac56f1e51 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -102,6 +102,10 @@
<item type="id" name="selection_end_handle" />
<item type="id" name="insertion_handle" />
<item type="id" name="floating_toolbar_menu_item_image_button" />
+ <item type="id" name="camera" />
+ <item type="id" name="mic" />
+ <item type="id" name="overlay" />
+ <item type="id" name="app_ops" />
<!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SHOW_ON_SCREEN}. -->
<item type="id" name="accessibilityActionShowOnScreen" />
@@ -141,6 +145,7 @@
<item type="id" name="remote_input_tag" />
<item type="id" name="pending_intent_tag" />
+ <item type="id" name="remote_checked_change_listener_tag" />
<item type="id" name="cross_task_transition" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 30cfb8986035..a1ea61c447c0 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3061,6 +3061,21 @@
<public name="nativeHeapZeroInit" />
<!-- @hide @SystemApi -->
<public name="hotwordDetectionService" />
+ <public name="previewLayout" />
+ <public name="clipToOutline" />
+ <public name="edgeEffectType" />
+ <public name="knownCerts" />
+ <public name="windowBackgroundBlurRadius"/>
+ <public name="windowSplashScreenBackground"/>
+ <public name="windowSplashScreenAnimatedIcon"/>
+ <public name="windowSplashScreenAnimationDuration"/>
+ <public name="windowSplashScreenBrandingImage"/>
+ <public name="splashScreenTheme" />
+ <public name="rippleStyle" />
+ <public name="maxResizeWidth" />
+ <public name="maxResizeHeight" />
+ <public name="targetCellWidth" />
+ <public name="targetCellHeight" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
@@ -3071,36 +3086,54 @@
<!-- color definitions go here -->
<!-- Material design dynamic system palette:-->
- <!-- Dominant color -->
- <public name="system_main_0" />
- <public name="system_main_50" />
- <public name="system_main_100" />
- <public name="system_main_200" />
- <public name="system_main_300" />
- <public name="system_main_400" />
- <public name="system_main_500" />
- <public name="system_main_600" />
- <public name="system_main_700" />
- <public name="system_main_800" />
- <public name="system_main_900" />
- <public name="system_main_1000" />
- <!-- Accent color -->
- <public name="system_accent_0" />
- <public name="system_accent_50" />
- <public name="system_accent_100" />
- <public name="system_accent_200" />
- <public name="system_accent_300" />
- <public name="system_accent_400" />
- <public name="system_accent_500" />
- <public name="system_accent_600" />
- <public name="system_accent_700" />
- <public name="system_accent_800" />
- <public name="system_accent_900" />
- <public name="system_accent_1000" />
+ <!-- Primary color -->
+ <public name="system_primary_0" />
+ <public name="system_primary_50" />
+ <public name="system_primary_100" />
+ <public name="system_primary_200" />
+ <public name="system_primary_300" />
+ <public name="system_primary_400" />
+ <public name="system_primary_500" />
+ <public name="system_primary_600" />
+ <public name="system_primary_700" />
+ <public name="system_primary_800" />
+ <public name="system_primary_900" />
+ <public name="system_primary_1000" />
+ <!-- Secondary color -->
+ <public name="system_secondary_0" />
+ <public name="system_secondary_50" />
+ <public name="system_secondary_100" />
+ <public name="system_secondary_200" />
+ <public name="system_secondary_300" />
+ <public name="system_secondary_400" />
+ <public name="system_secondary_500" />
+ <public name="system_secondary_600" />
+ <public name="system_secondary_700" />
+ <public name="system_secondary_800" />
+ <public name="system_secondary_900" />
+ <public name="system_secondary_1000" />
+ <!-- Neutral color -->
+ <public name="system_neutral_0" />
+ <public name="system_neutral_50" />
+ <public name="system_neutral_100" />
+ <public name="system_neutral_200" />
+ <public name="system_neutral_300" />
+ <public name="system_neutral_400" />
+ <public name="system_neutral_500" />
+ <public name="system_neutral_600" />
+ <public name="system_neutral_700" />
+ <public name="system_neutral_800" />
+ <public name="system_neutral_900" />
+ <public name="system_neutral_1000" />
</public-group>
<public-group type="dimen" first-id="0x01050008">
<!-- dimension definitions go here -->
+
+ <!-- System-provided dimensions for app widgets. -->
+ <public name="system_app_widget_background_radius" />
+ <public name="system_app_widget_inner_radius" />
+ <public name="system_app_widget_internal_padding" />
</public-group>
<public-group type="bool" first-id="0x01110007">
@@ -3115,9 +3148,15 @@
<!-- @hide @SystemApi @TestApi -->
<public name="config_systemAutomotiveCluster" />
<!-- @hide @SystemApi @TestApi -->
- <public name="config_systemVideoCall" />
- <!-- @hide @SystemApi @TestApi -->
<public name="config_systemAutomotiveProjection" />
+ <!-- @hide @SystemApi -->
+ <public name="config_systemShell" />
+ <!-- @hide @SystemApi -->
+ <public name="config_systemContacts" />
+ <!-- @hide @SystemApi -->
+ <public name="config_customMediaKeyDispatcher" />
+ <!-- @hide @SystemApi -->
+ <public name="config_customMediaSessionPolicyProvider" />
</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 891c0a48655b..4c63cf7e9b75 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1832,6 +1832,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_startViewPermissionUsage">Allows the holder to start the permission usage for an app. Should never be needed for normal apps.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+ <string name="permlab_highSamplingRateSensors">access sensor data at a high sampling rate</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this.[CHAR_LIMIT=NONE] -->
+ <string name="permdesc_highSamplingRateSensors">Allows the app to sample sensor data at a rate greater than 200 Hz</string>
+
<!-- Policy administration -->
<!-- Title of policy access to limiting the user's password choices -->
@@ -4534,9 +4539,8 @@
shown in the warning dialog about the accessibility shortcut. -->
<string name="color_correction_feature_name">Color Correction</string>
- <!-- TODO(b/170970602): remove translatable=false when RBC has official name and strings -->
- <!-- Title of Reduce Bright Colors feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
- <string name="reduce_bright_colors_feature_name" translatable="false">Reduce Bright Colors</string>
+ <!-- Title of Reduce Brightness feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
+ <string name="reduce_bright_colors_feature_name">Reduce Brightness</string>
<!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility service. [CHAR LIMIT=none] -->
<string name="accessibility_shortcut_enabling_service">Held volume keys. <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> turned on.</string>
@@ -5000,6 +5004,9 @@
<string name="stk_cc_ss_to_ussd">SS request changed to USSD request</string>
<string name="stk_cc_ss_to_ss">Changed to new SS request</string>
+ <!-- Content description of the phishing alert icon in the notification. [CHAR_LIMIT=NONE] -->
+ <string name="notification_phishing_alert_content_description">Phishing alert</string>
+
<!-- Content description of the work profile icon in the notification. -->
<string name="notification_work_profile_content_description">Work profile</string>
@@ -5042,6 +5049,18 @@
<!-- Tempalate for Notification.MessagingStyle to join a conversation name with the name of the sender of a message, to make a notification title [CHAR LIMIT=NONE] -->
<string name="notification_messaging_title_template"><xliff:g id="conversation_title" example="Tasty Treat Team">%1$s</xliff:g>: <xliff:g id="sender_name" example="Adrian Baker">%2$s</xliff:g></string>
+ <!-- Action text to be displayed for the "answer" action of an incoming call [CHAR LIMIT=13] -->
+ <string name="call_notification_answer_action">Answer</string>
+ <!-- Action text to be displayed for the "decline" action of an incoming call [CHAR LIMIT=13] -->
+ <string name="call_notification_decline_action">Decline</string>
+ <!-- Action text to be displayed for the "hang up" action of an ongoing call [CHAR LIMIT=13] -->
+ <string name="call_notification_hang_up_action">Hang Up</string>
+ <!-- Default notification text to be displayed in incoming call notifications [CHAR LIMIT=40] -->
+ <string name="call_notification_incoming_text">Incoming call</string>
+ <!-- Default notification text to be displayed in ongoing call notifications [CHAR LIMIT=40] -->
+ <string name="call_notification_ongoing_text">Ongoing call</string>
+ <!-- Default notification text to be displayed in screening call notifications [CHAR LIMIT=40] -->
+ <string name="call_notification_screening_text">Screening an incoming call</string>
<!-- Label describing the number of selected items [CHAR LIMIT=48] -->
<plurals name="selected_count">
diff --git a/core/res/res/values/styles_leanback.xml b/core/res/res/values/styles_leanback.xml
index aaeaadd7418d..7eaf36d5366a 100644
--- a/core/res/res/values/styles_leanback.xml
+++ b/core/res/res/values/styles_leanback.xml
@@ -16,11 +16,34 @@
<resources>
<style name="AlertDialog.Leanback" parent="AlertDialog.Material">
<item name="buttonPanelSideLayout">@android:layout/alert_dialog_leanback_button_panel_side</item>
+ <item name="layout">@android:layout/alert_dialog_leanback</item>
</style>
<style name="AlertDialog.Leanback.Light">
</style>
+ <style name="Widget.Leanback.Button" parent="Widget.Material.Button">
+ <item name="android:background">@drawable/btn_leanback</item>
+ <item name="android:textColor">@color/btn_leanback_text_color</item>
+ <item name="android:layout_height">@dimen/leanback_button_height</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:textAllCaps">false</item>
+ <item name="android:paddingHorizontal">@dimen/leanback_button_padding_horizontal</item>
+ <item name="android:paddingVertical">@dimen/leanback_button_padding_vertical</item>
+ </style>
+
+ <style name="Widget.Leanback.Button.ButtonBar" parent="Widget.Leanback.Button">
+ <item name="android:layout_marginStart">10dp</item>
+ </style>
+
+ <style name="Widget.Leanback.Button.ButtonBarGravityStart" parent="Widget.Leanback.Button">
+ <item name="android:layout_marginEnd">10dp</item>
+ </style>
+
+ <style name="Widget.Leanback.ButtonBar" parent="Widget.Material.ButtonBar">
+ <item name="android:padding">?android:attr/dialogPreferredPadding</item>
+ </style>
+
<style name="Widget.Leanback.TimePicker" parent="Widget.Material.TimePicker">
<item name="timePickerMode">spinner</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e9753f7e9987..900726ed0638 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -233,6 +233,7 @@
<java-symbol type="id" name="pin_confirm_text" />
<java-symbol type="id" name="pin_error_message" />
<java-symbol type="id" name="timePickerLayout" />
+ <java-symbol type="id" name="phishing_alert" />
<java-symbol type="id" name="profile_badge" />
<java-symbol type="id" name="alerted_icon" />
<java-symbol type="id" name="transitionPosition" />
@@ -1834,6 +1835,9 @@
<java-symbol type="string" name="forward_intent_to_owner" />
<java-symbol type="string" name="forward_intent_to_work" />
<java-symbol type="dimen" name="cross_profile_apps_thumbnail_size" />
+ <java-symbol type="layout" name="splash_screen_view" />
+ <java-symbol type="id" name="splashscreen_icon_view" />
+ <java-symbol type="id" name="splashscreen_branding_view" />
<!-- From services -->
<java-symbol type="anim" name="screen_rotate_0_enter" />
@@ -2665,6 +2669,7 @@
<java-symbol type="bool" name="config_defaultWindowFeatureContextMenu" />
<java-symbol type="bool" name="config_overrideRemoteViewsActivityTransition" />
<java-symbol type="attr" name="colorProgressBackgroundNormal" />
+ <java-symbol type="bool" name="config_allow_pin_storage_for_unattended_reboot" />
<java-symbol type="layout" name="simple_account_item" />
<java-symbol type="string" name="prohibit_manual_network_selection_in_gobal_mode" />
@@ -2698,6 +2703,8 @@
<java-symbol type="integer" name="config_jobSchedulerInactivityIdleThreshold" />
<java-symbol type="integer" name="config_jobSchedulerIdleWindowSlop" />
+ <java-symbol type="bool" name="config_jobSchedulerRestrictBackgroundUser" />
+ <java-symbol type="integer" name="config_jobSchedulerUserGracePeriod" />
<java-symbol type="style" name="Animation.ImmersiveModeConfirmation" />
@@ -2826,6 +2833,7 @@
<java-symbol type="id" name="smart_reply_container" />
<java-symbol type="id" name="remote_input_tag" />
<java-symbol type="id" name="pending_intent_tag" />
+ <java-symbol type="id" name="remote_checked_change_listener_tag" />
<java-symbol type="id" name="notification_action_index_tag" />
<java-symbol type="attr" name="seekBarDialogPreferenceStyle" />
@@ -3104,8 +3112,26 @@
<!-- TV Remote Service package -->
<java-symbol type="string" name="config_tvRemoteServicePackage" />
+
+ <!-- Notifications: MessagingStyle -->
<java-symbol type="string" name="notification_messaging_title_template" />
+ <!-- Notifications: CallStyle -->
+ <java-symbol type="layout" name="notification_template_material_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" />
+ <java-symbol type="string" name="call_notification_incoming_text" />
+ <java-symbol type="string" name="call_notification_ongoing_text" />
+ <java-symbol type="string" name="call_notification_screening_text" />
+ <java-symbol type="color" name="call_notification_decline_color"/>
+ <java-symbol type="color" name="call_notification_answer_color"/>
+ <java-symbol type="dimen" name="call_notification_collapsible_indent"/>
+ <java-symbol type="drawable" name="ic_call_answer" />
+ <java-symbol type="drawable" name="ic_call_decline" />
+ <java-symbol type="id" name="verification_icon" />
+ <java-symbol type="id" name="verification_text" />
+
<!-- Notification handler / dashboard package -->
<java-symbol type="string" name="config_notificationHandlerPackage" />
@@ -3431,6 +3457,7 @@
<java-symbol type="dimen" name="notification_media_image_max_width"/>
<java-symbol type="dimen" name="notification_media_image_max_height"/>
<java-symbol type="dimen" name="notification_right_icon_size"/>
+ <java-symbol type="dimen" name="notification_actions_icon_drawable_size"/>
<java-symbol type="dimen" name="notification_custom_view_max_image_height"/>
<java-symbol type="dimen" name="notification_custom_view_max_image_width"/>
@@ -4114,8 +4141,6 @@
<java-symbol type="string" name="notification_history_title_placeholder" />
- <java-symbol type="string" name="config_customMediaKeyDispatcher" />
- <java-symbol type="string" name="config_customSessionPolicyProvider" />
<!-- The max scale for the wallpaper when it's zoomed in -->
<java-symbol type="dimen" name="config_wallpaperMaxScale"/>
@@ -4175,6 +4200,8 @@
<java-symbol type="string" name="turn_on_magnification_settings_action" />
<java-symbol type="string" name="dismiss_action" />
+ <java-symbol type="bool" name="config_magnification_area" />
+
<java-symbol type="bool" name="config_trackerAppNeedsPermissions"/>
<!-- Package with global data query permissions for AppSearch -->
@@ -4202,6 +4229,4 @@
<java-symbol type="bool" name="config_telephony5gNonStandalone" />
<java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
-
- <java-symbol type="bool" name="config_enableOneHandedKeyguard" />
</resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 049ba23a1a97..87ae1623ca92 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -167,6 +167,9 @@ please see themes_device_defaults.xml.
<!-- Window attributes -->
<item name="windowBackground">@drawable/screen_background_selector_dark</item>
<item name="windowBackgroundFallback">?attr/colorBackground</item>
+ <item name="windowSplashScreenBackground">@color/transparent</item>
+ <item name="windowSplashScreenAnimatedIcon">@null</item>
+ <item name="windowSplashScreenBrandingImage">@null</item>
<item name="windowClipToOutline">false</item>
<item name="windowFrame">@null</item>
<item name="windowNoTitle">false</item>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index ee17f6f7ce21..e7e049da18c9 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -222,6 +222,11 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
+
+ <!-- Ripple style-->
+ <item name="rippleStyle">solid</item>
</style>
<style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />
@@ -236,6 +241,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -267,6 +274,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -300,6 +309,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -332,6 +343,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -381,6 +394,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -405,6 +420,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -435,6 +452,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -466,6 +485,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -513,6 +534,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -545,6 +568,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -575,6 +600,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -607,6 +634,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -638,6 +667,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -669,6 +700,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -700,6 +733,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -731,6 +766,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -766,6 +803,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Text styles -->
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
@@ -798,6 +837,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -827,6 +868,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1012,6 +1055,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<item name="colorPopupBackground">?attr/colorBackgroundFloating</item>
<item name="panelColorBackground">?attr/colorBackgroundFloating</item>
</style>
@@ -1027,6 +1072,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1057,6 +1104,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1088,6 +1137,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1121,6 +1172,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1153,6 +1206,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1204,6 +1259,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Progress bar attributes -->
<item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
@@ -1227,6 +1284,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1260,6 +1319,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1294,6 +1355,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1329,6 +1392,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -1346,6 +1411,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
</style>
<!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
@@ -1362,6 +1429,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1397,6 +1466,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1430,6 +1501,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1462,6 +1535,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1493,6 +1568,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1524,6 +1601,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1553,6 +1632,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1672,6 +1753,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
+ <item name="colorForeground">@color/foreground_device_default_dark</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1702,6 +1785,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
@@ -1742,6 +1827,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1775,6 +1862,8 @@ easier.
<item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
<item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
<item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
+ <item name="colorForeground">@color/foreground_device_default_light</item>
+ <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
<!-- Dialog attributes -->
<item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml
index 9dca9128e362..efe582612982 100644
--- a/core/res/res/values/themes_leanback.xml
+++ b/core/res/res/values/themes_leanback.xml
@@ -22,6 +22,8 @@
<item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
<item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item>
<item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
+ <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item>
+ <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item>
</style>
<style name="Theme.Leanback.Light.Dialog" parent="Theme.Material.Light.BaseDialog">
@@ -32,6 +34,8 @@
<item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
<item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item>
<item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
+ <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item>
+ <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item>
</style>
<style name="Theme.Leanback.Settings.Dialog" parent="Theme.Material.Settings.BaseDialog">
@@ -42,6 +46,8 @@
<item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
<item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item>
<item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
+ <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item>
+ <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item>
</style>
<style name="Theme.Leanback.Dialog.Alert" parent="Theme.Material.Dialog.BaseAlert">
@@ -52,6 +58,8 @@
<item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
<item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item>
<item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
+ <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item>
+ <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item>
</style>
<style name="Theme.Leanback.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.BaseAlert">
@@ -62,6 +70,8 @@
<item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
<item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item>
<item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
+ <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item>
+ <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item>
</style>
<style name="Theme.Leanback.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.BaseAlert">
@@ -72,6 +82,8 @@
<item name="timePickerStyle">@style/Widget.Leanback.TimePicker</item>
<item name="datePickerStyle">@style/Widget.Leanback.DatePicker</item>
<item name="numberPickerStyle">@style/Widget.Leanback.NumberPicker</item>
+ <item name="buttonBarButtonStyle">@style/Widget.Leanback.Button.ButtonBarGravityStart</item>
+ <item name="buttonBarStyle">@style/Widget.Leanback.ButtonBar</item>
</style>
<style name="Theme.Leanback.Dialog.AppError" parent="Theme.Leanback.Dialog">
diff --git a/core/sysprop/Android.bp b/core/sysprop/Android.bp
index 237ede2006ea..f89099e5630d 100644
--- a/core/sysprop/Android.bp
+++ b/core/sysprop/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
sysprop_library {
name: "com.android.sysprop.localization",
srcs: ["LocalizationProperties.sysprop"],
diff --git a/core/tests/BroadcastRadioTests/Android.bp b/core/tests/BroadcastRadioTests/Android.bp
index b8efd198aebe..113f45dfef55 100644
--- a/core/tests/BroadcastRadioTests/Android.bp
+++ b/core/tests/BroadcastRadioTests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "BroadcastRadioTests",
privileged: true,
diff --git a/core/tests/ConnectivityManagerTest/Android.bp b/core/tests/ConnectivityManagerTest/Android.bp
index a33d219f53d2..beaf17615f1d 100644
--- a/core/tests/ConnectivityManagerTest/Android.bp
+++ b/core/tests/ConnectivityManagerTest/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ConnectivityManagerTest",
libs: [
diff --git a/core/tests/PackageInstallerSessions/Android.bp b/core/tests/PackageInstallerSessions/Android.bp
index 055249d47cb3..c112cbb47b55 100644
--- a/core/tests/PackageInstallerSessions/Android.bp
+++ b/core/tests/PackageInstallerSessions/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksCorePackageInstallerSessionsTests",
diff --git a/core/tests/PlatformCompatFramework/Android.bp b/core/tests/PlatformCompatFramework/Android.bp
index 33802650fa12..95e23ad396af 100644
--- a/core/tests/PlatformCompatFramework/Android.bp
+++ b/core/tests/PlatformCompatFramework/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "PlatformCompatFrameworkTests",
// Include all test java files.
diff --git a/core/tests/bandwidthtests/Android.bp b/core/tests/bandwidthtests/Android.bp
index 5d881b8be68c..f1ecd45073eb 100644
--- a/core/tests/bandwidthtests/Android.bp
+++ b/core/tests/bandwidthtests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "BandwidthTests",
// Include all test java files.
diff --git a/core/tests/benchmarks/Android.bp b/core/tests/benchmarks/Android.bp
index 8dd7928e6fc2..4cd546753dbf 100644
--- a/core/tests/benchmarks/Android.bp
+++ b/core/tests/benchmarks/Android.bp
@@ -16,6 +16,15 @@
// build framework base core benchmarks
// ============================================================
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "frameworks-base-core-benchmarks",
installable: true,
diff --git a/core/tests/bluetoothtests/Android.bp b/core/tests/bluetoothtests/Android.bp
index 4b6f9db33afc..a2e4dff8f826 100644
--- a/core/tests/bluetoothtests/Android.bp
+++ b/core/tests/bluetoothtests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "BluetoothTests",
// Include all test java files.
diff --git a/core/tests/bugreports/Android.bp b/core/tests/bugreports/Android.bp
index e42b4b4ae2bf..f87797a90c90 100644
--- a/core/tests/bugreports/Android.bp
+++ b/core/tests/bugreports/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "BugreportManagerTestCases",
srcs: ["src/**/*.java"],
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index e2b975ff78ac..fbabf4ae1200 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksCoreTests",
diff --git a/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp b/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp
index 25e4fc366124..b47c4702eafe 100644
--- a/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp
+++ b/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "BinderDeathRecipientHelperApp1",
diff --git a/core/tests/coretests/BinderProxyCountingTestApp/Android.bp b/core/tests/coretests/BinderProxyCountingTestApp/Android.bp
index 6279a4873c67..b1df8dbbee19 100644
--- a/core/tests/coretests/BinderProxyCountingTestApp/Android.bp
+++ b/core/tests/coretests/BinderProxyCountingTestApp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "BinderProxyCountingTestApp",
diff --git a/core/tests/coretests/BinderProxyCountingTestService/Android.bp b/core/tests/coretests/BinderProxyCountingTestService/Android.bp
index 22718cb86d66..52c23a15e4e3 100644
--- a/core/tests/coretests/BinderProxyCountingTestService/Android.bp
+++ b/core/tests/coretests/BinderProxyCountingTestService/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "BinderProxyCountingTestService",
diff --git a/core/tests/coretests/BstatsTestApp/Android.bp b/core/tests/coretests/BstatsTestApp/Android.bp
index a89d72830686..c82da9e7b449 100644
--- a/core/tests/coretests/BstatsTestApp/Android.bp
+++ b/core/tests/coretests/BstatsTestApp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "BstatsTestApp",
diff --git a/core/tests/coretests/DisabledTestApp/Android.bp b/core/tests/coretests/DisabledTestApp/Android.bp
index 419816e42eff..3ab321827e53 100644
--- a/core/tests/coretests/DisabledTestApp/Android.bp
+++ b/core/tests/coretests/DisabledTestApp/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "DisabledTestApp",
diff --git a/core/tests/coretests/EnabledTestApp/Android.bp b/core/tests/coretests/EnabledTestApp/Android.bp
index bc4f4bd2e4d7..750b578a0253 100644
--- a/core/tests/coretests/EnabledTestApp/Android.bp
+++ b/core/tests/coretests/EnabledTestApp/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "EnabledTestApp",
diff --git a/core/tests/coretests/aidl/Android.bp b/core/tests/coretests/aidl/Android.bp
index 6e442db78500..2d848cca4618 100644
--- a/core/tests/coretests/aidl/Android.bp
+++ b/core/tests/coretests/aidl/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test {
name: "coretests-aidl",
sdk_version: "current",
diff --git a/core/tests/coretests/apks/Android.bp b/core/tests/coretests/apks/Android.bp
index 20c87b2d2ce9..eda875acfdb1 100644
--- a/core/tests/coretests/apks/Android.bp
+++ b/core/tests/coretests/apks/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_defaults {
name: "FrameworksCoreTests_apks_defaults",
sdk_version: "current",
diff --git a/core/tests/coretests/apks/install/Android.bp b/core/tests/coretests/apks/install/Android.bp
index e783fe2ec2dc..652b49130433 100644
--- a/core/tests/coretests/apks/install/Android.bp
+++ b/core/tests/coretests/apks/install/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_install",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/install_bad_dex/Android.bp b/core/tests/coretests/apks/install_bad_dex/Android.bp
index d156793cf22a..7b96c9b47553 100644
--- a/core/tests/coretests/apks/install_bad_dex/Android.bp
+++ b/core/tests/coretests/apks/install_bad_dex/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_install_bad_dex_",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/install_complete_package_info/Android.bp b/core/tests/coretests/apks/install_complete_package_info/Android.bp
index 123558bda076..3fee0c6e7f7c 100644
--- a/core/tests/coretests/apks/install_complete_package_info/Android.bp
+++ b/core/tests/coretests/apks/install_complete_package_info/Android.bp
@@ -1,7 +1,15 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_install_complete_package_info",
defaults: ["FrameworksCoreTests_apks_defaults"],
srcs: ["**/*.java"],
}
-
diff --git a/core/tests/coretests/apks/install_decl_perm/Android.bp b/core/tests/coretests/apks/install_decl_perm/Android.bp
index 868e8b51bd49..bf1f0de9672a 100644
--- a/core/tests/coretests/apks/install_decl_perm/Android.bp
+++ b/core/tests/coretests/apks/install_decl_perm/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_install_decl_perm",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/install_jni_lib/Android.bp b/core/tests/coretests/apks/install_jni_lib/Android.bp
index df19fa09adcf..d315e7b70cb9 100644
--- a/core/tests/coretests/apks/install_jni_lib/Android.bp
+++ b/core/tests/coretests/apks/install_jni_lib/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_test_library {
name: "libframeworks_coretests_jni",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp
index 602b704a0a5d..20cc96ebc5c0 100644
--- a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_install_jni_lib_open_from_apk",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/install_loc_auto/Android.bp b/core/tests/coretests/apks/install_loc_auto/Android.bp
index 6393915ba283..37daf7608a43 100644
--- a/core/tests/coretests/apks/install_loc_auto/Android.bp
+++ b/core/tests/coretests/apks/install_loc_auto/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_install_loc_auto",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/install_loc_internal/Android.bp b/core/tests/coretests/apks/install_loc_internal/Android.bp
index 770aaa511b9d..3e233132b58d 100644
--- a/core/tests/coretests/apks/install_loc_internal/Android.bp
+++ b/core/tests/coretests/apks/install_loc_internal/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_install_loc_internal",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/install_loc_sdcard/Android.bp b/core/tests/coretests/apks/install_loc_sdcard/Android.bp
index 177940102e83..708e655e07db 100644
--- a/core/tests/coretests/apks/install_loc_sdcard/Android.bp
+++ b/core/tests/coretests/apks/install_loc_sdcard/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_install_loc_sdcard",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/install_loc_unspecified/Android.bp b/core/tests/coretests/apks/install_loc_unspecified/Android.bp
index 21c0f82b44ff..76869e9b9ed5 100644
--- a/core/tests/coretests/apks/install_loc_unspecified/Android.bp
+++ b/core/tests/coretests/apks/install_loc_unspecified/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_install_loc_unspecified",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/install_use_perm_good/Android.bp b/core/tests/coretests/apks/install_use_perm_good/Android.bp
index bb41ebb2fdca..89700ddb94be 100644
--- a/core/tests/coretests/apks/install_use_perm_good/Android.bp
+++ b/core/tests/coretests/apks/install_use_perm_good/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_install_use_perm_good",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/install_uses_feature/Android.bp b/core/tests/coretests/apks/install_uses_feature/Android.bp
index 0ec747b0f151..913a96a1cd27 100644
--- a/core/tests/coretests/apks/install_uses_feature/Android.bp
+++ b/core/tests/coretests/apks/install_uses_feature/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_install_uses_feature",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/install_verifier_bad/Android.bp b/core/tests/coretests/apks/install_verifier_bad/Android.bp
index 1265739a3107..ed13d74789bf 100644
--- a/core/tests/coretests/apks/install_verifier_bad/Android.bp
+++ b/core/tests/coretests/apks/install_verifier_bad/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_install_verifier_bad",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/install_verifier_good/Android.bp b/core/tests/coretests/apks/install_verifier_good/Android.bp
index 4911ffbd2020..fe9a24f6bd7b 100644
--- a/core/tests/coretests/apks/install_verifier_good/Android.bp
+++ b/core/tests/coretests/apks/install_verifier_good/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_install_verifier_good",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/keyset/Android.bp b/core/tests/coretests/apks/keyset/Android.bp
index e252b081341a..93c3b1f60327 100644
--- a/core/tests/coretests/apks/keyset/Android.bp
+++ b/core/tests/coretests/apks/keyset/Android.bp
@@ -1,4 +1,13 @@
//apks signed by keyset_A
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_keyset_sa_unone",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/locales/Android.bp b/core/tests/coretests/apks/locales/Android.bp
index 4a730ef53404..4fe6c7fd09b2 100644
--- a/core/tests/coretests/apks/locales/Android.bp
+++ b/core/tests/coretests/apks/locales/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_locales",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/overlay_config/Android.bp b/core/tests/coretests/apks/overlay_config/Android.bp
index 957355726fe8..9c971fd2f1bc 100644
--- a/core/tests/coretests/apks/overlay_config/Android.bp
+++ b/core/tests/coretests/apks/overlay_config/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_overlay_config",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/version/Android.bp b/core/tests/coretests/apks/version/Android.bp
index 371ccfc301cb..8b750f7b5ee4 100644
--- a/core/tests/coretests/apks/version/Android.bp
+++ b/core/tests/coretests/apks/version/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_version_1",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/apks/version_nosys/Android.bp b/core/tests/coretests/apks/version_nosys/Android.bp
index 575667822393..de40f49849b7 100644
--- a/core/tests/coretests/apks/version_nosys/Android.bp
+++ b/core/tests/coretests/apks/version_nosys/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksCoreTests_version_1_nosys",
defaults: ["FrameworksCoreTests_apks_defaults"],
diff --git a/core/tests/coretests/certs/Android.bp b/core/tests/coretests/certs/Android.bp
index bd5c8293f6f6..8411183c3335 100644
--- a/core/tests/coretests/certs/Android.bp
+++ b/core/tests/coretests/certs/Android.bp
@@ -1,3 +1,14 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-MIT
+ // SPDX-license-identifier-Unicode-DFS
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app_certificate {
name: "FrameworksCoreTests_keyset_A_cert",
certificate: "keyset_A",
diff --git a/core/tests/coretests/jni/Android.bp b/core/tests/coretests/jni/Android.bp
index bb090e2a3408..edac8ef25fe7 100644
--- a/core/tests/coretests/jni/Android.bp
+++ b/core/tests/coretests/jni/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_test_library {
name: "libpowermanagertest_jni",
srcs: [
diff --git a/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java
index cfcfcc8cf044..ece37f8054cf 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java
@@ -201,6 +201,18 @@ public class BundleUtilTest {
assertThat(BundleUtil.deepHashCode(b1)).isNotEqualTo(BundleUtil.deepHashCode(b2));
}
+ @Test
+ public void testDeepHashCode_differentKeys() {
+ Bundle[] inputs = new Bundle[2];
+ for (int i = 0; i < 2; i++) {
+ Bundle b = new Bundle();
+ b.putString("key" + i, "value");
+ inputs[i] = b;
+ }
+ assertThat(BundleUtil.deepHashCode(inputs[0]))
+ .isNotEqualTo(BundleUtil.deepHashCode(inputs[1]));
+ }
+
private static Bundle createThoroughBundle() {
Bundle toy1 = new Bundle();
toy1.putString("a", "a");
diff --git a/core/tests/coretests/src/android/app/people/PeopleManagerTest.java b/core/tests/coretests/src/android/app/people/PeopleManagerTest.java
new file mode 100644
index 000000000000..a2afc777a552
--- /dev/null
+++ b/core/tests/coretests/src/android/app/people/PeopleManagerTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.people;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
+import android.os.test.TestLooper;
+import android.util.Pair;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Tests for {@link android.app.people.PeopleManager.ConversationListener} and relevant APIs.
+ */
+@RunWith(AndroidJUnit4.class)
+public class PeopleManagerTest {
+
+ private static final String CONVERSATION_ID_1 = "12";
+ private static final String CONVERSATION_ID_2 = "123";
+
+ private Context mContext;
+
+ private final TestLooper mTestLooper = new TestLooper();
+
+ @Mock
+ private IPeopleManager mService;
+ private PeopleManager mPeopleManager;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ MockitoAnnotations.initMocks(this);
+
+ mPeopleManager = new PeopleManager(mContext, mService);
+ }
+
+ @Test
+ public void testCorrectlyMapsToProxyConversationListener() throws Exception {
+ PeopleManager.ConversationListener listenerForConversation1 = mock(
+ PeopleManager.ConversationListener.class);
+ registerListener(CONVERSATION_ID_1, listenerForConversation1);
+ PeopleManager.ConversationListener listenerForConversation2 = mock(
+ PeopleManager.ConversationListener.class);
+ registerListener(CONVERSATION_ID_2, listenerForConversation2);
+
+ Map<PeopleManager.ConversationListener, Pair<Executor, IConversationListener>>
+ listenersToProxy =
+ mPeopleManager.mConversationListeners;
+ Pair<Executor, IConversationListener> listener = listenersToProxy.get(
+ listenerForConversation1);
+ ConversationChannel conversation = getConversation(CONVERSATION_ID_1);
+ listener.second.onConversationUpdate(getConversation(CONVERSATION_ID_1));
+ mTestLooper.dispatchAll();
+
+ // Only call the associated listener.
+ verify(listenerForConversation2, never()).onConversationUpdate(any());
+ // Should update the listeners mapped to the proxy.
+ ArgumentCaptor<ConversationChannel> capturedConversation = ArgumentCaptor.forClass(
+ ConversationChannel.class);
+ verify(listenerForConversation1, times(1)).onConversationUpdate(
+ capturedConversation.capture());
+ ConversationChannel conversationChannel = capturedConversation.getValue();
+ assertEquals(conversationChannel.getShortcutInfo().getId(), CONVERSATION_ID_1);
+ assertEquals(conversationChannel.getShortcutInfo().getLabel(),
+ conversation.getShortcutInfo().getLabel());
+ }
+
+ private ConversationChannel getConversation(String shortcutId) {
+ ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext,
+ shortcutId).setLongLabel(
+ "name").build();
+ NotificationChannel notificationChannel = new NotificationChannel("123",
+ "channel",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ return new ConversationChannel(shortcutInfo, 0,
+ notificationChannel, null,
+ 123L, false);
+ }
+
+ private void registerListener(String conversationId,
+ PeopleManager.ConversationListener listener) {
+ mPeopleManager.registerConversationListener(mContext.getPackageName(), mContext.getUserId(),
+ conversationId, listener,
+ mTestLooper.getNewExecutor());
+ }
+}
diff --git a/core/tests/coretests/src/android/app/timedetector/ExternalTimeSuggestionTest.java b/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java
index 6bcec25b9368..266ff3dd09f6 100644
--- a/core/tests/coretests/src/android/app/timedetector/ExternalTimeSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.timedetector;
+package android.app.time;
import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
diff --git a/core/tests/coretests/src/android/content/pm/PermissionInfoTest.java b/core/tests/coretests/src/android/content/pm/PermissionInfoTest.java
new file mode 100644
index 000000000000..606e81d64289
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/PermissionInfoTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.content.pm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.util.ArraySet;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public final class PermissionInfoTest {
+ private static final String KNOWN_CERT_DIGEST_1 =
+ "6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599";
+ private static final String KNOWN_CERT_DIGEST_2 =
+ "9369370ffcfdc1e92dae777252c05c483b8cbb55fa9d5fd9f6317f623ae6d8c6";
+
+ @Test
+ public void createFromParcel_returnsKnownCerts() {
+ // The platform supports a knownSigner permission protection flag that allows one or more
+ // trusted signing certificates to be specified with the permission declaration; if a
+ // requesting app is signed by any of these trusted certificates the permission is granted.
+ // This test verifies the Set of knownCerts is properly parceled / unparceled.
+ PermissionInfo permissionInfo = new PermissionInfo();
+ permissionInfo.protectionLevel =
+ PermissionInfo.PROTECTION_SIGNATURE | PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER;
+ permissionInfo.knownCerts = new ArraySet<>(2);
+ permissionInfo.knownCerts.add(KNOWN_CERT_DIGEST_1);
+ permissionInfo.knownCerts.add(KNOWN_CERT_DIGEST_2);
+ Parcel parcel = Parcel.obtain();
+ permissionInfo.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0);
+ PermissionInfo unparceledPermissionInfo = PermissionInfo.CREATOR.createFromParcel(parcel);
+
+ assertNotNull(unparceledPermissionInfo.knownCerts);
+ assertEquals(2, unparceledPermissionInfo.knownCerts.size());
+ assertTrue(unparceledPermissionInfo.knownCerts.contains(KNOWN_CERT_DIGEST_1));
+ assertTrue(unparceledPermissionInfo.knownCerts.contains(KNOWN_CERT_DIGEST_2));
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
index bffd1e4a86d6..49b720cfba07 100644
--- a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
+++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
@@ -29,6 +29,7 @@ import static org.junit.Assert.fail;
import android.content.pm.PackageParser.SigningDetails;
import android.util.ArraySet;
+import android.util.PackageUtils;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -817,6 +818,124 @@ public class SigningDetailsTest {
assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION));
}
+ @Test
+ public void hasAncestorOrSelfWithDigest_nullSet_returnsFalse() throws Exception {
+ // The hasAncestorOrSelfWithDigest method is intended to verify whether the SigningDetails
+ // is currently signed, or has previously been signed, by any of the certificate digests
+ // in the provided Set. This test verifies if a null Set is provided then false is returned.
+ SigningDetails details = createSigningDetails(FIRST_SIGNATURE);
+
+ assertFalse(details.hasAncestorOrSelfWithDigest(null));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_unknownDetails_returnsFalse() throws Exception {
+ // If hasAncestorOrSelfWithDigest is invoked against an UNKNOWN
+ // instance of the SigningDetails then false is returned.
+ SigningDetails details = SigningDetails.UNKNOWN;
+ Set<String> digests = createDigestSet(FIRST_SIGNATURE);
+
+ assertFalse(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_singleSignerInSet_returnsTrue() throws Exception {
+ // If the single signer of an app is in the provided digest Set then
+ // the method should return true.
+ SigningDetails details = createSigningDetails(FIRST_SIGNATURE);
+ Set<String> digests = createDigestSet(FIRST_SIGNATURE, SECOND_SIGNATURE);
+
+ assertTrue(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_singleSignerNotInSet_returnsFalse() throws Exception {
+ // If the single signer of an app is not in the provided digest Set then
+ // the method should return false.
+ SigningDetails details = createSigningDetails(FIRST_SIGNATURE);
+ Set<String> digests = createDigestSet(SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+ assertFalse(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_multipleSignersInSet_returnsTrue() throws Exception {
+ // If an app is signed by multiple signers and all of the signers are in
+ // the digest Set then the method should return true.
+ SigningDetails details = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE);
+ Set<String> digests = createDigestSet(FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+ assertTrue(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_multipleSignersNotInSet_returnsFalse()
+ throws Exception {
+ // If an app is signed by multiple signers then all signers must be in the digest Set; if
+ // only a subset of the signers are in the Set then the method should return false.
+ SigningDetails details = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE);
+ Set<String> digests = createDigestSet(FIRST_SIGNATURE, THIRD_SIGNATURE);
+
+ assertFalse(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_multipleSignersOneInSet_returnsFalse()
+ throws Exception {
+ // If an app is signed by multiple signers and the Set size is smaller than the number of
+ // signers then the method should immediately return false since there's no way for the
+ // requirement of all signers in the Set to be met.
+ SigningDetails details = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE);
+ Set<String> digests = createDigestSet(FIRST_SIGNATURE);
+
+ assertFalse(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_lineageSignerInSet_returnsTrue() throws Exception {
+ // If an app has a rotated signing key and a previous key in the lineage is in the digest
+ // Set then this method should return true.
+ SigningDetails details = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE);
+ Set<String> digests = createDigestSet(FIRST_SIGNATURE, THIRD_SIGNATURE);
+
+ assertTrue(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_lineageSignerNotInSet_returnsFalse() throws Exception {
+ // If an app has a rotated signing key, but neither the current key nor any of the signers
+ // in the lineage are in the digest set then the method should return false.
+ SigningDetails details = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE);
+ Set<String> digests = createDigestSet(THIRD_SIGNATURE, FOURTH_SIGNATURE);
+
+ assertFalse(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_lastSignerInLineageInSet_returnsTrue()
+ throws Exception {
+ // If an app has multiple signers in the lineage only one of those signers must be in the
+ // Set for this method to return true. This test verifies if the last signer in the lineage
+ // is in the set then the method returns true.
+ SigningDetails details = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE,
+ THIRD_SIGNATURE);
+ Set<String> digests = createDigestSet(SECOND_SIGNATURE);
+
+ assertTrue(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
+ @Test
+ public void hasAncestorOrSelfWithDigest_nullLineageSingleSIgner_returnsFalse()
+ throws Exception {
+ // Under some instances an app with only a single signer can have a null lineage; this
+ // test verifies that null lineage does not result in a NullPointerException and instead the
+ // method returns false if the single signer is not in the Set.
+ SigningDetails details = createSigningDetails(true, FIRST_SIGNATURE);
+ Set<String> digests = createDigestSet(SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+ assertFalse(details.hasAncestorOrSelfWithDigest(digests));
+ }
+
private SigningDetails createSigningDetailsWithLineage(String... signers) throws Exception {
int[] capabilities = new int[signers.length];
for (int i = 0; i < capabilities.length; i++) {
@@ -853,12 +972,21 @@ public class SigningDetailsTest {
// If there are multiple signers then the pastSigningCertificates should be set to null, but
// if there is only a single signer both the current signer and the past signers should be
// set to that one signer.
- if (signers.length > 1) {
+ if (signers.length > 1 || useNullPastSigners) {
return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, null);
}
return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, currentSignatures);
}
+ private Set<String> createDigestSet(String... signers) {
+ Set<String> digests = new ArraySet<>();
+ for (String signer : signers) {
+ String digest = PackageUtils.computeSha256Digest(new Signature(signer).toByteArray());
+ digests.add(digest);
+ }
+ return digests;
+ }
+
private void assertSigningDetailsContainsLineage(SigningDetails details,
String... pastSigners) {
// This method should only be invoked for results that contain a single signer.
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 45adf833de97..46dbe0fd658a 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -90,12 +90,12 @@ public class ResourcesManagerTest extends TestCase {
@SmallTest
public void testMultipleCallsWithIdenticalParametersCacheReference() {
Resources resources = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, null, null,
+ null, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources);
Resources newResources = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, null, null,
+ null, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(newResources);
assertSame(resources, newResources);
@@ -104,14 +104,14 @@ public class ResourcesManagerTest extends TestCase {
@SmallTest
public void testMultipleCallsWithDifferentParametersReturnDifferentReferences() {
Resources resources = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, null, null,
+ null, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources);
Configuration overrideConfig = new Configuration();
overrideConfig.smallestScreenWidthDp = 200;
Resources newResources = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, null, overrideConfig,
+ null, APP_ONE_RES_DIR, null, null, null, null, null, overrideConfig,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(newResources);
assertNotSame(resources, newResources);
@@ -120,12 +120,12 @@ public class ResourcesManagerTest extends TestCase {
@SmallTest
public void testAddingASplitCreatesANewImpl() {
Resources resources1 = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, null, null,
+ null, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources1);
Resources resources2 = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, new String[] { APP_ONE_RES_SPLIT_DIR }, null, null,
+ null, APP_ONE_RES_DIR, new String[] { APP_ONE_RES_SPLIT_DIR }, null, null, null,
null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null,
null);
assertNotNull(resources2);
@@ -137,12 +137,12 @@ public class ResourcesManagerTest extends TestCase {
@SmallTest
public void testUpdateConfigurationUpdatesAllAssetManagers() {
Resources resources1 = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, null, null,
+ null, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources1);
Resources resources2 = mResourcesManager.getResources(
- null, APP_TWO_RES_DIR, null, null, null, null, null,
+ null, APP_TWO_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources2);
@@ -150,7 +150,7 @@ public class ResourcesManagerTest extends TestCase {
final Configuration overrideConfig = new Configuration();
overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
Resources resources3 = mResourcesManager.getResources(
- activity, APP_ONE_RES_DIR, null, null, null, null,
+ activity, APP_ONE_RES_DIR, null, null, null, null, null,
overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources3);
@@ -183,13 +183,13 @@ public class ResourcesManagerTest extends TestCase {
public void testTwoActivitiesWithIdenticalParametersShareImpl() {
Binder activity1 = new Binder();
Resources resources1 = mResourcesManager.getResources(
- activity1, APP_ONE_RES_DIR, null, null, null, null, null,
+ activity1, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources1);
Binder activity2 = new Binder();
Resources resources2 = mResourcesManager.getResources(
- activity2, APP_ONE_RES_DIR, null, null, null, null, null,
+ activity2, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources1);
@@ -204,7 +204,7 @@ public class ResourcesManagerTest extends TestCase {
public void testThemesGetUpdatedWithNewImpl() {
Binder activity1 = new Binder();
Resources resources1 = mResourcesManager.createBaseTokenResources(
- activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ activity1, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources1);
@@ -237,15 +237,15 @@ public class ResourcesManagerTest extends TestCase {
Configuration config1 = new Configuration();
config1.densityDpi = 280;
Resources resources1 = mResourcesManager.createBaseTokenResources(
- activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, config1,
- CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
+ activity1, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY,
+ config1, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources1);
// Create a Resources based on the Activity.
Configuration config2 = new Configuration();
config2.screenLayout |= Configuration.SCREENLAYOUT_ROUND_YES;
Resources resources2 = mResourcesManager.getResources(
- activity1, APP_ONE_RES_DIR, null, null, null, null, config2,
+ activity1, APP_ONE_RES_DIR, null, null, null, null, null, config2,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources2);
@@ -286,8 +286,8 @@ public class ResourcesManagerTest extends TestCase {
final Configuration overrideConfig = new Configuration();
overrideConfig.densityDpi = originalOverrideDensity;
final Resources resources = mResourcesManager.createBaseTokenResources(
- token, APP_ONE_RES_DIR, null /* splitResDirs */, null /* overlayDirs */,
- null /* libDirs */, Display.DEFAULT_DISPLAY, overrideConfig,
+ token, APP_ONE_RES_DIR, null /* splitResDirs */, null /* legacyOverlayDirs */,
+ null /* overlayDirs */,null /* libDirs */, Display.DEFAULT_DISPLAY, overrideConfig,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* classLoader */,
null /* loaders */);
@@ -315,12 +315,12 @@ public class ResourcesManagerTest extends TestCase {
// Create a base token resources that are based on the default display.
Resources activityResources = mResourcesManager.createBaseTokenResources(
- activity, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ activity, APP_ONE_RES_DIR, null, null, null,null, Display.DEFAULT_DISPLAY, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
// Create another resources that explicitly override the display of the base token above
// and set it to DEFAULT_DISPLAY.
Resources defaultDisplayResources = mResourcesManager.getResources(
- activity, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ activity, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels,
diff --git a/core/tests/coretests/src/android/graphics/FontListParserTest.java b/core/tests/coretests/src/android/graphics/FontListParserTest.java
new file mode 100644
index 000000000000..eae41e37e5f3
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/FontListParserTest.java
@@ -0,0 +1,245 @@
+/*
+ * 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.graphics;
+
+import static android.graphics.fonts.FontStyle.FONT_SLANT_ITALIC;
+import static android.graphics.fonts.FontStyle.FONT_SLANT_UPRIGHT;
+import static android.graphics.fonts.FontStyle.FONT_WEIGHT_NORMAL;
+import static android.text.FontConfig.FontFamily.VARIANT_COMPACT;
+import static android.text.FontConfig.FontFamily.VARIANT_DEFAULT;
+import static android.text.FontConfig.FontFamily.VARIANT_ELEGANT;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.graphics.fonts.FontStyle;
+import android.os.LocaleList;
+import android.text.FontConfig;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class FontListParserTest {
+
+ @Test
+ public void named() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<family name='sans-serif'>"
+ + " <font>test.ttf</font>"
+ + "</family>";
+ FontConfig.FontFamily expected = new FontConfig.FontFamily(
+ Arrays.asList(
+ new FontConfig.Font(new File("test.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "", null)),
+ "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+
+ FontConfig.FontFamily family = readFamily(xml);
+ assertThat(family).isEqualTo(expected);
+
+ String serialized = writeFamily(family);
+ assertWithMessage("serialized = " + serialized)
+ .that(readFamily(serialized)).isEqualTo(expected);
+ }
+
+ @Test
+ public void fallback() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<family lang='en'>"
+ + " <font>test.ttf</font>"
+ + " <font fallbackFor='serif'>test.ttf</font>"
+ + "</family>";
+ FontConfig.FontFamily expected = new FontConfig.FontFamily(
+ Arrays.asList(
+ new FontConfig.Font(new File("test.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "", null),
+ new FontConfig.Font(new File("test.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "", "serif")),
+ null, LocaleList.forLanguageTags("en"), VARIANT_DEFAULT);
+
+ FontConfig.FontFamily family = readFamily(xml);
+ assertThat(family).isEqualTo(expected);
+
+ String serialized = writeFamily(family);
+ assertWithMessage("serialized = " + serialized)
+ .that(readFamily(serialized)).isEqualTo(expected);
+ }
+
+ @Test
+ public void compact() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<family lang='en' variant='compact'>"
+ + " <font>test.ttf</font>"
+ + "</family>";
+ FontConfig.FontFamily expected = new FontConfig.FontFamily(
+ Arrays.asList(
+ new FontConfig.Font(new File("test.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "", null)),
+ null, LocaleList.forLanguageTags("en"), VARIANT_COMPACT);
+
+ FontConfig.FontFamily family = readFamily(xml);
+ assertThat(family).isEqualTo(expected);
+
+ String serialized = writeFamily(family);
+ assertWithMessage("serialized = " + serialized)
+ .that(readFamily(serialized)).isEqualTo(expected);
+ }
+
+ @Test
+ public void elegant() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<family lang='en' variant='elegant'>"
+ + " <font>test.ttf</font>"
+ + "</family>";
+ FontConfig.FontFamily expected = new FontConfig.FontFamily(
+ Arrays.asList(
+ new FontConfig.Font(new File("test.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "", null)),
+ null, LocaleList.forLanguageTags("en"), VARIANT_ELEGANT);
+
+ FontConfig.FontFamily family = readFamily(xml);
+ assertThat(family).isEqualTo(expected);
+
+ String serialized = writeFamily(family);
+ assertWithMessage("serialized = " + serialized)
+ .that(readFamily(serialized)).isEqualTo(expected);
+ }
+
+ @Test
+ public void styles() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<family name='sans-serif'>"
+ + " <font style='normal'>normal.ttf</font>"
+ + " <font weight='100'>weight.ttf</font>"
+ + " <font style='italic'>italic.ttf</font>"
+ + "</family>";
+ FontConfig.FontFamily expected = new FontConfig.FontFamily(
+ Arrays.asList(
+ new FontConfig.Font(new File("normal.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "", null),
+ new FontConfig.Font(new File("weight.ttf"), null,
+ new FontStyle(100, FONT_SLANT_UPRIGHT),
+ 0, "", null),
+ new FontConfig.Font(new File("italic.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_ITALIC),
+ 0, "", null)),
+ "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+ FontConfig.FontFamily family = readFamily(xml);
+ assertThat(family).isEqualTo(expected);
+
+ String serialized = writeFamily(family);
+ assertWithMessage("serialized = " + serialized)
+ .that(readFamily(serialized)).isEqualTo(expected);
+ }
+
+ @Test
+ public void variable() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<family name='sans-serif'>"
+ + " <font>test-VF.ttf"
+ + " <axis tag='wdth' stylevalue='100' />"
+ + " <axis tag='wght' stylevalue='200' />"
+ + " </font>"
+ + " <font>test-VF.ttf"
+ + " <axis tag='wdth' stylevalue='400' />"
+ + " <axis tag='wght' stylevalue='700' />"
+ + " </font>"
+ + "</family>";
+ FontConfig.FontFamily expected = new FontConfig.FontFamily(
+ Arrays.asList(
+ new FontConfig.Font(new File("test-VF.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "'wdth' 100.0,'wght' 200.0", null),
+ new FontConfig.Font(new File("test-VF.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "'wdth' 400.0,'wght' 700.0", null)),
+ "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+ FontConfig.FontFamily family = readFamily(xml);
+ assertThat(family).isEqualTo(expected);
+
+ String serialized = writeFamily(family);
+ assertWithMessage("serialized = " + serialized)
+ .that(readFamily(serialized)).isEqualTo(expected);
+ }
+
+ @Test
+ public void ttc() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<family name='sans-serif'>"
+ + " <font index='0'>test.ttc</font>"
+ + " <font index='1'>test.ttc</font>"
+ + "</family>";
+ FontConfig.FontFamily expected = new FontConfig.FontFamily(
+ Arrays.asList(
+ new FontConfig.Font(new File("test.ttc"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "", null),
+ new FontConfig.Font(new File("test.ttc"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 1, "", null)),
+ "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+ FontConfig.FontFamily family = readFamily(xml);
+ assertThat(family).isEqualTo(expected);
+
+ String serialized = writeFamily(family);
+ assertWithMessage("serialized = " + serialized)
+ .that(readFamily(serialized)).isEqualTo(expected);
+ }
+
+ private FontConfig.FontFamily readFamily(String xml)
+ throws IOException, XmlPullParserException {
+ StandardCharsets.UTF_8.name();
+ ByteArrayInputStream buffer = new ByteArrayInputStream(
+ xml.getBytes(StandardCharsets.UTF_8));
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(buffer, "UTF-8");
+ parser.nextTag();
+ return FontListParser.readFamily(parser, "", null);
+ }
+
+ private String writeFamily(FontConfig.FontFamily family) throws IOException {
+ TypedXmlSerializer out = Xml.newFastSerializer();
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ out.setOutput(buffer, "UTF-8");
+ out.startTag(null, "family");
+ FontListParser.writeFamily(out, family);
+ out.endTag(null, "family");
+ out.endDocument();
+ return buffer.toString("UTF-8");
+ }
+}
diff --git a/core/tests/coretests/src/android/os/BrightnessLimit.java b/core/tests/coretests/src/android/os/BrightnessLimit.java
index be7935545543..219f741d8a08 100644
--- a/core/tests/coretests/src/android/os/BrightnessLimit.java
+++ b/core/tests/coretests/src/android/os/BrightnessLimit.java
@@ -42,7 +42,8 @@ public class BrightnessLimit extends Activity implements OnClickListener {
public void onClick(View v) {
DisplayManager dm = getSystemService(DisplayManager.class);
- dm.setTemporaryBrightness(0.0f);
+ final int displayId = getBaseContext().getDisplay().getDisplayId();
+ dm.setTemporaryBrightness(displayId, 0.0f);
Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 0);
}
}
diff --git a/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
index 564103efef65..11239db9f404 100644
--- a/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
@@ -17,6 +17,8 @@
package android.os;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
import static org.testng.Assert.assertThrows;
@@ -31,7 +33,6 @@ import java.util.Arrays;
@Presubmit
@RunWith(JUnit4.class)
public class CombinedVibrationEffectTest {
-
private static final VibrationEffect VALID_EFFECT = VibrationEffect.createOneShot(10, 255);
private static final VibrationEffect INVALID_EFFECT = new VibrationEffect.OneShot(-1, -1);
@@ -172,6 +173,37 @@ public class CombinedVibrationEffectTest {
}
@Test
+ public void testHasVibratorMono_returnsTrueForAnyVibrator() {
+ CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+ assertTrue(effect.hasVibrator(0));
+ assertTrue(effect.hasVibrator(1));
+ }
+
+ @Test
+ public void testHasVibratorStereo_returnsOnlyTheIdsSet() {
+ CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .combine();
+ assertFalse(effect.hasVibrator(0));
+ assertTrue(effect.hasVibrator(1));
+ assertFalse(effect.hasVibrator(2));
+ }
+
+ @Test
+ public void testHasVibratorSequential_returnsNestedVibrators() {
+ CombinedVibrationEffect effect = CombinedVibrationEffect.startSequential()
+ .addNext(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addNext(CombinedVibrationEffect.startSynced()
+ .addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_TICK))
+ .combine())
+ .combine();
+ assertFalse(effect.hasVibrator(0));
+ assertTrue(effect.hasVibrator(1));
+ assertTrue(effect.hasVibrator(2));
+ }
+
+ @Test
public void testSerializationMono() {
CombinedVibrationEffect original = CombinedVibrationEffect.createSynced(VALID_EFFECT);
diff --git a/core/tests/coretests/src/android/os/OWNERS b/core/tests/coretests/src/android/os/OWNERS
index 1a28b73de8cd..9a9b4748d4f1 100644
--- a/core/tests/coretests/src/android/os/OWNERS
+++ b/core/tests/coretests/src/android/os/OWNERS
@@ -2,8 +2,11 @@
per-file BrightnessLimit.java = michaelwr@google.com, santoscordon@google.com
# Haptics
+per-file CombinedVibrationEffectTest.java = michaelwr@google.com
per-file ExternalVibrationTest.java = michaelwr@google.com
per-file VibrationEffectTest.java = michaelwr@google.com
+per-file VibratorInfoTest.java = michaelwr@google.com
+per-file VibratorTest.java = michaelwr@google.com
# Power
per-file PowerManager*.java = michaelwr@google.com, santoscordon@google.com
diff --git a/core/tests/coretests/src/android/provider/OWNERS b/core/tests/coretests/src/android/provider/OWNERS
new file mode 100644
index 000000000000..581da714f326
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/provider/OWNERS
diff --git a/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java b/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java
index be3826007aa3..bc7be1b08626 100644
--- a/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java
+++ b/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java
@@ -16,14 +16,8 @@
package android.provider;
-import static com.google.common.truth.Truth.assertThat;
-
import static org.testng.Assert.assertThrows;
-import android.content.ContentValues;
-import android.os.Parcel;
-import android.provider.SimPhonebookContract.SimRecords.NameValidationResult;
-
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
@@ -71,50 +65,5 @@ public class SimPhonebookContractTest {
SimPhonebookContract.ElementaryFiles.EF_ADN, -1)
);
}
-
- @Test
- public void nameValidationResult_isValid_validNames() {
- assertThat(new NameValidationResult("", "", 0, 1).isValid()).isTrue();
- assertThat(new NameValidationResult("a", "a", 1, 1).isValid()).isTrue();
- assertThat(new NameValidationResult("First Last", "First Last", 10, 10).isValid()).isTrue();
- assertThat(
- new NameValidationResult("First Last", "First Last", 10, 100).isValid()).isTrue();
- }
-
- @Test
- public void nameValidationResult_isValid_invalidNames() {
- assertThat(new NameValidationResult("", "", 0, 0).isValid()).isFalse();
- assertThat(new NameValidationResult("ab", "ab", 2, 1).isValid()).isFalse();
- NameValidationResult unsupportedCharactersResult = new NameValidationResult("A_b_c",
- "A b c", 5, 5);
- assertThat(unsupportedCharactersResult.isValid()).isFalse();
- assertThat(unsupportedCharactersResult.isSupportedCharacter(0)).isTrue();
- assertThat(unsupportedCharactersResult.isSupportedCharacter(1)).isFalse();
- assertThat(unsupportedCharactersResult.isSupportedCharacter(2)).isTrue();
- assertThat(unsupportedCharactersResult.isSupportedCharacter(3)).isFalse();
- assertThat(unsupportedCharactersResult.isSupportedCharacter(4)).isTrue();
- }
-
- @Test
- public void nameValidationResult_parcel() {
- ContentValues values = new ContentValues();
- values.put("name", "Name");
- values.put("phone_number", "123");
-
- NameValidationResult result;
- Parcel parcel = Parcel.obtain();
- try {
- parcel.writeParcelable(new NameValidationResult("name", "sanitized name", 1, 2), 0);
- parcel.setDataPosition(0);
- result = parcel.readParcelable(NameValidationResult.class.getClassLoader());
- } finally {
- parcel.recycle();
- }
-
- assertThat(result.getName()).isEqualTo("name");
- assertThat(result.getSanitizedName()).isEqualTo("sanitized name");
- assertThat(result.getEncodedLength()).isEqualTo(1);
- assertThat(result.getMaxEncodedLength()).isEqualTo(2);
- }
}
diff --git a/core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java b/core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java
index a43b238405d1..a121941e7b73 100644
--- a/core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java
+++ b/core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java
@@ -16,14 +16,14 @@
package android.service.notification;
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
import static com.google.common.truth.Truth.assertThat;
-import android.app.NotificationChannel;
+import android.content.pm.VersionedPackage;
import android.os.Parcel;
import android.util.ArraySet;
@@ -45,16 +45,19 @@ public class NotificationListenerFilterTest {
assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isTrue();
assertThat(nlf.getTypes()).isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS
| FLAG_FILTER_TYPE_ALERTING
- | FLAG_FILTER_TYPE_SILENT);
+ | FLAG_FILTER_TYPE_SILENT
+ | FLAG_FILTER_TYPE_ONGOING);
assertThat(nlf.getDisallowedPackages()).isEmpty();
- assertThat(nlf.isPackageAllowed("pkg1")).isTrue();
+ assertThat(nlf.isPackageAllowed(new VersionedPackage("any", 0))).isTrue();
}
@Test
public void testConstructor() {
- ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1", "pkg2"});
+ VersionedPackage a1 = new VersionedPackage("pkg1", 243);
+ VersionedPackage a2= new VersionedPackage("pkg2", 2142534);
+ ArraySet<VersionedPackage> pkgs = new ArraySet<>(new VersionedPackage[] {a1, a2});
NotificationListenerFilter nlf =
new NotificationListenerFilter(FLAG_FILTER_TYPE_ALERTING, pkgs);
assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_CONVERSATIONS)).isFalse();
@@ -62,20 +65,21 @@ public class NotificationListenerFilterTest {
assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isFalse();
assertThat(nlf.getTypes()).isEqualTo(FLAG_FILTER_TYPE_ALERTING);
- assertThat(nlf.getDisallowedPackages()).contains("pkg1");
- assertThat(nlf.getDisallowedPackages()).contains("pkg2");
- assertThat(nlf.isPackageAllowed("pkg1")).isFalse();
- assertThat(nlf.isPackageAllowed("pkg2")).isFalse();
+ assertThat(nlf.getDisallowedPackages()).contains(a1);
+ assertThat(nlf.getDisallowedPackages()).contains(a2);
+ assertThat(nlf.isPackageAllowed(a1)).isFalse();
+ assertThat(nlf.isPackageAllowed(a2)).isFalse();
}
@Test
public void testSetDisallowedPackages() {
NotificationListenerFilter nlf = new NotificationListenerFilter();
- ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1"});
+ ArraySet<VersionedPackage> pkgs = new ArraySet<>(
+ new VersionedPackage[] {new VersionedPackage("pkg1", 0)});
nlf.setDisallowedPackages(pkgs);
- assertThat(nlf.isPackageAllowed("pkg1")).isFalse();
+ assertThat(nlf.isPackageAllowed(new VersionedPackage("pkg1", 0))).isFalse();
}
@Test
@@ -94,7 +98,9 @@ public class NotificationListenerFilterTest {
@Test
public void testDescribeContents() {
final int expected = 0;
- ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1", "pkg2"});
+ VersionedPackage a1 = new VersionedPackage("pkg1", 243);
+ VersionedPackage a2= new VersionedPackage("pkg2", 2142534);
+ ArraySet<VersionedPackage> pkgs = new ArraySet<>(new VersionedPackage[] {a1, a2});
NotificationListenerFilter nlf =
new NotificationListenerFilter(FLAG_FILTER_TYPE_ALERTING, pkgs);
assertThat(nlf.describeContents()).isEqualTo(expected);
@@ -102,7 +108,9 @@ public class NotificationListenerFilterTest {
@Test
public void testParceling() {
- ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1", "pkg2"});
+ VersionedPackage a1 = new VersionedPackage("pkg1", 243);
+ VersionedPackage a2= new VersionedPackage("pkg2", 2142534);
+ ArraySet<VersionedPackage> pkgs = new ArraySet<>(new VersionedPackage[] {a1, a2});
NotificationListenerFilter nlf =
new NotificationListenerFilter(FLAG_FILTER_TYPE_ALERTING, pkgs);
@@ -116,9 +124,9 @@ public class NotificationListenerFilterTest {
assertThat(nlf1.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isFalse();
assertThat(nlf1.getTypes()).isEqualTo(FLAG_FILTER_TYPE_ALERTING);
- assertThat(nlf1.getDisallowedPackages()).contains("pkg1");
- assertThat(nlf1.getDisallowedPackages()).contains("pkg2");
- assertThat(nlf1.isPackageAllowed("pkg1")).isFalse();
- assertThat(nlf1.isPackageAllowed("pkg2")).isFalse();
+ assertThat(nlf1.getDisallowedPackages()).contains(a1);
+ assertThat(nlf1.getDisallowedPackages()).contains(a2);
+ assertThat(nlf1.isPackageAllowed(a1)).isFalse();
+ assertThat(nlf1.isPackageAllowed(a2)).isFalse();
}
}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
index c61f33e15b18..2106b4bc5be9 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -169,6 +169,13 @@ public class InsetsSourceTest {
}
@Test
+ public void testCalculateInsetsForIme_noIntersection_horizontal() {
+ mImeSource.setFrame(new Rect(0, 0, 100, 500));
+ Insets insets = mImeSource.calculateInsets(new Rect(100, 0, 500, 500), false);
+ assertEquals(Insets.NONE, insets);
+ }
+
+ @Test
public void testCalculateInsets_zeroWidthIntersection_horizontal_start() {
mSource.setFrame(new Rect(0, 0, 100, 500));
Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 0), false);
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index 786ae89ac2ae..b3450de80092 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -169,4 +169,24 @@ public class MotionEventTest {
assertEquals(0x3 << 30, ID_SOURCE_MASK & event.getId());
}
}
+
+ @Test
+ public void testEventRotation() {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */);
+ MotionEvent rot90 = MotionEvent.obtain(event);
+ rot90.transform(MotionEvent.createRotateMatrix(/* 90 deg */1, 1000, 600));
+ assertEquals(50, (int) rot90.getX());
+ assertEquals(570, (int) rot90.getY());
+
+ MotionEvent rot180 = MotionEvent.obtain(event);
+ rot180.transform(MotionEvent.createRotateMatrix(/* 180 deg */2, 1000, 600));
+ assertEquals(970, (int) rot180.getX());
+ assertEquals(550, (int) rot180.getY());
+
+ MotionEvent rot270 = MotionEvent.obtain(event);
+ rot270.transform(MotionEvent.createRotateMatrix(/* 270 deg */3, 1000, 600));
+ assertEquals(950, (int) rot270.getX());
+ assertEquals(30, (int) rot270.getY());
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
index 64906bb27ff0..e16d44854516 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
@@ -100,7 +100,7 @@ public class ResolverListControllerTest {
final List<UsageStats> slices = new ArrayList<>();
slices.add(packageStats);
ParceledListSlice<UsageStats> stats = new ParceledListSlice<>(slices);
- when(mMockService.queryUsageStats(anyInt(), anyLong(), anyLong(), anyString()))
+ when(mMockService.queryUsageStats(anyInt(), anyLong(), anyLong(), anyString(), anyInt()))
.thenReturn(stats);
Answer<Void> answer = new Answer<Void>() {
@Override
diff --git a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
index e2a106484848..add04698e372 100644
--- a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
@@ -19,6 +19,7 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
import android.os.BatteryConsumer;
+import android.os.BatteryUsageStatsQuery;
import android.os.SystemBatteryConsumer;
import android.view.Display;
@@ -33,18 +34,29 @@ import org.junit.runner.RunWith;
@SmallTest
public class AmbientDisplayPowerCalculatorTest {
private static final double PRECISION = 0.00001;
+ private static final long MINUTE_IN_MS = 60 * 1000;
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
- .setAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY, 360.0);
+ .setAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY, 10.0);
@Test
- public void testTimerBasedModel() {
+ public void testMeasuredEnergyBasedModel() {
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
- stats.noteScreenStateLocked(Display.STATE_ON, 1000, 1000, 1000);
- stats.noteScreenStateLocked(Display.STATE_DOZE, 2000, 2000, 2000);
- stats.noteScreenStateLocked(Display.STATE_OFF, 3000, 3000, 3000);
+ stats.updateDisplayEnergyLocked(300_000_000, Display.STATE_ON, 0);
+
+ stats.noteScreenStateLocked(Display.STATE_DOZE, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS,
+ 30 * MINUTE_IN_MS);
+
+ stats.updateDisplayEnergyLocked(200_000_000, Display.STATE_DOZE,
+ 30 * MINUTE_IN_MS);
+
+ stats.noteScreenStateLocked(Display.STATE_OFF, 120 * MINUTE_IN_MS, 120 * MINUTE_IN_MS,
+ 120 * MINUTE_IN_MS);
+
+ stats.updateDisplayEnergyLocked(100_000_000, Display.STATE_OFF,
+ 120 * MINUTE_IN_MS);
AmbientDisplayPowerCalculator calculator =
new AmbientDisplayPowerCalculator(mStatsRule.getPowerProfile());
@@ -55,8 +67,32 @@ public class AmbientDisplayPowerCalculatorTest {
mStatsRule.getSystemBatteryConsumer(
SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY);
assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE))
- .isEqualTo(1000);
+ .isEqualTo(90 * MINUTE_IN_MS);
+ assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE))
+ .isWithin(PRECISION).of(7.5075075);
+ }
+
+ @Test
+ public void testPowerProfileBasedModel() {
+ BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+
+ stats.noteScreenStateLocked(Display.STATE_DOZE, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS,
+ 30 * MINUTE_IN_MS);
+ stats.noteScreenStateLocked(Display.STATE_OFF, 120 * MINUTE_IN_MS, 120 * MINUTE_IN_MS,
+ 120 * MINUTE_IN_MS);
+
+ AmbientDisplayPowerCalculator calculator =
+ new AmbientDisplayPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+ calculator);
+
+ SystemBatteryConsumer consumer =
+ mStatsRule.getSystemBatteryConsumer(
+ SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY);
+ assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE))
+ .isEqualTo(90 * MINUTE_IN_MS);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE))
- .isWithin(PRECISION).of(0.1);
+ .isWithin(PRECISION).of(15.0);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
index d2107eaefefc..08205b4d143c 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
@@ -218,7 +218,7 @@ public class BatteryStatsHelperTest extends TestCase {
sippers.add(sipperBg);
sippers.add(sipperFg);
- spc.smearScreenBatterySipper(sippers, mScreenBatterySipper);
+ spc.smearScreenBatterySipper(sippers, mScreenBatterySipper, 0);
assertThat(sipperNull.screenPowerMah).isWithin(PRECISION).of(0);
assertThat(sipperBg.screenPowerMah).isWithin(PRECISION).of(0);
@@ -253,7 +253,7 @@ public class BatteryStatsHelperTest extends TestCase {
doReturn(TIME_STATE_FOREGROUND_US).when(mUid).getProcessStateTime(eq(PROCESS_STATE_TOP),
anyLong(), anyInt());
- final long time = spc.getProcessForegroundTimeMs(mUid);
+ final long time = spc.getProcessForegroundTimeMs(mUid, 1000);
assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS);
}
@@ -296,7 +296,7 @@ public class BatteryStatsHelperTest extends TestCase {
doReturn(uidCode).when(sipper).getUid();
if (!isUidNull) {
final BatteryStats.Uid uid = mock(BatteryStats.Uid.class, RETURNS_DEEP_STUBS);
- doReturn(activityTime).when(spc).getProcessForegroundTimeMs(eq(uid));
+ doReturn(activityTime).when(spc).getProcessForegroundTimeMs(eq(uid), anyLong());
doReturn(uidCode).when(uid).getUid();
sipper.uidObj = uid;
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 97c07eaa819b..6652c64c4344 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -24,6 +24,7 @@ import android.os.BatteryStats;
import android.os.BatteryStats.HistoryItem;
import android.os.BatteryStats.Uid.Sensor;
import android.os.WorkSource;
+import android.util.SparseLongArray;
import android.view.Display;
import androidx.test.filters.SmallTest;
@@ -583,6 +584,95 @@ public class BatteryStatsNoteTest extends TestCase {
checkMeasuredEnergy("H", uid1, blame1, uid2, blame2, globalDoze, bi);
}
+ @SmallTest
+ public void testUpdateCustomMeasuredEnergyDataLocked_neverCalled() {
+ final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ bi.setOnBatteryInternal(true);
+
+ final int uid1 = 11500;
+ final int uid2 = 11501;
+
+ // Initially, all custom buckets report energy of 0.
+ checkCustomMeasuredEnergy("0", 0, 0, uid1, 0, 0, uid2, 0, 0, bi);
+ }
+
+ @SmallTest
+ public void testUpdateCustomMeasuredEnergyDataLocked() {
+ final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+ final int bucketA = 0; // Custom bucket 0
+ final int bucketB = 1; // Custom bucket 1
+
+ long totalBlameA = 0; // Total energy consumption for bucketA (may exceed sum of uids)
+ long totalBlameB = 0; // Total energy consumption for bucketB (may exceed sum of uids)
+
+ final int uid1 = 10500;
+ long blame1A = 0; // Blame for uid1 in bucketA
+ long blame1B = 0; // Blame for uid1 in bucketB
+
+ final int uid2 = 10501;
+ long blame2A = 0; // Blame for uid2 in bucketA
+ long blame2B = 0; // Blame for uid2 in bucketB
+
+ final SparseLongArray newEnergiesA = new SparseLongArray(2);
+ final SparseLongArray newEnergiesB = new SparseLongArray(2);
+
+
+ // ----- Case A: battery off (so blame does not increase)
+ bi.setOnBatteryInternal(false);
+
+ newEnergiesA.put(uid1, 20_000);
+ // Implicit newEnergiesA.put(uid2, 0);
+ bi.updateCustomMeasuredEnergyDataLocked(bucketA, 500_000, newEnergiesA);
+
+ newEnergiesB.put(uid1, 60_000);
+ // Implicit newEnergiesB.put(uid2, 0);
+ bi.updateCustomMeasuredEnergyDataLocked(bucketB, 700_000, newEnergiesB);
+
+ checkCustomMeasuredEnergy(
+ "A", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);
+
+
+ // ----- Case B: battery on
+ bi.setOnBatteryInternal(true);
+
+ newEnergiesA.put(uid1, 7_000); blame1A += 7_000;
+ // Implicit newEnergiesA.put(uid2, 0); blame2A += 0;
+ bi.updateCustomMeasuredEnergyDataLocked(bucketA, 310_000, newEnergiesA);
+ totalBlameA += 310_000;
+
+ newEnergiesB.put(uid1, 63_000); blame1B += 63_000;
+ newEnergiesB.put(uid2, 15_000); blame2B += 15_000;
+ bi.updateCustomMeasuredEnergyDataLocked(bucketB, 790_000, newEnergiesB);
+ totalBlameB += 790_000;
+
+ checkCustomMeasuredEnergy(
+ "B", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);
+
+
+ // ----- Case C: battery still on
+ newEnergiesA.delete(uid1); blame1A += 0;
+ newEnergiesA.put(uid2, 16_000); blame2A += 16_000;
+ bi.updateCustomMeasuredEnergyDataLocked(bucketA, 560_000, newEnergiesA);
+ totalBlameA += 560_000;
+
+ bi.updateCustomMeasuredEnergyDataLocked(bucketB, 10_000, null);
+ totalBlameB += 10_000;
+
+ checkCustomMeasuredEnergy(
+ "C", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);
+
+
+ // ----- Case D: battery still on
+ bi.updateCustomMeasuredEnergyDataLocked(bucketA, 0, newEnergiesA);
+ bi.updateCustomMeasuredEnergyDataLocked(bucketB, 15_000, new SparseLongArray(1));
+ totalBlameB += 15_000;
+ checkCustomMeasuredEnergy(
+ "D", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);
+ }
+
private void setFgState(int uid, boolean fgOn, MockBatteryStatsImpl bi) {
// Note that noteUidProcessStateLocked uses ActivityManager process states.
if (fgOn) {
@@ -610,4 +700,33 @@ public class BatteryStatsNoteTest extends TestCase {
assertEquals("Wrong doze for Case " + caseName, globalDoze,
bi.getScreenDozeEnergy());
}
+
+ private void checkCustomMeasuredEnergy(String caseName,
+ long totalBlameA, long totalBlameB,
+ int uid1, long blame1A, long blame1B,
+ int uid2, long blame2A, long blame2B,
+ MockBatteryStatsImpl bi) {
+
+ final long[] actualTotal = bi.getCustomMeasuredEnergiesMicroJoules();
+ final long[] actualUid1 = bi.getUidStatsLocked(uid1).getCustomMeasuredEnergiesMicroJoules();
+ final long[] actualUid2 = bi.getUidStatsLocked(uid2).getCustomMeasuredEnergiesMicroJoules();
+
+ assertNotNull(actualTotal);
+ assertNotNull(actualUid1);
+ assertNotNull(actualUid2);
+
+ assertEquals("Wrong total blame in bucket 0 for Case " + caseName, totalBlameA,
+ actualTotal[0]);
+
+ assertEquals("Wrong total blame in bucket 1 for Case " + caseName, totalBlameB,
+ actualTotal[1]);
+
+ assertEquals("Wrong uid1 blame in bucket 0 for Case " + caseName, blame1A, actualUid1[0]);
+
+ assertEquals("Wrong uid1 blame in bucket 1 for Case " + caseName, blame1B, actualUid1[1]);
+
+ assertEquals("Wrong uid2 blame in bucket 0 for Case " + caseName, blame2A, actualUid2[0]);
+
+ assertEquals("Wrong uid2 blame in bucket 1 for Case " + caseName, blame2B, actualUid2[1]);
+ }
}
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 143e07a1c8a6..b819d9edb2a8 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -45,6 +45,9 @@ import org.junit.runners.Suite;
BluetoothPowerCalculatorTest.class,
BstatsCpuTimesValidationTest.class,
CameraPowerCalculatorTest.class,
+ CpuPowerCalculatorTest.class,
+ CustomMeasuredPowerCalculatorTest.class,
+ DischargedPowerCalculatorTest.class,
FlashlightPowerCalculatorTest.class,
GnssPowerCalculatorTest.class,
IdlePowerCalculatorTest.class,
@@ -65,7 +68,9 @@ import org.junit.runners.Suite;
ScreenPowerCalculatorTest.class,
SensorPowerCalculatorTest.class,
SystemServicePowerCalculatorTest.class,
+ UserPowerCalculatorTest.class,
VideoPowerCalculatorTest.class,
+ WakelockPowerCalculatorTest.class,
com.android.internal.power.MeasuredEnergyStatsTest.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 2c71287fac4a..167994200ed7 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -28,6 +28,7 @@ import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
+import android.os.UserBatteryConsumer;
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
@@ -48,6 +49,7 @@ public class BatteryUsageStatsRule implements TestRule {
};
private BatteryUsageStats mBatteryUsageStats;
+ private boolean mScreenOn;
public BatteryUsageStatsRule() {
Context context = InstrumentationRegistry.getContext();
@@ -76,6 +78,31 @@ public class BatteryUsageStatsRule implements TestRule {
return this;
}
+ public BatteryUsageStatsRule setNumCpuClusters(int number) {
+ when(mPowerProfile.getNumCpuClusters()).thenReturn(number);
+ return this;
+ }
+
+ public BatteryUsageStatsRule setNumSpeedStepsInCpuCluster(int cluster, int speeds) {
+ when(mPowerProfile.getNumSpeedStepsInCpuCluster(cluster)).thenReturn(speeds);
+ return this;
+ }
+
+ public BatteryUsageStatsRule setAveragePowerForCpuCluster(int cluster, double value) {
+ when(mPowerProfile.getAveragePowerForCpuCluster(cluster)).thenReturn(value);
+ return this;
+ }
+
+ public BatteryUsageStatsRule setAveragePowerForCpuCore(int cluster, int step, double value) {
+ when(mPowerProfile.getAveragePowerForCpuCore(cluster, step)).thenReturn(value);
+ return this;
+ }
+
+ public BatteryUsageStatsRule startWithScreenOn(boolean screenOn) {
+ mScreenOn = screenOn;
+ return this;
+ }
+
public void setNetworkStats(NetworkStats networkStats) {
mBatteryStats.setNetworkStats(networkStats);
}
@@ -94,6 +121,7 @@ public class BatteryUsageStatsRule implements TestRule {
private void noteOnBattery() {
mBatteryStats.setOnBatteryInternal(true);
mBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0);
+ mBatteryStats.getOnBatteryScreenOffTimeBase().setRunning(!mScreenOn, 0, 0);
}
public PowerProfile getPowerProfile() {
@@ -108,22 +136,35 @@ public class BatteryUsageStatsRule implements TestRule {
return mBatteryStats.getUidStatsLocked(uid);
}
- public void setTime(long realtimeUs, long uptimeUs) {
- mMockClocks.realtime = realtimeUs;
- mMockClocks.uptime = uptimeUs;
+ public void setTime(long realtimeMs, long uptimeMs) {
+ mMockClocks.realtime = realtimeMs;
+ mMockClocks.uptime = uptimeMs;
}
- void apply(PowerCalculator calculator) {
- BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(0, 0);
+ BatteryUsageStats apply(PowerCalculator... calculators) {
+ return apply(BatteryUsageStatsQuery.DEFAULT, calculators);
+ }
+
+ BatteryUsageStats apply(BatteryUsageStatsQuery query, PowerCalculator... calculators) {
+ final long[] customMeasuredEnergiesMicroJoules =
+ mBatteryStats.getCustomMeasuredEnergiesMicroJoules();
+ final int customMeasuredEnergiesCount = customMeasuredEnergiesMicroJoules != null
+ ? customMeasuredEnergiesMicroJoules.length
+ : 0;
+ BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
+ customMeasuredEnergiesCount, 0);
SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats();
for (int i = 0; i < uidStats.size(); i++) {
builder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i));
}
- calculator.calculate(builder, mBatteryStats, mMockClocks.realtime, mMockClocks.uptime,
- BatteryUsageStatsQuery.DEFAULT, null);
+ for (PowerCalculator calculator : calculators) {
+ calculator.calculate(builder, mBatteryStats, mMockClocks.realtime, mMockClocks.uptime,
+ query);
+ }
mBatteryUsageStats = builder.build();
+ return mBatteryUsageStats;
}
public UidBatteryConsumer getUidBatteryConsumer(int uid) {
@@ -144,4 +185,13 @@ public class BatteryUsageStatsRule implements TestRule {
}
return null;
}
+
+ public UserBatteryConsumer getUserBatteryConsumer(int userId) {
+ for (UserBatteryConsumer ubc : mBatteryUsageStats.getUserBatteryConsumers()) {
+ if (ubc.getUserId() == userId) {
+ return ubc;
+ }
+ }
+ return null;
+ }
}
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 018a810772be..355ac6dbcc00 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -67,8 +67,8 @@ public class BatteryUsageStatsTest {
final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000);
final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1);
- builder.setConsumedPower(100);
builder.setDischargePercentage(20);
+ builder.setDischargedPowerRange(1000, 2000);
final UidBatteryConsumer.Builder uidBatteryConsumerBuilder =
builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid);
@@ -100,6 +100,8 @@ public class BatteryUsageStatsTest {
public void validateBatteryUsageStats(BatteryUsageStats batteryUsageStats) {
assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(100);
assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(20);
+ assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo(1000);
+ assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo(2000);
final List<UidBatteryConsumer> uidBatteryConsumers =
batteryUsageStats.getUidBatteryConsumers();
diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
new file mode 100644
index 000000000000..9cf0d375ff51
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+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.Process;
+import android.os.UidBatteryConsumer;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CpuPowerCalculatorTest {
+ private static final double PRECISION = 0.00001;
+
+ private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+ private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 272;
+
+ @Rule
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+ .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
+ .setNumCpuClusters(2)
+ .setNumSpeedStepsInCpuCluster(0, 2)
+ .setNumSpeedStepsInCpuCluster(1, 2)
+ .setAveragePowerForCpuCluster(0, 360)
+ .setAveragePowerForCpuCluster(1, 480)
+ .setAveragePowerForCpuCore(0, 0, 300)
+ .setAveragePowerForCpuCore(0, 1, 400)
+ .setAveragePowerForCpuCore(1, 0, 500)
+ .setAveragePowerForCpuCore(1, 1, 600);
+
+ private final KernelCpuSpeedReader[] mMockKernelCpuSpeedReaders = new KernelCpuSpeedReader[]{
+ mock(KernelCpuSpeedReader.class),
+ mock(KernelCpuSpeedReader.class),
+ };
+
+ @Mock
+ private BatteryStatsImpl.UserInfoProvider mMockUserInfoProvider;
+ @Mock
+ private KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader mMockKernelCpuUidClusterTimeReader;
+ @Mock
+ private KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader;
+ @Mock
+ private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mMockKernelCpuUidUserSysTimeReader;
+ @Mock
+ private KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader mMockKerneCpuUidActiveTimeReader;
+ @Mock
+ private SystemServerCpuThreadReader mMockSystemServerCpuThreadReader;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mStatsRule.getBatteryStats()
+ .setUserInfoProvider(mMockUserInfoProvider)
+ .setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders)
+ .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader)
+ .setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader)
+ .setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader)
+ .setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader)
+ .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader);
+ }
+
+ @Test
+ public void testTimerBasedModel() {
+ 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(0);
+ // 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());
+
+ // Active CPU time
+ doAnswer(invocation -> {
+ final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
+ callback.onUidCpuTime(APP_UID1, 1111L);
+ callback.onUidCpuTime(APP_UID2, 3333L);
+ return null;
+ }).when(mMockKerneCpuUidActiveTimeReader).readDelta(any());
+
+ // Per-cluster CPU time
+ doAnswer(invocation -> {
+ final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0);
+ callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222});
+ callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444});
+ return null;
+ }).when(mMockKernelCpuUidClusterTimeReader).readDelta(any());
+
+ mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true);
+
+ 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(1.092233);
+ 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(2.672322);
+ 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
new file mode 100644
index 000000000000..a4ea8923794a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryConsumer;
+import android.os.Process;
+import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
+import android.util.SparseLongArray;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CustomMeasuredPowerCalculatorTest {
+ private static final double PRECISION = 0.00001;
+
+ private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+
+ @Rule
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+
+ @Test
+ public void testMeasuredEnergyCopiedIntoBatteryConsumers() {
+ final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+ SparseLongArray uidEnergies = new SparseLongArray();
+ uidEnergies.put(APP_UID, 30_000_000);
+ batteryStats.updateCustomMeasuredEnergyDataLocked(0, 100_000_000, uidEnergies);
+
+ uidEnergies.put(APP_UID, 120_000_000);
+ batteryStats.updateCustomMeasuredEnergyDataLocked(1, 200_000_000, uidEnergies);
+
+ CustomMeasuredPowerCalculator calculator =
+ new CustomMeasuredPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(calculator);
+
+ UidBatteryConsumer uid = mStatsRule.getUidBatteryConsumer(APP_UID);
+ assertThat(uid.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID))
+ .isWithin(PRECISION).of(2.252252);
+ assertThat(uid.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1))
+ .isWithin(PRECISION).of(9.009009);
+
+ SystemBatteryConsumer systemConsumer = mStatsRule.getSystemBatteryConsumer(
+ SystemBatteryConsumer.DRAIN_TYPE_CUSTOM);
+ assertThat(systemConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID))
+ .isWithin(PRECISION).of(7.5075075);
+ assertThat(systemConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1))
+ .isWithin(PRECISION).of(15.015015);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/DischargedPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/DischargedPowerCalculatorTest.java
new file mode 100644
index 000000000000..bec3d1644e81
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/DischargedPowerCalculatorTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryManager;
+import android.os.BatteryUsageStats;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DischargedPowerCalculatorTest {
+ private static final double PRECISION = 0.00001;
+
+ @Rule
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+ .setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0);
+
+ @Test
+ public void testDischargeTotals() {
+ final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+ mStatsRule.setTime(1000, 1000);
+ batteryStats.resetAllStatsCmdLocked();
+ batteryStats.setNoAutoReset(true);
+ batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
+ /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 1_000_000,
+ 1_000_000, 1_000_000);
+ batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
+ /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0, 2_000_000,
+ 2_000_000, 2_000_000);
+
+ DischargedPowerCalculator calculator =
+ new DischargedPowerCalculator(mStatsRule.getPowerProfile());
+
+ final BatteryUsageStats batteryUsageStats = mStatsRule.apply(calculator);
+
+ assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(10);
+ assertThat(batteryUsageStats.getDischargedPowerRange().getLower())
+ .isWithin(PRECISION).of(360.0);
+ assertThat(batteryUsageStats.getDischargedPowerRange().getUpper())
+ .isWithin(PRECISION).of(400.0);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
index 8f81ea237a15..7dca0cb92f9d 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
@@ -93,35 +93,62 @@ public class KernelCpuUidUserSysTimeReaderTest {
mReader.setThrottle(500);
writeToFile(uidLines(mUids, mInitialTimes));
- mReader.readDelta(mCallback);
+ mReader.readDelta(false, mCallback);
assertEquals(6, mCallback.mData.size());
long[][] times1 = increaseTime(mInitialTimes);
writeToFile(uidLines(mUids, times1));
mCallback.clear();
- mReader.readDelta(mCallback);
+ mReader.readDelta(false, mCallback);
assertEquals(0, mCallback.mData.size());
+ // TODO(b/180473895): Replace sleeps with injected simulated time.
SystemClock.sleep(600);
long[][] times2 = increaseTime(times1);
writeToFile(uidLines(mUids, times2));
mCallback.clear();
- mReader.readDelta(mCallback);
+ mReader.readDelta(false, mCallback);
assertEquals(6, mCallback.mData.size());
long[][] times3 = increaseTime(times2);
writeToFile(uidLines(mUids, times3));
mCallback.clear();
- mReader.readDelta(mCallback);
+ mReader.readDelta(false, mCallback);
assertEquals(0, mCallback.mData.size());
+
+ // Force the delta read, previously skipped increments should now be read
+ mCallback.clear();
+ mReader.readDelta(true, mCallback);
+ assertEquals(6, mCallback.mData.size());
+
+ SystemClock.sleep(600);
+
+ long[][] times4 = increaseTime(times3);
+ writeToFile(uidLines(mUids, times4));
+ mCallback.clear();
+ mReader.readDelta(true, mCallback);
+ assertEquals(6, mCallback.mData.size());
+
+ // Don't force the delta read, throttle should be set from last read.
+ long[][] times5 = increaseTime(times4);
+ writeToFile(uidLines(mUids, times5));
+ mCallback.clear();
+ mReader.readDelta(false, mCallback);
+ assertEquals(0, mCallback.mData.size());
+
+ SystemClock.sleep(600);
+
+ mCallback.clear();
+ mReader.readDelta(false, mCallback);
+ assertEquals(6, mCallback.mData.size());
}
@Test
public void testReadDelta() throws Exception {
final long[][] times1 = mInitialTimes;
writeToFile(uidLines(mUids, times1));
- mReader.readDelta(mCallback);
+ mReader.readDelta(false, mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], times1[i]);
}
@@ -131,7 +158,7 @@ public class KernelCpuUidUserSysTimeReaderTest {
// Verify that a second call will only return deltas.
final long[][] times2 = increaseTime(times1);
writeToFile(uidLines(mUids, times2));
- mReader.readDelta(mCallback);
+ mReader.readDelta(false, mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
}
@@ -139,20 +166,20 @@ public class KernelCpuUidUserSysTimeReaderTest {
mCallback.clear();
// Verify that there won't be a callback if the proc file values didn't change.
- mReader.readDelta(mCallback);
+ mReader.readDelta(false, mCallback);
mCallback.verifyNoMoreInteractions();
mCallback.clear();
// Verify that calling with a null callback doesn't result in any crashes
final long[][] times3 = increaseTime(times2);
writeToFile(uidLines(mUids, times3));
- mReader.readDelta(null);
+ mReader.readDelta(false, null);
// Verify that the readDelta call will only return deltas when
// the previous call had null callback.
final long[][] times4 = increaseTime(times3);
writeToFile(uidLines(mUids, times4));
- mReader.readDelta(mCallback);
+ mReader.readDelta(false, mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
}
@@ -165,7 +192,7 @@ public class KernelCpuUidUserSysTimeReaderTest {
public void testReadDeltaWrongData() throws Exception {
final long[][] times1 = mInitialTimes;
writeToFile(uidLines(mUids, times1));
- mReader.readDelta(mCallback);
+ mReader.readDelta(false, mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], times1[i]);
}
@@ -176,7 +203,7 @@ public class KernelCpuUidUserSysTimeReaderTest {
final long[][] times2 = increaseTime(times1);
times2[0][0] = 1000;
writeToFile(uidLines(mUids, times2));
- mReader.readDelta(mCallback);
+ mReader.readDelta(false, mCallback);
for (int i = 1; i < mUids.length; i++) {
mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
}
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 fdfc7ac5587c..d50bb05c3588 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.-$$Lambda$LooperStatsTest$");
+ "com.android.internal.os.LooperStatsTest-$$ExternalSyntheticLambda");
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/OWNERS b/core/tests/coretests/src/com/android/internal/os/OWNERS
index 4068e2bc03b7..3f8f9e29c6a0 100644
--- a/core/tests/coretests/src/com/android/internal/os/OWNERS
+++ b/core/tests/coretests/src/com/android/internal/os/OWNERS
@@ -1 +1,4 @@
include /BATTERY_STATS_OWNERS
+
+# CPU
+per-file *Cpu* = file:/core/java/com/android/internal/os/CPU_OWNERS
diff --git a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
index e43caa37f711..86e615c035c7 100644
--- a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
@@ -18,8 +18,12 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
+import android.app.ActivityManager;
import android.os.BatteryConsumer;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Process;
import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
import android.view.Display;
import androidx.test.filters.SmallTest;
@@ -33,20 +37,37 @@ import org.junit.runner.RunWith;
@SmallTest
public class ScreenPowerCalculatorTest {
private static final double PRECISION = 0.00001;
+ private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+ private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 43;
+ private static final long MINUTE_IN_MS = 60 * 1000;
+ private static final long MINUTE_IN_US = 60 * 1000 * 1000;
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
- .setAveragePower(PowerProfile.POWER_SCREEN_ON, 360.0)
- .setAveragePower(PowerProfile.POWER_SCREEN_FULL, 3600.0);
+ .setAveragePower(PowerProfile.POWER_SCREEN_ON, 36.0)
+ .setAveragePower(PowerProfile.POWER_SCREEN_FULL, 48.0);
@Test
- public void testTimerBasedModel() {
- BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ public void testMeasuredEnergyBasedModel() {
+ BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
- stats.noteScreenStateLocked(Display.STATE_ON, 1000, 1000, 1000);
- stats.noteScreenBrightnessLocked(100, 1000, 1000);
- stats.noteScreenBrightnessLocked(200, 2000, 2000);
- stats.noteScreenStateLocked(Display.STATE_OFF, 3000, 3000, 3000);
+ batteryStats.noteScreenStateLocked(Display.STATE_ON, 0, 0, 0);
+ batteryStats.updateDisplayEnergyLocked(0, Display.STATE_ON, 2 * MINUTE_IN_MS);
+
+ setFgState(APP_UID1, true, 2 * MINUTE_IN_MS, 2 * MINUTE_IN_MS);
+ setFgState(APP_UID1, false, 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
+ setFgState(APP_UID2, true, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
+
+ batteryStats.updateDisplayEnergyLocked(300_000_000, Display.STATE_ON,
+ 60 * MINUTE_IN_MS);
+
+ batteryStats.noteScreenStateLocked(Display.STATE_OFF,
+ 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
+
+ batteryStats.updateDisplayEnergyLocked(100_000_000, Display.STATE_DOZE,
+ 120 * MINUTE_IN_MS);
+
+ mStatsRule.setTime(120 * MINUTE_IN_US, 120 * MINUTE_IN_US);
ScreenPowerCalculator calculator =
new ScreenPowerCalculator(mStatsRule.getPowerProfile());
@@ -56,8 +77,79 @@ public class ScreenPowerCalculatorTest {
SystemBatteryConsumer consumer =
mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_SCREEN);
assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE))
- .isEqualTo(2000);
+ .isEqualTo(80 * MINUTE_IN_MS);
+ assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE))
+ .isWithin(PRECISION).of(30.03003);
+
+ UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
+ assertThat(uid1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN))
+ .isEqualTo(18 * MINUTE_IN_MS);
+ assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isWithin(PRECISION).of(8.44594);
+
+ UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
+ assertThat(uid2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN))
+ .isEqualTo(90 * MINUTE_IN_MS);
+ assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isWithin(PRECISION).of(21.58408);
+ }
+
+ @Test
+ public void testPowerProfileBasedModel() {
+ BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+ batteryStats.noteScreenStateLocked(Display.STATE_ON, 0, 0, 0);
+
+ setFgState(APP_UID1, true, 2 * MINUTE_IN_MS, 2 * MINUTE_IN_MS);
+
+ batteryStats.noteScreenBrightnessLocked(100, 5 * MINUTE_IN_MS, 5 * MINUTE_IN_MS);
+ batteryStats.noteScreenBrightnessLocked(200, 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
+
+ setFgState(APP_UID1, false, 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
+
+ setFgState(APP_UID2, true, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
+
+ batteryStats.noteScreenStateLocked(Display.STATE_OFF,
+ 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
+
+ mStatsRule.setTime(120 * MINUTE_IN_US, 120 * MINUTE_IN_US);
+
+ ScreenPowerCalculator calculator =
+ new ScreenPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+ calculator);
+
+ SystemBatteryConsumer consumer =
+ mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_SCREEN);
+ assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE))
+ .isEqualTo(80 * MINUTE_IN_MS);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE))
- .isWithin(PRECISION).of(1.2);
+ .isWithin(PRECISION).of(88.4);
+
+ UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
+ assertThat(uid1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN))
+ .isEqualTo(18 * MINUTE_IN_MS);
+ assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isWithin(PRECISION).of(14.73333);
+
+ UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
+ assertThat(uid2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN))
+ .isEqualTo(90 * MINUTE_IN_MS);
+ assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isWithin(PRECISION).of(73.66666);
+ }
+
+ private void setFgState(int uid, boolean fgOn, long realtimeMs, long uptimeMs) {
+ BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+ if (fgOn) {
+ batteryStats.noteActivityResumedLocked(uid, realtimeMs, uptimeMs);
+ batteryStats.noteUidProcessStateLocked(uid, ActivityManager.PROCESS_STATE_TOP,
+ realtimeMs, uptimeMs);
+ } else {
+ batteryStats.noteActivityPausedLocked(uid, realtimeMs, uptimeMs);
+ batteryStats.noteUidProcessStateLocked(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
+ realtimeMs, uptimeMs);
+ }
}
}
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 24741fe110ce..dfbf28b286c6 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -18,9 +18,16 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.BatteryUsageStatsQuery;
import android.os.Binder;
import android.os.Process;
+import android.os.UidBatteryConsumer;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
@@ -39,34 +46,48 @@ import java.util.Collection;
@RunWith(AndroidJUnit4.class)
public class SystemServicePowerCalculatorTest {
- private static final double PRECISION = 0.0000001;
+ private static final double PRECISION = 0.000001;
@Rule
- public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
-
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+ .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
+ .setNumCpuClusters(2)
+ .setNumSpeedStepsInCpuCluster(0, 2)
+ .setNumSpeedStepsInCpuCluster(1, 2)
+ .setAveragePowerForCpuCluster(0, 360)
+ .setAveragePowerForCpuCluster(1, 480)
+ .setAveragePowerForCpuCore(0, 0, 300)
+ .setAveragePowerForCpuCore(0, 1, 400)
+ .setAveragePowerForCpuCore(1, 0, 500)
+ .setAveragePowerForCpuCore(1, 1, 600);
+
+ private BatteryStatsImpl.UserInfoProvider mMockUserInfoProvider;
private MockBatteryStatsImpl mMockBatteryStats;
private MockKernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader;
private MockSystemServerCpuThreadReader mMockSystemServerCpuThreadReader;
@Before
public void setUp() throws IOException {
+ mMockUserInfoProvider = mock(BatteryStatsImpl.UserInfoProvider.class);
mMockSystemServerCpuThreadReader = new MockSystemServerCpuThreadReader();
mMockCpuUidFreqTimeReader = new MockKernelCpuUidFreqTimeReader();
mMockBatteryStats = mStatsRule.getBatteryStats()
.setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader)
.setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader)
- .setUserInfoProvider(new MockUserInfoProvider());
+ .setUserInfoProvider(mMockUserInfoProvider);
}
@Test
- public void testCalculateApp() {
- // Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total
+ public void testPowerProfileBasedModel() {
+ when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
+
+ // Test Power Profile has two CPU clusters with 2 speeds each, thus 4 freq times total
mMockSystemServerCpuThreadReader.setCpuTimes(
- new long[] {30000, 40000, 50000, 60000, 70000, 80000, 90000},
- new long[] {20000, 30000, 40000, 50000, 60000, 70000, 80000});
+ new long[] {30000, 40000, 50000, 60000},
+ new long[] {20000, 30000, 40000, 50000});
mMockCpuUidFreqTimeReader.setSystemServerCpuTimes(
- new long[] {10000, 20000, 30000, 40000, 50000, 60000, 70000}
+ new long[] {10000, 20000, 30000, 40000}
);
mMockBatteryStats.readKernelUidCpuFreqTimesLocked(null, true, false);
@@ -101,14 +122,17 @@ public class SystemServicePowerCalculatorTest {
SystemServicePowerCalculator calculator = new SystemServicePowerCalculator(
mStatsRule.getPowerProfile());
- mStatsRule.apply(calculator);
+ mStatsRule.apply(new FakeCpuPowerCalculator(), calculator);
assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid1)
.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
- .isWithin(PRECISION).of(0.00016269);
+ .isWithin(PRECISION).of(1.888888);
assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid2)
.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
- .isWithin(PRECISION).of(0.00146426);
+ .isWithin(PRECISION).of(17.0);
+ assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID)
+ .getConsumedPower(BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS))
+ .isWithin(PRECISION).of(-18.888888);
}
private static class MockKernelCpuUidFreqTimeReader extends
@@ -137,7 +161,7 @@ public class SystemServicePowerCalculatorTest {
}
private static class MockSystemServerCpuThreadReader extends SystemServerCpuThreadReader {
- private SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes();
+ private final SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes();
MockSystemServerCpuThreadReader() {
super(null);
@@ -154,16 +178,15 @@ public class SystemServicePowerCalculatorTest {
}
}
- private static class MockUserInfoProvider extends BatteryStatsImpl.UserInfoProvider {
- @Nullable
- @Override
- protected int[] getUserIds() {
- return new int[0];
- }
-
+ private static class FakeCpuPowerCalculator extends PowerCalculator {
@Override
- public boolean exists(int userId) {
- return true;
+ protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ if (u.getUid() == Process.SYSTEM_UID) {
+ // SystemServer must be attributed at least as much power as the total
+ // of all system services requested by apps.
+ app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 1000000);
+ }
}
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java
new file mode 100644
index 000000000000..6fa1d3bae0f7
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/UserPowerCalculatorTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Process;
+import android.os.UidBatteryConsumer;
+import android.os.UserBatteryConsumer;
+import android.os.UserHandle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class UserPowerCalculatorTest {
+ public static final int USER1 = 0;
+ public static final int USER2 = 1625;
+
+ private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+ private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 272;
+ private static final int APP_UID3 = Process.FIRST_APPLICATION_UID + 314;
+
+ @Rule
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+
+ @Test
+ public void testAllUsers() {
+ prepareUidBatteryConsumers();
+
+ UserPowerCalculator calculator = new UserPowerCalculator();
+
+ mStatsRule.apply(BatteryUsageStatsQuery.DEFAULT, calculator, new FakeAudioPowerCalculator(),
+ new FakeVideoPowerCalculator());
+
+ assertThat(mStatsRule.getUserBatteryConsumer(USER1)).isNull();
+
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(3000);
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(7000);
+
+ assertThat(mStatsRule.getUserBatteryConsumer(USER2)).isNull();
+
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID2))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(5555);
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID2))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(9999);
+ }
+
+ @Test
+ public void testSpecificUser() {
+ prepareUidBatteryConsumers();
+
+ UserPowerCalculator calculator = new UserPowerCalculator();
+
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder().addUser(UserHandle.of(USER1)).build(),
+ calculator, new FakeAudioPowerCalculator(), new FakeVideoPowerCalculator());
+
+ assertThat(mStatsRule.getUserBatteryConsumer(USER1)).isNull();
+
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(3000);
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID1))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(7000);
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID2))).isNull();
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID3))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)).isEqualTo(7070);
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER1, APP_UID3))
+ .getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)).isEqualTo(11110);
+
+ UserBatteryConsumer user2 = mStatsRule.getUserBatteryConsumer(USER2);
+ assertThat(user2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO))
+ .isEqualTo(15308);
+ assertThat(user2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO))
+ .isEqualTo(24196);
+
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID1))).isNull();
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID2))).isNull();
+ assertThat(mStatsRule.getUidBatteryConsumer(UserHandle.getUid(USER2, APP_UID3))).isNull();
+ }
+
+ private void prepareUidBatteryConsumers() {
+ prepareUidBatteryConsumer(USER1, APP_UID1, 1000, 2000, 3000, 4000);
+ prepareUidBatteryConsumer(USER2, APP_UID2, 2222, 3333, 4444, 5555);
+ prepareUidBatteryConsumer(USER1, APP_UID3, 3030, 4040, 5050, 6060);
+ prepareUidBatteryConsumer(USER2, APP_UID3, 4321, 5432, 6543, 7654);
+ }
+
+ private void prepareUidBatteryConsumer(int userId, int uid, long audioDuration1Ms,
+ long audioDuration2Ms, long videoDuration1Ms, long videoDuration2Ms) {
+ BatteryStatsImpl.Uid uidStats = mStatsRule.getUidStats(UserHandle.getUid(userId, uid));
+
+ // Use "audio" and "video" to fake some power consumption. Could be any other type of usage.
+ uidStats.noteAudioTurnedOnLocked(0);
+ uidStats.noteAudioTurnedOffLocked(audioDuration1Ms);
+ uidStats.noteAudioTurnedOnLocked(1000000);
+ uidStats.noteAudioTurnedOffLocked(1000000 + audioDuration2Ms);
+
+ uidStats.noteVideoTurnedOnLocked(0);
+ uidStats.noteVideoTurnedOffLocked(videoDuration1Ms);
+ uidStats.noteVideoTurnedOnLocked(2000000);
+ uidStats.noteVideoTurnedOffLocked(2000000 + videoDuration2Ms);
+ }
+
+ private static class FakeAudioPowerCalculator extends PowerCalculator {
+ @Override
+ protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ long durationMs = u.getAudioTurnedOnTimer().getTotalTimeLocked(rawRealtimeUs, 0);
+ app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO, durationMs / 1000);
+ }
+ }
+
+ private static class FakeVideoPowerCalculator extends PowerCalculator {
+ @Override
+ protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ long durationMs = u.getVideoTurnedOnTimer().getTotalTimeLocked(rawRealtimeUs, 0);
+ app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO, durationMs / 1000);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java
new file mode 100644
index 000000000000..4f71b438c6fa
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Process;
+import android.os.UidBatteryConsumer;
+import android.os.WorkSource;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class WakelockPowerCalculatorTest {
+ private static final double PRECISION = 0.00001;
+
+ private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+ private static final int APP_PID = 3145;
+
+ @Rule
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+ .setAveragePower(PowerProfile.POWER_CPU_IDLE, 360.0);
+
+ @Test
+ public void testTimerBasedModel() {
+ mStatsRule.getUidStats(Process.ROOT_UID);
+
+ BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+ batteryStats.noteStartWakeFromSourceLocked(new WorkSource(APP_UID), APP_PID, "awake", "",
+ BatteryStats.WAKE_TYPE_PARTIAL, true, 1000, 1000);
+ batteryStats.noteStopWakeFromSourceLocked(new WorkSource(APP_UID), APP_PID, "awake", "",
+ BatteryStats.WAKE_TYPE_PARTIAL, 2000, 2000);
+
+ mStatsRule.setTime(10_000_000, 6_000_000);
+
+ WakelockPowerCalculator calculator =
+ new WakelockPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(calculator);
+
+ UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+ assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK))
+ .isEqualTo(1000);
+ assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK))
+ .isWithin(PRECISION).of(0.1);
+
+ UidBatteryConsumer osConsumer = mStatsRule.getUidBatteryConsumer(Process.ROOT_UID);
+ assertThat(osConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK))
+ .isEqualTo(5000);
+ assertThat(osConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK))
+ .isWithin(PRECISION).of(0.5);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index b9908f46c81c..5fd5a7838c3a 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -387,6 +387,59 @@ public class MeasuredEnergyStatsTest {
}
@Test
+ public void testIsValidCustomBucket() {
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 3);
+ assertFalse(stats.isValidCustomBucket(-1));
+ assertTrue(stats.isValidCustomBucket(0));
+ assertTrue(stats.isValidCustomBucket(1));
+ assertTrue(stats.isValidCustomBucket(2));
+ assertFalse(stats.isValidCustomBucket(3));
+ assertFalse(stats.isValidCustomBucket(4));
+
+ final MeasuredEnergyStats boringStats
+ = new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 0);
+ assertFalse(boringStats.isValidCustomBucket(-1));
+ assertFalse(boringStats.isValidCustomBucket(0));
+ assertFalse(boringStats.isValidCustomBucket(1));
+ }
+
+ @Test
+ public void testGetAccumulatedCustomBucketEnergies() {
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 3);
+
+ stats.updateCustomBucket(0, 50, true);
+ stats.updateCustomBucket(1, 60, true);
+ stats.updateCustomBucket(2, 13, true);
+ stats.updateCustomBucket(1, 70, true);
+
+ final long[] output = stats.getAccumulatedCustomBucketEnergies();
+ assertEquals(3, output.length);
+
+ assertEquals(50, output[0]);
+ assertEquals(60 + 70, output[1]);
+ assertEquals(13, output[2]);
+ }
+
+ @Test
+ public void testGetAccumulatedCustomBucketEnergies_empty() {
+ final MeasuredEnergyStats stats
+ = new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 0);
+
+ final long[] output = stats.getAccumulatedCustomBucketEnergies();
+ assertEquals(0, output.length);
+ }
+
+ @Test
+ public void testGetNumberCustomEnergyBuckets() {
+ assertEquals(0, new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 0)
+ .getNumberCustomEnergyBuckets());
+ assertEquals(3, new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 3)
+ .getNumberCustomEnergyBuckets());
+ }
+
+ @Test
public void testReset() {
final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_ENERGY_BUCKETS];
final int numCustomBuckets = 2;
diff --git a/core/tests/coretests/src/com/android/internal/widget/OWNERS b/core/tests/coretests/src/com/android/internal/widget/OWNERS
new file mode 100644
index 000000000000..b40fe240d80c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/OWNERS
@@ -0,0 +1,3 @@
+# LockSettings related
+per-file *LockPattern* = file:/services/core/java/com/android/server/locksettings/OWNERS
+per-file *Lockscreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
diff --git a/core/tests/featureflagtests/Android.bp b/core/tests/featureflagtests/Android.bp
index 8730b7012c4a..d9f608ea34c4 100644
--- a/core/tests/featureflagtests/Android.bp
+++ b/core/tests/featureflagtests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksCoreFeatureFlagTests",
// We only want this apk build for tests.
diff --git a/core/tests/hdmitests/Android.bp b/core/tests/hdmitests/Android.bp
index 4755e0ea5259..f49e05329f62 100644
--- a/core/tests/hdmitests/Android.bp
+++ b/core/tests/hdmitests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "HdmiCecTests",
// Include all test java files
diff --git a/core/tests/hosttests/test-apps/AutoLocTestApp/Android.bp b/core/tests/hosttests/test-apps/AutoLocTestApp/Android.bp
index 14424811d3be..b4efce2c822b 100644
--- a/core/tests/hosttests/test-apps/AutoLocTestApp/Android.bp
+++ b/core/tests/hosttests/test-apps/AutoLocTestApp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AutoLocTestApp",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v1/Android.bp b/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v1/Android.bp
index 438ed2533716..0e07ddca5edc 100644
--- a/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v1/Android.bp
+++ b/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v1/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AutoLocVersionedTestApp_v1",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v2/Android.bp b/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v2/Android.bp
index 2ac72a13fee3..f40870004e92 100644
--- a/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v2/Android.bp
+++ b/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v2/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AutoLocVersionedTestApp_v2",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/ExternalLocAllPermsTestApp/Android.bp b/core/tests/hosttests/test-apps/ExternalLocAllPermsTestApp/Android.bp
index e06e82e38ef4..964401d851af 100644
--- a/core/tests/hosttests/test-apps/ExternalLocAllPermsTestApp/Android.bp
+++ b/core/tests/hosttests/test-apps/ExternalLocAllPermsTestApp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ExternalLocAllPermsTestApp",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/Android.bp b/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/Android.bp
index c48dcd525232..297ed57a6b34 100644
--- a/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/Android.bp
+++ b/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ExternalLocPermFLTestApp",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/ExternalLocTestApp/Android.bp b/core/tests/hosttests/test-apps/ExternalLocTestApp/Android.bp
index 5c1c15a4999f..cf8c7e1abcf3 100644
--- a/core/tests/hosttests/test-apps/ExternalLocTestApp/Android.bp
+++ b/core/tests/hosttests/test-apps/ExternalLocTestApp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ExternalLocTestApp",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v1/Android.bp b/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v1/Android.bp
index 13f5066c7170..909bd7f9d0da 100644
--- a/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v1/Android.bp
+++ b/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v1/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ExternalLocVersionedTestApp_v1",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v2/Android.bp b/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v2/Android.bp
index e02ffb307ba2..c5f91924d243 100644
--- a/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v2/Android.bp
+++ b/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v2/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ExternalLocVersionedTestApp_v2",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.bp b/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.bp
index de098005b40e..1545f4d0e5f9 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.bp
+++ b/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ExternalSharedPermsTestApp",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.bp b/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.bp
index 435144f31edd..8690bdfc2f93 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.bp
+++ b/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ExternalSharedPermsBTTestApp",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.bp b/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.bp
index 445bac94f10d..21b7c4c1ea77 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.bp
+++ b/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ExternalSharedPermsDiffKeyTestApp",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.bp b/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.bp
index d1da3998ca4a..63bf8dca6968 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.bp
+++ b/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ExternalSharedPermsFLTestApp",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/InternalLocTestApp/Android.bp b/core/tests/hosttests/test-apps/InternalLocTestApp/Android.bp
index fab9d4394fc5..f05cde430992 100644
--- a/core/tests/hosttests/test-apps/InternalLocTestApp/Android.bp
+++ b/core/tests/hosttests/test-apps/InternalLocTestApp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "InternalLocTestApp",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp
index dcf168760d4d..56f10fe075ed 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "MultiDexLegacyTestServicesTests",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/NoLocTestApp/Android.bp b/core/tests/hosttests/test-apps/NoLocTestApp/Android.bp
index 50a2de499d37..88e3722a5ed4 100644
--- a/core/tests/hosttests/test-apps/NoLocTestApp/Android.bp
+++ b/core/tests/hosttests/test-apps/NoLocTestApp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "NoLocTestApp",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v1/Android.bp b/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v1/Android.bp
index 4bc9edcd9f20..fd5ab265d1e2 100644
--- a/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v1/Android.bp
+++ b/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v1/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "NoLocVersionedTestApp_v1",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v2/Android.bp b/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v2/Android.bp
index dd2952be00dd..fa821d84ace4 100644
--- a/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v2/Android.bp
+++ b/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v2/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "NoLocVersionedTestApp_v2",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/SharedUid/32/Android.bp b/core/tests/hosttests/test-apps/SharedUid/32/Android.bp
index f3e3eded5fba..6f3d4cf9c027 100644
--- a/core/tests/hosttests/test-apps/SharedUid/32/Android.bp
+++ b/core/tests/hosttests/test-apps/SharedUid/32/Android.bp
@@ -19,6 +19,15 @@
// Build activity
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "PMTest_Java32",
srcs: ["**/*.java"],
diff --git a/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp b/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp
index 9e6c17f0247a..867d65cbe81c 100644
--- a/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp
+++ b/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp
@@ -17,6 +17,15 @@
// This makefile supplies the rules for building a library of JNI code for
// use by our example of how to bundle a shared library with an APK.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_test_library {
// This is the target being built.
diff --git a/core/tests/hosttests/test-apps/SharedUid/64/Android.bp b/core/tests/hosttests/test-apps/SharedUid/64/Android.bp
index 5d9c0b597ef1..6bb25a82b354 100644
--- a/core/tests/hosttests/test-apps/SharedUid/64/Android.bp
+++ b/core/tests/hosttests/test-apps/SharedUid/64/Android.bp
@@ -19,6 +19,15 @@
// Build activity
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "PMTest_Java64",
srcs: ["**/*.java"],
diff --git a/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp b/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp
index 91da6e44a8be..c032ba1503d1 100644
--- a/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp
+++ b/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp
@@ -17,6 +17,15 @@
// This Android.bp supplies the rules for building a library of JNI code for
// use by our example of how to bundle a shared library with an APK.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_test_library {
// This is the target being built.
name: "libpmtest64",
diff --git a/core/tests/hosttests/test-apps/SharedUid/dual/Android.bp b/core/tests/hosttests/test-apps/SharedUid/dual/Android.bp
index c42192dcdbf2..94dd0c6758c2 100644
--- a/core/tests/hosttests/test-apps/SharedUid/dual/Android.bp
+++ b/core/tests/hosttests/test-apps/SharedUid/dual/Android.bp
@@ -19,6 +19,15 @@
// Build activity
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "PMTest_Java_dual",
srcs: ["**/*.java"],
diff --git a/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp b/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp
index 662755dd35b0..6b9d7f337fc8 100644
--- a/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp
+++ b/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp
@@ -17,6 +17,15 @@
// This Android.bp supplies the rules for building a library of JNI code for
// use by our example of how to bundle a shared library with an APK.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_test_library {
// This is the target being built.
diff --git a/core/tests/hosttests/test-apps/SharedUid/java_only/Android.bp b/core/tests/hosttests/test-apps/SharedUid/java_only/Android.bp
index baedc6e35734..6d632671e261 100644
--- a/core/tests/hosttests/test-apps/SharedUid/java_only/Android.bp
+++ b/core/tests/hosttests/test-apps/SharedUid/java_only/Android.bp
@@ -19,6 +19,15 @@
// Build activity
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "PMTest_Java",
srcs: ["**/*.java"],
diff --git a/core/tests/hosttests/test-apps/SimpleTestApp/Android.bp b/core/tests/hosttests/test-apps/SimpleTestApp/Android.bp
index 5f443bd83605..b022bdaa6016 100644
--- a/core/tests/hosttests/test-apps/SimpleTestApp/Android.bp
+++ b/core/tests/hosttests/test-apps/SimpleTestApp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "SimpleTestApp",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/Android.bp b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/Android.bp
index 800e0833d35d..0ac825b3dd2e 100644
--- a/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/Android.bp
+++ b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "UpdateExtToIntLocTestApp_v1_ext",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/Android.bp b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/Android.bp
index 299887c9fbfc..6f66a5104499 100644
--- a/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/Android.bp
+++ b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "UpdateExtToIntLocTestApp_v2_int",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/Android.bp b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/Android.bp
index 1530422841f6..d88bce04638b 100644
--- a/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/Android.bp
+++ b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "UpdateExternalLocTestApp_v1_ext",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/Android.bp b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/Android.bp
index 4c7975e5f18a..d1240df8a3ce 100644
--- a/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/Android.bp
+++ b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "UpdateExternalLocTestApp_v2_none",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_Auto/Android.bp b/core/tests/hosttests/test-apps/VersatileTestApp_Auto/Android.bp
index c6b60c352e38..751071cd947a 100644
--- a/core/tests/hosttests/test-apps/VersatileTestApp_Auto/Android.bp
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_Auto/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "VersatileTestApp_Auto",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_External/Android.bp b/core/tests/hosttests/test-apps/VersatileTestApp_External/Android.bp
index db521efc5935..b9ed015b22eb 100644
--- a/core/tests/hosttests/test-apps/VersatileTestApp_External/Android.bp
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_External/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "VersatileTestApp_External",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_Internal/Android.bp b/core/tests/hosttests/test-apps/VersatileTestApp_Internal/Android.bp
index ca059302ff0d..7b725704651c 100644
--- a/core/tests/hosttests/test-apps/VersatileTestApp_Internal/Android.bp
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_Internal/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "VersatileTestApp_Internal",
srcs: ["src/**/*.java"],
diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_None/Android.bp b/core/tests/hosttests/test-apps/VersatileTestApp_None/Android.bp
index 6e1aac7d6c94..5f67bce361c5 100644
--- a/core/tests/hosttests/test-apps/VersatileTestApp_None/Android.bp
+++ b/core/tests/hosttests/test-apps/VersatileTestApp_None/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "VersatileTestApp_None",
srcs: ["src/**/*.java"],
diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp
index ae3ff8612eee..96811be37f5a 100644
--- a/core/tests/mockingcoretests/Android.bp
+++ b/core/tests/mockingcoretests/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksMockingCoreTests",
diff --git a/core/tests/notificationtests/Android.bp b/core/tests/notificationtests/Android.bp
index e744d5a2e631..1c0e39d9e022 100644
--- a/core/tests/notificationtests/Android.bp
+++ b/core/tests/notificationtests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "NotificationStressTests",
// Include all test java files.
diff --git a/core/tests/overlaytests/device/Android.bp b/core/tests/overlaytests/device/Android.bp
index f86ac9ce37e1..0d3b15a41e8c 100644
--- a/core/tests/overlaytests/device/Android.bp
+++ b/core/tests/overlaytests/device/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "OverlayDeviceTests",
srcs: ["src/**/*.java"],
diff --git a/core/tests/overlaytests/device/res/values/config.xml b/core/tests/overlaytests/device/res/values/config.xml
index e918268fa073..a30d66f82128 100644
--- a/core/tests/overlaytests/device/res/values/config.xml
+++ b/core/tests/overlaytests/device/res/values/config.xml
@@ -2,6 +2,7 @@
<resources>
<string name="str">none</string>
<string name="str2">none</string>
+ <integer name="overlaid">0</integer>
<integer name="matrix_100000">100</integer>
<integer name="matrix_100001">100</integer>
<integer name="matrix_100010">100</integer>
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/FabricatedOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/FabricatedOverlaysTest.java
new file mode 100644
index 000000000000..34659897e588
--- /dev/null
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/FabricatedOverlaysTest.java
@@ -0,0 +1,277 @@
+/*
+ * 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.overlaytest;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.content.om.FabricatedOverlay;
+import android.content.om.OverlayIdentifier;
+import android.content.om.OverlayInfo;
+import android.content.om.OverlayManager;
+import android.content.om.OverlayManagerTransaction;
+import android.content.res.Resources;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.TypedValue;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(JUnit4.class)
+@MediumTest
+public class FabricatedOverlaysTest {
+ private static final String TAG = "FabricatedOverlaysTest";
+ private final String TEST_RESOURCE = "integer/overlaid";
+ private final String TEST_OVERLAY_NAME = "Test";
+
+ private Context mContext;
+ private Resources mResources;
+ private OverlayManager mOverlayManager;
+ private int mUserId;
+ private UserHandle mUserHandle;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mResources = mContext.getResources();
+ mOverlayManager = mContext.getSystemService(OverlayManager.class);
+ mUserId = UserHandle.myUserId();
+ mUserHandle = UserHandle.of(mUserId);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ final OverlayManagerTransaction.Builder cleanUp = new OverlayManagerTransaction.Builder();
+ mOverlayManager.getOverlayInfosForTarget(mContext.getPackageName(), mUserHandle).forEach(
+ info -> {
+ if (info.isFabricated()) {
+ cleanUp.unregisterFabricatedOverlay(info.getOverlayIdentifier());
+ }
+ });
+ mOverlayManager.commit(cleanUp.build());
+ }
+
+ @Test
+ public void testFabricatedOverlay() throws Exception {
+ final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
+ mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName())
+ .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
+ .build();
+
+ waitForResourceValue(0);
+ mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(overlay)
+ .build());
+
+ OverlayInfo info = mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle);
+ assertNotNull(info);
+ assertFalse(info.isEnabled());
+
+ mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+ .setEnabled(overlay.getIdentifier(), true, mUserId)
+ .build());
+
+ info = mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle);
+ assertNotNull(info);
+ assertTrue(info.isEnabled());
+
+ waitForResourceValue(1);
+ mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+ .unregisterFabricatedOverlay(overlay.getIdentifier())
+ .build());
+
+ waitForResourceValue(0);
+ }
+
+ @Test
+ public void testRegisterEnableAtomic() throws Exception {
+ final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
+ mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName())
+ .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
+ .build();
+
+ waitForResourceValue(0);
+ mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(overlay)
+ .setEnabled(overlay.getIdentifier(), true, mUserId)
+ .build());
+
+ waitForResourceValue(1);
+ }
+
+ @Test
+ public void testRegisterTwice() throws Exception {
+ FabricatedOverlay overlay = new FabricatedOverlay.Builder(
+ mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName())
+ .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
+ .build();
+
+ waitForResourceValue(0);
+ mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(overlay)
+ .setEnabled(overlay.getIdentifier(), true, mUserId)
+ .build());
+
+ waitForResourceValue(1);
+ overlay = new FabricatedOverlay.Builder(
+ mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName())
+ .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 2)
+ .build();
+
+ mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(overlay)
+ .build());
+ waitForResourceValue(2);
+ }
+
+ @Test
+ public void testInvalidOwningPackageName() throws Exception {
+ final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
+ "android", TEST_OVERLAY_NAME, mContext.getPackageName())
+ .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
+ .build();
+
+ waitForResourceValue(0);
+ assertThrows(SecurityException.class, () ->
+ mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(overlay)
+ .setEnabled(overlay.getIdentifier(), true, mUserId)
+ .build()));
+
+ assertNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle));
+ }
+
+ @Test
+ public void testInvalidOverlayName() throws Exception {
+ final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
+ mContext.getPackageName(), "invalid@name", mContext.getPackageName())
+ .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
+ .build();
+
+ waitForResourceValue(0);
+ assertThrows(SecurityException.class, () ->
+ mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(overlay)
+ .setEnabled(overlay.getIdentifier(), true, mUserId)
+ .build()));
+
+ assertNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle));
+ }
+
+ @Test
+ public void testOverlayIdentifierLongest() throws Exception {
+ final int maxLength = 255 - 11; // 11 reserved characters
+ final String longestName = String.join("",
+ Collections.nCopies(maxLength - mContext.getPackageName().length(), "a"));
+ {
+ FabricatedOverlay overlay = new FabricatedOverlay.Builder(mContext.getPackageName(),
+ longestName, mContext.getPackageName())
+ .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
+ .build();
+
+ mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(overlay)
+ .build());
+ assertNotNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle));
+ }
+ {
+ FabricatedOverlay overlay = new FabricatedOverlay.Builder(mContext.getPackageName(),
+ longestName + "a", mContext.getPackageName())
+ .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
+ .build();
+
+ assertThrows(SecurityException.class, () ->
+ mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(overlay)
+ .build()));
+
+ assertNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle));
+ }
+ }
+
+ @Test
+ public void testInvalidResourceValues() throws Exception {
+ final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
+ "android", TEST_OVERLAY_NAME, mContext.getPackageName())
+ .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
+ .setResourceValue("something", TypedValue.TYPE_INT_DEC, 1)
+ .build();
+
+ waitForResourceValue(0);
+ assertThrows(SecurityException.class, () ->
+ mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(overlay)
+ .setEnabled(overlay.getIdentifier(), true, mUserId)
+ .build()));
+
+ assertNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle));
+ }
+
+ @Test
+ public void testTransactionFailRollback() throws Exception {
+ final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
+ mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName())
+ .setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
+ .build();
+
+ waitForResourceValue(0);
+ assertThrows(SecurityException.class, () ->
+ mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(overlay)
+ .setEnabled(overlay.getIdentifier(), true, mUserId)
+ .setEnabled(new OverlayIdentifier("not-valid"), true, mUserId)
+ .build()));
+
+ assertNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle));
+ }
+
+ void waitForResourceValue(final int expectedValue) throws TimeoutException {
+ final long timeOutDuration = 10000;
+ final long endTime = System.currentTimeMillis() + timeOutDuration;
+ final String resourceName = TEST_RESOURCE;
+ final int resourceId = mResources.getIdentifier(resourceName, "",
+ mContext.getPackageName());
+ int resourceValue = 0;
+ while (System.currentTimeMillis() < endTime) {
+ resourceValue = mResources.getInteger(resourceId);
+ if (resourceValue == expectedValue) {
+ return;
+ }
+ }
+ final String paths = TextUtils.join(",", mResources.getAssets().getApkPaths());
+ Log.w(TAG, "current paths: [" + paths + "]", new Throwable());
+ throw new TimeoutException("Timed out waiting for '" + resourceName + "' value to equal '"
+ + expectedValue + "': current value is '" + resourceValue + "'");
+ }
+}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
index 76c01a7e1125..3c0c1311a1aa 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
@@ -20,6 +20,7 @@ import static java.util.concurrent.TimeUnit.SECONDS;
import android.annotation.NonNull;
import android.content.Context;
+import android.content.om.OverlayIdentifier;
import android.content.om.OverlayManager;
import android.content.om.OverlayManagerTransaction;
import android.os.UserHandle;
@@ -32,14 +33,14 @@ import java.util.concurrent.FutureTask;
class LocalOverlayManager {
private static final long TIMEOUT = 30;
- public static void toggleOverlaysAndWait(@NonNull final String[] overlaysToEnable,
- @NonNull final String[] overlaysToDisable) throws Exception {
+ public static void toggleOverlaysAndWait(@NonNull final OverlayIdentifier[] overlaysToEnable,
+ @NonNull final OverlayIdentifier[] overlaysToDisable) throws Exception {
final int userId = UserHandle.myUserId();
OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder();
- for (String pkg : overlaysToEnable) {
+ for (OverlayIdentifier pkg : overlaysToEnable) {
builder.setEnabled(pkg, true, userId);
}
- for (String pkg : overlaysToDisable) {
+ for (OverlayIdentifier pkg : overlaysToDisable) {
builder.setEnabled(pkg, false, userId);
}
OverlayManagerTransaction transaction = builder.build();
@@ -48,7 +49,7 @@ class LocalOverlayManager {
FutureTask<Boolean> task = new FutureTask<>(() -> {
while (true) {
final String[] paths = ctx.getResources().getAssets().getApkPaths();
- if (arrayTailContains(paths, overlaysToEnable)
+ if (arrayTailContainsOverlays(paths, overlaysToEnable)
&& arrayDoesNotContain(paths, overlaysToDisable)) {
return true;
}
@@ -64,15 +65,15 @@ class LocalOverlayManager {
task.get(TIMEOUT, SECONDS);
}
- private static boolean arrayTailContains(@NonNull final String[] array,
- @NonNull final String[] substrings) {
- if (array.length < substrings.length) {
+ private static boolean arrayTailContainsOverlays(@NonNull final String[] array,
+ @NonNull final OverlayIdentifier[] overlays) {
+ if (array.length < overlays.length) {
return false;
}
- for (int i = 0; i < substrings.length; i++) {
- String a = array[array.length - substrings.length + i];
- String s = substrings[i];
- if (!a.contains(s)) {
+ for (int i = 0; i < overlays.length; i++) {
+ String a = array[array.length - overlays.length + i];
+ OverlayIdentifier s = overlays[i];
+ if (!a.contains(s.getPackageName())) {
return false;
}
}
@@ -80,10 +81,10 @@ class LocalOverlayManager {
}
private static boolean arrayDoesNotContain(@NonNull final String[] array,
- @NonNull final String[] substrings) {
- for (String s : substrings) {
+ @NonNull final OverlayIdentifier[] overlays) {
+ for (OverlayIdentifier s : overlays) {
for (String a : array) {
- if (a.contains(s)) {
+ if (a.contains(s.getPackageName())) {
return false;
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
index 636f4c8b59ca..8e4b9efafccd 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.content.Context;
+import android.content.om.OverlayIdentifier;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -58,9 +59,12 @@ public abstract class OverlayBaseTest {
static final int MODE_SINGLE_OVERLAY = 1;
static final int MODE_MULTIPLE_OVERLAYS = 2;
- static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one";
- static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two";
- static final String FRAMEWORK_OVERLAY_PKG = "com.android.overlaytest.framework";
+ static final OverlayIdentifier APP_OVERLAY_ONE_PKG =
+ new OverlayIdentifier("com.android.overlaytest.app_overlay_one");
+ static final OverlayIdentifier APP_OVERLAY_TWO_PKG =
+ new OverlayIdentifier("com.android.overlaytest.app_overlay_two");
+ static final OverlayIdentifier FRAMEWORK_OVERLAY_PKG =
+ new OverlayIdentifier("com.android.overlaytest.framework");
protected OverlayBaseTest(int mode) {
mMode = mode;
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java
index 0b4f5e227169..27d73429f171 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java
@@ -16,14 +16,19 @@
package com.android.overlaytest;
+import static com.android.overlaytest.OverlayBaseTest.APP_OVERLAY_ONE_PKG;
+import static com.android.overlaytest.OverlayBaseTest.APP_OVERLAY_TWO_PKG;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.testng.Assert.assertThrows;
import android.content.Context;
+import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.content.om.OverlayManager;
import android.content.om.OverlayManagerTransaction;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.UserHandle;
@@ -40,8 +45,6 @@ import java.util.List;
@RunWith(JUnit4.class)
@MediumTest
public class TransactionTest {
- static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one";
- static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two";
private Context mContext;
private Resources mResources;
@@ -58,8 +61,8 @@ public class TransactionTest {
mUserHandle = UserHandle.of(mUserId);
LocalOverlayManager.toggleOverlaysAndWait(
- new String[]{},
- new String[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
+ new OverlayIdentifier[]{},
+ new OverlayIdentifier[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
}
@Test
@@ -78,8 +81,8 @@ public class TransactionTest {
List<OverlayInfo> ois =
mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
assertEquals(ois.size(), 2);
- assertEquals(ois.get(0).packageName, APP_OVERLAY_ONE_PKG);
- assertEquals(ois.get(1).packageName, APP_OVERLAY_TWO_PKG);
+ assertEquals(ois.get(0).getOverlayIdentifier(), APP_OVERLAY_ONE_PKG);
+ assertEquals(ois.get(1).getOverlayIdentifier(), APP_OVERLAY_TWO_PKG);
OverlayManagerTransaction t2 = new OverlayManagerTransaction.Builder()
.setEnabled(APP_OVERLAY_TWO_PKG, true)
@@ -92,8 +95,8 @@ public class TransactionTest {
List<OverlayInfo> ois2 =
mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
assertEquals(ois2.size(), 2);
- assertEquals(ois2.get(0).packageName, APP_OVERLAY_TWO_PKG);
- assertEquals(ois2.get(1).packageName, APP_OVERLAY_ONE_PKG);
+ assertEquals(ois2.get(0).getOverlayIdentifier(), APP_OVERLAY_TWO_PKG);
+ assertEquals(ois2.get(1).getOverlayIdentifier(), APP_OVERLAY_ONE_PKG);
OverlayManagerTransaction t3 = new OverlayManagerTransaction.Builder()
.setEnabled(APP_OVERLAY_TWO_PKG, false)
@@ -105,8 +108,8 @@ public class TransactionTest {
List<OverlayInfo> ois3 =
mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
assertEquals(ois3.size(), 2);
- assertEquals(ois3.get(0).packageName, APP_OVERLAY_TWO_PKG);
- assertEquals(ois3.get(1).packageName, APP_OVERLAY_ONE_PKG);
+ assertEquals(ois3.get(0).getOverlayIdentifier(), APP_OVERLAY_TWO_PKG);
+ assertEquals(ois3.get(1).getOverlayIdentifier(), APP_OVERLAY_ONE_PKG);
}
@Test
@@ -116,7 +119,7 @@ public class TransactionTest {
OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
.setEnabled(APP_OVERLAY_ONE_PKG, true)
- .setEnabled("does-not-exist", true)
+ .setEnabled(new OverlayIdentifier("does-not-exist"), true)
.setEnabled(APP_OVERLAY_TWO_PKG, true)
.build();
assertThrows(SecurityException.class, () -> mOverlayManager.commit(t));
@@ -125,9 +128,10 @@ public class TransactionTest {
assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
}
- private void assertOverlayIsEnabled(final String packageName, boolean enabled, int userId) {
- final OverlayInfo oi = mOverlayManager.getOverlayInfo(packageName, UserHandle.of(userId));
+ private void assertOverlayIsEnabled(final OverlayIdentifier overlay, boolean enabled,
+ int userId) {
+ final OverlayInfo oi = mOverlayManager.getOverlayInfo(overlay, UserHandle.of(userId));
assertNotNull(oi);
- assertEquals(oi.isEnabled(), enabled);
+ assertEquals(enabled, oi.isEnabled());
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
index 420f755c5251..558720368cc6 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -16,6 +16,8 @@
package com.android.overlaytest;
+import android.content.om.OverlayIdentifier;
+
import androidx.test.filters.MediumTest;
import org.junit.BeforeClass;
@@ -32,7 +34,9 @@ public class WithMultipleOverlaysTest extends OverlayBaseTest {
@BeforeClass
public static void enableOverlay() throws Exception {
LocalOverlayManager.toggleOverlaysAndWait(
- new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG},
- new String[]{});
+ new OverlayIdentifier[]{
+ FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG
+ },
+ new OverlayIdentifier[]{});
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
index a86255e96388..d27543384f6c 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
@@ -16,6 +16,8 @@
package com.android.overlaytest;
+import android.content.om.OverlayIdentifier;
+
import androidx.test.filters.MediumTest;
import org.junit.BeforeClass;
@@ -32,7 +34,7 @@ public class WithOverlayTest extends OverlayBaseTest {
@BeforeClass
public static void enableOverlays() throws Exception {
LocalOverlayManager.toggleOverlaysAndWait(
- new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG},
- new String[]{APP_OVERLAY_TWO_PKG});
+ new OverlayIdentifier[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG},
+ new OverlayIdentifier[]{APP_OVERLAY_TWO_PKG});
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
index 51c411819b87..72cba8b87c8c 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -16,6 +16,8 @@
package com.android.overlaytest;
+import android.content.om.OverlayIdentifier;
+
import androidx.test.filters.MediumTest;
import org.junit.BeforeClass;
@@ -32,7 +34,9 @@ public class WithoutOverlayTest extends OverlayBaseTest {
@BeforeClass
public static void disableOverlays() throws Exception {
LocalOverlayManager.toggleOverlaysAndWait(
- new String[]{},
- new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
+ new OverlayIdentifier[]{},
+ new OverlayIdentifier[]{
+ FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG
+ });
}
}
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
index 847b4915530b..4ff59fa56a68 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "OverlayDeviceTests_AppOverlayOne",
sdk_version: "current",
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml
index 38e5fa18300c..926b1864d97c 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml
@@ -25,7 +25,6 @@
<item target="integer/matrix_100100" value="@integer/matrix_100100"/>
<item target="integer/matrix_100101" value="@integer/matrix_100101"/>
<item target="integer/matrix_100110" value="@integer/matrix_100110"/>
- <item target="integer/matrix_100110" value="@integer/matrix_100110"/>
<item target="integer/matrix_100111" value="@integer/matrix_100111"/>
<item target="integer/matrix_101000" value="@integer/matrix_101000"/>
<item target="integer/matrix_101001" value="@integer/matrix_101001"/>
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
index 7d5f82a71b44..1f5763ee60ae 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "OverlayDeviceTests_AppOverlayTwo",
sdk_version: "current",
diff --git a/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.bp b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.bp
index 50dbc6f054d3..178a02009dda 100644
--- a/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.bp
+++ b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "OverlayDeviceTests_FrameworkOverlay",
sdk_version: "current",
diff --git a/core/tests/overlaytests/host/Android.bp b/core/tests/overlaytests/host/Android.bp
index a2fcef56b780..e4c3fbec8afc 100644
--- a/core/tests/overlaytests/host/Android.bp
+++ b/core/tests/overlaytests/host/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test_host {
name: "OverlayHostTests",
srcs: ["src/**/*.java"],
diff --git a/core/tests/overlaytests/remount/Android.bp b/core/tests/overlaytests/remount/Android.bp
index 939334c94312..0a6b88bcbb63 100644
--- a/core/tests/overlaytests/remount/Android.bp
+++ b/core/tests/overlaytests/remount/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test_host {
name: "OverlayRemountedTest",
srcs: ["src/**/*.java"],
diff --git a/core/tests/overlaytests/remount/test-apps/Overlay/Android.bp b/core/tests/overlaytests/remount/test-apps/Overlay/Android.bp
index 032a0cdcb50d..a341c9aa88f7 100644
--- a/core/tests/overlaytests/remount/test-apps/Overlay/Android.bp
+++ b/core/tests/overlaytests/remount/test-apps/Overlay/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "OverlayRemountedTest_Overlay",
sdk_version: "current",
@@ -24,4 +33,4 @@ android_test_helper_app {
name: "OverlayRemountedTest_Overlay_SameCert",
certificate: ":rro-remounted-test-a",
sdk_version: "current",
-} \ No newline at end of file
+}
diff --git a/core/tests/overlaytests/remount/test-apps/SharedLibrary/Android.bp b/core/tests/overlaytests/remount/test-apps/SharedLibrary/Android.bp
index ffb0572c3fd0..bc88d61df77a 100644
--- a/core/tests/overlaytests/remount/test-apps/SharedLibrary/Android.bp
+++ b/core/tests/overlaytests/remount/test-apps/SharedLibrary/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "OverlayRemountedTest_SharedLibrary",
sdk_version: "current",
diff --git a/core/tests/overlaytests/remount/test-apps/SharedLibraryOverlay/Android.bp b/core/tests/overlaytests/remount/test-apps/SharedLibraryOverlay/Android.bp
index 0d29aec909d5..88a05ab8427b 100644
--- a/core/tests/overlaytests/remount/test-apps/SharedLibraryOverlay/Android.bp
+++ b/core/tests/overlaytests/remount/test-apps/SharedLibraryOverlay/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "OverlayRemountedTest_SharedLibraryOverlay",
sdk_version: "current",
diff --git a/core/tests/overlaytests/remount/test-apps/Target/Android.bp b/core/tests/overlaytests/remount/test-apps/Target/Android.bp
index e4b4eaa8d220..869a75f5225d 100644
--- a/core/tests/overlaytests/remount/test-apps/Target/Android.bp
+++ b/core/tests/overlaytests/remount/test-apps/Target/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "OverlayRemountedTest_Target",
sdk_version: "test_current",
diff --git a/core/tests/overlaytests/remount/test-apps/certs/Android.bp b/core/tests/overlaytests/remount/test-apps/certs/Android.bp
index 06114efc1249..f5d89bcabdad 100644
--- a/core/tests/overlaytests/remount/test-apps/certs/Android.bp
+++ b/core/tests/overlaytests/remount/test-apps/certs/Android.bp
@@ -13,6 +13,17 @@
// limitations under the License.
// development/tools/make_key rro-remounted-test-a '/CN=rro_test_a'
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-MIT
+ // SPDX-license-identifier-Unicode-DFS
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app_certificate {
name: "rro-remounted-test-a",
certificate: "rro-remounted-test-a",
diff --git a/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp b/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp
index e6ebd5ea76d8..42421ce0e9f3 100644
--- a/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp
+++ b/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp
@@ -12,6 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-MIT
+ // SPDX-license-identifier-Unicode-DFS
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
genrule {
name: "com.android.overlaytest.overlaid.pem",
out: ["com.android.overlaytest.overlaid.pem"],
diff --git a/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp b/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp
index 07f27ee55d39..0b52dcc4fb85 100644
--- a/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp
+++ b/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp
@@ -12,6 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-MIT
+ // SPDX-license-identifier-Unicode-DFS
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
genrule {
name: "com.android.overlaytest.overlay.pem",
out: ["com.android.overlaytest.overlay.pem"],
diff --git a/core/tests/packagemanagertests/Android.bp b/core/tests/packagemanagertests/Android.bp
index 6f39af8bd468..5ce71c902c7c 100644
--- a/core/tests/packagemanagertests/Android.bp
+++ b/core/tests/packagemanagertests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksCorePackageManagerTests",
// We only want this apk build for tests.
diff --git a/core/tests/privacytests/Android.bp b/core/tests/privacytests/Android.bp
index 7f5699287796..bc3dd810be8c 100644
--- a/core/tests/privacytests/Android.bp
+++ b/core/tests/privacytests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksPrivacyLibraryTests",
srcs: ["src/**/*.java"],
diff --git a/core/tests/screenshothelpertests/Android.bp b/core/tests/screenshothelpertests/Android.bp
index 3d54b68b7ddc..37af99c58d42 100644
--- a/core/tests/screenshothelpertests/Android.bp
+++ b/core/tests/screenshothelpertests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ScreenshotHelperTests",
@@ -25,4 +34,3 @@ android_test {
certificate: "platform",
}
-
diff --git a/core/tests/systemproperties/Android.bp b/core/tests/systemproperties/Android.bp
index 7ef1b9b8f74b..765ca3e5110a 100644
--- a/core/tests/systemproperties/Android.bp
+++ b/core/tests/systemproperties/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksCoreSystemPropertiesTests",
// Include all test java files.
diff --git a/core/tests/utillib/Android.bp b/core/tests/utillib/Android.bp
index 1f742c208ba2..d40d1d2bb6e2 100644
--- a/core/tests/utillib/Android.bp
+++ b/core/tests/utillib/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "frameworks-core-util-lib",
diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp
index a9b9c41d4719..72d6a2c67739 100644
--- a/core/tests/utiltests/Android.bp
+++ b/core/tests/utiltests/Android.bp
@@ -2,6 +2,15 @@
// Build FrameworksUtilTests package
//########################################################################
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksUtilTests",
diff --git a/core/tests/utiltests/jni/Android.bp b/core/tests/utiltests/jni/Android.bp
index 6b75471a358a..a9c695ed09f3 100644
--- a/core/tests/utiltests/jni/Android.bp
+++ b/core/tests/utiltests/jni/Android.bp
@@ -11,6 +11,15 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_shared {
name: "libmemoryintarraytest",
diff --git a/core/tests/uwbtests/Android.bp b/core/tests/uwbtests/Android.bp
index 8ee86f470c9e..31f446f7a60d 100644
--- a/core/tests/uwbtests/Android.bp
+++ b/core/tests/uwbtests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "UwbManagerTests",
static_libs: [
diff --git a/core/tests/uwbtests/src/android/uwb/AngleMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/AngleMeasurementTest.java
deleted file mode 100644
index 7769c28202f2..000000000000
--- a/core/tests/uwbtests/src/android/uwb/AngleMeasurementTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.uwb;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-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;
-
-/**
- * Test of {@link AngleMeasurement}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class AngleMeasurementTest {
- private static final double EPSILON = 0.00000000001;
-
- @Test
- public void testBuilder() {
- double radians = 0.1234;
- double errorRadians = 0.5678;
- double confidence = 0.5;
-
- AngleMeasurement.Builder builder = new AngleMeasurement.Builder();
- tryBuild(builder, false);
-
- builder.setRadians(radians);
- tryBuild(builder, false);
-
- builder.setErrorRadians(errorRadians);
- tryBuild(builder, false);
-
- builder.setConfidenceLevel(confidence);
- AngleMeasurement measurement = tryBuild(builder, true);
-
- assertEquals(measurement.getRadians(), radians, 0);
- assertEquals(measurement.getErrorRadians(), errorRadians, 0);
- assertEquals(measurement.getConfidenceLevel(), confidence, 0);
- }
-
- private AngleMeasurement tryBuild(AngleMeasurement.Builder builder, boolean expectSuccess) {
- AngleMeasurement measurement = null;
- try {
- measurement = builder.build();
- if (!expectSuccess) {
- fail("Expected AngleMeasurement.Builder.build() to fail, but it succeeded");
- }
- } catch (IllegalStateException e) {
- if (expectSuccess) {
- fail("Expected AngleMeasurement.Builder.build() to succeed, but it failed");
- }
- }
- return measurement;
- }
-
- @Test
- public void testParcel() {
- Parcel parcel = Parcel.obtain();
- AngleMeasurement measurement = UwbTestUtils.getAngleMeasurement();
- measurement.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- AngleMeasurement fromParcel = AngleMeasurement.CREATOR.createFromParcel(parcel);
- assertEquals(measurement, fromParcel);
- }
-}
diff --git a/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java
deleted file mode 100644
index 9394dec7f46f..000000000000
--- a/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.uwb;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-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;
-
-/**
- * Test of {@link AngleOfArrivalMeasurement}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class AngleOfArrivalMeasurementTest {
-
- @Test
- public void testBuilder() {
- AngleMeasurement azimuth = UwbTestUtils.getAngleMeasurement();
- AngleMeasurement altitude = UwbTestUtils.getAngleMeasurement();
-
- AngleOfArrivalMeasurement.Builder builder = new AngleOfArrivalMeasurement.Builder();
- tryBuild(builder, false);
-
- builder.setAltitude(altitude);
- tryBuild(builder, false);
-
- builder.setAzimuth(azimuth);
- AngleOfArrivalMeasurement measurement = tryBuild(builder, true);
-
- assertEquals(azimuth, measurement.getAzimuth());
- assertEquals(altitude, measurement.getAltitude());
- }
-
- private AngleMeasurement getAngleMeasurement(double radian, double error, double confidence) {
- return new AngleMeasurement.Builder()
- .setRadians(radian)
- .setErrorRadians(error)
- .setConfidenceLevel(confidence)
- .build();
- }
-
- private AngleOfArrivalMeasurement tryBuild(AngleOfArrivalMeasurement.Builder builder,
- boolean expectSuccess) {
- AngleOfArrivalMeasurement measurement = null;
- try {
- measurement = builder.build();
- if (!expectSuccess) {
- fail("Expected AngleOfArrivalMeasurement.Builder.build() to fail");
- }
- } catch (IllegalStateException e) {
- if (expectSuccess) {
- fail("Expected AngleOfArrivalMeasurement.Builder.build() to succeed");
- }
- }
- return measurement;
- }
-
- @Test
- public void testParcel() {
- Parcel parcel = Parcel.obtain();
- AngleOfArrivalMeasurement measurement = UwbTestUtils.getAngleOfArrivalMeasurement();
- measurement.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- AngleOfArrivalMeasurement fromParcel =
- AngleOfArrivalMeasurement.CREATOR.createFromParcel(parcel);
- assertEquals(measurement, fromParcel);
- }
-}
diff --git a/core/tests/uwbtests/src/android/uwb/DistanceMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/DistanceMeasurementTest.java
deleted file mode 100644
index 439c884723be..000000000000
--- a/core/tests/uwbtests/src/android/uwb/DistanceMeasurementTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.uwb;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-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;
-
-/**
- * Test of {@link DistanceMeasurement}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class DistanceMeasurementTest {
- private static final double EPSILON = 0.00000000001;
-
- @Test
- public void testBuilder() {
- double meters = 0.12;
- double error = 0.54;
- double confidence = 0.99;
-
- DistanceMeasurement.Builder builder = new DistanceMeasurement.Builder();
- tryBuild(builder, false);
-
- builder.setMeters(meters);
- tryBuild(builder, false);
-
- builder.setErrorMeters(error);
- tryBuild(builder, false);
-
- builder.setConfidenceLevel(confidence);
- DistanceMeasurement measurement = tryBuild(builder, true);
-
- assertEquals(meters, measurement.getMeters(), 0);
- assertEquals(error, measurement.getErrorMeters(), 0);
- assertEquals(confidence, measurement.getConfidenceLevel(), 0);
- }
-
- private DistanceMeasurement tryBuild(DistanceMeasurement.Builder builder,
- boolean expectSuccess) {
- DistanceMeasurement measurement = null;
- try {
- measurement = builder.build();
- if (!expectSuccess) {
- fail("Expected DistanceMeasurement.Builder.build() to fail");
- }
- } catch (IllegalStateException e) {
- if (expectSuccess) {
- fail("Expected DistanceMeasurement.Builder.build() to succeed");
- }
- }
- return measurement;
- }
-
- @Test
- public void testParcel() {
- Parcel parcel = Parcel.obtain();
- DistanceMeasurement measurement = UwbTestUtils.getDistanceMeasurement();
- measurement.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- DistanceMeasurement fromParcel =
- DistanceMeasurement.CREATOR.createFromParcel(parcel);
- assertEquals(measurement, fromParcel);
- }
-}
diff --git a/core/tests/uwbtests/src/android/uwb/RangingMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/RangingMeasurementTest.java
deleted file mode 100644
index edd4d08992ba..000000000000
--- a/core/tests/uwbtests/src/android/uwb/RangingMeasurementTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.uwb;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-import android.os.Parcel;
-import android.os.SystemClock;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test of {@link RangingMeasurement}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class RangingMeasurementTest {
-
- @Test
- public void testBuilder() {
- int status = RangingMeasurement.RANGING_STATUS_SUCCESS;
- UwbAddress address = UwbTestUtils.getUwbAddress(false);
- long time = SystemClock.elapsedRealtimeNanos();
- AngleOfArrivalMeasurement angleMeasurement = UwbTestUtils.getAngleOfArrivalMeasurement();
- DistanceMeasurement distanceMeasurement = UwbTestUtils.getDistanceMeasurement();
-
- RangingMeasurement.Builder builder = new RangingMeasurement.Builder();
-
- builder.setStatus(status);
- tryBuild(builder, false);
-
- builder.setElapsedRealtimeNanos(time);
- tryBuild(builder, false);
-
- builder.setAngleOfArrivalMeasurement(angleMeasurement);
- tryBuild(builder, false);
-
- builder.setDistanceMeasurement(distanceMeasurement);
- tryBuild(builder, false);
-
- builder.setRemoteDeviceAddress(address);
- RangingMeasurement measurement = tryBuild(builder, true);
-
- assertEquals(status, measurement.getStatus());
- assertEquals(address, measurement.getRemoteDeviceAddress());
- assertEquals(time, measurement.getElapsedRealtimeNanos());
- assertEquals(angleMeasurement, measurement.getAngleOfArrivalMeasurement());
- assertEquals(distanceMeasurement, measurement.getDistanceMeasurement());
- }
-
- private RangingMeasurement tryBuild(RangingMeasurement.Builder builder,
- boolean expectSuccess) {
- RangingMeasurement measurement = null;
- try {
- measurement = builder.build();
- if (!expectSuccess) {
- fail("Expected RangingMeasurement.Builder.build() to fail");
- }
- } catch (IllegalStateException e) {
- if (expectSuccess) {
- fail("Expected DistanceMeasurement.Builder.build() to succeed");
- }
- }
- return measurement;
- }
-
- @Test
- public void testParcel() {
- Parcel parcel = Parcel.obtain();
- RangingMeasurement measurement = UwbTestUtils.getRangingMeasurement();
- measurement.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- RangingMeasurement fromParcel = RangingMeasurement.CREATOR.createFromParcel(parcel);
- assertEquals(measurement, fromParcel);
- }
-}
diff --git a/core/tests/uwbtests/src/android/uwb/RangingReportTest.java b/core/tests/uwbtests/src/android/uwb/RangingReportTest.java
deleted file mode 100644
index 64c48ba4b6f4..000000000000
--- a/core/tests/uwbtests/src/android/uwb/RangingReportTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.uwb;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-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;
-
-import java.util.List;
-
-/**
- * Test of {@link RangingReport}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class RangingReportTest {
-
- @Test
- public void testBuilder() {
- List<RangingMeasurement> measurements = UwbTestUtils.getRangingMeasurements(5);
-
- RangingReport.Builder builder = new RangingReport.Builder();
- builder.addMeasurements(measurements);
- RangingReport report = tryBuild(builder, true);
- verifyMeasurementsEqual(measurements, report.getMeasurements());
-
-
- builder = new RangingReport.Builder();
- for (RangingMeasurement measurement : measurements) {
- builder.addMeasurement(measurement);
- }
- report = tryBuild(builder, true);
- verifyMeasurementsEqual(measurements, report.getMeasurements());
- }
-
- private void verifyMeasurementsEqual(List<RangingMeasurement> expected,
- List<RangingMeasurement> actual) {
- assertEquals(expected.size(), actual.size());
- for (int i = 0; i < expected.size(); i++) {
- assertEquals(expected.get(i), actual.get(i));
- }
- }
-
- private RangingReport tryBuild(RangingReport.Builder builder,
- boolean expectSuccess) {
- RangingReport report = null;
- try {
- report = builder.build();
- if (!expectSuccess) {
- fail("Expected RangingReport.Builder.build() to fail");
- }
- } catch (IllegalStateException e) {
- if (expectSuccess) {
- fail("Expected RangingReport.Builder.build() to succeed");
- }
- }
- return report;
- }
-
- @Test
- public void testParcel() {
- Parcel parcel = Parcel.obtain();
- RangingReport report = UwbTestUtils.getRangingReports(5);
- report.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- RangingReport fromParcel = RangingReport.CREATOR.createFromParcel(parcel);
- assertEquals(report, fromParcel);
- }
-}
diff --git a/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java b/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java
deleted file mode 100644
index e5eea26f5d11..000000000000
--- a/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java
+++ /dev/null
@@ -1,378 +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 android.uwb;
-
-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 static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.os.PersistableBundle;
-import android.os.RemoteException;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.util.concurrent.Executor;
-
-/**
- * Test of {@link RangingSession}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class RangingSessionTest {
- private static final Executor EXECUTOR = UwbTestUtils.getExecutor();
- private static final PersistableBundle PARAMS = new PersistableBundle();
- private static final @RangingSession.Callback.Reason int REASON =
- RangingSession.Callback.REASON_GENERIC_ERROR;
-
- @Test
- public void testOnRangingOpened_OnOpenSuccessCalled() {
- SessionHandle handle = new SessionHandle(123);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
- verifyOpenState(session, false);
-
- session.onRangingOpened();
- verifyOpenState(session, true);
-
- // Verify that the onOpenSuccess callback was invoked
- verify(callback, times(1)).onOpened(eq(session));
- verify(callback, times(0)).onClosed(anyInt(), any());
- }
-
- @Test
- public void testOnRangingOpened_CannotOpenClosedSession() {
- SessionHandle handle = new SessionHandle(123);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
-
- session.onRangingOpened();
- verifyOpenState(session, true);
- verify(callback, times(1)).onOpened(eq(session));
- verify(callback, times(0)).onClosed(anyInt(), any());
-
- session.onRangingClosed(REASON, PARAMS);
- verifyOpenState(session, false);
- verify(callback, times(1)).onOpened(eq(session));
- verify(callback, times(1)).onClosed(anyInt(), any());
-
- // Now invoke the ranging started callback and ensure the session remains closed
- session.onRangingOpened();
- verifyOpenState(session, false);
- verify(callback, times(1)).onOpened(eq(session));
- verify(callback, times(1)).onClosed(anyInt(), any());
- }
-
- @Test
- public void testOnRangingClosed_OnClosedCalledWhenSessionNotOpen() {
- SessionHandle handle = new SessionHandle(123);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
- verifyOpenState(session, false);
-
- session.onRangingClosed(REASON, PARAMS);
- verifyOpenState(session, false);
-
- // Verify that the onOpenSuccess callback was invoked
- verify(callback, times(0)).onOpened(eq(session));
- verify(callback, times(1)).onClosed(anyInt(), any());
- }
-
- @Test
- public void testOnRangingClosed_OnClosedCalled() {
- SessionHandle handle = new SessionHandle(123);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
- session.onRangingStarted(PARAMS);
- session.onRangingClosed(REASON, PARAMS);
- verify(callback, times(1)).onClosed(anyInt(), any());
-
- verifyOpenState(session, false);
- session.onRangingClosed(REASON, PARAMS);
- verify(callback, times(2)).onClosed(anyInt(), any());
- }
-
- @Test
- public void testOnRangingResult_OnReportReceivedCalled() {
- SessionHandle handle = new SessionHandle(123);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
- verifyOpenState(session, false);
-
- session.onRangingStarted(PARAMS);
- verifyOpenState(session, true);
-
- RangingReport report = UwbTestUtils.getRangingReports(1);
- session.onRangingResult(report);
- verify(callback, times(1)).onReportReceived(eq(report));
- }
-
- @Test
- public void testStart_CannotStartIfAlreadyStarted() throws RemoteException {
- SessionHandle handle = new SessionHandle(123);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
- doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any());
- session.onRangingOpened();
-
- session.start(PARAMS);
- verify(callback, times(1)).onStarted(any());
-
- // Calling start again should throw an illegal state
- verifyThrowIllegalState(() -> session.start(PARAMS));
- verify(callback, times(1)).onStarted(any());
- }
-
- @Test
- public void testStop_CannotStopIfAlreadyStopped() throws RemoteException {
- SessionHandle handle = new SessionHandle(123);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
- doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any());
- doAnswer(new StopAnswer(session)).when(adapter).stopRanging(any());
- session.onRangingOpened();
- session.start(PARAMS);
-
- verifyNoThrowIllegalState(session::stop);
- verify(callback, times(1)).onStopped();
-
- // Calling stop again should throw an illegal state
- verifyThrowIllegalState(session::stop);
- verify(callback, times(1)).onStopped();
- }
-
- @Test
- public void testReconfigure_OnlyWhenOpened() throws RemoteException {
- SessionHandle handle = new SessionHandle(123);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
- doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any());
- doAnswer(new ReconfigureAnswer(session)).when(adapter).reconfigureRanging(any(), any());
-
- verifyThrowIllegalState(() -> session.reconfigure(PARAMS));
- verify(callback, times(0)).onReconfigured(any());
- verifyOpenState(session, false);
-
- session.onRangingOpened();
- verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS));
- verify(callback, times(1)).onReconfigured(any());
- verifyOpenState(session, true);
-
- session.onRangingStarted(PARAMS);
- verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS));
- verify(callback, times(2)).onReconfigured(any());
- verifyOpenState(session, true);
-
- session.onRangingStopped();
- verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS));
- verify(callback, times(3)).onReconfigured(any());
- verifyOpenState(session, true);
-
-
- session.onRangingClosed(REASON, PARAMS);
- verifyThrowIllegalState(() -> session.reconfigure(PARAMS));
- verify(callback, times(3)).onReconfigured(any());
- verifyOpenState(session, false);
- }
-
- @Test
- public void testClose_NoCallbackUntilInvoked() throws RemoteException {
- SessionHandle handle = new SessionHandle(123);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
- session.onRangingOpened();
-
- // Calling close multiple times should invoke closeRanging until the session receives
- // the onClosed callback.
- int totalCallsBeforeOnRangingClosed = 3;
- for (int i = 1; i <= totalCallsBeforeOnRangingClosed; i++) {
- session.close();
- verifyOpenState(session, true);
- verify(adapter, times(i)).closeRanging(handle);
- verify(callback, times(0)).onClosed(anyInt(), any());
- }
-
- // After onClosed is invoked, then the adapter should no longer be called for each call to
- // the session's close.
- final int totalCallsAfterOnRangingClosed = 2;
- for (int i = 1; i <= totalCallsAfterOnRangingClosed; i++) {
- session.onRangingClosed(REASON, PARAMS);
- verifyOpenState(session, false);
- verify(adapter, times(totalCallsBeforeOnRangingClosed)).closeRanging(handle);
- verify(callback, times(i)).onClosed(anyInt(), any());
- }
- }
-
- @Test
- public void testClose_OnClosedCalled() throws RemoteException {
- SessionHandle handle = new SessionHandle(123);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
- doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any());
- session.onRangingOpened();
-
- session.close();
- verify(callback, times(1)).onClosed(anyInt(), any());
- }
-
- @Test
- public void testClose_CannotInteractFurther() throws RemoteException {
- SessionHandle handle = new SessionHandle(123);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
- doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any());
- session.close();
-
- verifyThrowIllegalState(() -> session.start(PARAMS));
- verifyThrowIllegalState(() -> session.reconfigure(PARAMS));
- verifyThrowIllegalState(() -> session.stop());
- verifyNoThrowIllegalState(() -> session.close());
- }
-
- @Test
- public void testOnRangingResult_OnReportReceivedCalledWhenOpen() {
- SessionHandle handle = new SessionHandle(123);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
-
- assertFalse(session.isOpen());
- session.onRangingStarted(PARAMS);
- assertTrue(session.isOpen());
-
- // Verify that the onReportReceived callback was invoked
- RangingReport report = UwbTestUtils.getRangingReports(1);
- session.onRangingResult(report);
- verify(callback, times(1)).onReportReceived(report);
- }
-
- @Test
- public void testOnRangingResult_OnReportReceivedNotCalledWhenNotOpen() {
- SessionHandle handle = new SessionHandle(123);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
-
- assertFalse(session.isOpen());
-
- // Verify that the onReportReceived callback was invoked
- RangingReport report = UwbTestUtils.getRangingReports(1);
- session.onRangingResult(report);
- verify(callback, times(0)).onReportReceived(report);
- }
-
- private void verifyOpenState(RangingSession session, boolean expected) {
- assertEquals(expected, session.isOpen());
- }
-
- private void verifyThrowIllegalState(Runnable runnable) {
- try {
- runnable.run();
- fail();
- } catch (IllegalStateException e) {
- // Pass
- }
- }
-
- private void verifyNoThrowIllegalState(Runnable runnable) {
- try {
- runnable.run();
- } catch (IllegalStateException e) {
- fail();
- }
- }
-
- abstract class AdapterAnswer implements Answer {
- protected RangingSession mSession;
-
- protected AdapterAnswer(RangingSession session) {
- mSession = session;
- }
- }
-
- class StartAnswer extends AdapterAnswer {
- StartAnswer(RangingSession session) {
- super(session);
- }
-
- @Override
- public Object answer(InvocationOnMock invocation) {
- mSession.onRangingStarted(PARAMS);
- return null;
- }
- }
-
- class ReconfigureAnswer extends AdapterAnswer {
- ReconfigureAnswer(RangingSession session) {
- super(session);
- }
-
- @Override
- public Object answer(InvocationOnMock invocation) {
- mSession.onRangingReconfigured(PARAMS);
- return null;
- }
- }
-
- class StopAnswer extends AdapterAnswer {
- StopAnswer(RangingSession session) {
- super(session);
- }
-
- @Override
- public Object answer(InvocationOnMock invocation) {
- mSession.onRangingStopped();
- return null;
- }
- }
-
- class CloseAnswer extends AdapterAnswer {
- CloseAnswer(RangingSession session) {
- super(session);
- }
-
- @Override
- public Object answer(InvocationOnMock invocation) {
- mSession.onRangingClosed(REASON, PARAMS);
- return null;
- }
- }
-}
diff --git a/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java b/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java
deleted file mode 100644
index 8b42ff7f62a7..000000000000
--- a/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.uwb;
-
-import static org.junit.Assert.assertEquals;
-
-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;
-
-/**
- * Test of {@link SessionHandle}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class SessionHandleTest {
-
- @Test
- public void testBasic() {
- int handleId = 12;
- SessionHandle handle = new SessionHandle(handleId);
- assertEquals(handle.getId(), handleId);
- }
-
- @Test
- public void testParcel() {
- Parcel parcel = Parcel.obtain();
- SessionHandle handle = new SessionHandle(10);
- handle.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- SessionHandle fromParcel = SessionHandle.CREATOR.createFromParcel(parcel);
- assertEquals(handle, fromParcel);
- }
-}
diff --git a/core/tests/uwbtests/src/android/uwb/UwbAddressTest.java b/core/tests/uwbtests/src/android/uwb/UwbAddressTest.java
deleted file mode 100644
index ccc88a9a5399..000000000000
--- a/core/tests/uwbtests/src/android/uwb/UwbAddressTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.uwb;
-
-import static org.junit.Assert.assertEquals;
-
-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;
-
-/**
- * Test of {@link UwbAddress}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class UwbAddressTest {
-
- @Test
- public void testFromBytes_Short() {
- runFromBytes(UwbAddress.SHORT_ADDRESS_BYTE_LENGTH);
- }
-
- @Test
- public void testFromBytes_Extended() {
- runFromBytes(UwbAddress.EXTENDED_ADDRESS_BYTE_LENGTH);
- }
-
- private void runFromBytes(int len) {
- byte[] addressBytes = getByteArray(len);
- UwbAddress address = UwbAddress.fromBytes(addressBytes);
- assertEquals(address.size(), len);
- assertEquals(addressBytes, address.toBytes());
- }
-
- private byte[] getByteArray(int len) {
- byte[] res = new byte[len];
- for (int i = 0; i < len; i++) {
- res[i] = (byte) i;
- }
- return res;
- }
-
- @Test
- public void testParcel_Short() {
- runParcel(true);
- }
-
- @Test
- public void testParcel_Extended() {
- runParcel(false);
- }
-
- private void runParcel(boolean useShortAddress) {
- Parcel parcel = Parcel.obtain();
- UwbAddress address = UwbTestUtils.getUwbAddress(useShortAddress);
- address.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- UwbAddress fromParcel = UwbAddress.CREATOR.createFromParcel(parcel);
- assertEquals(address, fromParcel);
- }
-}
diff --git a/core/tests/uwbtests/src/android/uwb/UwbManagerTest.java b/core/tests/uwbtests/src/android/uwb/UwbManagerTest.java
deleted file mode 100644
index 4983bed742fd..000000000000
--- a/core/tests/uwbtests/src/android/uwb/UwbManagerTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.uwb;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import android.content.Context;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test of {@link UwbManager}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class UwbManagerTest {
-
- public final Context mContext = InstrumentationRegistry.getContext();
-
- @Test
- public void testServiceAvailable() {
- UwbManager manager = mContext.getSystemService(UwbManager.class);
- if (UwbTestUtils.isUwbSupported(mContext)) {
- assertNotNull(manager);
- } else {
- assertNull(manager);
- }
- }
-}
diff --git a/core/xsd/Android.bp b/core/xsd/Android.bp
index 738f33076ac9..5387f85bffb2 100644
--- a/core/xsd/Android.bp
+++ b/core/xsd/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
xsd_config {
name: "permission",
srcs: ["permission.xsd"],
diff --git a/core/xsd/vts/Android.bp b/core/xsd/vts/Android.bp
index ca655f18149c..5d8407f584bc 100644
--- a/core/xsd/vts/Android.bp
+++ b/core/xsd/vts/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_test {
name: "vts_permission_validate_test",
srcs: [
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 201f649cde52..85b60f8cc4b0 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -15,6 +15,15 @@
// Sysconfig files
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
prebuilt_etc {
name: "framework-sysconfig.xml",
sub_dir: "sysconfig",
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index 65d3a012b129..549e074d297c 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -6,7 +6,6 @@ jeffv@google.com
jsharkey@android.com
jsharkey@google.com
lorenzo@google.com
-moltmann@google.com
svetoslavganov@android.com
svetoslavganov@google.com
toddke@android.com
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 41a6fea4f9de..f6d76855ca68 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -16,6 +16,15 @@
// Privapp permission whitelist files
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
prebuilt_etc {
name: "allowed_privapp_android.car.cluster.loggingrenderer",
sub_dir: "permissions",
diff --git a/data/etc/car/com.android.car.developeroptions.xml b/data/etc/car/com.android.car.developeroptions.xml
index cf0199bd1b85..c9405748d7de 100644
--- a/data/etc/car/com.android.car.developeroptions.xml
+++ b/data/etc/car/com.android.car.developeroptions.xml
@@ -26,6 +26,7 @@
<permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/>
<permission name="android.permission.DELETE_PACKAGES"/>
<permission name="android.permission.FORCE_STOP_PACKAGES"/>
+ <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
<permission name="android.permission.MANAGE_DEBUGGING"/>
<permission name="android.permission.MANAGE_FINGERPRINT"/>
@@ -40,10 +41,12 @@
<permission name="android.permission.MOVE_PACKAGE"/>
<permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
<permission name="android.permission.PACKAGE_USAGE_STATS"/>
+ <permission name="android.permission.READ_DREAM_STATE"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<permission name="android.permission.READ_SEARCH_INDEXABLES"/>
<permission name="android.permission.REBOOT"/>
<permission name="android.permission.REQUEST_NETWORK_SCORES"/>
+ <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/>
<permission name="android.permission.SET_TIME"/>
<permission name="android.permission.STATUS_BAR"/>
<permission name="android.permission.TETHER_PRIVILEGED"/>
diff --git a/data/etc/car/com.android.car.provision.xml b/data/etc/car/com.android.car.provision.xml
index 4fd9cae53bd7..42cfd3ce2558 100644
--- a/data/etc/car/com.android.car.provision.xml
+++ b/data/etc/car/com.android.car.provision.xml
@@ -17,6 +17,7 @@
<permissions>
<privapp-permissions package="com.android.car.provision">
<permission name="android.car.permission.CAR_POWERTRAIN"/>
+ <permission name="android.permission.DISPATCH_PROVISIONING_MESSAGE"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
<permission name="android.permission.MANAGE_USERS"/>
diff --git a/data/etc/car/com.android.car.xml b/data/etc/car/com.android.car.xml
index 19548bc404d1..48f6ab376c82 100644
--- a/data/etc/car/com.android.car.xml
+++ b/data/etc/car/com.android.car.xml
@@ -25,6 +25,7 @@
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.REBOOT"/>
<permission name="android.permission.READ_LOGS"/>
+ <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index 3a20a9cb1695..bd30d7a61517 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -27,8 +27,10 @@
<permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
<permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
<permission name="android.permission.LOCATION_HARDWARE"/>
+ <permission name="android.permission.LOCK_DEVICE"/>
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.MASTER_CLEAR"/>
<!-- use for CarServiceTest -->
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
<permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
@@ -40,9 +42,11 @@
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.READ_LOGS"/>
<permission name="android.permission.REBOOT"/>
+ <permission name="android.permission.RESET_PASSWORD"/>
<permission name="android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS"/>
<!-- use for CarServiceTest -->
<permission name="android.permission.SET_ACTIVITY_WATCHER"/>
+ <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
<!-- use for rotary fragment to enable/disable packages related to rotary -->
diff --git a/data/etc/com.android.emergency.xml b/data/etc/com.android.emergency.xml
index 734561ceeb01..2d6ae2ebfb0a 100644
--- a/data/etc/com.android.emergency.xml
+++ b/data/etc/com.android.emergency.xml
@@ -19,6 +19,9 @@
<!-- Required to place emergency calls from emergency info screen. -->
<permission name="android.permission.CALL_PRIVILEGED"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
+ <!-- Required to update emergency gesture settings -->
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/com.android.provision.xml b/data/etc/com.android.provision.xml
index d2ea0ec085d3..68f82985102c 100644
--- a/data/etc/com.android.provision.xml
+++ b/data/etc/com.android.provision.xml
@@ -17,7 +17,7 @@
<permissions>
<privapp-permissions package="com.android.provision">
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- <permissionn ame="android.permission.DISPATCH_PROVISIONING_MESSAGE"/>
+ <permission name="android.permission.DISPATCH_PROVISIONING_MESSAGE"/>
<permission name="android.permission.MASTER_CLEAR"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index e473c55ce010..3fdb0da80875 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -58,5 +58,6 @@
<permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
<permission name="android.permission.READ_DREAM_STATE"/>
<permission name="android.permission.READ_DREAM_SUPPRESSION"/>
+ <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index cdc61a1c5752..8fd5d804adc0 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -316,6 +316,8 @@ applications that come with the platform
<permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
<permission name="android.permission.ACCESS_LOWPAN_STATE"/>
<permission name="android.permission.BACKUP"/>
+ <!-- Needed for test only -->
+ <permission name="android.permission.BATTERY_PREDICTION"/>
<permission name="android.permission.BATTERY_STATS"/>
<permission name="android.permission.BIND_APPWIDGET"/>
<permission name="android.permission.CHANGE_APP_IDLE_STATE"/>
@@ -465,10 +467,19 @@ applications that come with the platform
<permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
<!-- Permission required for CTS test CarrierMessagingServiceWrapperTest -->
<permission name="android.permission.BIND_CARRIER_SERVICES"/>
+ <!-- Permission required for CTS test - MusicRecognitionManagerTest -->
+ <permission name="android.permission.MANAGE_MUSIC_RECOGNITION" />
<!-- Permission required for CTS test - CallLogTest -->
<permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
<permission name="android.permission.MODIFY_QUIET_MODE" />
+ <!-- Permission required for GTS test - GtsAssistIntentTestCases -->
+ <permission name="android.permission.MANAGE_SOUND_TRIGGER" />
+ <permission name="android.permission.CAPTURE_AUDIO_HOTWORD" />
+ <!-- Permission required for CTS test - CtsRebootReadinessTestCases -->
+ <permission name="android.permission.SIGNAL_REBOOT_READINESS" />
+ <!-- Permission required for CTS test - PeopleManagerTest -->
+ <permission name="android.permission.READ_PEOPLE_DATA" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
@@ -482,6 +493,8 @@ applications that come with the platform
<permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/>
<!-- Permissions required for quick settings tile -->
<permission name="android.permission.STATUS_BAR"/>
+ <!-- Permissions required to query Betterbug -->
+ <permission name="android.permission.QUERY_ALL_PACKAGES"/>
</privapp-permissions>
<privapp-permissions package="com.android.tv">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 222c9bdf2cb4..5633de348433 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -259,6 +259,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1834214907": {
+ "message": "createNonAppWindowAnimations()",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
"-1824578273": {
"message": "Reporting new frame to %s: %s",
"level": "VERBOSE",
@@ -811,6 +817,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
+ "-1153814764": {
+ "message": "onAnimationCancelled",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java"
+ },
"-1144293044": {
"message": "SURFACE SET FREEZE LAYER: %s",
"level": "INFO",
@@ -895,12 +907,6 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowSurfaceController.java"
},
- "-1088782910": {
- "message": "Translucent=%s Floating=%s ShowWallpaper=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STARTING_WINDOW",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-1076978367": {
"message": "thawRotation: mRotation=%d",
"level": "VERBOSE",
@@ -1087,12 +1093,6 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
},
- "-856025122": {
- "message": "SURFACE transparentRegionHint=%s: %s",
- "level": "INFO",
- "group": "WM_SHOW_TRANSACTIONS",
- "at": "com\/android\/server\/wm\/WindowManagerService.java"
- },
"-855366859": {
"message": " merging children in from %s: %s",
"level": "VERBOSE",
@@ -1261,12 +1261,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java"
},
- "-639305784": {
- "message": "Could not report config changes to the window token client.",
- "level": "WARN",
- "group": "WM_ERROR",
- "at": "com\/android\/server\/wm\/WindowToken.java"
- },
"-639217716": {
"message": "setFocusedApp %s displayId=%d Callers=%s",
"level": "INFO",
@@ -1417,12 +1411,6 @@
"group": "WM_DEBUG_KEEP_SCREEN_ON",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
- "-477481651": {
- "message": "SURFACE DESTROY PENDING: %s. %s",
- "level": "INFO",
- "group": "WM_SHOW_SURFACE_ALLOC",
- "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
- },
"-463348344": {
"message": "Removing and adding activity %s to root task at top callers=%s",
"level": "INFO",
@@ -1693,6 +1681,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/Task.java"
},
+ "-124316973": {
+ "message": "Translucent=%s Floating=%s ShowWallpaper=%s Disable=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-118786523": {
"message": "Resume failed; resetting state to %s: %s",
"level": "VERBOSE",
@@ -1903,12 +1897,6 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "123161180": {
- "message": "SEVER CHILDREN",
- "level": "INFO",
- "group": "WM_SHOW_TRANSACTIONS",
- "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
- },
"140319294": {
"message": "IME target changed within ActivityRecord",
"level": "DEBUG",
@@ -2569,12 +2557,6 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "838570988": {
- "message": "Could not report token removal to the window token client.",
- "level": "WARN",
- "group": "WM_ERROR",
- "at": "com\/android\/server\/wm\/WindowToken.java"
- },
"872933199": {
"message": "Changing focus from %s to %s displayId=%d Callers=%s",
"level": "DEBUG",
@@ -2851,12 +2833,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "1217926207": {
- "message": "Activity not running, resuming next.",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1219600119": {
"message": "addWindow: win=%s Callers=%s",
"level": "DEBUG",
@@ -3235,6 +3211,12 @@
"group": "WM_DEBUG_SYNC_ENGINE",
"at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
},
+ "1699269281": {
+ "message": "Don't organize or trigger events for untrusted displayId=%d",
+ "level": "WARN",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+ },
"1720229827": {
"message": "Creating animation bounds layer",
"level": "INFO",
@@ -3355,6 +3337,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/Task.java"
},
+ "1847414670": {
+ "message": "Activity not running or entered PiP, resuming next.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/Task.java"
+ },
"1853793312": {
"message": "Notify removed startingWindow %s",
"level": "VERBOSE",
diff --git a/data/fonts/Android.bp b/data/fonts/Android.bp
index ee5c5b0591cc..f90a74d939f4 100644
--- a/data/fonts/Android.bp
+++ b/data/fonts/Android.bp
@@ -12,6 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["frameworks_base_data_fonts_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_data_fonts_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
prebuilt_font {
name: "DroidSansMono.ttf",
src: "DroidSansMono.ttf",
diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk
index 7949c77ab558..6ae88007f0f5 100644
--- a/data/keyboards/Android.mk
+++ b/data/keyboards/Android.mk
@@ -22,6 +22,9 @@ include $(LOCAL_PATH)/common.mk
include $(CLEAR_VARS)
LOCAL_MODULE := validate_framework_keymaps
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
intermediates := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE),,COMMON)
LOCAL_BUILT_MODULE := $(intermediates)/stamp
diff --git a/drm/jni/Android.bp b/drm/jni/Android.bp
index 68757d86fb89..669f10994365 100644
--- a/drm/jni/Android.bp
+++ b/drm/jni/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_shared {
name: "libdrmframework_jni",
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index c60191c5b980..d1e94dfed4a8 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -1,4 +1,13 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_plugin {
name: "error_prone_android_framework",
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index bb795cd99e6e..63df0db99764 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -25,6 +25,8 @@ import android.graphics.fonts.FontVariationAxis;
import android.os.Build;
import android.os.LocaleList;
import android.text.FontConfig;
+import android.text.TextUtils;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
@@ -45,6 +47,27 @@ import java.util.regex.Pattern;
*/
public class FontListParser {
+ // XML constants for FontFamily.
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_LANG = "lang";
+ private static final String ATTR_VARIANT = "variant";
+ private static final String TAG_FONT = "font";
+ private static final String VARIANT_COMPACT = "compact";
+ private static final String VARIANT_ELEGANT = "elegant";
+
+ // XML constants for Font.
+ public static final String ATTR_INDEX = "index";
+ public static final String ATTR_WEIGHT = "weight";
+ public static final String ATTR_STYLE = "style";
+ public static final String ATTR_FALLBACK_FOR = "fallbackFor";
+ public static final String STYLE_ITALIC = "italic";
+ public static final String STYLE_NORMAL = "normal";
+ public static final String TAG_AXIS = "axis";
+
+ // XML constants for FontVariationAxis.
+ public static final String ATTR_TAG = "tag";
+ public static final String ATTR_STYLEVALUE = "stylevalue";
+
/* Parse fallback list (no names) */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException {
@@ -148,7 +171,7 @@ public class FontListParser {
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
final String tag = parser.getName();
- if (tag.equals("font")) {
+ if (tag.equals(TAG_FONT)) {
fonts.add(readFont(parser, fontDir, updatableFontMap));
} else {
skip(parser);
@@ -156,15 +179,41 @@ public class FontListParser {
}
int intVariant = FontConfig.FontFamily.VARIANT_DEFAULT;
if (variant != null) {
- if (variant.equals("compact")) {
+ if (variant.equals(VARIANT_COMPACT)) {
intVariant = FontConfig.FontFamily.VARIANT_COMPACT;
- } else if (variant.equals("elegant")) {
+ } else if (variant.equals(VARIANT_ELEGANT)) {
intVariant = FontConfig.FontFamily.VARIANT_ELEGANT;
}
}
return new FontConfig.FontFamily(fonts, name, LocaleList.forLanguageTags(lang), intVariant);
}
+ /**
+ * Write a family tag representing {@code fontFamily}. The tag should be started by the caller.
+ */
+ public static void writeFamily(TypedXmlSerializer out, FontConfig.FontFamily fontFamily)
+ throws IOException {
+ if (!TextUtils.isEmpty(fontFamily.getName())) {
+ out.attribute(null, ATTR_NAME, fontFamily.getName());
+ }
+ if (!fontFamily.getLocaleList().isEmpty()) {
+ out.attribute(null, ATTR_LANG, fontFamily.getLocaleList().toLanguageTags());
+ }
+ switch (fontFamily.getVariant()) {
+ case FontConfig.FontFamily.VARIANT_COMPACT:
+ out.attribute(null, ATTR_VARIANT, VARIANT_COMPACT);
+ break;
+ case FontConfig.FontFamily.VARIANT_ELEGANT:
+ out.attribute(null, ATTR_VARIANT, VARIANT_ELEGANT);
+ break;
+ }
+ for (FontConfig.Font font : fontFamily.getFontList()) {
+ out.startTag(null, TAG_FONT);
+ writeFont(out, font);
+ out.endTag(null, TAG_FONT);
+ }
+ }
+
/** Matches leading and trailing XML whitespace. */
private static final Pattern FILENAME_WHITESPACE_PATTERN =
Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$");
@@ -175,13 +224,13 @@ public class FontListParser {
@Nullable Map<String, File> updatableFontMap)
throws XmlPullParserException, IOException {
- String indexStr = parser.getAttributeValue(null, "index");
+ String indexStr = parser.getAttributeValue(null, ATTR_INDEX);
int index = indexStr == null ? 0 : Integer.parseInt(indexStr);
List<FontVariationAxis> axes = new ArrayList<>();
- String weightStr = parser.getAttributeValue(null, "weight");
- int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
- boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
- String fallbackFor = parser.getAttributeValue(null, "fallbackFor");
+ String weightStr = parser.getAttributeValue(null, ATTR_WEIGHT);
+ int weight = weightStr == null ? FontStyle.FONT_WEIGHT_NORMAL : Integer.parseInt(weightStr);
+ boolean isItalic = STYLE_ITALIC.equals(parser.getAttributeValue(null, ATTR_STYLE));
+ String fallbackFor = parser.getAttributeValue(null, ATTR_FALLBACK_FOR);
StringBuilder filename = new StringBuilder();
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() == XmlPullParser.TEXT) {
@@ -189,7 +238,7 @@ public class FontListParser {
}
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
- if (tag.equals("axis")) {
+ if (tag.equals(TAG_AXIS)) {
axes.add(readAxis(parser));
} else {
skip(parser);
@@ -237,14 +286,48 @@ public class FontListParser {
return null;
}
+ private static void writeFont(TypedXmlSerializer out, FontConfig.Font font)
+ throws IOException {
+ if (font.getTtcIndex() != 0) {
+ out.attributeInt(null, ATTR_INDEX, font.getTtcIndex());
+ }
+ if (font.getStyle().getWeight() != FontStyle.FONT_WEIGHT_NORMAL) {
+ out.attributeInt(null, ATTR_WEIGHT, font.getStyle().getWeight());
+ }
+ if (font.getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC) {
+ out.attribute(null, ATTR_STYLE, STYLE_ITALIC);
+ } else {
+ out.attribute(null, ATTR_STYLE, STYLE_NORMAL);
+ }
+ if (!TextUtils.isEmpty(font.getFontFamilyName())) {
+ out.attribute(null, ATTR_FALLBACK_FOR, font.getFontFamilyName());
+ }
+ out.text(font.getFile().getName());
+ FontVariationAxis[] axes =
+ FontVariationAxis.fromFontVariationSettings(font.getFontVariationSettings());
+ if (axes != null) {
+ for (FontVariationAxis axis : axes) {
+ out.startTag(null, TAG_AXIS);
+ writeAxis(out, axis);
+ out.endTag(null, TAG_AXIS);
+ }
+ }
+ }
+
private static FontVariationAxis readAxis(XmlPullParser parser)
throws XmlPullParserException, IOException {
- String tagStr = parser.getAttributeValue(null, "tag");
- String styleValueStr = parser.getAttributeValue(null, "stylevalue");
+ String tagStr = parser.getAttributeValue(null, ATTR_TAG);
+ String styleValueStr = parser.getAttributeValue(null, ATTR_STYLEVALUE);
skip(parser); // axis tag is empty, ignore any contents and consume end tag
return new FontVariationAxis(tagStr, Float.parseFloat(styleValueStr));
}
+ private static void writeAxis(TypedXmlSerializer out, FontVariationAxis axis)
+ throws IOException {
+ out.attribute(null, ATTR_TAG, axis.getTag());
+ out.attributeFloat(null, ATTR_STYLEVALUE, axis.getStyleValue());
+ }
+
/**
* Reads alias elements
*/
@@ -255,7 +338,7 @@ public class FontListParser {
String weightStr = parser.getAttributeValue(null, "weight");
int weight;
if (weightStr == null) {
- weight = 400;
+ weight = FontStyle.FONT_WEIGHT_NORMAL;
} else {
weight = Integer.parseInt(weightStr);
}
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index cb4dd9e8cacd..88cf96a9eb4e 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -39,6 +39,7 @@ import android.view.IGraphicsStatsCallback;
import android.view.NativeVectorDrawableAnimator;
import android.view.PixelCopy;
import android.view.Surface;
+import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.animation.AnimationUtils;
@@ -46,7 +47,6 @@ import java.io.File;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.stream.Stream;
@@ -315,6 +315,16 @@ public class HardwareRenderer {
}
/**
+ * Sets the SurfaceControl to be used internally inside render thread
+ * @hide
+ * @param surfaceControl The surface control to pass to render thread in hwui.
+ * If null, any previous references held in render thread will be discarded.
+ */
+ public void setSurfaceControl(@Nullable SurfaceControl surfaceControl) {
+ nSetSurfaceControl(mNativeProxy, surfaceControl != null ? surfaceControl.mNativeObject : 0);
+ }
+
+ /**
* Sets the parameters that can be used to control a render request for a
* {@link HardwareRenderer}. This is not thread-safe and must not be held on to for longer
* than a single frame request.
@@ -1148,24 +1158,14 @@ public class HardwareRenderer {
// Default to SRGB if the display doesn't support wide color
.orElse(Dataspace.SRGB);
- float maxRefreshRate =
- (float) Arrays.stream(display.getSupportedModes())
- .mapToDouble(Mode::getRefreshRate)
- .max()
- .orElseGet(() -> {
- Log.i(LOG_TAG, "Failed to find the maximum display refresh rate");
- // Assume that the max refresh rate is 60hz if we can't find one.
- return 60.0;
- });
// Grab the physical screen dimensions from the active display mode
// Strictly speaking the screen resolution may not always be constant - it is for
// sizing the font cache for the underlying rendering thread. Since it's a
// heuristic we don't need to be always 100% correct.
Mode activeMode = display.getMode();
nInitDisplayInfo(activeMode.getPhysicalWidth(), activeMode.getPhysicalHeight(),
- display.getRefreshRate(), maxRefreshRate,
- wideColorDataspace.mNativeDataspace, display.getAppVsyncOffsetNanos(),
- display.getPresentationDeadlineNanos());
+ display.getRefreshRate(), wideColorDataspace.mNativeDataspace,
+ display.getAppVsyncOffsetNanos(), display.getPresentationDeadlineNanos());
// Defensively clear out the context
mContext = null;
@@ -1227,6 +1227,8 @@ public class HardwareRenderer {
private static native void nSetSurface(long nativeProxy, Surface window, boolean discardBuffer);
+ private static native void nSetSurfaceControl(long nativeProxy, long nativeSurfaceControl);
+
private static native boolean nPause(long nativeProxy);
private static native void nSetStopped(long nativeProxy, boolean stopped);
@@ -1324,6 +1326,5 @@ public class HardwareRenderer {
private static native void nSetDisplayDensityDpi(int densityDpi);
private static native void nInitDisplayInfo(int width, int height, float refreshRate,
- float maxRefreshRate, int wideColorDataspace, long appVsyncOffsetNanos,
- long presentationDeadlineNanos);
+ int wideColorDataspace, long appVsyncOffsetNanos, long presentationDeadlineNanos);
}
diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java
index cf2f970e3d58..25f76f6f6da7 100644
--- a/graphics/java/android/graphics/Point.java
+++ b/graphics/java/android/graphics/Point.java
@@ -17,6 +17,7 @@
package android.graphics;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -96,6 +97,27 @@ public class Point implements Parcelable {
}
/**
+ * @return Returns a {@link String} that represents this point which can be parsed with
+ * {@link #unflattenFromString(String)}.
+ * @hide
+ */
+ @NonNull
+ public String flattenToString() {
+ return x + "x" + y;
+ }
+
+ /**
+ * @return Returns a {@link Point} from a short string created from {@link #flattenToString()}.
+ * @hide
+ */
+ @Nullable
+ public static Point unflattenFromString(String s) throws NumberFormatException {
+ final int sep_ix = s.indexOf("x");
+ return new Point(Integer.parseInt(s.substring(0, sep_ix)),
+ Integer.parseInt(s.substring(sep_ix + 1)));
+ }
+
+ /**
* Parcelable interface methods
*/
@Override
diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java
index 496e4707dbff..ad4c3fe86175 100644
--- a/graphics/java/android/graphics/RenderEffect.java
+++ b/graphics/java/android/graphics/RenderEffect.java
@@ -190,7 +190,7 @@ public final class RenderEffect {
}
/**
- * Create a filter that applies the color filter to the provided RenderEffect
+ * Create a {@link RenderEffect} that applies the color filter to the provided RenderEffect
*
* @param colorFilter ColorFilter applied to the content in the input RenderEffect
* @param renderEffect Source to be transformed by the specified {@link ColorFilter}
@@ -209,7 +209,7 @@ public final class RenderEffect {
}
/**
- * Create a filter that applies the color filter to the contents of the
+ * Create a {@link RenderEffect} that applies the color filter to the contents of the
* {@link android.graphics.RenderNode} that this RenderEffect is installed on
* @param colorFilter ColorFilter applied to the content in the input RenderEffect
*/
@@ -224,7 +224,7 @@ public final class RenderEffect {
}
/**
- * {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances
+ * Create a {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances
* combined by the specified {@link BlendMode}
*
* @param dst The Dst pixels used in blending
@@ -248,8 +248,8 @@ public final class RenderEffect {
}
/**
- * Create a filter that composes 'inner' with 'outer', such that the results of 'inner' are
- * treated as the source bitmap passed to 'outer', i.e.
+ * Create a {@link RenderEffect} that composes 'inner' with 'outer', such that the results of
+ * 'inner' are treated as the source bitmap passed to 'outer', i.e.
*
* <pre>
* {@code
@@ -278,6 +278,18 @@ public final class RenderEffect {
);
}
+ /**
+ * Create a {@link RenderEffect} that renders the contents of the input {@link Shader}.
+ * This is useful to create an input for other {@link RenderEffect} types such as
+ * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)}
+ * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} or
+ * {@link RenderEffect#createColorFilterEffect(ColorFilter, RenderEffect)}.
+ */
+ @NonNull
+ public static RenderEffect createShaderEffect(@NonNull Shader shader) {
+ return new RenderEffect(nativeCreateShaderEffect(shader.getNativeInstance()));
+ }
+
private final long mNativeRenderEffect;
/* only constructed from static factory methods */
@@ -305,5 +317,6 @@ public final class RenderEffect {
private static native long nativeCreateColorFilterEffect(long colorFilter, long nativeInput);
private static native long nativeCreateBlendModeEffect(long dst, long src, int blendmode);
private static native long nativeCreateChainEffect(long outer, long inner);
+ private static native long nativeCreateShaderEffect(long shader);
private static native long nativeGetFinalizer();
}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 35e6b8595c2a..32c777cf498c 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1346,6 +1346,19 @@ public class Typeface {
}
}
+ static {
+ // Preload Roboto-Regular.ttf in Zygote for improving app launch performance.
+ // TODO: add new attribute to fonts.xml to preload fonts in Zygote.
+ preloadFontFile("/system/fonts/Roboto-Regular.ttf");
+ }
+
+ private static void preloadFontFile(String filePath) {
+ File file = new File(filePath);
+ if (file.exists()) {
+ nativeWarmUpCache(filePath);
+ }
+ }
+
/** @hide */
@VisibleForTesting
public static void destroySystemFontMap() {
@@ -1414,6 +1427,16 @@ public class Typeface {
return Arrays.binarySearch(mSupportedAxes, axis) >= 0;
}
+ /** @hide */
+ public List<FontFamily> getFallback() {
+ ArrayList<FontFamily> families = new ArrayList<>();
+ int familySize = nativeGetFamilySize(native_instance);
+ for (int i = 0; i < familySize; ++i) {
+ families.add(new FontFamily(nativeGetFamily(native_instance, i)));
+ }
+ return families;
+ }
+
private static native long nativeCreateFromTypeface(long native_instance, int style);
private static native long nativeCreateFromTypefaceWithExactStyle(
long native_instance, int weight, boolean italic);
@@ -1439,6 +1462,13 @@ public class Typeface {
@CriticalNative
private static native long nativeGetReleaseFunc();
+ @CriticalNative
+ private static native int nativeGetFamilySize(long naitvePtr);
+
+ @CriticalNative
+ private static native long nativeGetFamily(long nativePtr, int index);
+
+
private static native void nativeRegisterGenericFamily(String str, long nativePtr);
private static native int nativeWriteTypefaces(
@@ -1447,4 +1477,6 @@ public class Typeface {
private static native @Nullable long[] nativeReadTypefaces(@NonNull ByteBuffer buffer);
private static native void nativeForceSetStaticFinalField(String fieldName, Typeface typeface);
+
+ private static native void nativeWarmUpCache(String fileName);
}
diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
new file mode 100644
index 000000000000..80f65f919fa6
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable;
+
+import android.animation.Animator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
+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.LinearInterpolator;
+
+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 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 Consumer<RippleAnimationSession> mOnSessionEnd;
+ private 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);
+
+ 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();
+ }
+
+ @NonNull RippleAnimationSession enter(Canvas canvas) {
+ if (isHwAccelerated(canvas)) {
+ enterHardware((RecordingCanvas) canvas);
+ } else {
+ enterSoftware();
+ }
+ mStartTime = System.nanoTime();
+ return this;
+ }
+
+ @NonNull RippleAnimationSession exit(Canvas canvas) {
+ if (isHwAccelerated(canvas)) exitHardware((RecordingCanvas) canvas);
+ else exitSoftware();
+ return this;
+ }
+
+ private void onAnimationEnd(Animator anim) {
+ mActiveAnimations.remove(anim);
+ }
+
+ @NonNull RippleAnimationSession setOnSessionEnd(
+ @Nullable Consumer<RippleAnimationSession> onSessionEnd) {
+ mOnSessionEnd = onSessionEnd;
+ return this;
+ }
+
+ RippleAnimationSession setOnAnimationUpdated(@Nullable Runnable run) {
+ mOnUpdate = run;
+ mProperties.setOnChange(mOnUpdate);
+ return this;
+ }
+
+ private boolean isHwAccelerated(Canvas canvas) {
+ return canvas.isHardwareAccelerated() && !mForceSoftware;
+ }
+
+ private void exitSoftware() {
+ ValueAnimator expand = ValueAnimator.ofFloat(.5f, 1f);
+ expand.setDuration(EXIT_ANIM_DURATION);
+ expand.setStartDelay(computeDelay());
+ expand.addUpdateListener(updatedAnimation -> {
+ notifyUpdate();
+ mProperties.getShader().setProgress((Float) expand.getAnimatedValue());
+ });
+ expand.addListener(new AnimatorListener(this) {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ 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);
+ }
+ private void notifyUpdate() {
+ Runnable onUpdate = mOnUpdate;
+ if (onUpdate != null) onUpdate.run();
+ }
+
+ RippleAnimationSession setForceSoftwareAnimation(boolean forceSw) {
+ mForceSoftware = forceSw;
+ return this;
+ }
+
+
+ private void exitHardware(RecordingCanvas canvas) {
+ AnimationProperties<CanvasProperty<Float>, CanvasProperty<Paint>>
+ props = getCanvasProperties();
+ RenderNodeAnimator exit =
+ new RenderNodeAnimator(props.getProgress(), 1f);
+ exit.setDuration(EXIT_ANIM_DURATION);
+ exit.addListener(new AnimatorListener(this) {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ Consumer<RippleAnimationSession> onEnd = mOnSessionEnd;
+ if (onEnd != null) onEnd.accept(RippleAnimationSession.this);
+ }
+ });
+ exit.setTarget(canvas);
+ exit.setInterpolator(DECELERATE_INTERPOLATOR);
+
+ long delay = computeDelay();
+ exit.setStartDelay(delay);
+ exit.start();
+ mActiveAnimations.add(exit);
+ }
+
+ private void enterHardware(RecordingCanvas can) {
+ AnimationProperties<CanvasProperty<Float>, CanvasProperty<Paint>>
+ props = getCanvasProperties();
+ RenderNodeAnimator expand =
+ new RenderNodeAnimator(props.getProgress(), .5f);
+ expand.setTarget(can);
+ expand.setDuration(ENTER_ANIM_DURATION);
+ expand.addListener(new AnimatorListener(this));
+ expand.setInterpolator(LINEAR_INTERPOLATOR);
+ expand.start();
+ mActiveAnimations.add(expand);
+ }
+
+ private void enterSoftware() {
+ ValueAnimator expand = ValueAnimator.ofFloat(0f, 0.5f);
+ expand.addUpdateListener(updatedAnimation -> {
+ notifyUpdate();
+ mProperties.getShader().setProgress((Float) expand.getAnimatedValue());
+ });
+ expand.addListener(new AnimatorListener(this));
+ expand.setInterpolator(LINEAR_INTERPOLATOR);
+ expand.start();
+ mActiveAnimations.add(expand);
+ }
+
+ @NonNull AnimationProperties<Float, Paint> getProperties() {
+ return mProperties;
+ }
+
+ @NonNull AnimationProperties getCanvasProperties() {
+ if (mCanvasProperties == null) {
+ mCanvasProperties = new AnimationProperties<>(
+ CanvasProperty.createFloat(mProperties.getX()),
+ CanvasProperty.createFloat(mProperties.getY()),
+ CanvasProperty.createFloat(mProperties.getMaxRadius()),
+ CanvasProperty.createPaint(mProperties.getPaint()),
+ CanvasProperty.createFloat(mProperties.getProgress()),
+ mProperties.getShader());
+ }
+ return mCanvasProperties;
+ }
+
+ private static class AnimatorListener implements Animator.AnimatorListener {
+ private final RippleAnimationSession mSession;
+
+ AnimatorListener(RippleAnimationSession session) {
+ mSession = session;
+ }
+ @Override
+ public void onAnimationStart(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mSession.onAnimationEnd(animation);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
+ }
+
+ static class AnimationProperties<FloatType, PaintType> {
+ private final FloatType mY;
+ private FloatType mProgress;
+ private 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;
+ }
+
+ AnimationProperties(FloatType x, FloatType y, FloatType maxRadius,
+ PaintType paint, FloatType progress, RippleShader shader) {
+ mY = y;
+ mX = x;
+ mMaxRadius = maxRadius;
+ mPaint = paint;
+ mShader = shader;
+ mProgress = progress;
+ }
+
+ FloatType getProgress() {
+ return mProgress;
+ }
+
+ FloatType getX() {
+ return mX;
+ }
+
+ FloatType getY() {
+ return mY;
+ }
+
+ FloatType getMaxRadius() {
+ return mMaxRadius;
+ }
+
+ PaintType getPaint() {
+ return mPaint;
+ }
+
+ RippleShader getShader() {
+ return mShader;
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index bab80cee2b26..5024875aab3c 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -16,6 +16,14 @@
package android.graphics.drawable;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.animation.ValueAnimator;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -27,17 +35,21 @@ import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
import android.graphics.Color;
+import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
+import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.Shader;
import android.os.Build;
import android.util.AttributeSet;
+import android.view.animation.LinearInterpolator;
import com.android.internal.R;
@@ -45,6 +57,9 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.ArrayList;
import java.util.Arrays;
/**
@@ -94,6 +109,17 @@ 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 {
/**
@@ -102,6 +128,29 @@ public class RippleDrawable extends LayerDrawable {
*/
public static final int RADIUS_AUTO = -1;
+ /**
+ * Ripple style where a solid circle is drawn. This is also the default style
+ * @see #setRippleStyle(int)
+ */
+ 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)
+ */
+ public static final int STYLE_PATTERNED = 1;
+
+ /**
+ * Ripple drawing style
+ * @hide
+ */
+ @Retention(SOURCE)
+ @Target({PARAMETER, METHOD, LOCAL_VARIABLE, FIELD})
+ @IntDef({STYLE_SOLID, STYLE_PATTERNED})
+ public @interface RippleStyle {
+ }
+
+ private static final int BACKGROUND_OPACITY_DURATION = 80;
private static final int MASK_UNKNOWN = -1;
private static final int MASK_NONE = 0;
private static final int MASK_CONTENT = 1;
@@ -109,6 +158,7 @@ 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();
private final Rect mTempRect = new Rect();
@@ -172,6 +222,14 @@ public class RippleDrawable extends LayerDrawable {
*/
private boolean mForceSoftware;
+ // Patterned
+ private float mTargetBackgroundOpacity;
+ private ValueAnimator mBackgroundAnimation;
+ private float mBackgroundOpacity;
+ private boolean mRunBackgroundAnimation;
+ private boolean mExitingAnimation;
+ private ArrayList<RippleAnimationSession> mRunningAnimations = new ArrayList<>();
+
/**
* Constructor used for drawable inflation.
*/
@@ -235,7 +293,7 @@ public class RippleDrawable extends LayerDrawable {
Arrays.fill(ripples, 0, count, null);
}
mExitingRipplesCount = 0;
-
+ mExitingAnimation = true;
// Always draw an additional "clean" frame after canceling animations.
invalidateSelf(false);
}
@@ -276,21 +334,37 @@ public class RippleDrawable extends LayerDrawable {
private void setRippleActive(boolean active) {
if (mRippleActive != active) {
mRippleActive = active;
+ }
+ if (mState.mRippleStyle == STYLE_SOLID) {
if (active) {
tryRippleEnter();
} else {
tryRippleExit();
}
+ } else {
+ if (active) {
+ startPatternedAnimation();
+ } else {
+ exitPatternedAnimation();
+ }
}
}
private void setBackgroundActive(boolean hovered, boolean focused, boolean pressed) {
- if (mBackground == null && (hovered || focused)) {
- mBackground = new RippleBackground(this, mHotspotBounds, isBounded());
- mBackground.setup(mState.mMaxRadius, mDensity);
- }
- if (mBackground != null) {
- mBackground.setState(focused, hovered, pressed);
+ if (mState.mRippleStyle == STYLE_SOLID) {
+ if (mBackground == null && (hovered || focused)) {
+ mBackground = new RippleBackground(this, mHotspotBounds, isBounded());
+ mBackground.setup(mState.mMaxRadius, mDensity);
+ }
+ if (mBackground != null) {
+ mBackground.setState(focused, hovered, pressed);
+ }
+ } else {
+ if (focused || hovered) {
+ enterPatternedBackgroundAnimation(focused, hovered);
+ } else {
+ exitPatternedBackgroundAnimation();
+ }
}
}
@@ -317,6 +391,9 @@ public class RippleDrawable extends LayerDrawable {
mRipple.onBoundsChange();
}
+ mState.mMaxRadius = mState.mMaxRadius <= 0 && mState.mRippleStyle != STYLE_SOLID
+ ? (int) computeRadius()
+ : mState.mMaxRadius;
invalidateSelf();
}
@@ -330,7 +407,11 @@ public class RippleDrawable extends LayerDrawable {
// If we just became visible, ensure the background and ripple
// visibilities are consistent with their internal states.
if (mRippleActive) {
- tryRippleEnter();
+ if (mState.mRippleStyle == STYLE_SOLID) {
+ tryRippleEnter();
+ } else {
+ invalidateSelf();
+ }
}
// Skip animations, just show the correct final states.
@@ -489,6 +570,8 @@ 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);
}
private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
@@ -535,9 +618,9 @@ public class RippleDrawable extends LayerDrawable {
@Override
public void setHotspot(float x, float y) {
+ mPendingX = x;
+ mPendingY = y;
if (mRipple == null || mBackground == null) {
- mPendingX = x;
- mPendingY = y;
mHasPending = true;
}
@@ -665,6 +748,14 @@ public class RippleDrawable extends LayerDrawable {
*/
@Override
public void draw(@NonNull Canvas canvas) {
+ if (mState.mRippleStyle == STYLE_SOLID) {
+ drawSolid(canvas);
+ } else {
+ drawPatterned(canvas);
+ }
+ }
+
+ private void drawSolid(Canvas canvas) {
pruneRipples();
// Clip to the dirty bounds, which will be the drawable bounds if we
@@ -681,6 +772,178 @@ public class RippleDrawable extends LayerDrawable {
canvas.restoreToCount(saveCount);
}
+ private void exitPatternedBackgroundAnimation() {
+ mTargetBackgroundOpacity = 0;
+ if (mBackgroundAnimation != null) mBackgroundAnimation.cancel();
+ // after cancel
+ mRunBackgroundAnimation = true;
+ invalidateSelf(false);
+ }
+
+ private void startPatternedAnimation() {
+ mRippleActive = true;
+ invalidateSelf(false);
+ }
+
+ private void exitPatternedAnimation() {
+ mExitingAnimation = true;
+ invalidateSelf(false);
+ }
+
+ private void enterPatternedBackgroundAnimation(boolean focused, boolean hovered) {
+ mBackgroundOpacity = 0;
+ mTargetBackgroundOpacity = focused ? .6f : hovered ? .2f : 0f;
+ if (mBackgroundAnimation != null) mBackgroundAnimation.cancel();
+ // after cancel
+ mRunBackgroundAnimation = true;
+ invalidateSelf(false);
+ }
+
+ private void startBackgroundAnimation() {
+ mRunBackgroundAnimation = false;
+ mBackgroundAnimation = ValueAnimator.ofFloat(mBackgroundOpacity, mTargetBackgroundOpacity);
+ mBackgroundAnimation.setInterpolator(LINEAR_INTERPOLATOR);
+ mBackgroundAnimation.setDuration(BACKGROUND_OPACITY_DURATION);
+ mBackgroundAnimation.addUpdateListener(update -> {
+ mBackgroundOpacity = (float) update.getAnimatedValue();
+ invalidateSelf(false);
+ });
+ mBackgroundAnimation.start();
+ }
+
+ private void drawPatterned(@NonNull Canvas canvas) {
+ final Rect bounds = getBounds();
+ final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+ boolean useCanvasProps = shouldUseCanvasProps(canvas);
+ boolean changedHotspotBounds = !bounds.equals(mHotspotBounds);
+ if (isBounded()) {
+ canvas.clipRect(mHotspotBounds);
+ }
+ float x, y;
+ if (changedHotspotBounds) {
+ x = mHotspotBounds.exactCenterX();
+ y = mHotspotBounds.exactCenterY();
+ useCanvasProps = false;
+ } else {
+ x = mPendingX;
+ y = mPendingY;
+ }
+ boolean shouldAnimate = mRippleActive;
+ boolean shouldExit = mExitingAnimation;
+ mRippleActive = false;
+ mExitingAnimation = false;
+ getRipplePaint();
+ drawContent(canvas);
+ drawPatternedBackground(canvas);
+ if (shouldAnimate && mRunningAnimations.size() <= MAX_RIPPLES) {
+ RippleAnimationSession.AnimationProperties<Float, Paint> properties =
+ createAnimationProperties(x, y);
+ mRunningAnimations.add(new RippleAnimationSession(properties, !useCanvasProps)
+ .setOnAnimationUpdated(useCanvasProps ? null : () -> invalidateSelf(false))
+ .setOnSessionEnd(session -> {
+ mRunningAnimations.remove(session);
+ })
+ .setForceSoftwareAnimation(!useCanvasProps)
+ .enter(canvas));
+ }
+ if (shouldExit) {
+ for (int i = 0; i < mRunningAnimations.size(); i++) {
+ RippleAnimationSession s = mRunningAnimations.get(i);
+ s.exit(canvas);
+ }
+ }
+ for (int i = 0; i < mRunningAnimations.size(); i++) {
+ RippleAnimationSession s = mRunningAnimations.get(i);
+ if (useCanvasProps) {
+ RippleAnimationSession.AnimationProperties<CanvasProperty<Float>,
+ CanvasProperty<Paint>>
+ p = s.getCanvasProperties();
+ RecordingCanvas can = (RecordingCanvas) canvas;
+ can.drawRipple(p.getX(), p.getY(), p.getMaxRadius(), p.getPaint(),
+ p.getProgress(), p.getShader());
+ } 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.restoreToCount(saveCount);
+ }
+
+ private void drawPatternedBackground(Canvas c) {
+ if (mRunBackgroundAnimation) {
+ startBackgroundAnimation();
+ }
+ if (mBackgroundOpacity == 0) return;
+ Paint p = mRipplePaint;
+ float newOpacity = mBackgroundOpacity;
+ final int origAlpha = p.getAlpha();
+ final int alpha = Math.min((int) (origAlpha * newOpacity + 0.5f), 255);
+ if (alpha > 0) {
+ ColorFilter origFilter = p.getColorFilter();
+ p.setColorFilter(mMaskColorFilter);
+ p.setAlpha(alpha);
+ Rect b = mHotspotBounds;
+ c.drawCircle(b.centerX(), b.centerY(), mState.mMaxRadius, p);
+ p.setAlpha(origAlpha);
+ p.setColorFilter(origFilter);
+ }
+ }
+
+ private float computeRadius() {
+ Rect b = getDirtyBounds();
+ float gap = 0;
+ float radius = (float) Math.sqrt(b.width() * b.width() + b.height() * b.height()) / 2 + gap;
+ return radius;
+ }
+
+ @NonNull
+ private RippleAnimationSession.AnimationProperties<Float, Paint> createAnimationProperties(
+ float x, float y) {
+ Paint p = new Paint(mRipplePaint);
+ float radius = mState.mMaxRadius;
+ RippleAnimationSession.AnimationProperties<Float, Paint> properties;
+ RippleShader shader = new RippleShader();
+ int color = mMaskColorFilter == null
+ ? mState.mColor.getColorForState(getState(), Color.BLACK)
+ : mMaskColorFilter.getColor();
+ color = color | 0xFF000000;
+ shader.setColor(color);
+ shader.setOrigin(x, y);
+ shader.setRadius(radius);
+ shader.setProgress(.0f);
+ properties = new RippleAnimationSession.AnimationProperties<>(
+ x, y, radius, p, 0f,
+ shader);
+ if (mMaskShader == null) {
+ shader.setHasMask(false);
+ } else {
+ shader.setShader(mMaskShader);
+ shader.setHasMask(true);
+ }
+ p.setShader(shader);
+ p.setColorFilter(null);
+ p.setColor(color);
+ return properties;
+ }
+
+ private boolean shouldUseCanvasProps(Canvas c) {
+ return !mForceSoftware && c.isHardwareAccelerated();
+ }
+
@Override
public void invalidateSelf() {
invalidateSelf(true);
@@ -774,18 +1037,23 @@ public class RippleDrawable extends LayerDrawable {
// Draw the appropriate mask anchored to (0,0).
final int left = bounds.left;
final int top = bounds.top;
- mMaskCanvas.translate(-left, -top);
+ if (mState.mRippleStyle == STYLE_SOLID) {
+ mMaskCanvas.translate(-left, -top);
+ }
if (maskType == MASK_EXPLICIT) {
drawMask(mMaskCanvas);
} else if (maskType == MASK_CONTENT) {
drawContent(mMaskCanvas);
}
- mMaskCanvas.translate(left, top);
+ if (mState.mRippleStyle == STYLE_SOLID) {
+ mMaskCanvas.translate(left, top);
+ }
}
private int getMaskType() {
if (mRipple == null && mExitingRipplesCount <= 0
- && (mBackground == null || !mBackground.isVisible())) {
+ && (mBackground == null || !mBackground.isVisible())
+ && mState.mRippleStyle == STYLE_SOLID) {
// We might need a mask later.
return MASK_UNKNOWN;
}
@@ -874,7 +1142,7 @@ public class RippleDrawable extends LayerDrawable {
updateMaskShaderIfNeeded();
// Position the shader to account for canvas translation.
- if (mMaskShader != null) {
+ if (mMaskShader != null && mState.mRippleStyle == STYLE_SOLID) {
final Rect bounds = getBounds();
mMaskMatrix.setTranslate(bounds.left - x, bounds.top - y);
mMaskShader.setLocalMatrix(mMaskMatrix);
@@ -973,6 +1241,31 @@ public class RippleDrawable extends LayerDrawable {
return this;
}
+ /**
+ * Sets the visual style of the ripple.
+ *
+ * @see #STYLE_SOLID
+ * @see #STYLE_PATTERNED
+ *
+ * @param style The style of the ripple
+ */
+ public void setRippleStyle(@RippleStyle int style) throws IllegalArgumentException {
+ if (style == STYLE_SOLID || style == STYLE_PATTERNED) {
+ mState.mRippleStyle = style;
+ } else {
+ throw new IllegalArgumentException("Invalid style value " + style);
+ }
+ }
+
+ /**
+ * Get the current ripple style
+ * @return Ripple style
+ */
+ public @RippleStyle int getRippleStyle() {
+ return mState.mRippleStyle;
+ }
+
+
@Override
RippleState createConstantState(LayerState state, Resources res) {
return new RippleState(state, this, res);
@@ -983,6 +1276,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;
public RippleState(LayerState orig, RippleDrawable owner, Resources res) {
super(orig, owner, res);
@@ -992,6 +1286,7 @@ public class RippleDrawable extends LayerDrawable {
mTouchThemeAttrs = origs.mTouchThemeAttrs;
mColor = origs.mColor;
mMaxRadius = origs.mMaxRadius;
+ mRippleStyle = origs.mRippleStyle;
if (origs.mDensity != mDensity) {
applyDensityScaling(orig.mDensity, mDensity);
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
new file mode 100644
index 000000000000..500efdd84854
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -0,0 +1,89 @@
+/*
+ * 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.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"
+ + "uniform float in_progress;\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"
+ + "}\n";
+
+ RippleShader() {
+ super(SHADER, false);
+ }
+
+ public void setShader(@NonNull Shader s) {
+ setInputShader("in_shader", s);
+ }
+
+ public void setRadius(float radius) {
+ setUniform("in_maxRadius", radius);
+ }
+
+ 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 setHasMask(boolean hasMask) {
+ setUniform("in_hasMask", hasMask ? 1 : 0);
+ }
+
+ 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()});
+ }
+}
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 9214ff1eb088..f826b24b2df2 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -26,9 +26,7 @@ import android.graphics.Paint;
import android.graphics.RectF;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
-import android.util.Log;
-import android.util.LongSparseArray;
-import android.util.LongSparseLongArray;
+import android.text.TextUtils;
import android.util.TypedValue;
import com.android.internal.annotations.GuardedBy;
@@ -44,7 +42,6 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
@@ -61,27 +58,19 @@ public final class Font {
private static final int STYLE_ITALIC = 1;
private static final int STYLE_NORMAL = 0;
- private static final Object MAP_LOCK = new Object();
- // We need to have mapping from native ptr to Font object for later accessing from TextShape
- // result since Typeface doesn't have reference to Font object and it is not always created from
- // Font object. Sometimes Typeface is created in native layer only and there might not be Font
- // object in Java layer. So, if not found in this cache, create new Font object for API user.
- @GuardedBy("MAP_LOCK")
- private static final LongSparseArray<WeakReference<Font>> FONT_PTR_MAP =
- new LongSparseArray<>();
+ private static final NativeAllocationRegistry BUFFER_REGISTRY =
+ NativeAllocationRegistry.createMalloced(
+ ByteBuffer.class.getClassLoader(), nGetReleaseNativeFont());
- private static final Object SOURCE_ID_LOCK = new Object();
- @GuardedBy("SOURCE_ID_LOCK")
- private static final LongSparseLongArray FONT_SOURCE_ID_MAP =
- new LongSparseLongArray(300); // System font has 200 fonts, so 300 should be enough.
+ private static final NativeAllocationRegistry FONT_REGISTRY =
+ NativeAllocationRegistry.createMalloced(Font.class.getClassLoader(),
+ nGetReleaseNativeFont());
/**
* A builder class for creating new Font.
*/
public static final class Builder {
- private static final NativeAllocationRegistry sFontRegistry =
- NativeAllocationRegistry.createMalloced(Font.class.getClassLoader(),
- nGetReleaseNativeFont());
+
private @Nullable ByteBuffer mBuffer;
private @Nullable File mFile;
@@ -484,26 +473,15 @@ public final class Font {
final String filePath = mFile == null ? "" : mFile.getAbsolutePath();
long ptr;
- int fontIdentifier;
+ final Font font;
if (mFont == null) {
ptr = nBuild(builderPtr, readonlyBuffer, filePath, mLocaleList, mWeight, italic,
mTtcIndex);
- long fontBufferPtr = nGetFontBufferAddress(ptr);
- synchronized (SOURCE_ID_LOCK) {
- long id = FONT_SOURCE_ID_MAP.get(fontBufferPtr, -1);
- if (id == -1) {
- id = FONT_SOURCE_ID_MAP.size();
- FONT_SOURCE_ID_MAP.put(fontBufferPtr, id);
- }
- fontIdentifier = (int) id;
- }
+ font = new Font(ptr);
} else {
ptr = nClone(mFont.getNativePtr(), builderPtr, mWeight, italic, mTtcIndex);
- fontIdentifier = mFont.mSourceIdentifier;
+ font = new Font(ptr);
}
- final Font font = new Font(ptr, readonlyBuffer, mFile,
- new FontStyle(mWeight, slant), mTtcIndex, mAxes, mLocaleList, fontIdentifier);
- sFontRegistry.registerNativeAllocation(font, ptr);
return font;
}
@@ -525,33 +503,33 @@ public final class Font {
}
private final long mNativePtr; // address of the shared ptr of minikin::Font
- private final @NonNull ByteBuffer mBuffer;
- private final @Nullable File mFile;
- private final FontStyle mFontStyle;
- private final @IntRange(from = 0) int mTtcIndex;
- private final @Nullable FontVariationAxis[] mAxes;
- private final @NonNull String mLocaleList;
- private final int mSourceIdentifier; // An identifier of font source data.
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private @NonNull ByteBuffer mBuffer = null;
+ @GuardedBy("mLock")
+ private boolean mIsFileInitialized = false;
+ @GuardedBy("mLock")
+ private @Nullable File mFile = null;
+ @GuardedBy("mLock")
+ private FontStyle mFontStyle = null;
+ @GuardedBy("mLock")
+ private @Nullable FontVariationAxis[] mAxes = null;
+ @GuardedBy("mLock")
+ private @NonNull LocaleList mLocaleList = null;
/**
* Use Builder instead
+ *
+ * Caller must increment underlying minikin::Font ref count.
+ * This class takes the ownership of the passing native objects.
+ *
+ * @hide
*/
- private Font(long nativePtr, @NonNull ByteBuffer buffer, @Nullable File file,
- @NonNull FontStyle fontStyle, @IntRange(from = 0) int ttcIndex,
- @Nullable FontVariationAxis[] axes, @NonNull String localeList,
- int sourceIdentifier) {
- mBuffer = buffer;
- mFile = file;
- mFontStyle = fontStyle;
+ public Font(long nativePtr) {
mNativePtr = nativePtr;
- mTtcIndex = ttcIndex;
- mAxes = axes;
- mLocaleList = localeList;
- mSourceIdentifier = sourceIdentifier;
- synchronized (MAP_LOCK) {
- FONT_PTR_MAP.append(nGetNativeFontPtr(mNativePtr), new WeakReference<>(this));
- }
+ FONT_REGISTRY.registerNativeAllocation(this, mNativePtr);
}
/**
@@ -563,7 +541,22 @@ public final class Font {
* @return a font buffer
*/
public @NonNull ByteBuffer getBuffer() {
- return mBuffer;
+ synchronized (mLock) {
+ if (mBuffer == null) {
+ // Create new instance of native FontWrapper, i.e. incrementing ref count of
+ // minikin Font instance for keeping buffer fo ByteBuffer reference which may live
+ // longer than this object.
+ long ref = nCloneFont(mNativePtr);
+ ByteBuffer fromNative = nNewByteBuffer(mNativePtr);
+
+ // Bind ByteBuffer's lifecycle with underlying font object.
+ BUFFER_REGISTRY.registerNativeAllocation(fromNative, ref);
+
+ // JNI NewDirectBuffer creates writable ByteBuffer even if it is mmaped readonly.
+ mBuffer = fromNative.asReadOnlyBuffer();
+ }
+ return mBuffer;
+ }
}
/**
@@ -574,7 +567,16 @@ public final class Font {
* @return a file path of the font
*/
public @Nullable File getFile() {
- return mFile;
+ synchronized (mLock) {
+ if (!mIsFileInitialized) {
+ String path = nGetFontPath(mNativePtr);
+ if (!TextUtils.isEmpty(path)) {
+ mFile = new File(path);
+ }
+ mIsFileInitialized = true;
+ }
+ return mFile;
+ }
}
/**
@@ -585,7 +587,16 @@ public final class Font {
* @return a font style
*/
public @NonNull FontStyle getStyle() {
- return mFontStyle;
+ synchronized (mLock) {
+ if (mFontStyle == null) {
+ int packedStyle = nGetPackedStyle(mNativePtr);
+ mFontStyle = new FontStyle(
+ FontFileUtil.unpackWeight(packedStyle),
+ FontFileUtil.unpackItalic(packedStyle)
+ ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT);
+ }
+ return mFontStyle;
+ }
}
/**
@@ -597,7 +608,7 @@ public final class Font {
* @return a TTC index value
*/
public @IntRange(from = 0) int getTtcIndex() {
- return mTtcIndex;
+ return nGetIndex(mNativePtr);
}
/**
@@ -608,7 +619,23 @@ public final class Font {
* @return font variation settings
*/
public @Nullable FontVariationAxis[] getAxes() {
- return mAxes == null ? null : mAxes.clone();
+ synchronized (mLock) {
+ if (mAxes == null) {
+ int axisCount = nGetAxisCount(mNativePtr);
+ mAxes = new FontVariationAxis[axisCount];
+ char[] charBuffer = new char[4];
+ for (int i = 0; i < axisCount; ++i) {
+ long packedAxis = nGetAxisInfo(mNativePtr, i);
+ float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL));
+ charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >>> 56);
+ charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >>> 48);
+ charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >>> 40);
+ charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >>> 32);
+ mAxes[i] = new FontVariationAxis(new String(charBuffer), value);
+ }
+ }
+ }
+ return mAxes;
}
/**
@@ -618,7 +645,17 @@ public final class Font {
* @return a locale list
*/
public @NonNull LocaleList getLocaleList() {
- return LocaleList.forLanguageTags(mLocaleList);
+ synchronized (mLock) {
+ if (mLocaleList == null) {
+ String langTags = nGetLocaleList(mNativePtr);
+ if (TextUtils.isEmpty(langTags)) {
+ mLocaleList = LocaleList.getEmptyLocaleList();
+ } else {
+ mLocaleList = LocaleList.forLanguageTags(langTags);
+ }
+ }
+ return mLocaleList;
+ }
}
/**
@@ -713,7 +750,7 @@ public final class Font {
* @return an unique identifier for the font source data.
*/
public int getSourceIdentifier() {
- return mSourceIdentifier;
+ return nGetSourceId(mNativePtr);
}
/**
@@ -736,13 +773,16 @@ public final class Font {
private boolean isSameSource(@NonNull Font other) {
Objects.requireNonNull(other);
+ ByteBuffer myBuffer = getBuffer();
+ ByteBuffer otherBuffer = other.getBuffer();
+
// Shortcut for the same instance.
- if (mBuffer == other.mBuffer) {
+ if (myBuffer == otherBuffer) {
return true;
}
// Shortcut for different font buffer check by comparing size.
- if (mBuffer.capacity() != other.mBuffer.capacity()) {
+ if (myBuffer.capacity() != otherBuffer.capacity()) {
return false;
}
@@ -750,15 +790,15 @@ public final class Font {
// underlying native font object holds buffer address, check if this buffer points exactly
// the same address as a shortcut of equality. For being compatible with of API30 or before,
// check buffer position even if the buffer points the same address.
- if (mSourceIdentifier == other.mSourceIdentifier
- && mBuffer.position() == other.mBuffer.position()) {
+ if (getSourceIdentifier() == other.getSourceIdentifier()
+ && myBuffer.position() == otherBuffer.position()) {
return true;
}
// Unfortunately, need to compare bytes one-by-one since the buffer may be different font
// file but has the same file size, or two font has same content but they are allocated
// differently. For being compatible with API30 ore before, compare with ByteBuffer#equals.
- return mBuffer.equals(other.mBuffer);
+ return myBuffer.equals(otherBuffer);
}
@Override
@@ -769,10 +809,20 @@ public final class Font {
if (!(o instanceof Font)) {
return false;
}
+
Font f = (Font) o;
- boolean paramEqual = mFontStyle.equals(f.mFontStyle) && f.mTtcIndex == mTtcIndex
- && Arrays.equals(f.mAxes, mAxes) && Objects.equals(f.mLocaleList, mLocaleList)
- && Objects.equals(mFile, f.mFile);
+
+ // The underlying minikin::Font object is the source of the truth of font information. Thus,
+ // Pointer equality is the object equality.
+ if (nGetMinikinFontPtr(mNativePtr) == nGetMinikinFontPtr(f.mNativePtr)) {
+ return true;
+ }
+
+ boolean paramEqual = f.getStyle().equals(getStyle())
+ && f.getTtcIndex() == getTtcIndex()
+ && Arrays.equals(f.getAxes(), getAxes())
+ && Objects.equals(f.getLocaleList(), getLocaleList())
+ && Objects.equals(getFile(), f.getFile());
if (!paramEqual) {
return false;
@@ -784,64 +834,45 @@ public final class Font {
@Override
public int hashCode() {
return Objects.hash(
- mFontStyle,
- mTtcIndex,
- Arrays.hashCode(mAxes),
+ getStyle(),
+ getTtcIndex(),
+ Arrays.hashCode(getAxes()),
// Use Buffer size instead of ByteBuffer#hashCode since ByteBuffer#hashCode traverse
// data which is not performant e.g. for HashMap. The hash collision are less likely
// happens because it is unlikely happens the different font files has exactly the
// same size.
- mLocaleList);
+ getLocaleList());
}
@Override
public String toString() {
return "Font {"
- + "path=" + mFile
- + ", style=" + mFontStyle
- + ", ttcIndex=" + mTtcIndex
- + ", axes=" + FontVariationAxis.toFontVariationSettings(mAxes)
- + ", localeList=" + mLocaleList
- + ", buffer=" + mBuffer
+ + "path=" + getFile()
+ + ", style=" + getStyle()
+ + ", ttcIndex=" + getTtcIndex()
+ + ", axes=" + FontVariationAxis.toFontVariationSettings(getAxes())
+ + ", localeList=" + getLocaleList()
+ + ", buffer=" + getBuffer()
+ "}";
}
- /**
- * Lookup Font object from native pointer or create new one if not found.
- * @hide
- */
- public static Font findOrCreateFontFromNativePtr(long ptr) {
- // First, lookup from known mapps.
- synchronized (MAP_LOCK) {
- WeakReference<Font> fontRef = FONT_PTR_MAP.get(ptr);
- if (fontRef != null) {
- Font font = fontRef.get();
- if (font != null) {
- return font;
- }
- }
+ @CriticalNative
+ private static native long nGetMinikinFontPtr(long font);
- // If not found, create Font object from native object for Java API users.
- ByteBuffer buffer = NativeFontBufferHelper.refByteBuffer(ptr);
- NativeFont.Font font = NativeFont.readNativeFont(ptr);
+ @CriticalNative
+ private static native long nCloneFont(long font);
- Font.Builder builder = new Font.Builder(buffer, font.getFile(), "")
- .setWeight(font.getStyle().getWeight())
- .setSlant(font.getStyle().getSlant())
- .setTtcIndex(font.getIndex())
- .setFontVariationSettings(font.getAxes());
+ @FastNative
+ private static native ByteBuffer nNewByteBuffer(long font);
- Font newFont = null;
- try {
- newFont = builder.build();
- FONT_PTR_MAP.append(ptr, new WeakReference<>(newFont));
- } catch (IOException e) {
- // This must not happen since the buffer was already created once.
- Log.e("Font", "Failed to create font object from existing buffer.", e);
- }
- return newFont;
- }
- }
+ @CriticalNative
+ private static native long nGetBufferAddress(long font);
+
+ @CriticalNative
+ private static native int nGetSourceId(long font);
+
+ @CriticalNative
+ private static native long nGetReleaseNativeFont();
@FastNative
private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect);
@@ -849,9 +880,21 @@ public final class Font {
@FastNative
private static native float nGetFontMetrics(long font, long paint, Paint.FontMetrics metrics);
+ @FastNative
+ private static native String nGetFontPath(long fontPtr);
+
+ @FastNative
+ private static native String nGetLocaleList(long familyPtr);
+
+ @CriticalNative
+ private static native int nGetPackedStyle(long fontPtr);
+
+ @CriticalNative
+ private static native int nGetIndex(long fontPtr);
+
@CriticalNative
- private static native long nGetNativeFontPtr(long ptr);
+ private static native int nGetAxisCount(long fontPtr);
@CriticalNative
- private static native long nGetFontBufferAddress(long font);
+ private static native long nGetAxisInfo(long fontPtr, int i);
}
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index 77f86fe726f3..a771a6e35345 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -125,7 +125,7 @@ public final class FontFamily {
nAddFont(builderPtr, mFonts.get(i).getNativePtr());
}
final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback);
- final FontFamily family = new FontFamily(mFonts, ptr);
+ final FontFamily family = new FontFamily(ptr);
sFamilyRegistory.registerNativeAllocation(family, ptr);
return family;
}
@@ -143,12 +143,11 @@ public final class FontFamily {
private static native long nGetReleaseNativeFamily();
}
- private final ArrayList<Font> mFonts;
private final long mNativePtr;
// Use Builder instead.
- private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) {
- mFonts = fonts;
+ /** @hide */
+ public FontFamily(long ptr) {
mNativePtr = ptr;
}
@@ -176,7 +175,10 @@ public final class FontFamily {
* @return a registered font
*/
public @NonNull Font getFont(@IntRange(from = 0) int index) {
- return mFonts.get(index);
+ if (index < 0 || getSize() <= index) {
+ throw new IndexOutOfBoundsException();
+ }
+ return new Font(nGetFont(mNativePtr, index));
}
/**
@@ -185,7 +187,7 @@ public final class FontFamily {
* @return the number of fonts registered in this family.
*/
public @IntRange(from = 1) int getSize() {
- return mFonts.size();
+ return nGetFontSize(mNativePtr);
}
/** @hide */
@@ -193,6 +195,12 @@ public final class FontFamily {
return mNativePtr;
}
+ @CriticalNative
+ private static native int nGetFontSize(long family);
+
+ @CriticalNative
+ private static native long nGetFont(long family, int i);
+
@FastNative
private static native String nGetLangTags(long family);
diff --git a/graphics/java/android/graphics/fonts/FontVariationAxis.java b/graphics/java/android/graphics/fonts/FontVariationAxis.java
index 7bd581723d18..d1fe2cdbcd77 100644
--- a/graphics/java/android/graphics/fonts/FontVariationAxis.java
+++ b/graphics/java/android/graphics/fonts/FontVariationAxis.java
@@ -23,6 +23,7 @@ import android.os.Build;
import android.text.TextUtils;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;
@@ -189,6 +190,17 @@ public final class FontVariationAxis {
return TextUtils.join(",", axes);
}
+ /**
+ * Stringify the array of FontVariationAxis.
+ * @hide
+ */
+ public static @NonNull String toFontVariationSettings(@Nullable List<FontVariationAxis> axes) {
+ if (axes == null) {
+ return "";
+ }
+ return TextUtils.join(",", axes);
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (o == this) {
diff --git a/graphics/java/android/graphics/fonts/NativeFont.java b/graphics/java/android/graphics/fonts/NativeFont.java
deleted file mode 100644
index 9e9d76a89385..000000000000
--- a/graphics/java/android/graphics/fonts/NativeFont.java
+++ /dev/null
@@ -1,205 +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 android.graphics.fonts;
-
-import android.graphics.Typeface;
-
-import dalvik.annotation.optimization.CriticalNative;
-import dalvik.annotation.optimization.FastNative;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Read native font objects.
- *
- * @hide
- */
-public class NativeFont {
-
- /**
- * Represents native font object.
- */
- public static final class Font {
- private final File mFile;
- private final int mIndex;
- private final FontVariationAxis[] mAxes;
- private final FontStyle mStyle;
-
- public Font(File file, int index, FontVariationAxis[] axes, FontStyle style) {
- mFile = file;
- mIndex = index;
- mAxes = axes;
- mStyle = style;
- }
-
- public File getFile() {
- return mFile;
- }
-
- public FontVariationAxis[] getAxes() {
- return mAxes;
- }
-
- public FontStyle getStyle() {
- return mStyle;
- }
-
- public int getIndex() {
- return mIndex;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Font font = (Font) o;
- return mIndex == font.mIndex && mFile.equals(font.mFile)
- && Arrays.equals(mAxes, font.mAxes) && mStyle.equals(font.mStyle);
- }
-
- @Override
- public int hashCode() {
- int result = Objects.hash(mFile, mIndex, mStyle);
- result = 31 * result + Arrays.hashCode(mAxes);
- return result;
- }
- }
-
- /**
- * Represents native font family object.
- */
- public static final class Family {
- private final List<Font> mFonts;
- private final String mLocale;
-
- public Family(List<Font> fonts, String locale) {
- mFonts = fonts;
- mLocale = locale;
- }
-
- public List<Font> getFonts() {
- return mFonts;
- }
-
- public String getLocale() {
- return mLocale;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Family family = (Family) o;
- return mFonts.equals(family.mFonts) && mLocale.equals(family.mLocale);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mFonts, mLocale);
- }
- }
-
- /**
- * Get underlying font families from Typeface
- *
- * @param typeface a typeface
- * @return list of family
- */
- public static List<Family> readTypeface(Typeface typeface) {
- int familyCount = nGetFamilyCount(typeface.native_instance);
- List<Family> result = new ArrayList<>(familyCount);
- for (int i = 0; i < familyCount; ++i) {
- result.add(readNativeFamily(nGetFamily(typeface.native_instance, i)));
- }
- return result;
- }
-
- /**
- * Read family object from native pointer
- *
- * @param familyPtr a font family pointer
- * @return a family
- */
- public static Family readNativeFamily(long familyPtr) {
- int fontCount = nGetFontCount(familyPtr);
- List<Font> result = new ArrayList<>(fontCount);
- for (int i = 0; i < fontCount; ++i) {
- result.add(readNativeFont(nGetFont(familyPtr, i)));
- }
- String localeList = nGetLocaleList(familyPtr);
- return new Family(result, localeList);
- }
-
- /**
- * Read font object from native pointer.
- *
- * @param ptr a font pointer
- * @return a font
- */
- public static Font readNativeFont(long ptr) {
- long packed = nGetFontInfo(ptr);
- int weight = (int) (packed & 0x0000_0000_0000_FFFFL);
- boolean italic = (packed & 0x0000_0000_0001_0000L) != 0;
- int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32);
- int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48);
- FontVariationAxis[] axes = new FontVariationAxis[axisCount];
- char[] charBuffer = new char[4];
- for (int i = 0; i < axisCount; ++i) {
- long packedAxis = nGetAxisInfo(ptr, i);
- float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL));
- charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56);
- charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48);
- charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40);
- charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32);
- axes[i] = new FontVariationAxis(new String(charBuffer), value);
- }
- String path = nGetFontPath(ptr);
- File file = (path == null) ? null : new File(path);
- FontStyle style = new FontStyle(weight,
- italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT);
-
- return new Font(file, ttcIndex, axes, style);
- }
-
- @CriticalNative
- private static native int nGetFamilyCount(long ptr);
-
- @CriticalNative
- private static native long nGetFamily(long ptr, int index);
-
- @FastNative
- private static native String nGetLocaleList(long familyPtr);
-
- @CriticalNative
- private static native long nGetFont(long familyPtr, int fontIndex);
-
- @CriticalNative
- private static native int nGetFontCount(long familyPtr);
-
- @CriticalNative
- private static native long nGetFontInfo(long fontPtr);
-
- @CriticalNative
- private static native long nGetAxisInfo(long fontPtr, int i);
-
- @FastNative
- private static native String nGetFontPath(long fontPtr);
-}
diff --git a/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java b/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java
deleted file mode 100644
index 5655e7fafc1b..000000000000
--- a/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java
+++ /dev/null
@@ -1,62 +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 android.graphics.fonts;
-
-import android.annotation.NonNull;
-
-import dalvik.annotation.optimization.CriticalNative;
-import dalvik.annotation.optimization.FastNative;
-
-import libcore.util.NativeAllocationRegistry;
-
-import java.nio.ByteBuffer;
-
-/**
- * This is a helper class for showing native allocated buffer in Java API.
- *
- * @hide
- */
-public class NativeFontBufferHelper {
- private NativeFontBufferHelper() {}
-
- private static final NativeAllocationRegistry REGISTRY =
- NativeAllocationRegistry.createMalloced(
- ByteBuffer.class.getClassLoader(), nGetReleaseFunc());
-
- /**
- * Wrap native buffer with ByteBuffer with adding reference to it.
- */
- public static @NonNull ByteBuffer refByteBuffer(long fontPtr) {
- long refPtr = nRefFontBuffer(fontPtr);
- ByteBuffer buffer = nWrapByteBuffer(refPtr);
-
- // Releasing native object so that decreasing shared pointer ref count when the byte buffer
- // is GCed.
- REGISTRY.registerNativeAllocation(buffer, refPtr);
-
- return buffer;
- }
-
- @CriticalNative
- private static native long nRefFontBuffer(long fontPtr);
-
- @FastNative
- private static native ByteBuffer nWrapByteBuffer(long refPtr);
-
- @CriticalNative
- private static native long nGetReleaseFunc();
-}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 904085feb8cb..255f9e659c36 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -68,44 +68,23 @@ public final class SystemFonts {
*/
public static @NonNull Set<Font> getAvailableFonts() {
synchronized (LOCK) {
- if (sAvailableFonts != null) {
- return sAvailableFonts;
- }
-
- if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
- sAvailableFonts = collectAllFonts();
- } else {
+ if (sAvailableFonts == null) {
Set<Font> set = new ArraySet<>();
- for (FontFamily[] items : sFamilyMap.values()) {
- for (FontFamily family : items) {
- for (int i = 0; i < family.getSize(); ++i) {
- set.add(family.getFont(i));
+ for (Typeface tf : Typeface.getSystemFontMap().values()) {
+ List<FontFamily> families = tf.getFallback();
+ for (int i = 0; i < families.size(); ++i) {
+ FontFamily family = families.get(i);
+ for (int j = 0; j < family.getSize(); ++j) {
+ set.add(family.getFont(j));
}
}
}
-
sAvailableFonts = Collections.unmodifiableSet(set);
}
return sAvailableFonts;
}
}
- private static @NonNull Set<Font> collectAllFonts() {
- // TODO: use updated fonts
- FontConfig fontConfig = getSystemPreinstalledFontConfig();
- Map<String, FontFamily[]> map = buildSystemFallback(fontConfig);
-
- Set<Font> res = new ArraySet<>();
- for (FontFamily[] families : map.values()) {
- for (FontFamily family : families) {
- for (int i = 0; i < family.getSize(); ++i) {
- res.add(family.getFont(i));
- }
- }
- }
- return res;
- }
-
private static @Nullable ByteBuffer mmap(@NonNull String fullPath) {
try (FileInputStream file = new FileInputStream(fullPath)) {
final FileChannel fileChannel = file.getChannel();
@@ -329,13 +308,4 @@ public final class SystemFonts {
Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result);
return result;
}
-
- /**
- * @hide
- */
- public void resetFallbackMapping(Map<String, FontFamily[]> fallbackMap) {
- synchronized (LOCK) {
- sFamilyMap = fallbackMap;
- }
- }
}
diff --git a/graphics/java/android/graphics/pdf/OWNERS b/graphics/java/android/graphics/pdf/OWNERS
index f04e2008a437..057dc0d9583c 100644
--- a/graphics/java/android/graphics/pdf/OWNERS
+++ b/graphics/java/android/graphics/pdf/OWNERS
@@ -5,4 +5,3 @@ djsollen@google.com
sumir@google.com
svetoslavganov@android.com
svetoslavganov@google.com
-moltmann@google.com
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index c2de0acebca9..8d20e9cee7d7 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -184,7 +184,7 @@ public final class PositionedGlyphs {
long ptr = nGetFont(layoutPtr, i);
if (prevPtr != ptr) {
prevPtr = ptr;
- prevFont = Font.findOrCreateFontFromNativePtr(ptr);
+ prevFont = new Font(ptr);
}
mFonts.add(prevFont);
}
@@ -224,9 +224,7 @@ public final class PositionedGlyphs {
if (getGlyphId(i) != that.getGlyphId(i)) return false;
if (getGlyphX(i) != that.getGlyphX(i)) return false;
if (getGlyphY(i) != that.getGlyphY(i)) return false;
- // Intentionally using reference equality since font equality is heavy due to buffer
- // compare.
- if (getFont(i) != that.getFont(i)) return false;
+ if (!getFont(i).equals(that.getFont(i))) return false;
}
return true;
diff --git a/graphics/proto/Android.bp b/graphics/proto/Android.bp
index ea79b731a365..1b192668b4a3 100644
--- a/graphics/proto/Android.bp
+++ b/graphics/proto/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library_static {
name: "updatable-driver-protos",
host_supported: true,
diff --git a/keystore/Android.bp b/keystore/Android.bp
new file mode 100644
index 000000000000..5db668e48431
--- /dev/null
+++ b/keystore/Android.bp
@@ -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 {
+ default_applicable_licenses: ["frameworks_base_keystore_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_keystore_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
new file mode 100644
index 000000000000..c81c8c54d88a
--- /dev/null
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.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 android.security;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.security.usermanager.IKeystoreUserManager;
+import android.system.keystore2.ResponseCode;
+import android.util.Log;
+
+/**
+ * @hide This is the client side for IKeystoreUserManager AIDL.
+ * It shall only be used by the LockSettingsService.
+ */
+public class AndroidKeyStoreMaintenance {
+ private static final String TAG = "AndroidKeyStoreMaintenance";
+
+ public static final int SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR;
+
+ private static IKeystoreUserManager getService() {
+ return IKeystoreUserManager.Stub.asInterface(
+ ServiceManager.checkService("android.security.usermanager"));
+ }
+
+ /**
+ * Informs keystore2 about adding a user
+ *
+ * @param userId - Android user id of the user being added
+ * @return 0 if successful or a {@code ResponseCode}
+ * @hide
+ */
+ public static int onUserAdded(@NonNull int userId) {
+ if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
+ try {
+ getService().onUserAdded(userId);
+ return 0;
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "onUserAdded failed", e);
+ return e.errorCode;
+ } catch (Exception e) {
+ Log.e(TAG, "Can not connect to keystore", e);
+ return SYSTEM_ERROR;
+ }
+ }
+
+ /**
+ * Informs keystore2 about removing a usergit mer
+ *
+ * @param userId - Android user id of the user being removed
+ * @return 0 if successful or a {@code ResponseCode}
+ * @hide
+ */
+ public static int onUserRemoved(int userId) {
+ if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
+ try {
+ getService().onUserRemoved(userId);
+ return 0;
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "onUserRemoved failed", e);
+ return e.errorCode;
+ } catch (Exception e) {
+ Log.e(TAG, "Can not connect to keystore", e);
+ return SYSTEM_ERROR;
+ }
+ }
+
+ /**
+ * Informs keystore2 about changing user's password
+ *
+ * @param userId - Android user id of the user
+ * @param password - a secret derived from the synthetic password provided by the
+ * LockSettingService
+ * @return 0 if successful or a {@code ResponseCode}
+ * @hide
+ */
+ public static int onUserPasswordChanged(int userId, @Nullable byte[] password) {
+ if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
+ try {
+ getService().onUserPasswordChanged(userId, password);
+ return 0;
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "onUserPasswordChanged 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/Authorization.java b/keystore/java/android/security/Authorization.java
index 21d23b1b2575..50a90820117d 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -33,20 +33,12 @@ import android.util.Log;
*/
public class Authorization {
private static final String TAG = "KeystoreAuthorization";
- private static IKeystoreAuthorization sIKeystoreAuthorization;
public static final int SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR;
- public Authorization() {
- sIKeystoreAuthorization = null;
- }
-
- private static synchronized IKeystoreAuthorization getService() {
- if (sIKeystoreAuthorization == null) {
- sIKeystoreAuthorization = IKeystoreAuthorization.Stub.asInterface(
+ private static IKeystoreAuthorization getService() {
+ return IKeystoreAuthorization.Stub.asInterface(
ServiceManager.checkService("android.security.authorization"));
- }
- return sIKeystoreAuthorization;
}
/**
@@ -55,12 +47,12 @@ public class Authorization {
* @param authToken created by Android authenticators.
* @return 0 if successful or {@code ResponseCode.SYSTEM_ERROR}.
*/
- public int addAuthToken(@NonNull HardwareAuthToken authToken) {
+ public static int addAuthToken(@NonNull HardwareAuthToken authToken) {
if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
try {
getService().addAuthToken(authToken);
return 0;
- } catch (RemoteException e) {
+ } catch (RemoteException | NullPointerException e) {
Log.w(TAG, "Can not connect to keystore", e);
return SYSTEM_ERROR;
} catch (ServiceSpecificException e) {
@@ -73,7 +65,7 @@ public class Authorization {
* @param authToken
* @return 0 if successful or a {@code ResponseCode}.
*/
- public int addAuthToken(@NonNull byte[] authToken) {
+ public static int addAuthToken(@NonNull byte[] authToken) {
return addAuthToken(AuthTokenUtils.toHardwareAuthToken(authToken));
}
@@ -86,7 +78,7 @@ public class Authorization {
*
* @return 0 if successful or a {@code ResponseCode}.
*/
- public int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId,
+ public static int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId,
@Nullable byte[] syntheticPassword) {
if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
try {
@@ -96,7 +88,7 @@ public class Authorization {
getService().onLockScreenEvent(LockScreenEvent.UNLOCK, userId, syntheticPassword);
}
return 0;
- } catch (RemoteException e) {
+ } catch (RemoteException | NullPointerException e) {
Log.w(TAG, "Can not connect to keystore", e);
return SYSTEM_ERROR;
} catch (ServiceSpecificException e) {
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index e19d88c182ff..198df40c7d7b 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -996,7 +996,7 @@ public class KeyStore {
*/
public int addAuthToken(byte[] authToken) {
try {
- new Authorization().addAuthToken(authToken);
+ Authorization.addAuthToken(authToken);
return mBinder.addAuthToken(authToken);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index f7477bf92c81..476e4d7b7b18 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -107,7 +107,6 @@ public class KeyStore2 {
try {
return request.execute(service);
} catch (ServiceSpecificException e) {
- Log.e(TAG, "KeyStore exception", e);
throw getKeyStoreException(e.errorCode);
} catch (RemoteException e) {
if (firstTry) {
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 372add9b7ecb..d188b6525579 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -190,7 +190,7 @@ public class KeyStoreSecurityLevel {
keyDescriptor.blob = wrappedKey;
keyDescriptor.domain = wrappedKeyDescriptor.domain;
- return handleExceptions(() -> mSecurityLevel.importWrappedKey(wrappedKeyDescriptor,
+ return handleExceptions(() -> mSecurityLevel.importWrappedKey(keyDescriptor,
wrappingKeyDescriptor, maskingKey,
args.toArray(new KeyParameter[args.size()]), authenticatorSpecs));
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 334b1110d651..988838b46334 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -17,6 +17,7 @@
package android.security.keystore;
import android.annotation.Nullable;
+import android.content.Context;
import android.os.Build;
import android.security.Credentials;
import android.security.KeyPairGeneratorSpec;
@@ -25,6 +26,8 @@ import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterCertificateChain;
import android.security.keymaster.KeymasterDefs;
+import android.telephony.TelephonyManager;
+import android.util.ArraySet;
import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
@@ -477,11 +480,11 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
success = true;
return keyPair;
- } catch (ProviderException e) {
+ } catch (ProviderException | IllegalArgumentException | DeviceIdAttestationException e) {
if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) {
throw new SecureKeyImportUnavailableException(e);
} else {
- throw e;
+ throw new ProviderException(e);
}
} finally {
if (!success) {
@@ -491,7 +494,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
}
private Iterable<byte[]> createCertificateChain(final String privateKeyAlias, KeyPair keyPair)
- throws ProviderException {
+ throws ProviderException, DeviceIdAttestationException {
byte[] challenge = mSpec.getAttestationChallenge();
if (challenge != null) {
KeymasterArguments args = new KeymasterArguments();
@@ -510,6 +513,60 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
Build.MODEL.getBytes(StandardCharsets.UTF_8));
}
+ int[] idTypes = mSpec.getAttestationIds();
+ if (idTypes != null) {
+ final Set<Integer> idTypesSet = new ArraySet<>(idTypes.length);
+ for (int idType : idTypes) {
+ idTypesSet.add(idType);
+ }
+ TelephonyManager telephonyService = null;
+ if (idTypesSet.contains(AttestationUtils.ID_TYPE_IMEI)
+ || idTypesSet.contains(AttestationUtils.ID_TYPE_MEID)) {
+ telephonyService =
+ (TelephonyManager) KeyStore.getApplicationContext().getSystemService(
+ Context.TELEPHONY_SERVICE);
+ if (telephonyService == null) {
+ throw new DeviceIdAttestationException(
+ "Unable to access telephony service");
+ }
+ }
+ for (final Integer idType : idTypesSet) {
+ switch (idType) {
+ case AttestationUtils.ID_TYPE_SERIAL:
+ args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_SERIAL,
+ Build.getSerial().getBytes(StandardCharsets.UTF_8)
+ );
+ break;
+ case AttestationUtils.ID_TYPE_IMEI: {
+ final String imei = telephonyService.getImei(0);
+ if (imei == null) {
+ throw new DeviceIdAttestationException("Unable to retrieve IMEI");
+ }
+ args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_IMEI,
+ imei.getBytes(StandardCharsets.UTF_8)
+ );
+ break;
+ }
+ case AttestationUtils.ID_TYPE_MEID: {
+ final String meid = telephonyService.getMeid(0);
+ if (meid == null) {
+ throw new DeviceIdAttestationException("Unable to retrieve MEID");
+ }
+ args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MEID,
+ meid.getBytes(StandardCharsets.UTF_8)
+ );
+ break;
+ }
+ case AttestationUtils.USE_INDIVIDUAL_ATTESTATION: {
+ args.addBoolean(KeymasterDefs.KM_TAG_DEVICE_UNIQUE_ATTESTATION);
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Unknown device ID type " + idType);
+ }
+ }
+ }
+
return getAttestationChain(privateKeyAlias, keyPair, args);
}
@@ -547,7 +604,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
}
}
- private KeymasterArguments constructKeyGenerationArguments() {
+ private KeymasterArguments constructKeyGenerationArguments()
+ throws IllegalArgumentException, DeviceIdAttestationException {
KeymasterArguments args = new KeymasterArguments();
args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
@@ -565,9 +623,9 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
mSpec.getKeyValidityForConsumptionEnd());
addAlgorithmSpecificParameters(args);
- if (mSpec.isUniqueIdIncluded())
+ if (mSpec.isUniqueIdIncluded()) {
args.addBoolean(KeymasterDefs.KM_TAG_INCLUDE_UNIQUE_ID);
-
+ }
return args;
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 16bf5469296f..087151711138 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -434,14 +434,16 @@ public class AndroidKeyStoreProvider extends Provider {
@NonNull
public static java.security.KeyStore getKeyStoreForUid(int uid)
throws KeyStoreException, NoSuchProviderException {
- String providerName = PROVIDER_NAME;
+ final java.security.KeyStore.LoadStoreParameter loadParameter;
if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) {
- providerName = "AndroidKeyStoreLegacy";
+ loadParameter = new android.security.keystore2.AndroidKeyStoreLoadStoreParameter(
+ KeyProperties.legacyUidToNamespace(uid));
+ } else {
+ loadParameter = new AndroidKeyStoreLoadStoreParameter(uid);
}
- java.security.KeyStore result =
- java.security.KeyStore.getInstance(providerName);
+ java.security.KeyStore result = java.security.KeyStore.getInstance(PROVIDER_NAME);
try {
- result.load(new AndroidKeyStoreLoadStoreParameter(uid));
+ result.load(loadParameter);
} catch (NoSuchAlgorithmException | CertificateException | IOException e) {
throw new KeyStoreException(
"Failed to load AndroidKeyStore KeyStore for UID " + uid, e);
diff --git a/keystore/java/android/security/keystore/ArrayUtils.java b/keystore/java/android/security/keystore/ArrayUtils.java
index c8c1de4a5e83..f22b6041800f 100644
--- a/keystore/java/android/security/keystore/ArrayUtils.java
+++ b/keystore/java/android/security/keystore/ArrayUtils.java
@@ -34,6 +34,14 @@ public abstract class ArrayUtils {
return ((array != null) && (array.length > 0)) ? array.clone() : array;
}
+ /**
+ * Clones an array if it is not null and has a length greater than 0. Otherwise, returns the
+ * array.
+ */
+ public static int[] cloneIfNotEmpty(int[] array) {
+ return ((array != null) && (array.length > 0)) ? array.clone() : array;
+ }
+
public static byte[] cloneIfNotEmpty(byte[] array) {
return ((array != null) && (array.length > 0)) ? array.clone() : array;
}
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index c2a7b2ee6323..c79c12cd3343 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -236,10 +236,51 @@ import javax.security.auth.x500.X500Principal;
* keyStore.load(null);
* key = (SecretKey) keyStore.getKey("key2", null);
* }</pre>
+ *
+ * <p><h3 id="example:ecdh">Example: EC key for ECDH key agreement</h3>
+ * This example illustrates how to generate an elliptic curve key pair, used to establish a shared
+ * secret with another party using ECDH key agreement.
+ * <pre> {@code
+ * KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
+ * KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
+ * keyPairGenerator.initialize(
+ * new KeyGenParameterSpec.Builder(
+ * "eckeypair",
+ * KeyProperties.PURPOSE_AGREE_KEY)
+ * .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
+ * .build());
+ * KeyPair myKeyPair = keyPairGenerator.generateKeyPair();
+ *
+ * // Exchange public keys with server. A new ephemeral key MUST be used for every message.
+ * PublicKey serverEphemeralPublicKey; // Ephemeral key received from server.
+ *
+ * // Create a shared secret based on our private key and the other party's public key.
+ * KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH", "AndroidKeyStore");
+ * keyAgreement.init(myKeyPair.getPrivate());
+ * keyAgreement.doPhase(serverEphemeralPublicKey, true);
+ * byte[] sharedSecret = keyAgreement.generateSecret();
+ *
+ * // sharedSecret cannot safely be used as a key yet. We must run it through a key derivation
+ * // function with some other data: "salt" and "info". Salt is an optional random value,
+ * // omitted in this example. It's good practice to include both public keys and any other
+ * // key negotiation data in info. Here we use the public keys and a label that indicates
+ * // messages encrypted with this key are coming from the server.
+ * byte[] salt = {};
+ * ByteArrayOutputStream info = new ByteArrayOutputStream();
+ * info.write("ECDH secp256r1 AES-256-GCM-SIV\0".getBytes(StandardCharsets.UTF_8));
+ * info.write(myKeyPair.getPublic().getEncoded());
+ * info.write(serverEphemeralPublicKey.getEncoded());
+ *
+ * // This example uses the Tink library and the HKDF key derivation function.
+ * AesGcmSiv key = new AesGcmSiv(Hkdf.computeHkdf(
+ * "HMACSHA256", sharedSecret, salt, info.toByteArray(), 32));
+ * byte[] associatedData = {};
+ * return key.decrypt(ciphertext, associatedData);
+ * }
*/
public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs {
-
- private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
+ private static final X500Principal DEFAULT_CERT_SUBJECT =
+ new X500Principal("CN=Android Keystore Key");
private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970
private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
@@ -267,6 +308,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
private final boolean mUserPresenceRequired;
private final byte[] mAttestationChallenge;
private final boolean mDevicePropertiesAttestationIncluded;
+ private final int[] mAttestationIds;
private final boolean mUniqueIdIncluded;
private final boolean mUserAuthenticationValidWhileOnBody;
private final boolean mInvalidatedByBiometricEnrollment;
@@ -275,6 +317,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
private final boolean mUnlockedDeviceRequired;
private final boolean mCriticalToDeviceEncryption;
private final int mMaxUsageCount;
+ private final String mAttestKeyAlias;
/*
* ***NOTE***: All new fields MUST also be added to the following:
* ParcelableKeyGenParameterSpec class.
@@ -308,6 +351,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
boolean userPresenceRequired,
byte[] attestationChallenge,
boolean devicePropertiesAttestationIncluded,
+ int[] attestationIds,
boolean uniqueIdIncluded,
boolean userAuthenticationValidWhileOnBody,
boolean invalidatedByBiometricEnrollment,
@@ -315,7 +359,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
boolean userConfirmationRequired,
boolean unlockedDeviceRequired,
boolean criticalToDeviceEncryption,
- int maxUsageCount) {
+ int maxUsageCount,
+ String attestKeyAlias) {
if (TextUtils.isEmpty(keyStoreAlias)) {
throw new IllegalArgumentException("keyStoreAlias must not be empty");
}
@@ -361,6 +406,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
mUserAuthenticationType = userAuthenticationType;
mAttestationChallenge = Utils.cloneIfNotNull(attestationChallenge);
mDevicePropertiesAttestationIncluded = devicePropertiesAttestationIncluded;
+ mAttestationIds = attestationIds;
mUniqueIdIncluded = uniqueIdIncluded;
mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
@@ -369,6 +415,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
mUnlockedDeviceRequired = unlockedDeviceRequired;
mCriticalToDeviceEncryption = criticalToDeviceEncryption;
mMaxUsageCount = maxUsageCount;
+ mAttestKeyAlias = attestKeyAlias;
}
/**
@@ -720,6 +767,25 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
+ * @hide
+ * Allows the caller to specify device IDs to be attested to in the certificate for the
+ * generated key pair. These values are the enums specified in
+ * {@link android.security.keystore.AttestationUtils}
+ *
+ * @see android.security.keystore.AttestationUtils#ID_TYPE_SERIAL
+ * @see android.security.keystore.AttestationUtils#ID_TYPE_IMEI
+ * @see android.security.keystore.AttestationUtils#ID_TYPE_MEID
+ * @see android.security.keystore.AttestationUtils#USE_INDIVIDUAL_ATTESTATION
+ *
+ * @return integer array representing the requested device IDs to attest.
+ */
+ @SystemApi
+ @Nullable
+ public int[] getAttestationIds() {
+ return Utils.cloneIfNotNull(mAttestationIds);
+ }
+
+ /**
* @hide This is a system-only API
*
* Returns {@code true} if the attestation certificate will contain a unique ID field.
@@ -806,6 +872,18 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
+ * Returns the alias of the attestation key that will be used to sign the attestation
+ * certificate of the generated key. Note that an attestation certificate will only be
+ * generated if an attestation challenge is set.
+ *
+ * @see Builder#setAttestKeyAlias(String)
+ */
+ @Nullable
+ public String getAttestKeyAlias() {
+ return mAttestKeyAlias;
+ }
+
+ /**
* Builder of {@link KeyGenParameterSpec} instances.
*/
public final static class Builder {
@@ -834,6 +912,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
private boolean mUserPresenceRequired = false;
private byte[] mAttestationChallenge = null;
private boolean mDevicePropertiesAttestationIncluded = false;
+ private int[] mAttestationIds = null;
private boolean mUniqueIdIncluded = false;
private boolean mUserAuthenticationValidWhileOnBody;
private boolean mInvalidatedByBiometricEnrollment = true;
@@ -842,6 +921,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
private boolean mUnlockedDeviceRequired = false;
private boolean mCriticalToDeviceEncryption = false;
private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
+ private String mAttestKeyAlias = null;
/**
* Creates a new instance of the {@code Builder}.
@@ -902,6 +982,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
mAttestationChallenge = sourceSpec.getAttestationChallenge();
mDevicePropertiesAttestationIncluded =
sourceSpec.isDevicePropertiesAttestationIncluded();
+ mAttestationIds = sourceSpec.getAttestationIds();
mUniqueIdIncluded = sourceSpec.isUniqueIdIncluded();
mUserAuthenticationValidWhileOnBody = sourceSpec.isUserAuthenticationValidWhileOnBody();
mInvalidatedByBiometricEnrollment = sourceSpec.isInvalidatedByBiometricEnrollment();
@@ -910,6 +991,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
mUnlockedDeviceRequired = sourceSpec.isUnlockedDeviceRequired();
mCriticalToDeviceEncryption = sourceSpec.isCriticalToDeviceEncryption();
mMaxUsageCount = sourceSpec.getMaxUsageCount();
+ mAttestKeyAlias = sourceSpec.getAttestKeyAlias();
}
/**
@@ -1473,6 +1555,26 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
+ * @hide
+ * Sets which IDs to attest in the attestation certificate for the key. The acceptable
+ * values in this integer array are the enums specified in
+ * {@link android.security.keystore.AttestationUtils}
+ *
+ * @param attestationIds the array of ID types to attest to in the certificate.
+ *
+ * @see android.security.keystore.AttestationUtils#ID_TYPE_SERIAL
+ * @see android.security.keystore.AttestationUtils#ID_TYPE_IMEI
+ * @see android.security.keystore.AttestationUtils#ID_TYPE_MEID
+ * @see android.security.keystore.AttestationUtils#USE_INDIVIDUAL_ATTESTATION
+ */
+ @SystemApi
+ @NonNull
+ public Builder setAttestationIds(@NonNull int[] attestationIds) {
+ mAttestationIds = attestationIds;
+ return this;
+ }
+
+ /**
* @hide Only system apps can use this method.
*
* Sets whether to include a temporary unique ID field in the attestation certificate.
@@ -1610,6 +1712,28 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
+ * Sets the alias of the attestation key that will be used to sign the attestation
+ * certificate for the generated key pair, if an attestation challenge is set with {@link
+ * #setAttestationChallenge}. If an attestKeyAlias is set but no challenge, {@link
+ * java.security.KeyPairGenerator#initialize} will throw {@link
+ * java.security.InvalidAlgorithmParameterException}.
+ *
+ * <p>If the attestKeyAlias is set to null (the default), Android Keystore will select an
+ * appropriate system-provided attestation signing key. If not null, the alias must
+ * reference an Android Keystore Key that was created with {@link
+ * android.security.keystore.KeyProperties#PURPOSE_ATTEST_KEY}, or key generation will throw
+ * {@link java.security.InvalidAlgorithmParameterException}.
+ *
+ * @param attestKeyAlias the alias of the attestation key to be used to sign the
+ * attestation certificate.
+ */
+ @NonNull
+ public Builder setAttestKeyAlias(@Nullable String attestKeyAlias) {
+ mAttestKeyAlias = attestKeyAlias;
+ return this;
+ }
+
+ /**
* Builds an instance of {@code KeyGenParameterSpec}.
*/
@NonNull
@@ -1638,6 +1762,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
mUserPresenceRequired,
mAttestationChallenge,
mDevicePropertiesAttestationIncluded,
+ mAttestationIds,
mUniqueIdIncluded,
mUserAuthenticationValidWhileOnBody,
mInvalidatedByBiometricEnrollment,
@@ -1645,7 +1770,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
mUserConfirmationRequired,
mUnlockedDeviceRequired,
mCriticalToDeviceEncryption,
- mMaxUsageCount);
+ mMaxUsageCount,
+ mAttestKeyAlias);
}
}
}
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 3ebca6ad302d..7b0fa91380e1 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -70,6 +70,7 @@ public abstract class KeyProperties {
PURPOSE_VERIFY,
PURPOSE_WRAP_KEY,
PURPOSE_AGREE_KEY,
+ PURPOSE_ATTEST_KEY,
})
public @interface PurposeEnum {}
@@ -100,10 +101,26 @@ public abstract class KeyProperties {
/**
* Purpose of key: creating a shared ECDH secret through key agreement.
+ *
+ * <p>A key having this purpose can be combined with the elliptic curve public key of another
+ * party to establish a shared secret over an insecure channel. It should be used as a
+ * parameter to {@link javax.crypto.KeyAgreement#init(java.security.Key)} (a complete example is
+ * available <a
+ * href="{@docRoot}reference/android/security/keystore/KeyGenParameterSpec#example:ecdh"
+ * >here</a>).
+ * See <a href="https://en.wikipedia.org/wiki/Elliptic-curve_Diffie%E2%80%93Hellman">this
+ * article</a> for a more detailed explanation.
*/
public static final int PURPOSE_AGREE_KEY = 1 << 6;
/**
+ * Purpose of key: Signing attestaions. This purpose is incompatible with all others, meaning
+ * that when generating a key with PURPOSE_ATTEST_KEY, no other purposes may be specified. In
+ * addition, PURPOSE_ATTEST_KEY may not be specified for imported keys.
+ */
+ public static final int PURPOSE_ATTEST_KEY = 1 << 7;
+
+ /**
* @hide
*/
public static abstract class Purpose {
@@ -123,6 +140,8 @@ public abstract class KeyProperties {
return KeymasterDefs.KM_PURPOSE_WRAP;
case PURPOSE_AGREE_KEY:
return KeymasterDefs.KM_PURPOSE_AGREE_KEY;
+ case PURPOSE_ATTEST_KEY:
+ return KeymasterDefs.KM_PURPOSE_ATTEST_KEY;
default:
throw new IllegalArgumentException("Unknown purpose: " + purpose);
}
@@ -142,6 +161,8 @@ public abstract class KeyProperties {
return PURPOSE_WRAP_KEY;
case KeymasterDefs.KM_PURPOSE_AGREE_KEY:
return PURPOSE_AGREE_KEY;
+ case KeymasterDefs.KM_PURPOSE_ATTEST_KEY:
+ return PURPOSE_ATTEST_KEY;
default:
throw new IllegalArgumentException("Unknown purpose: " + purpose);
}
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index aaa37150c0e9..fe92270ca508 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -588,7 +588,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
private boolean mCriticalToDeviceEncryption = false;
private boolean mIsStrongBoxBacked = false;
- private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
+ private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
+ private String mAttestKeyAlias = null;
/**
* Creates a new instance of the {@code Builder}.
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 8163472abdfb..c20cf01a993e 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -101,6 +101,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
out.writeBoolean(mSpec.isUserPresenceRequired());
out.writeByteArray(mSpec.getAttestationChallenge());
out.writeBoolean(mSpec.isDevicePropertiesAttestationIncluded());
+ out.writeIntArray(mSpec.getAttestationIds());
out.writeBoolean(mSpec.isUniqueIdIncluded());
out.writeBoolean(mSpec.isUserAuthenticationValidWhileOnBody());
out.writeBoolean(mSpec.isInvalidatedByBiometricEnrollment());
@@ -109,6 +110,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
out.writeBoolean(mSpec.isUnlockedDeviceRequired());
out.writeBoolean(mSpec.isCriticalToDeviceEncryption());
out.writeInt(mSpec.getMaxUsageCount());
+ out.writeString(mSpec.getAttestKeyAlias());
}
private static Date readDateOrNull(Parcel in) {
@@ -160,6 +162,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
final boolean userPresenceRequired = in.readBoolean();
final byte[] attestationChallenge = in.createByteArray();
final boolean devicePropertiesAttestationIncluded = in.readBoolean();
+ final int[] attestationIds = in.createIntArray();
final boolean uniqueIdIncluded = in.readBoolean();
final boolean userAuthenticationValidWhileOnBody = in.readBoolean();
final boolean invalidatedByBiometricEnrollment = in.readBoolean();
@@ -168,6 +171,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
final boolean unlockedDeviceRequired = in.readBoolean();
final boolean criticalToDeviceEncryption = in.readBoolean();
final int maxUsageCount = in.readInt();
+ final String attestKeyAlias = in.readString();
// The KeyGenParameterSpec is intentionally not constructed using a Builder here:
// The intention is for this class to break if new parameters are added to the
// KeyGenParameterSpec constructor (whereas using a builder would silently drop them).
@@ -195,6 +199,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
userPresenceRequired,
attestationChallenge,
devicePropertiesAttestationIncluded,
+ attestationIds,
uniqueIdIncluded,
userAuthenticationValidWhileOnBody,
invalidatedByBiometricEnrollment,
@@ -202,7 +207,8 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
userConfirmationRequired,
unlockedDeviceRequired,
criticalToDeviceEncryption,
- maxUsageCount);
+ maxUsageCount,
+ attestKeyAlias);
}
public static final @android.annotation.NonNull Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() {
diff --git a/keystore/java/android/security/keystore/Utils.java b/keystore/java/android/security/keystore/Utils.java
index 5722c7b53ef4..e58b1ccb5370 100644
--- a/keystore/java/android/security/keystore/Utils.java
+++ b/keystore/java/android/security/keystore/Utils.java
@@ -33,4 +33,8 @@ abstract class Utils {
static byte[] cloneIfNotNull(byte[] value) {
return (value != null) ? value.clone() : null;
}
+
+ static int[] cloneIfNotNull(int[] value) {
+ return (value != null) ? value.clone() : null;
+ }
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
index 8475ad9fd57b..0f777495a3fe 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
@@ -164,6 +164,9 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC
List<KeyParameter> parameters = new ArrayList<>();
parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_SIGN
+ ));
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC
));
parameters.add(KeyStore2ParameterUtils.makeEnum(
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
index 32650aeda1b1..5619585d9c3c 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
@@ -21,7 +21,6 @@ import android.security.KeyStoreSecurityLevel;
import android.system.keystore2.Authorization;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
-import android.util.Log;
import java.security.Key;
@@ -127,15 +126,6 @@ public class AndroidKeyStoreKey implements Key {
return false;
}
- // If the key ids are equal and the class matches all the other fields cannot differ
- // unless we have a bug.
- if (!mAlgorithm.equals(other.mAlgorithm)
- || !mAuthorizations.equals(other.mAuthorizations)
- || !mDescriptor.equals(other.mDescriptor)) {
- Log.e("AndroidKeyStoreKey", "Bug: key ids are identical, but key metadata"
- + "differs.");
- return false;
- }
return true;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 6a92980de37c..b3bfd6a3a97a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -18,26 +18,36 @@ package android.security.keystore2;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyPurpose;
import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.keymint.Tag;
import android.os.Build;
import android.security.KeyPairGeneratorSpec;
+import android.security.KeyStore;
import android.security.KeyStore2;
import android.security.KeyStoreException;
import android.security.KeyStoreSecurityLevel;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.ArrayUtils;
+import android.security.keystore.AttestationUtils;
+import android.security.keystore.DeviceIdAttestationException;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeymasterUtils;
import android.security.keystore.SecureKeyImportUnavailableException;
import android.security.keystore.StrongBoxUnavailableException;
+import android.system.keystore2.Authorization;
import android.system.keystore2.Domain;
import android.system.keystore2.IKeystoreSecurityLevel;
import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyEntryResponse;
import android.system.keystore2.KeyMetadata;
import android.system.keystore2.ResponseCode;
+import android.telephony.TelephonyManager;
+import android.util.ArraySet;
import android.util.Log;
import libcore.util.EmptyArray;
@@ -55,6 +65,7 @@ import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -63,6 +74,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.function.Predicate;
/**
* Provides a way to create instances of a KeyPair which will be placed in the
@@ -147,6 +159,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
private int mKeymasterAlgorithm = -1;
private int mKeySizeBits;
private SecureRandom mRng;
+ private KeyDescriptor mAttestKeyDescriptor;
private int[] mKeymasterPurposes;
private int[] mKeymasterBlockModes;
@@ -191,83 +204,9 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
// Legacy/deprecated spec
KeyPairGeneratorSpec legacySpec = (KeyPairGeneratorSpec) params;
try {
- KeyGenParameterSpec.Builder specBuilder;
- String specKeyAlgorithm = legacySpec.getKeyType();
- if (specKeyAlgorithm != null) {
- // Spec overrides the generator's default key algorithm
- try {
- keymasterAlgorithm =
- KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
- specKeyAlgorithm);
- } catch (IllegalArgumentException e) {
- throw new InvalidAlgorithmParameterException(
- "Invalid key type in parameters", e);
- }
- }
- switch (keymasterAlgorithm) {
- case KeymasterDefs.KM_ALGORITHM_EC:
- specBuilder = new KeyGenParameterSpec.Builder(
- legacySpec.getKeystoreAlias(),
- KeyProperties.PURPOSE_SIGN
- | KeyProperties.PURPOSE_VERIFY);
- // Authorized to be used with any digest (including no digest).
- // MD5 was never offered for Android Keystore for ECDSA.
- specBuilder.setDigests(
- KeyProperties.DIGEST_NONE,
- KeyProperties.DIGEST_SHA1,
- KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256,
- KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512);
- break;
- case KeymasterDefs.KM_ALGORITHM_RSA:
- specBuilder = new KeyGenParameterSpec.Builder(
- legacySpec.getKeystoreAlias(),
- KeyProperties.PURPOSE_ENCRYPT
- | KeyProperties.PURPOSE_DECRYPT
- | KeyProperties.PURPOSE_SIGN
- | KeyProperties.PURPOSE_VERIFY);
- // Authorized to be used with any digest (including no digest).
- specBuilder.setDigests(
- KeyProperties.DIGEST_NONE,
- KeyProperties.DIGEST_MD5,
- KeyProperties.DIGEST_SHA1,
- KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256,
- KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512);
- // Authorized to be used with any encryption and signature padding
- // schemes (including no padding).
- specBuilder.setEncryptionPaddings(
- KeyProperties.ENCRYPTION_PADDING_NONE,
- KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
- KeyProperties.ENCRYPTION_PADDING_RSA_OAEP);
- specBuilder.setSignaturePaddings(
- KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
- KeyProperties.SIGNATURE_PADDING_RSA_PSS);
- // Disable randomized encryption requirement to support encryption
- // padding NONE above.
- specBuilder.setRandomizedEncryptionRequired(false);
- break;
- default:
- throw new ProviderException(
- "Unsupported algorithm: " + mKeymasterAlgorithm);
- }
-
- if (legacySpec.getKeySize() != -1) {
- specBuilder.setKeySize(legacySpec.getKeySize());
- }
- if (legacySpec.getAlgorithmParameterSpec() != null) {
- specBuilder.setAlgorithmParameterSpec(
- legacySpec.getAlgorithmParameterSpec());
- }
- specBuilder.setCertificateSubject(legacySpec.getSubjectDN());
- specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber());
- specBuilder.setCertificateNotBefore(legacySpec.getStartDate());
- specBuilder.setCertificateNotAfter(legacySpec.getEndDate());
- specBuilder.setUserAuthenticationRequired(false);
-
- spec = specBuilder.build();
+ keymasterAlgorithm = getKeymasterAlgorithmFromLegacy(keymasterAlgorithm,
+ legacySpec);
+ spec = buildKeyGenParameterSpecFromLegacy(legacySpec, keymasterAlgorithm);
} catch (NullPointerException | IllegalArgumentException e) {
throw new InvalidAlgorithmParameterException(e);
}
@@ -336,6 +275,10 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
mJcaKeyAlgorithm = jcaKeyAlgorithm;
mRng = random;
mKeyStore = KeyStore2.getInstance();
+
+ mAttestKeyDescriptor = buildAndCheckAttestKeyDescriptor(spec);
+ checkAttestKeyPurpose(spec);
+
success = true;
} finally {
if (!success) {
@@ -344,6 +287,156 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
}
}
+ private void checkAttestKeyPurpose(KeyGenParameterSpec spec)
+ throws InvalidAlgorithmParameterException {
+ if ((spec.getPurposes() & KeyProperties.PURPOSE_ATTEST_KEY) != 0
+ && spec.getPurposes() != KeyProperties.PURPOSE_ATTEST_KEY) {
+ throw new InvalidAlgorithmParameterException(
+ "PURPOSE_ATTEST_KEY may not be specified with any other purposes");
+ }
+ }
+
+ private KeyDescriptor buildAndCheckAttestKeyDescriptor(KeyGenParameterSpec spec)
+ throws InvalidAlgorithmParameterException {
+ if (spec.getAttestKeyAlias() != null) {
+ KeyDescriptor attestKeyDescriptor = new KeyDescriptor();
+ attestKeyDescriptor.domain = Domain.APP;
+ attestKeyDescriptor.alias = spec.getAttestKeyAlias();
+ try {
+ KeyEntryResponse attestKey = mKeyStore.getKeyEntry(attestKeyDescriptor);
+ checkAttestKeyChallenge(spec);
+ checkAttestKeyPurpose(attestKey.metadata.authorizations);
+ checkAttestKeySecurityLevel(spec, attestKey);
+ } catch (KeyStoreException e) {
+ throw new InvalidAlgorithmParameterException("Invalid attestKeyAlias", e);
+ }
+ return attestKeyDescriptor;
+ }
+ return null;
+ }
+
+ private void checkAttestKeyChallenge(KeyGenParameterSpec spec)
+ throws InvalidAlgorithmParameterException {
+ if (spec.getAttestationChallenge() == null) {
+ throw new InvalidAlgorithmParameterException(
+ "AttestKey specified but no attestation challenge provided");
+ }
+ }
+
+ private void checkAttestKeyPurpose(Authorization[] keyAuths)
+ throws InvalidAlgorithmParameterException {
+ Predicate<Authorization> isAttestKeyPurpose = x -> x.keyParameter.tag == Tag.PURPOSE
+ && x.keyParameter.value.getKeyPurpose() == KeyPurpose.ATTEST_KEY;
+
+ if (Arrays.stream(keyAuths).noneMatch(isAttestKeyPurpose)) {
+ throw new InvalidAlgorithmParameterException(
+ ("Invalid attestKey, does not have PURPOSE_ATTEST_KEY"));
+ }
+ }
+
+ private void checkAttestKeySecurityLevel(KeyGenParameterSpec spec, KeyEntryResponse key)
+ throws InvalidAlgorithmParameterException {
+ boolean attestKeyInStrongBox = key.metadata.keySecurityLevel == SecurityLevel.STRONGBOX;
+ if (spec.isStrongBoxBacked() != attestKeyInStrongBox) {
+ if (attestKeyInStrongBox) {
+ throw new InvalidAlgorithmParameterException(
+ "Invalid security level: Cannot sign non-StrongBox key with "
+ + "StrongBox attestKey");
+
+ } else {
+ throw new InvalidAlgorithmParameterException(
+ "Invalid security level: Cannot sign StrongBox key with "
+ + "non-StrongBox attestKey");
+ }
+ }
+ }
+
+ private int getKeymasterAlgorithmFromLegacy(int keymasterAlgorithm,
+ KeyPairGeneratorSpec legacySpec) throws InvalidAlgorithmParameterException {
+ String specKeyAlgorithm = legacySpec.getKeyType();
+ if (specKeyAlgorithm != null) {
+ // Spec overrides the generator's default key algorithm
+ try {
+ keymasterAlgorithm =
+ KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
+ specKeyAlgorithm);
+ } catch (IllegalArgumentException e) {
+ throw new InvalidAlgorithmParameterException(
+ "Invalid key type in parameters", e);
+ }
+ }
+ return keymasterAlgorithm;
+ }
+
+ private KeyGenParameterSpec buildKeyGenParameterSpecFromLegacy(KeyPairGeneratorSpec legacySpec,
+ int keymasterAlgorithm) {
+ KeyGenParameterSpec.Builder specBuilder;
+ switch (keymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_EC:
+ specBuilder = new KeyGenParameterSpec.Builder(
+ legacySpec.getKeystoreAlias(),
+ KeyProperties.PURPOSE_SIGN
+ | KeyProperties.PURPOSE_VERIFY);
+ // Authorized to be used with any digest (including no digest).
+ // MD5 was never offered for Android Keystore for ECDSA.
+ specBuilder.setDigests(
+ KeyProperties.DIGEST_NONE,
+ KeyProperties.DIGEST_SHA1,
+ KeyProperties.DIGEST_SHA224,
+ KeyProperties.DIGEST_SHA256,
+ KeyProperties.DIGEST_SHA384,
+ KeyProperties.DIGEST_SHA512);
+ break;
+ case KeymasterDefs.KM_ALGORITHM_RSA:
+ specBuilder = new KeyGenParameterSpec.Builder(
+ legacySpec.getKeystoreAlias(),
+ KeyProperties.PURPOSE_ENCRYPT
+ | KeyProperties.PURPOSE_DECRYPT
+ | KeyProperties.PURPOSE_SIGN
+ | KeyProperties.PURPOSE_VERIFY);
+ // Authorized to be used with any digest (including no digest).
+ specBuilder.setDigests(
+ KeyProperties.DIGEST_NONE,
+ KeyProperties.DIGEST_MD5,
+ KeyProperties.DIGEST_SHA1,
+ KeyProperties.DIGEST_SHA224,
+ KeyProperties.DIGEST_SHA256,
+ KeyProperties.DIGEST_SHA384,
+ KeyProperties.DIGEST_SHA512);
+ // Authorized to be used with any encryption and signature padding
+ // schemes (including no padding).
+ specBuilder.setEncryptionPaddings(
+ KeyProperties.ENCRYPTION_PADDING_NONE,
+ KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
+ KeyProperties.ENCRYPTION_PADDING_RSA_OAEP);
+ specBuilder.setSignaturePaddings(
+ KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
+ KeyProperties.SIGNATURE_PADDING_RSA_PSS);
+ // Disable randomized encryption requirement to support encryption
+ // padding NONE above.
+ specBuilder.setRandomizedEncryptionRequired(false);
+ break;
+ default:
+ throw new ProviderException(
+ "Unsupported algorithm: " + mKeymasterAlgorithm);
+ }
+
+ if (legacySpec.getKeySize() != -1) {
+ specBuilder.setKeySize(legacySpec.getKeySize());
+ }
+ if (legacySpec.getAlgorithmParameterSpec() != null) {
+ specBuilder.setAlgorithmParameterSpec(
+ legacySpec.getAlgorithmParameterSpec());
+ }
+ specBuilder.setCertificateSubject(legacySpec.getSubjectDN());
+ specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber());
+ specBuilder.setCertificateNotBefore(legacySpec.getStartDate());
+ specBuilder.setCertificateNotAfter(legacySpec.getEndDate());
+ specBuilder.setUserAuthenticationRequired(false);
+
+ return specBuilder.build();
+ }
+
private void resetAll() {
mEntryAlias = null;
mEntryUid = KeyProperties.NAMESPACE_APPLICATION;
@@ -458,7 +551,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
try {
KeyStoreSecurityLevel iSecurityLevel = mKeyStore.getSecurityLevel(securityLevel);
- KeyMetadata metadata = iSecurityLevel.generateKey(descriptor, null,
+ KeyMetadata metadata = iSecurityLevel.generateKey(descriptor, mAttestKeyDescriptor,
constructKeyGenerationArguments(), flags, additionalEntropy);
AndroidKeyStorePublicKey publicKey =
@@ -478,7 +571,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
}
throw p;
}
- } catch (UnrecoverableKeyException e) {
+ } catch (UnrecoverableKeyException | IllegalArgumentException
+ | DeviceIdAttestationException e) {
throw new ProviderException(
"Failed to construct key object from newly generated key pair.", e);
} finally {
@@ -496,7 +590,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
}
private void addAttestationParameters(@NonNull List<KeyParameter> params)
- throws ProviderException {
+ throws ProviderException, IllegalArgumentException, DeviceIdAttestationException {
byte[] challenge = mSpec.getAttestationChallenge();
if (challenge != null) {
@@ -526,15 +620,69 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
Build.MODEL.getBytes(StandardCharsets.UTF_8)
));
}
- } else {
- if (mSpec.isDevicePropertiesAttestationIncluded()) {
- throw new ProviderException("An attestation challenge must be provided when "
- + "requesting device properties attestation.");
+
+ int[] idTypes = mSpec.getAttestationIds();
+ if (idTypes == null) {
+ return;
+ }
+ final Set<Integer> idTypesSet = new ArraySet<>(idTypes.length);
+ for (int idType : idTypes) {
+ idTypesSet.add(idType);
+ }
+ TelephonyManager telephonyService = null;
+ if (idTypesSet.contains(AttestationUtils.ID_TYPE_IMEI)
+ || idTypesSet.contains(AttestationUtils.ID_TYPE_MEID)) {
+ telephonyService =
+ (TelephonyManager) KeyStore.getApplicationContext().getSystemService(
+ Context.TELEPHONY_SERVICE);
+ if (telephonyService == null) {
+ throw new DeviceIdAttestationException("Unable to access telephony service");
+ }
+ }
+ for (final Integer idType : idTypesSet) {
+ switch (idType) {
+ case AttestationUtils.ID_TYPE_SERIAL:
+ params.add(KeyStore2ParameterUtils.makeBytes(
+ KeymasterDefs.KM_TAG_ATTESTATION_ID_SERIAL,
+ Build.getSerial().getBytes(StandardCharsets.UTF_8)
+ ));
+ break;
+ case AttestationUtils.ID_TYPE_IMEI: {
+ final String imei = telephonyService.getImei(0);
+ if (imei == null) {
+ throw new DeviceIdAttestationException("Unable to retrieve IMEI");
+ }
+ params.add(KeyStore2ParameterUtils.makeBytes(
+ KeymasterDefs.KM_TAG_ATTESTATION_ID_IMEI,
+ imei.getBytes(StandardCharsets.UTF_8)
+ ));
+ break;
+ }
+ case AttestationUtils.ID_TYPE_MEID: {
+ final String meid = telephonyService.getMeid(0);
+ if (meid == null) {
+ throw new DeviceIdAttestationException("Unable to retrieve MEID");
+ }
+ params.add(KeyStore2ParameterUtils.makeBytes(
+ KeymasterDefs.KM_TAG_ATTESTATION_ID_MEID,
+ meid.getBytes(StandardCharsets.UTF_8)
+ ));
+ break;
+ }
+ case AttestationUtils.USE_INDIVIDUAL_ATTESTATION: {
+ params.add(KeyStore2ParameterUtils.makeBool(
+ KeymasterDefs.KM_TAG_DEVICE_UNIQUE_ATTESTATION));
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Unknown device ID type " + idType);
+ }
}
}
}
- private Collection<KeyParameter> constructKeyGenerationArguments() {
+ private Collection<KeyParameter> constructKeyGenerationArguments()
+ throws DeviceIdAttestationException, IllegalArgumentException {
List<KeyParameter> params = new ArrayList<>();
params.add(KeyStore2ParameterUtils.makeInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits));
params.add(KeyStore2ParameterUtils.makeEnum(
@@ -585,6 +733,30 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
mSpec.getKeyValidityForConsumptionEnd()
));
}
+ if (mSpec.getCertificateNotAfter() != null) {
+ params.add(KeyStore2ParameterUtils.makeDate(
+ KeymasterDefs.KM_TAG_CERTIFICATE_NOT_AFTER,
+ mSpec.getCertificateNotAfter()
+ ));
+ }
+ if (mSpec.getCertificateNotBefore() != null) {
+ params.add(KeyStore2ParameterUtils.makeDate(
+ KeymasterDefs.KM_TAG_CERTIFICATE_NOT_BEFORE,
+ mSpec.getCertificateNotBefore()
+ ));
+ }
+ if (mSpec.getCertificateSerialNumber() != null) {
+ params.add(KeyStore2ParameterUtils.makeBignum(
+ KeymasterDefs.KM_TAG_CERTIFICATE_SERIAL,
+ mSpec.getCertificateSerialNumber()
+ ));
+ }
+ if (mSpec.getCertificateSubject() != null) {
+ params.add(KeyStore2ParameterUtils.makeBytes(
+ KeymasterDefs.KM_TAG_CERTIFICATE_SUBJECT,
+ mSpec.getCertificateSubject().getEncoded()
+ ));
+ }
if (mSpec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
params.add(KeyStore2ParameterUtils.makeInt(
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 8c8acc418a0e..39607aeb3852 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -866,7 +866,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
try {
response = mKeyStore.getKeyEntry(wrappingkey);
} catch (android.security.KeyStoreException e) {
- throw new KeyStoreException("Failed to load wrapping key.", e);
+ throw new KeyStoreException("Failed to import wrapped key. Keystore error code: "
+ + e.getErrorCode(), e);
}
KeyDescriptor wrappedKey = makeKeyDescriptor(alias);
diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
index 4c8ab8d6c713..dcdd7defd752 100644
--- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
@@ -28,6 +28,7 @@ import android.security.keystore.KeyProperties;
import android.security.keystore.UserAuthArgs;
import android.system.keystore2.Authorization;
+import java.math.BigInteger;
import java.security.ProviderException;
import java.util.ArrayList;
import java.util.Date;
@@ -154,6 +155,23 @@ public abstract class KeyStore2ParameterUtils {
}
/**
+ * This function constructs a {@link KeyParameter} expressing a Bignum.
+ * @param tag Must be KeyMint tag with the associated type BIGNUM.
+ * @param b A BitInteger to be stored in the new key parameter.
+ * @return An instance of {@link KeyParameter}.
+ * @hide
+ */
+ static @NonNull KeyParameter makeBignum(int tag, @NonNull BigInteger b) {
+ if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BIGNUM) {
+ throw new IllegalArgumentException("Not a bignum tag: " + tag);
+ }
+ KeyParameter p = new KeyParameter();
+ p.tag = tag;
+ p.value = KeyParameterValue.blob(b.toByteArray());
+ return p;
+ }
+
+ /**
* This function constructs a {@link KeyParameter} expressing date.
* @param tag Must be KeyMint tag with the associated type DATE.
* @param date A date
@@ -167,10 +185,6 @@ public abstract class KeyStore2ParameterUtils {
KeyParameter p = new KeyParameter();
p.tag = tag;
p.value = KeyParameterValue.dateTime(date.getTime());
- if (p.value.getDateTime() < 0) {
- throw new IllegalArgumentException("Date tag value out of range: "
- + p.value.getDateTime());
- }
return p;
}
/**
diff --git a/keystore/tests/Android.bp b/keystore/tests/Android.bp
index e9b22c140742..2315a8568c64 100644
--- a/keystore/tests/Android.bp
+++ b/keystore/tests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_keystore_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_keystore_license"],
+}
+
android_test {
name: "KeystoreTests",
// LOCAL_MODULE := keystore
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index 4612ba28d1db..d8f00bbc1ad3 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
// Sidecar
android_library_import {
name: "window-sidecar",
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
index 0bf6965beb5f..5c91cf41bfc6 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
@@ -72,7 +72,7 @@ class SampleExtensionImpl extends StubExtension implements
private List<ExtensionDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
List<ExtensionDisplayFeature> features = new ArrayList<>();
- int displayId = activity.getDisplayId();
+ int displayId = activity.getDisplay().getDisplayId();
if (displayId != DEFAULT_DISPLAY) {
Log.w(TAG, "This sample doesn't support display features on secondary displays");
return features;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
index 1094a0e2b4da..d3700f88d97f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -84,7 +84,7 @@ class SampleSidecarImpl extends StubSidecar implements
private List<SidecarDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>();
- int displayId = activity.getDisplayId();
+ int displayId = activity.getDisplay().getDisplayId();
if (displayId != DEFAULT_DISPLAY) {
Log.w(TAG, "This sample doesn't support display features on secondary displays");
return features;
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 96e0559b0df6..1b5dc8bdbcaa 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
// Begin ProtoLog
java_library {
name: "wm_shell_protolog-groups",
@@ -71,27 +80,6 @@ genrule {
"$(locations :wm_shell-sources)",
out: ["wm_shell_protolog.json"],
}
-
-filegroup {
- name: "wm_shell_protolog.json",
- srcs: ["res/raw/wm_shell_protolog.json"],
-}
-
-genrule {
- name: "checked-wm_shell_protolog.json",
- srcs: [
- ":generate-wm_shell_protolog.json",
- ":wm_shell_protolog.json",
- ],
- cmd: "cp $(location :generate-wm_shell_protolog.json) $(out) && " +
- "{ ! (diff $(out) $(location :wm_shell_protolog.json) | grep -q '^<') || " +
- "{ echo -e '\\n\\n################################################################\\n#\\n" +
- "# ERROR: ProtoLog viewer config is stale. To update it, run:\\n#\\n" +
- "# cp $(location :generate-wm_shell_protolog.json) " +
- "$(location :wm_shell_protolog.json)\\n#\\n" +
- "################################################################\\n\\n' >&2 && false; } }",
- out: ["wm_shell_protolog.json"],
-}
// End ProtoLog
java_library {
@@ -115,6 +103,9 @@ android_library {
resource_dirs: [
"res",
],
+ java_resources: [
+ ":generate-wm_shell_protolog.json"
+ ],
static_libs: [
"androidx.appcompat_appcompat",
"androidx.arch.core_core-runtime",
diff --git a/libs/WindowManager/Shell/res/drawable/pip_expand.xml b/libs/WindowManager/Shell/res/drawable/pip_expand.xml
index c99d81934aab..d36c4f72ecd0 100644
--- a/libs/WindowManager/Shell/res/drawable/pip_expand.xml
+++ b/libs/WindowManager/Shell/res/drawable/pip_expand.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="36dp"
- android:height="36dp"
+ android:width="@dimen/pip_expand_action_inner_size"
+ android:height="@dimen/pip_expand_action_inner_size"
android:viewportWidth="36"
android:viewportHeight="36">
@@ -25,4 +25,4 @@
android:fillColor="#FFFFFF"
android:pathData="M10 21H7v8h8v-3h-5v-5zm-3-6h3v-5h5V7H7v8zm19 11h-5v3h8v-8h-3v5zM21
7v3h5v5h3V7h-8z" />
-</vector> \ No newline at end of file
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml
index bcc850a854de..60456267afef 100644
--- a/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml
@@ -15,8 +15,8 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
+ android:width="@dimen/pip_action_inner_size"
+ android:height="@dimen/pip_action_inner_size"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_pause_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_pause_white.xml
index ef9b2d9c1c63..0c469f7abb78 100644
--- a/libs/WindowManager/Shell/res/drawable/pip_ic_pause_white.xml
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_pause_white.xml
@@ -15,8 +15,8 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="@dimen/pip_action_inner_size"
+ android:height="@dimen/pip_action_inner_size"
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_play_arrow_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_play_arrow_white.xml
index f12d2cbebc87..8567afa3232c 100644
--- a/libs/WindowManager/Shell/res/drawable/pip_ic_play_arrow_white.xml
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_play_arrow_white.xml
@@ -15,8 +15,8 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="@dimen/pip_action_inner_size"
+ android:height="@dimen/pip_action_inner_size"
android:viewportWidth="24"
android:viewportHeight="24">
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml
index b61e98ce2f9f..73ec167f1f6f 100644
--- a/libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml
@@ -15,8 +15,8 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="@dimen/pip_action_inner_size"
+ android:height="@dimen/pip_action_inner_size"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_skip_next_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_skip_next_white.xml
index 040c7e642241..6c5542131ab8 100644
--- a/libs/WindowManager/Shell/res/drawable/pip_ic_skip_next_white.xml
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_skip_next_white.xml
@@ -15,8 +15,8 @@ Copyright (C) 2017 The Android Open Source Project
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="@dimen/pip_action_inner_size"
+ android:height="@dimen/pip_action_inner_size"
android:viewportWidth="24"
android:viewportHeight="24">
@@ -25,4 +25,4 @@ Copyright (C) 2017 The Android Open Source Project
android:pathData="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z" />
<path
android:pathData="M0 0h24v24H0z" />
-</vector> \ No newline at end of file
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_skip_previous_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_skip_previous_white.xml
index b9b94b73a00f..6b5382b662b1 100644
--- a/libs/WindowManager/Shell/res/drawable/pip_ic_skip_previous_white.xml
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_skip_previous_white.xml
@@ -15,8 +15,8 @@ Copyright (C) 2017 The Android Open Source Project
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="@dimen/pip_action_inner_size"
+ android:height="@dimen/pip_action_inner_size"
android:viewportWidth="24"
android:viewportHeight="24">
@@ -25,4 +25,4 @@ Copyright (C) 2017 The Android Open Source Project
android:pathData="M6 6h2v12H6zm3.5 6l8.5 6V6z" />
<path
android:pathData="M0 0h24v24H0z" />
-</vector> \ No newline at end of file
+</vector>
diff --git a/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml b/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml
index dc54caf0f14a..0190aad2d0ef 100644
--- a/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml
+++ b/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml
@@ -54,8 +54,8 @@
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginBottom="0dp"
- android:layout_marginStart="86dp"
- android:layout_marginEnd="86dp"
+ android:layout_marginStart="46dp"
+ android:layout_marginEnd="46dp"
android:gravity="center_horizontal"
android:fontFamily="roboto-regular"
android:text="@string/one_handed_tutorial_description"
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
index 93a6e7b70c54..9fe024748610 100644
--- a/libs/WindowManager/Shell/res/layout/pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml
@@ -40,7 +40,7 @@
android:layout_height="@dimen/pip_expand_action_size"
android:layout_gravity="center"
android:contentDescription="@string/pip_phone_expand"
- android:padding="10dp"
+ android:gravity="center"
android:src="@drawable/pip_expand"
android:background="?android:selectableItemBackgroundBorderless" />
</FrameLayout>
@@ -72,8 +72,8 @@
android:id="@+id/settings"
android:layout_width="@dimen/pip_action_size"
android:layout_height="@dimen/pip_action_size"
- android:padding="@dimen/pip_action_padding"
android:contentDescription="@string/pip_phone_settings"
+ android:gravity="center"
android:src="@drawable/pip_ic_settings"
android:background="?android:selectableItemBackgroundBorderless" />
@@ -81,8 +81,8 @@
android:id="@+id/dismiss"
android:layout_width="@dimen/pip_action_size"
android:layout_height="@dimen/pip_action_size"
- android:padding="@dimen/pip_action_padding"
android:contentDescription="@string/pip_phone_close"
+ android:gravity="center"
android:src="@drawable/pip_ic_close_white"
android:background="?android:selectableItemBackgroundBorderless" />
</LinearLayout>
@@ -97,14 +97,4 @@
android:padding="@dimen/pip_resize_handle_padding"
android:src="@drawable/pip_resize_handle"
android:background="?android:selectableItemBackgroundBorderless" />
-
- <!-- invisible layer to trap the focus, b/169372603 -->
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@null"
- android:defaultFocusHighlightEnabled="false"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:focusedByDefault="true" />
-t </FrameLayout>
+</FrameLayout>
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu_action.xml b/libs/WindowManager/Shell/res/layout/pip_menu_action.xml
index 7a026ca63f50..a733b31d9fb0 100644
--- a/libs/WindowManager/Shell/res/layout/pip_menu_action.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu_action.xml
@@ -14,10 +14,18 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<ImageButton
+<com.android.wm.shell.pip.phone.PipMenuActionView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/pip_action_size"
android:layout_height="@dimen/pip_action_size"
- android:padding="@dimen/pip_action_padding"
android:background="?android:selectableItemBackgroundBorderless"
- android:forceHasOverlappingRendering="false" />
+ android:forceHasOverlappingRendering="false">
+
+ <ImageView
+ android:id="@+id/image"
+ android:layout_width="@dimen/pip_action_inner_size"
+ android:layout_height="@dimen/pip_action_inner_size"
+ android:layout_gravity="center"
+ android:scaleType="fitXY"/>
+
+</com.android.wm.shell.pip.phone.PipMenuActionView>
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml b/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
new file mode 100644
index 000000000000..cd3153145be3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
@@ -0,0 +1,30 @@
+<?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.wm.shell.sizecompatui.SizeCompatRestartButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ImageButton
+ android:id="@+id/size_compat_restart_button"
+ android:layout_width="@dimen/size_compat_button_size"
+ android:layout_height="@dimen/size_compat_button_size"
+ android:layout_gravity="center"
+ android:src="@drawable/size_compat_restart_button"
+ android:contentDescription="@string/restart_button_description"/>
+
+</com.android.wm.shell.sizecompatui.SizeCompatRestartButton>
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
deleted file mode 100644
index 9c3d84e72f8c..000000000000
--- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
+++ /dev/null
@@ -1,286 +0,0 @@
-{
- "version": "1.0.0",
- "messages": {
- "-2076257741": {
- "message": "Transition requested: %s %s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TRANSITIONS",
- "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
- },
- "-1683614271": {
- "message": "Existing task: id=%d component=%s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
- },
- "-1671119352": {
- "message": " Delegate animation for %s to %s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TRANSITIONS",
- "at": "com\/android\/wm\/shell\/transition\/RemoteTransitionHandler.java"
- },
- "-1501874464": {
- "message": "Fullscreen Task Appeared: #%d",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/FullscreenTaskListener.java"
- },
- "-1382704050": {
- "message": "Display removed: %d",
- "level": "VERBOSE",
- "group": "WM_SHELL_DRAG_AND_DROP",
- "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
- },
- "-1362429294": {
- "message": "%s onTaskAppeared Primary taskId=%d",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/legacysplitscreen\/LegacySplitScreenTaskListener.java"
- },
- "-1340279385": {
- "message": "Remove listener=%s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
- },
- "-1325223370": {
- "message": "Task appeared taskId=%d listener=%s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
- },
- "-1312360667": {
- "message": "createRootTask() displayId=%d winMode=%d listener=%s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
- },
- "-1308483871": {
- "message": " try handler %s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TRANSITIONS",
- "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
- },
- "-1297259344": {
- "message": " animated by %s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TRANSITIONS",
- "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
- },
- "-1269886472": {
- "message": "Transition %s doesn't have explicit remote, search filters for match for %s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TRANSITIONS",
- "at": "com\/android\/wm\/shell\/transition\/RemoteTransitionHandler.java"
- },
- "-1006733970": {
- "message": "Display added: %d",
- "level": "VERBOSE",
- "group": "WM_SHELL_DRAG_AND_DROP",
- "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
- },
- "-1000962629": {
- "message": "Animate bounds: from=%s to=%s",
- "level": "VERBOSE",
- "group": "WM_SHELL_DRAG_AND_DROP",
- "at": "com\/android\/wm\/shell\/draganddrop\/DropOutlineDrawable.java"
- },
- "-880817403": {
- "message": "Task vanished taskId=%d",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
- },
- "-742394458": {
- "message": "pair task1=%d task2=%d in AppPair=%s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/apppairs\/AppPair.java"
- },
- "-710770147": {
- "message": "Add target: %s",
- "level": "VERBOSE",
- "group": "WM_SHELL_DRAG_AND_DROP",
- "at": "com\/android\/wm\/shell\/draganddrop\/DragLayout.java"
- },
- "-298656957": {
- "message": "%s onTaskAppeared unknown taskId=%d winMode=%d",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/legacysplitscreen\/LegacySplitScreenTaskListener.java"
- },
- "-234284913": {
- "message": "unpair taskId=%d pair=%s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/apppairs\/AppPairsController.java"
- },
- "138343607": {
- "message": " try firstHandler %s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TRANSITIONS",
- "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
- },
- "157713005": {
- "message": "Task info changed taskId=%d",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
- },
- "214412327": {
- "message": "RemoteTransition directly requested for %s: %s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TRANSITIONS",
- "at": "com\/android\/wm\/shell\/transition\/RemoteTransitionHandler.java"
- },
- "274140888": {
- "message": "Animate alpha: from=%d to=%d",
- "level": "VERBOSE",
- "group": "WM_SHELL_DRAG_AND_DROP",
- "at": "com\/android\/wm\/shell\/draganddrop\/DropOutlineDrawable.java"
- },
- "325110414": {
- "message": "Transition animations finished, notifying core %s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TRANSITIONS",
- "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
- },
- "375908576": {
- "message": "Clip description: handlingDrag=%b itemCount=%d mimeTypes=%s",
- "level": "VERBOSE",
- "group": "WM_SHELL_DRAG_AND_DROP",
- "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
- },
- "410592459": {
- "message": "Invalid root leash (%s): %s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TRANSITIONS",
- "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
- },
- "473543554": {
- "message": "%s onTaskAppeared Supported",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/legacysplitscreen\/LegacySplitScreenTaskListener.java"
- },
- "481673835": {
- "message": "addListenerForTaskId taskId=%s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
- },
- "564235578": {
- "message": "Fullscreen Task Vanished: #%d",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/FullscreenTaskListener.java"
- },
- "580605218": {
- "message": "Registering organizer",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
- },
- "707170340": {
- "message": " animated by firstHandler",
- "level": "VERBOSE",
- "group": "WM_SHELL_TRANSITIONS",
- "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
- },
- "900599280": {
- "message": "Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b",
- "level": "ERROR",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/apppairs\/AppPair.java"
- },
- "950299522": {
- "message": "taskId %d isn't isn't in an app-pair.",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/apppairs\/AppPairsController.java"
- },
- "980952660": {
- "message": "Task root back pressed taskId=%d",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
- },
- "982027396": {
- "message": "%s onTaskAppeared Secondary taskId=%d",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/legacysplitscreen\/LegacySplitScreenTaskListener.java"
- },
- "990371881": {
- "message": " Checking filter %s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TRANSITIONS",
- "at": "com\/android\/wm\/shell\/transition\/RemoteTransitionHandler.java"
- },
- "1070270131": {
- "message": "onTransitionReady %s: %s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TRANSITIONS",
- "at": "com\/android\/wm\/shell\/transition\/Transitions.java"
- },
- "1079041527": {
- "message": "incrementPool size=%d",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/apppairs\/AppPairsPool.java"
- },
- "1184615936": {
- "message": "Set drop target window visibility: displayId=%d visibility=%d",
- "level": "VERBOSE",
- "group": "WM_SHELL_DRAG_AND_DROP",
- "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
- },
- "1481772149": {
- "message": "Current target: %s",
- "level": "VERBOSE",
- "group": "WM_SHELL_DRAG_AND_DROP",
- "at": "com\/android\/wm\/shell\/draganddrop\/DragLayout.java"
- },
- "1862198614": {
- "message": "Drag event: action=%s x=%f y=%f xOffset=%f yOffset=%f",
- "level": "VERBOSE",
- "group": "WM_SHELL_DRAG_AND_DROP",
- "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
- },
- "1891981945": {
- "message": "release entry.taskId=%s listener=%s size=%d",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/apppairs\/AppPairsPool.java"
- },
- "1990759023": {
- "message": "addListenerForType types=%s listener=%s",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
- },
- "2006473416": {
- "message": "acquire entry.taskId=%s listener=%s size=%d",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/apppairs\/AppPairsPool.java"
- },
- "2057038970": {
- "message": "Display changed: %d",
- "level": "VERBOSE",
- "group": "WM_SHELL_DRAG_AND_DROP",
- "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
- }
- },
- "groups": {
- "WM_SHELL_DRAG_AND_DROP": {
- "tag": "WindowManagerShell"
- },
- "WM_SHELL_TASK_ORG": {
- "tag": "WindowManagerShell"
- },
- "WM_SHELL_TRANSITIONS": {
- "tag": "WindowManagerShell"
- }
- }
-}
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index ea634cfa907c..c3ae053d156d 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Borrel"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tik om hierdie program te herbegin en maak volskerm oop."</string>
+ <string name="got_it" msgid="4428750913636945527">"Het dit"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index e4628d7b5278..c889039cffdb 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"አረፋ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ያቀናብሩ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"አረፋ ተሰናብቷል።"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ይህን መተግበሪያ ዳግም ለማስነሳት መታ ያድርጉ እና ወደ ሙሉ ማያ ገጽ ይሂዱ።"</string>
+ <string name="got_it" msgid="4428750913636945527">"ገባኝ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 7b5bda72ccd4..2c89b1d4eb56 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"فقاعة"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"إدارة"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"تم إغلاق الفقاعة."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"انقر لإعادة تشغيل هذا التطبيق والانتقال إلى وضع ملء الشاشة."</string>
+ <string name="got_it" msgid="4428750913636945527">"حسنًا"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 47294c438729..0a74ac6391ce 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"পৰিচালনা কৰক"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল অগ্ৰাহ্য কৰা হৈছে"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"এপ্‌টো ৰিষ্টাৰ্ট কৰিবলৈ আৰু পূৰ্ণ স্ক্ৰীন ব্যৱহাৰ কৰিবলৈ টিপক।"</string>
+ <string name="got_it" msgid="4428750913636945527">"বুজি পালোঁ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 923ff79e0627..54483bfa730c 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Qabarcıq"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"İdarə edin"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Qabarcıqdan imtina edilib."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Bu tətbiqi sıfırlayaraq tam ekrana keçmək üçün toxunun."</string>
+ <string name="got_it" msgid="4428750913636945527">"Anladım"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 02e609cd5c9b..5ed79c4901cd 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste restartovali aplikaciju i prešli u režim celog ekrana."</string>
+ <string name="got_it" msgid="4428750913636945527">"Važi"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index ccea3180f64e..a9a62de6c8f9 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Усплывальнае апавяшчэнне"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Кіраваць"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Усплывальнае апавяшчэнне адхілена."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Націсніце, каб перазапусціць гэту праграму і перайсці ў поўнаэкранны рэжым."</string>
+ <string name="got_it" msgid="4428750913636945527">"Зразумела"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index d29660b9c24d..80895dc0c032 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управление"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отхвърлено."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Докоснете, за да рестартирате това приложение в режим на цял екран."</string>
+ <string name="got_it" msgid="4428750913636945527">"Разбрах"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 84bcaf907d91..bdda799b001f 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ম্যানেজ করুন"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল বাতিল করা হয়েছে।"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন ও \'ফুল-স্ক্রিন\' মোড ব্যবহার করুন।"</string>
+ <string name="got_it" msgid="4428750913636945527">"বুঝেছি"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 85e08d7ca555..759e9b8c415b 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da ponovo pokrenete ovu aplikaciju i aktivirate prikaz preko cijelog ekrana."</string>
+ <string name="got_it" msgid="4428750913636945527">"Razumijem"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index a80b7fbec09a..202ea2083b7e 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bombolla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestiona"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"La bombolla s\'ha ignorat."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toca per reiniciar aquesta aplicació i passar a pantalla completa."</string>
+ <string name="got_it" msgid="4428750913636945527">"Entesos"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index e8257bc8ee92..08a4201b2fae 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Klepnutím aplikaci restartujete a přejdete na režim celé obrazovky"</string>
+ <string name="got_it" msgid="4428750913636945527">"Rozumím"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 17f8286e8069..395f6e75cdbc 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen blev lukket."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tryk for at genstarte denne app, og gå til fuld skærm."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index f04796aca753..ab3461a1ebf5 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Verwalten"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble verworfen."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tippe, um die App im Vollbildmodus neu zu starten."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ok"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index cc329e8f3274..75e4379284b8 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Συννεφάκι"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Διαχείριση"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Το συννεφάκι παραβλέφθηκε."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Πατήστε για επανεκκίνηση αυτής της εφαρμογής και ενεργοποίηση πλήρους οθόνης."</string>
+ <string name="got_it" msgid="4428750913636945527">"Το κατάλαβα"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 90c71c0e11ea..0d7b60ffefc6 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 90c71c0e11ea..0d7b60ffefc6 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 90c71c0e11ea..0d7b60ffefc6 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 90c71c0e11ea..0d7b60ffefc6 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index d8b5b40035f7..4bff89d68963 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‎‏‎‎Bubble‎‏‎‎‏‎"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎Manage‎‏‎‎‏‎"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‎Bubble dismissed.‎‏‎‎‏‎"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎Tap to restart this app and go full screen.‎‏‎‎‏‎"</string>
+ <string name="got_it" msgid="4428750913636945527">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‎‎‏‏‏‎‏‏‏‎Got it‎‏‎‎‏‎"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 7244b1a1bcf5..90c4d51b995a 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Cuadro"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Se descartó el cuadro."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Presiona para reiniciar esta app y acceder al modo de pantalla completa."</string>
+ <string name="got_it" msgid="4428750913636945527">"Entendido"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 65e75bde573d..f3baad7a02e4 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbuja"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toca para reiniciar esta aplicación e ir a la pantalla completa."</string>
+ <string name="got_it" msgid="4428750913636945527">"Listo"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 0ccfcfee85d6..9222a91b044c 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Mull"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Halda"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Mullist loobuti."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Puudutage rakenduse taaskäivitamiseks ja täisekraanrežiimi aktiveerimiseks."</string>
+ <string name="got_it" msgid="4428750913636945527">"Selge"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 6682ea80cf42..4a59b59df27b 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -25,8 +25,8 @@
<string name="pip_notification_message" msgid="8854051911700302620">"Ez baduzu nahi <xliff:g id="NAME">%s</xliff:g> zerbitzuak eginbide hori erabiltzea, sakatu hau ezarpenak ireki eta aukera desaktibatzeko."</string>
<string name="pip_play" msgid="3496151081459417097">"Erreproduzitu"</string>
<string name="pip_pause" msgid="690688849510295232">"Pausatu"</string>
- <string name="pip_skip_to_next" msgid="8403429188794867653">"Saltatu hurrengora"</string>
- <string name="pip_skip_to_prev" msgid="7172158111196394092">"Saltatu aurrekora"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Joan hurrengora"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Joan aurrekora"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Aldatu tamaina"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Baliteke aplikazioak ez funtzionatzea pantaila zatituan."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikazioak ez du onartzen pantaila zatitua"</string>
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbuila"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kudeatu"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Baztertu da globoa."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Saka ezazu aplikazioa berrabiarazteko, eta ezarri pantaila osoko modua."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ados"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index a41811d53357..fed3ea9b53f9 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"حباب"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"برای بازراه‌اندازی این برنامه و تغییر به حالت تمام‌صفحه، ضربه بزنید."</string>
+ <string name="got_it" msgid="4428750913636945527">"متوجه‌ام"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index fcdc70fc9cda..332dc9b14da2 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Kupla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Ylläpidä"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Kupla ohitettu."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Napauta, niin sovellus käynnistyy uudelleen ja siirtyy koko näytön tilaan."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index ed822373e557..f51fc66b6b83 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorée."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Touchez pour redémarrer cette application et passer en plein écran."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index ad98b85d5d5d..8fa06e88cd50 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle fermée."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Appuyez pour redémarrer cette application et activer le mode plein écran."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 529825e68151..56188d4e92ba 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbulla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toca o botón para reiniciar esta aplicación e abrila en pantalla completa."</string>
+ <string name="got_it" msgid="4428750913636945527">"Entendido"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index ee23e1e967ec..b76e91010dcc 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"બબલ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"મેનેજ કરો"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"બબલ છોડી દેવાયો."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"આ ઍપ ફરીથી ચાલુ કરવા માટે ટૅપ કરીને પૂર્ણ સ્ક્રીન કરો."</string>
+ <string name="got_it" msgid="4428750913636945527">"સમજાઈ ગયું"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 34c1c85211f6..a9693865e2c4 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"प्रबंधित करें"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"इस ऐप्लिकेशन को रीस्टार्ट करने और फ़ुल स्क्रीन पर देखने के लिए टैप करें."</string>
+ <string name="got_it" msgid="4428750913636945527">"ठीक है"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 32b21aadbb2f..769d1d26aed2 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić odbačen."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste ponovo pokrenuli tu aplikaciju i prikazali je na cijelom zaslonu."</string>
+ <string name="got_it" msgid="4428750913636945527">"Shvaćam"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 123b127bd5a3..05655f1bb607 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Buborék"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kezelés"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Buborék elvetve."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Koppintson az alkalmazás újraindításához és a teljes képernyős mód elindításához."</string>
+ <string name="got_it" msgid="4428750913636945527">"Rendben"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index b047cf131aa8..5f7495e63613 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Պղպջակ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Կառավարել"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ամպիկը փակվեց։"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Հպեք՝ հավելվածը վերագործարկելու և լիաէկրան ռեժիմին անցնելու համար։"</string>
+ <string name="got_it" msgid="4428750913636945527">"Եղավ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index a75cdb4b2b85..2cf50c0644f1 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kelola"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon ditutup."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Ketuk untuk memulai ulang aplikasi ini dan membuka layar penuh."</string>
+ <string name="got_it" msgid="4428750913636945527">"Oke"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 3b28148e3171..7a3b6a693e7a 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Blaðra"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Stjórna"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Blöðru lokað."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Ýttu til að endurræsa forritið og sýna það á öllum skjánum."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ég skil"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 8a2b9dbd9ba8..b061d1f592c4 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Fumetto"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestisci"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Fumetto ignorato."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tocca per riavviare l\'app e passare alla modalità a schermo intero."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 20114a7fa5f3..b75ee4507e94 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"בועה"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ניהול"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"הבועה נסגרה."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"צריך להקיש כדי להפעיל מחדש את האפליקציה הזו ולעבור למסך מלא."</string>
+ <string name="got_it" msgid="4428750913636945527">"הבנתי"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index fbb2951a06e1..ab693d2e5045 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"バブル"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ふきだしが非表示になっています。"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"タップしてこのアプリを再起動すると、全画面表示になります。"</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index f978481be23d..ef9a84f4ce2f 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ბუშტი"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"მართვა"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ბუშტი დაიხურა."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"შეეხეთ ამ აპის გადასატვირთად და გადადით სრულ ეკრანზე."</string>
+ <string name="got_it" msgid="4428750913636945527">"გასაგებია"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 2d27fafcc98b..13f3a4eed8c5 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Көпіршік"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Басқару"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқымалы анықтама өшірілді."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Бұл қолданбаны қайта қосып, толық экранға өту үшін түртіңіз."</string>
+ <string name="got_it" msgid="4428750913636945527">"Түсінікті"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index d503b7a5edca..134d3c2334f0 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ពពុះ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"គ្រប់គ្រង"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"បានច្រានចោល​សារលេចឡើង។"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ចុចដើម្បី​ចាប់ផ្ដើម​កម្មវិធី​នេះឡើងវិញ រួចចូលប្រើ​ពេញអេក្រង់។"</string>
+ <string name="got_it" msgid="4428750913636945527">"យល់ហើយ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 3d61d84f4810..c8b3389a3a60 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ಬಬಲ್"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ನಿರ್ವಹಿಸಿ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ಬಬಲ್ ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಮತ್ತು ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ನೋಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <string name="got_it" msgid="4428750913636945527">"ಅರ್ಥವಾಯಿತು"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index ea7ad56bf9d2..b29612e337f2 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"버블"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"관리"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"대화창을 닫았습니다."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"탭하여 이 앱을 다시 시작하고 전체 화면으로 이동합니다."</string>
+ <string name="got_it" msgid="4428750913636945527">"확인"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 611b2d60a8c1..530d40a79ca3 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Көбүк"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Башкаруу"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Калкып чыкма билдирме жабылды."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Бул колдонмону өчүрүп күйгүзүп, толук экранга өтүү үчүн таптап коюңуз."</string>
+ <string name="got_it" msgid="4428750913636945527">"Түшүндүм"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index a1c998c078de..5ccf164b3d67 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ຟອງ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ຈັດການ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ປິດ Bubble ໄສ້ແລ້ວ."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ ແລະ ໃຊ້ແບບເຕັມຈໍ."</string>
+ <string name="got_it" msgid="4428750913636945527">"ເຂົ້າໃຈແລ້ວ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index b2ccd5709e21..1433312ded60 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Debesėlis"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Tvarkyti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Debesėlio atsisakyta."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Palieskite, kad paleistumėte iš naujo šią programą ir įjungtumėte viso ekrano režimą."</string>
+ <string name="got_it" msgid="4428750913636945527">"Supratau"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index e6d0c7725bbf..fb297b8f2990 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbulis"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pārvaldīt"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbulis ir noraidīts."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Pieskarieties, lai restartētu šo lietotni un pārietu pilnekrāna režīmā."</string>
+ <string name="got_it" msgid="4428750913636945527">"Labi"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 43f2881fd553..80b33293af34 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управувајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отфрлено."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Допрете за да ја рестартирате апликацијава и да ја отворите на цел екран."</string>
+ <string name="got_it" msgid="4428750913636945527">"Сфатив"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index e675861166a3..5d47911137ec 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ബബ്ൾ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"മാനേജ് ചെയ്യുക"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബ്ൾ ഡിസ്മിസ് ചെയ്തു."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ഈ ആപ്പ് റീസ്‌റ്റാർട്ട് ചെയ്‌ത് പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറാൻ ടാപ്പ് ചെയ്യുക."</string>
+ <string name="got_it" msgid="4428750913636945527">"മനസ്സിലായി"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 044fd9fa7544..a5e7f957b829 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Бөмбөлөг"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Удирдах"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Бөмбөлгийг үл хэрэгссэн."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Энэ аппыг дахин эхлүүлж, бүтэн дэлгэцэд орохын тулд товшино уу."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ойлголоо"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index e838cf59331e..0450189c515d 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापित करा"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल डिसमिस केला."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"हे अ‍ॅप रीस्टार्ट करण्यासाठी आणि फुल स्क्रीन करण्यासाठी टॅप करा."</string>
+ <string name="got_it" msgid="4428750913636945527">"समजले"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 6664f38f3879..c0c1cbd6ed3c 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Gelembung"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Urus"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Gelembung diketepikan."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Ketik untuk memulakan semula apl ini dan menggunakan skrin penuh."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 9681d14a6a88..0d78f89d9e54 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ပူဖောင်းဖောက်သံ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"စီမံရန်"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ပူဖောင်းကွက် ဖယ်လိုက်သည်။"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ဤအက်ပ်ကို ပြန်စပြီး ဖန်သားပြင်အပြည့်လုပ်ရန် တို့ပါ။"</string>
+ <string name="got_it" msgid="4428750913636945527">"ရပြီ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 986e890dfe3a..fab0c0cacef4 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Trykk for å starte denne appen på nytt og vise den i fullskjerm."</string>
+ <string name="got_it" msgid="4428750913636945527">"Greit"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 0369c6dd2831..882ac374fde9 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापन गर्नुहोस्"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल हटाइयो।"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"यो एप रिस्टार्ट गर्न ट्याप गर्नुहोस् र फुल स्क्रिन मोडमा जानुहोस्।"</string>
+ <string name="got_it" msgid="4428750913636945527">"बुझेँ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 26c276e7e690..1527e8983079 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubbel"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tik om deze app opnieuw te starten en te openen op het volledige scherm."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 27f16226a421..50d20076a26f 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ବବଲ୍"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ବବଲ୍ ଖାରଜ କରାଯାଇଛି।"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ଏହି ଆପକୁ ରିଷ୍ଟାର୍ଟ କରି ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
+ <string name="got_it" msgid="4428750913636945527">"ବୁଝିଗଲି"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 96688b952d66..dd3d26e56b4c 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ਬੁਲਬੁਲਾ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕੀਤਾ ਗਿਆ।"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਪੂਰੀ ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਜਾਓ।"</string>
+ <string name="got_it" msgid="4428750913636945527">"ਸਮਝ ਲਿਆ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 6b640b54f898..ca2bdcb270da 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Dymek"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Zarządzaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Zamknięto dymek"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Kliknij, by uruchomić tę aplikację ponownie i przejść w tryb pełnoekranowy."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 465d2d17a5e7..bdd0d4b8baec 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ok"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index df841bf3eda4..6661b0506c5e 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Balão"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão ignorado."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar esta app e ficar em ecrã inteiro."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 465d2d17a5e7..bdd0d4b8baec 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ok"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 55a437668b22..9112543c8f60 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionați"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Atingeți ca să reporniți aplicația și să treceți în modul ecran complet."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 8ae00d28f896..5120136e3c68 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Всплывающая подсказка"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Настроить"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Всплывающий чат закрыт."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Нажмите, чтобы перезапустить приложение и перейти в полноэкранный режим."</string>
+ <string name="got_it" msgid="4428750913636945527">"ОК"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 081926fd101b..e1d9a825a004 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"බුබුළු"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"කළමනා කරන්න"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"බුබුල ඉවත දමා ඇත."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"මෙම යෙදුම යළි ඇරඹීමට සහ පූර්ණ තිරයට යාමට තට්ටු කරන්න."</string>
+ <string name="got_it" msgid="4428750913636945527">"තේරුණා"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 24fded7ebb04..c88099b35c0d 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovať"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina bola zavretá."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Klepnutím reštartujete túto aplikáciu a prejdete do režimu celej obrazovky."</string>
+ <string name="got_it" msgid="4428750913636945527">"Dobre"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 3f425302a5ac..42d7be7a146d 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Mehurček"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblaček je bil opuščen."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Dotaknite se za vnovični zagon te aplikacije in preklop v celozaslonski način."</string>
+ <string name="got_it" msgid="4428750913636945527">"Razumem"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index ddae724e7569..1f373b53d0a4 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Flluskë"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Menaxho"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Flluska u hoq."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Trokit për ta rinisur këtë aplikacion dhe për të kaluar në ekranin e plotë."</string>
+ <string name="got_it" msgid="4428750913636945527">"E kuptova"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 74c9ac0867e3..2bbbbf9e0714 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Облачић"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управљајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Облачић је одбачен."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Додирните да бисте рестартовали апликацију и прешли у режим целог екрана."</string>
+ <string name="got_it" msgid="4428750913636945527">"Важи"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 81328a836345..692b5ed8576f 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubbla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Hantera"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubblan ignorerades."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tryck för att starta om appen i helskärmsläge."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 4559832b1d85..61c95ee183a0 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Kiputo"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Dhibiti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Umeondoa kiputo."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Gusa ili uzime na uwashe programu hii, kisha nenda kwenye skrini nzima."</string>
+ <string name="got_it" msgid="4428750913636945527">"Nimeelewa"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 586ee94a1098..32a925a7994e 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"பபிள்"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"நிர்வகி"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"குமிழ் நிராகரிக்கப்பட்டது."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கலாம், முழுத்திரையில் பார்க்கலாம்."</string>
+ <string name="got_it" msgid="4428750913636945527">"சரி"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 4e85b4371220..3db12e7d5661 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"బబుల్"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"మేనేజ్ చేయండి"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"బబుల్ విస్మరించబడింది."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ఈ యాప్‌ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేసి, ఆపై పూర్తి స్క్రీన్‌లోకి వెళ్లండి."</string>
+ <string name="got_it" msgid="4428750913636945527">"అర్థమైంది"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 66c701812ce8..7df76e84cbb7 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"บับเบิล"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"จัดการ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ปิดบับเบิลแล้ว"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"แตะเพื่อรีสตาร์ทแอปนี้และแสดงแบบเต็มหน้าจอ"</string>
+ <string name="got_it" msgid="4428750913636945527">"รับทราบ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index a76bf6f1350c..d6c2784f764f 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pamahalaan"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Na-dismiss na ang bubble."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"I-tap para i-restart ang app na ito at mag-full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index b3276dad50e7..47d5966549ef 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Baloncuk"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Yönet"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon kapatıldı."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Bu uygulamayı yeniden başlatmak ve tam ekrana geçmek için dokunun."</string>
+ <string name="got_it" msgid="4428750913636945527">"Anladım"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 8e303cf45a39..c57f16f059df 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Спливаюче сповіщення"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Налаштувати"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Спливаюче сповіщення закрито."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Натисніть, щоб перезапустити додаток і перейти в повноекранний режим."</string>
+ <string name="got_it" msgid="4428750913636945527">"Зрозуміло"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 4b0adc640ddd..97a22e77c069 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"بلبلہ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"نظم کریں"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"بلبلہ برخاست کر دیا گیا۔"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"یہ ایپ دوبارہ شروع کرنے کے لیے تھپتھپائیں اور پوری اسکرین پر جائیں۔"</string>
+ <string name="got_it" msgid="4428750913636945527">"سمجھ آ گئی"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 74b135d44522..4e91e7624434 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Pufaklar"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Boshqarish"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulutcha yopildi."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Bu ilovani qaytadan ishga tushirish va butun ekranda ochish uchun bosing."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index ce372317b0b8..169e986f7721 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bong bóng"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Quản lý"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Đã đóng bong bóng."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Nhấn để khởi động lại ứng dụng này và xem ở chế độ toàn màn hình."</string>
+ <string name="got_it" msgid="4428750913636945527">"Tôi hiểu"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 3143130fa4ce..1999703ddd2a 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"气泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭对话泡。"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"点按即可重启此应用并进入全屏模式。"</string>
+ <string name="got_it" msgid="4428750913636945527">"知道了"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 4f8bfe016f6f..f82d6d5e771b 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"氣泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"對話氣泡已關閉。"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"輕按即可重新開啟此應用程式並放大至全螢幕。"</string>
+ <string name="got_it" msgid="4428750913636945527">"知道了"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 6fb8ed963ba7..596e7c717aa2 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"泡泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已關閉泡泡。"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"輕觸即可重新啟動這個應用程式並進入全螢幕模式。"</string>
+ <string name="got_it" msgid="4428750913636945527">"我知道了"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index cab277647d26..3fed41f27c0a 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -69,4 +69,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Ibhamuza"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Phatha"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ibhamuza licashisiwe."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Thepha ukuze uqale kabusha lolu hlelo lokusebenza uphinde uye kusikrini esigcwele."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ngiyezwa"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 13f1fddfdfb6..24198659e15d 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -48,4 +48,7 @@
<!-- one handed background panel default alpha -->
<item name="config_one_handed_background_alpha" format="float" type="dimen">0.5</item>
+
+ <!-- maximum animation duration for the icon when entering the starting window -->
+ <integer name="max_starting_window_intro_icon_anim_duration">1000</integer>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 034e65c608a3..75bed3777a9d 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -21,14 +21,20 @@
<dimen name="floating_dismiss_gradient_height">250dp</dimen>
<!-- The padding around a PiP actions. -->
- <dimen name="pip_action_padding">12dp</dimen>
+ <dimen name="pip_action_padding">16dp</dimen>
<!-- The height of the PiP actions container in which the actions are vertically centered. -->
<dimen name="pip_action_size">48dp</dimen>
- <!-- The width and height of the PiP expand action. -->
+ <!-- The width and height of the PiP action asset drawn within the container. -->
+ <dimen name="pip_action_inner_size">20dp</dimen>
+
+ <!-- The width and height of the PiP expand action container. -->
<dimen name="pip_expand_action_size">60dp</dimen>
+ <!-- The width and height of the PiP expand action asset drawn within the container. -->
+ <dimen name="pip_expand_action_inner_size">28dp</dimen>
+
<!-- The padding between actions in the PiP in landscape Note that the PiP does not reflect
the configuration of the device, so we can't use -land resources. -->
<dimen name="pip_between_action_padding_land">8dp</dimen>
@@ -173,4 +179,13 @@
<!-- The width/height of the icon view on staring surface. -->
<dimen name="starting_surface_icon_size">108dp</dimen>
+
+ <!-- The width/height of the size compat restart button. -->
+ <dimen name="size_compat_button_size">48dp</dimen>
+
+ <!-- The width of the brand image on staring surface. -->
+ <dimen name="starting_surface_brand_image_width">200dp</dimen>
+
+ <!-- The height of the brand image on staring surface. -->
+ <dimen name="starting_surface_brand_image_height">80dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
index afe523af7cb0..6984ea458ccf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
@@ -95,9 +95,16 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
}
@Override
+ public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ if (!mLeashByTaskId.contains(taskId)) {
+ throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
+ }
+ b.setParent(mLeashByTaskId.get(taskId));
+ }
+
+ @Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
- final String childPrefix = innerPrefix + " ";
pw.println(prefix + this);
pw.println(innerPrefix + mLeashByTaskId.size() + " Tasks");
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
index aa82339a436a..73fd6931066d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
@@ -16,24 +16,16 @@
package com.android.wm.shell;
-import android.util.Slog;
-
-import com.android.wm.shell.apppairs.AppPairs;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
-import com.android.wm.shell.onehanded.OneHanded;
-import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.common.annotations.ExternalThread;
import java.io.PrintWriter;
-import java.util.Optional;
-import java.util.concurrent.TimeUnit;
/**
* An entry point into the shell for dumping shell internal state and running adb commands.
*
* Use with {@code adb shell dumpsys activity service SystemUIService WMShell ...}.
*/
+@ExternalThread
public interface ShellCommandHandler {
/**
* Dumps the shell state.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index fe97e24fac41..eaed24d6195a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -18,13 +18,13 @@ package com.android.wm.shell;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
-import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
+import com.android.wm.shell.onehanded.OneHandedController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import java.io.PrintWriter;
import java.util.Optional;
@@ -37,24 +37,24 @@ import java.util.Optional;
public final class ShellCommandHandlerImpl {
private static final String TAG = ShellCommandHandlerImpl.class.getSimpleName();
- private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
- private final Optional<SplitScreen> mSplitScreenOptional;
+ private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
+ private final Optional<SplitScreenController> mSplitScreenOptional;
private final Optional<Pip> mPipOptional;
- private final Optional<OneHanded> mOneHandedOptional;
- private final Optional<HideDisplayCutout> mHideDisplayCutout;
+ private final Optional<OneHandedController> mOneHandedOptional;
+ private final Optional<HideDisplayCutoutController> mHideDisplayCutout;
+ private final Optional<AppPairsController> mAppPairsOptional;
private final ShellTaskOrganizer mShellTaskOrganizer;
- private final Optional<AppPairs> mAppPairsOptional;
private final ShellExecutor mMainExecutor;
private final HandlerImpl mImpl = new HandlerImpl();
public static ShellCommandHandler create(
ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreen> splitScreenOptional,
+ Optional<LegacySplitScreenController> legacySplitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
Optional<Pip> pipOptional,
- Optional<OneHanded> oneHandedOptional,
- Optional<HideDisplayCutout> hideDisplayCutout,
- Optional<AppPairs> appPairsOptional,
+ Optional<OneHandedController> oneHandedOptional,
+ Optional<HideDisplayCutoutController> hideDisplayCutout,
+ Optional<AppPairsController> appPairsOptional,
ShellExecutor mainExecutor) {
return new ShellCommandHandlerImpl(shellTaskOrganizer, legacySplitScreenOptional,
splitScreenOptional, pipOptional, oneHandedOptional, hideDisplayCutout,
@@ -63,12 +63,12 @@ public final class ShellCommandHandlerImpl {
private ShellCommandHandlerImpl(
ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreen> splitScreenOptional,
+ Optional<LegacySplitScreenController> legacySplitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
Optional<Pip> pipOptional,
- Optional<OneHanded> oneHandedOptional,
- Optional<HideDisplayCutout> hideDisplayCutout,
- Optional<AppPairs> appPairsOptional,
+ Optional<OneHandedController> oneHandedOptional,
+ Optional<HideDisplayCutoutController> hideDisplayCutout,
+ Optional<AppPairsController> appPairsOptional,
ShellExecutor mainExecutor) {
mShellTaskOrganizer = shellTaskOrganizer;
mLegacySplitScreenOptional = legacySplitScreenOptional;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
index 0958a070c82d..7376d9898ab8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -18,13 +18,13 @@ package com.android.wm.shell;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
-import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -38,9 +38,9 @@ public class ShellInitImpl {
private final DisplayImeController mDisplayImeController;
private final DragAndDropController mDragAndDropController;
private final ShellTaskOrganizer mShellTaskOrganizer;
- private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
- private final Optional<SplitScreen> mSplitScreenOptional;
- private final Optional<AppPairs> mAppPairsOptional;
+ private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
+ private final Optional<SplitScreenController> mSplitScreenOptional;
+ private final Optional<AppPairsController> mAppPairsOptional;
private final FullscreenTaskListener mFullscreenTaskListener;
private final ShellExecutor mMainExecutor;
private final Transitions mTransitions;
@@ -50,9 +50,9 @@ public class ShellInitImpl {
public static ShellInit create(DisplayImeController displayImeController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreen> splitScreenOptional,
- Optional<AppPairs> appPairsOptional,
+ Optional<LegacySplitScreenController> legacySplitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
+ Optional<AppPairsController> appPairsOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
ShellExecutor mainExecutor) {
@@ -70,9 +70,9 @@ public class ShellInitImpl {
private ShellInitImpl(DisplayImeController displayImeController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreen> splitScreenOptional,
- Optional<AppPairs> appPairsOptional,
+ Optional<LegacySplitScreenController> legacySplitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
+ Optional<AppPairsController> appPairsOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
ShellExecutor mainExecutor) {
@@ -96,8 +96,8 @@ public class ShellInitImpl {
// Register the shell organizer
mShellTaskOrganizer.registerOrganizer();
- mAppPairsOptional.ifPresent(AppPairs::onOrganizerRegistered);
- mSplitScreenOptional.ifPresent(SplitScreen::onOrganizerRegistered);
+ mAppPairsOptional.ifPresent(AppPairsController::onOrganizerRegistered);
+ mSplitScreenOptional.ifPresent(SplitScreenController::onOrganizerRegistered);
// Bind the splitscreen impl to the drag drop controller
mDragAndDropController.initialize(mSplitScreenOptional);
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 a570c0af698d..efc55c4fbe31 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -25,6 +25,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.os.Binder;
@@ -38,13 +40,10 @@ import android.window.StartingWindowInfo;
import android.window.TaskAppearedInfo;
import android.window.TaskOrganizer;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
+import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.startingsurface.StartingSurfaceDrawer;
import java.io.PrintWriter;
@@ -83,6 +82,16 @@ public class ShellTaskOrganizer extends TaskOrganizer {
default void onTaskInfoChanged(RunningTaskInfo taskInfo) {}
default void onTaskVanished(RunningTaskInfo taskInfo) {}
default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {}
+ /** Whether this task listener supports size compat UI. */
+ default boolean supportSizeCompatUI() {
+ // All TaskListeners should support size compat except PIP.
+ return true;
+ }
+ /** Attaches the a child window surface to the task surface. */
+ default void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ throw new IllegalStateException(
+ "This task listener doesn't support child surface attachment.");
+ }
default void dump(@NonNull PrintWriter pw, String prefix) {};
}
@@ -108,26 +117,26 @@ public class ShellTaskOrganizer extends TaskOrganizer {
* compat.
*/
@Nullable
- private final SizeCompatUI mSizeCompatUI;
+ private final SizeCompatUIController mSizeCompatUI;
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
- this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */);
+ this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */,
+ new StartingSurfaceDrawer(context, mainExecutor));
}
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
- SizeCompatUI sizeCompatUI) {
- this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI);
+ SizeCompatUIController sizeCompatUI) {
+ this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
+ new StartingSurfaceDrawer(context, mainExecutor));
}
@VisibleForTesting
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor,
- Context context, @Nullable SizeCompatUI sizeCompatUI) {
+ Context context, @Nullable SizeCompatUIController sizeCompatUI,
+ StartingSurfaceDrawer startingSurfaceDrawer) {
super(taskOrganizerController, mainExecutor);
- // TODO(b/131727939) temporarily live here, the starting surface drawer should be controlled
- // by a controller, that class should be create while porting
- // ActivityRecord#addStartingWindow to WMShell.
- mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, mainExecutor);
mSizeCompatUI = sizeCompatUI;
+ mStartingSurfaceDrawer = startingSurfaceDrawer;
}
@Override
@@ -254,6 +263,11 @@ public class ShellTaskOrganizer extends TaskOrganizer {
}
@Override
+ public void copySplashScreenView(int taskId) {
+ mStartingSurfaceDrawer.copySplashScreenView(taskId);
+ }
+
+ @Override
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
synchronized (mLock) {
onTaskAppeared(new TaskAppearedInfo(taskInfo, leash));
@@ -342,8 +356,8 @@ public class ShellTaskOrganizer extends TaskOrganizer {
}
/**
- * Notifies {@link SizeCompatUI} about the size compat info changed on the give Task to update
- * the UI accordingly.
+ * Notifies {@link SizeCompatUIController} about the size compat info changed on the give Task
+ * to update the UI accordingly.
*
* @param taskInfo the new Task info
* @param taskListener listener to handle the Task Surface placement. {@code null} if task is
@@ -354,8 +368,10 @@ public class ShellTaskOrganizer extends TaskOrganizer {
return;
}
- // The task is vanished, notify to remove size compat UI on this Task if there is any.
- if (taskListener == null) {
+ // The task is vanished or doesn't support size compat UI, notify to remove size compat UI
+ // on this Task if there is any.
+ if (taskListener == null || !taskListener.supportSizeCompatUI()
+ || !taskInfo.topActivityInSizeCompat) {
mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
null /* taskConfig */, null /* sizeCompatActivity*/,
null /* taskListener */);
@@ -363,10 +379,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
}
mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
- taskInfo.configuration.windowConfiguration.getBounds(),
- // null if the top activity not in size compat.
- taskInfo.topActivityInSizeCompat ? taskInfo.topActivityToken : null,
- taskListener);
+ taskInfo.configuration, taskInfo.topActivityToken, taskListener);
}
private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo) {
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 bb8a97344664..5992447bd6da 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -301,6 +301,14 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
}
@Override
+ public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ if (mTaskInfo.taskId != taskId) {
+ throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
+ }
+ b.setParent(mTaskLeash);
+ }
+
+ @Override
public void dump(@androidx.annotation.NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
final String childPrefix = innerPrefix + " ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
index a5dd79b373bd..58ca1fbaba24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
@@ -30,6 +30,7 @@ import java.util.function.Consumer;
public class TaskViewFactoryController {
private final ShellTaskOrganizer mTaskOrganizer;
private final ShellExecutor mShellExecutor;
+ private final TaskViewFactory mImpl = new TaskViewFactoryImpl();
public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer,
ShellExecutor shellExecutor) {
@@ -37,8 +38,11 @@ public class TaskViewFactoryController {
mShellExecutor = shellExecutor;
}
+ public TaskViewFactory asTaskViewFactory() {
+ return mImpl;
+ }
+
/** Creates an {@link TaskView} */
- @ShellMainThread
public void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate) {
TaskView taskView = new TaskView(context, mTaskOrganizer);
executor.execute(() -> {
@@ -46,10 +50,6 @@ public class TaskViewFactoryController {
});
}
- public TaskViewFactory getTaskViewFactory() {
- return new TaskViewFactoryImpl();
- }
-
private class TaskViewFactoryImpl implements TaskViewFactory {
@ExternalThread
public void create(@UiContext Context context,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
index abd92577c1d4..59271e9fb63c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
@@ -27,6 +27,7 @@ import com.android.wm.shell.pip.PinnedStackListenerForwarder.PinnedStackListener
/**
* The singleton wrapper to communicate between WindowManagerService and WMShell features
* (e.g: PIP, SplitScreen, Bubble, OneHandedMode...etc)
+ * TODO: Remove once PinnedStackListenerForwarder can be removed
*/
public class WindowManagerShellWrapper {
private static final String TAG = WindowManagerShellWrapper.class.getSimpleName();
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 bab5140e2f52..79f9dcd8a1fb 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
@@ -214,6 +214,19 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
}
@Override
+ public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ if (getRootTaskId() == taskId) {
+ b.setParent(mRootTaskLeash);
+ } else if (getTaskId1() == taskId) {
+ b.setParent(mTaskLeash1);
+ } else if (getTaskId2() == taskId) {
+ b.setParent(mTaskLeash2);
+ } else {
+ throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
+ }
+ }
+
+ @Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
final String childPrefix = innerPrefix + " ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
index f5aa852c87ae..a9b1dbc3c23b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
@@ -35,8 +35,4 @@ public interface AppPairs {
boolean pair(ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2);
/** Unpairs any app-pair containing this task id. */
void unpair(int taskId);
- /** Dumps current status of app pairs. */
- void dump(@NonNull PrintWriter pw, String prefix);
- /** Called when the shell organizer has been registered. */
- void onOrganizerRegistered();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
index e380426b9ca2..0415f12496f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
@@ -51,18 +51,7 @@ public class AppPairsController {
private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>();
private final DisplayController mDisplayController;
- /**
- * Creates {@link AppPairs}, returns {@code null} if the feature is not supported.
- */
- @Nullable
- public static AppPairs create(ShellTaskOrganizer organizer,
- SyncTransactionQueue syncQueue, DisplayController displayController,
- ShellExecutor mainExecutor) {
- return new AppPairsController(organizer, syncQueue, displayController,
- mainExecutor).mImpl;
- }
-
- AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
+ public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
DisplayController displayController, ShellExecutor mainExecutor) {
mTaskOrganizer = organizer;
mSyncQueue = syncQueue;
@@ -70,18 +59,22 @@ public class AppPairsController {
mMainExecutor = mainExecutor;
}
- void onOrganizerRegistered() {
+ public AppPairs asAppPairs() {
+ return mImpl;
+ }
+
+ public void onOrganizerRegistered() {
if (mPairsPool == null) {
setPairsPool(new AppPairsPool(this));
}
}
@VisibleForTesting
- void setPairsPool(AppPairsPool pool) {
+ public void setPairsPool(AppPairsPool pool) {
mPairsPool = pool;
}
- boolean pair(int taskId1, int taskId2) {
+ public boolean pair(int taskId1, int taskId2) {
final ActivityManager.RunningTaskInfo task1 = mTaskOrganizer.getRunningTaskInfo(taskId1);
final ActivityManager.RunningTaskInfo task2 = mTaskOrganizer.getRunningTaskInfo(taskId2);
if (task1 == null || task2 == null) {
@@ -90,13 +83,13 @@ public class AppPairsController {
return pair(task1, task2);
}
- boolean pair(ActivityManager.RunningTaskInfo task1,
+ public boolean pair(ActivityManager.RunningTaskInfo task1,
ActivityManager.RunningTaskInfo task2) {
return pairInner(task1, task2) != null;
}
@VisibleForTesting
- AppPair pairInner(
+ public AppPair pairInner(
@NonNull ActivityManager.RunningTaskInfo task1,
@NonNull ActivityManager.RunningTaskInfo task2) {
final AppPair pair = mPairsPool.acquire();
@@ -109,11 +102,11 @@ public class AppPairsController {
return pair;
}
- void unpair(int taskId) {
+ public void unpair(int taskId) {
unpair(taskId, true /* releaseToPool */);
}
- void unpair(int taskId, boolean releaseToPool) {
+ public void unpair(int taskId, boolean releaseToPool) {
AppPair pair = mActiveAppPairs.get(taskId);
if (pair == null) {
for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) {
@@ -137,19 +130,19 @@ public class AppPairsController {
}
}
- ShellTaskOrganizer getTaskOrganizer() {
+ public ShellTaskOrganizer getTaskOrganizer() {
return mTaskOrganizer;
}
- SyncTransactionQueue getSyncTransactionQueue() {
+ public SyncTransactionQueue getSyncTransactionQueue() {
return mSyncQueue;
}
- DisplayController getDisplayController() {
+ public DisplayController getDisplayController() {
return mDisplayController;
}
- private void dump(@NonNull PrintWriter pw, String prefix) {
+ public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
final String childPrefix = innerPrefix + " ";
pw.println(prefix + this);
@@ -202,21 +195,5 @@ public class AppPairsController {
AppPairsController.this.unpair(taskId);
});
}
-
- @Override
- public void onOrganizerRegistered() {
- mMainExecutor.execute(() -> {
- AppPairsController.this.onOrganizerRegistered();
- });
- }
-
- @Override
- public void dump(@NonNull PrintWriter pw, String prefix) {
- try {
- mMainExecutor.executeBlocking(() -> AppPairsController.this.dump(pw, prefix));
- } catch (InterruptedException e) {
- Slog.e(TAG, "Failed to dump AppPairsController in 2s");
- }
- }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 40fdb97b0094..0ee1f0642352 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -398,6 +398,14 @@ public class Bubble implements BubbleViewProvider {
}
}
+ @Override
+ public void setExpandedContentAlpha(float alpha) {
+ if (mExpandedView != null) {
+ mExpandedView.setAlpha(alpha);
+ mExpandedView.setTaskViewAlpha(alpha);
+ }
+ }
+
/**
* Set visibility of bubble in the expanded state.
*
@@ -407,7 +415,7 @@ public class Bubble implements BubbleViewProvider {
* and setting {@code false} actually means rendering the expanded view in transparent.
*/
@Override
- public void setContentVisibility(boolean visibility) {
+ public void setTaskViewVisibility(boolean visibility) {
if (mExpandedView != null) {
mExpandedView.setContentVisibility(visibility);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index d73fc6dca4c6..047df5ba7ca9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -86,6 +86,7 @@ import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -192,9 +193,9 @@ public class BubbleController {
private boolean mIsStatusBarShade = true;
/**
- * Injected constructor.
+ * Creates an instance of the BubbleController.
*/
- public static Bubbles create(Context context,
+ public static BubbleController create(Context context,
@Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
FloatingContentCoordinator floatingContentCoordinator,
@Nullable IStatusBarService statusBarService,
@@ -211,14 +212,14 @@ public class BubbleController {
return new BubbleController(context, data, synchronizer, floatingContentCoordinator,
new BubbleDataRepository(context, launcherApps, mainExecutor),
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
- logger, organizer, positioner, mainExecutor, mainHandler).mImpl;
+ logger, organizer, positioner, mainExecutor, mainHandler);
}
/**
* Testing constructor.
*/
@VisibleForTesting
- public BubbleController(Context context,
+ protected BubbleController(Context context,
BubbleData data,
@Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
FloatingContentCoordinator floatingContentCoordinator,
@@ -322,7 +323,7 @@ public class BubbleController {
}
@VisibleForTesting
- public Bubbles getImpl() {
+ public Bubbles asBubbles() {
return mImpl;
}
@@ -586,11 +587,15 @@ public class BubbleController {
// There were no bubbles saved for this used.
return;
}
- for (BubbleEntry e : mSysuiProxy.getShouldRestoredEntries(savedBubbleKeys)) {
- if (canLaunchInActivityView(mContext, e)) {
- updateBubble(e, true /* suppressFlyout */, false /* showInShade */);
- }
- }
+ mSysuiProxy.getShouldRestoredEntries(savedBubbleKeys, (entries) -> {
+ mMainExecutor.execute(() -> {
+ for (BubbleEntry e : entries) {
+ if (canLaunchInActivityView(mContext, e)) {
+ updateBubble(e, true /* suppressFlyout */, false /* showInShade */);
+ }
+ }
+ });
+ });
// Finally, remove the entries for this user now that bubbles are restored.
mSavedBubbleKeysPerUser.remove(mCurrentUserId);
}
@@ -856,21 +861,24 @@ public class BubbleController {
}
}
- private void onRankingUpdated(RankingMap rankingMap) {
+ private void onRankingUpdated(RankingMap rankingMap,
+ HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey) {
if (mTmpRanking == null) {
mTmpRanking = new NotificationListenerService.Ranking();
}
String[] orderedKeys = rankingMap.getOrderedKeys();
for (int i = 0; i < orderedKeys.length; i++) {
String key = orderedKeys[i];
- BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(key);
+ Pair<BubbleEntry, Boolean> entryData = entryDataByKey.get(key);
+ BubbleEntry entry = entryData.first;
+ boolean shouldBubbleUp = entryData.second;
rankingMap.getRanking(key, mTmpRanking);
boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key);
if (isActiveBubble && !mTmpRanking.canBubble()) {
// If this entry is no longer allowed to bubble, dismiss with the BLOCKED reason.
// This means that the app or channel's ability to bubble has been revoked.
mBubbleData.dismissBubbleWithKey(key, DISMISS_BLOCKED);
- } else if (isActiveBubble && !mSysuiProxy.shouldBubbleUp(key)) {
+ } else if (isActiveBubble && !shouldBubbleUp) {
// If this entry is allowed to bubble, but cannot currently bubble up, dismiss it.
// This happens when DND is enabled and configured to hide bubbles. Dismissing with
// the reason DISMISS_NO_BUBBLE_UP will retain the underlying notification, so that
@@ -919,17 +927,20 @@ public class BubbleController {
private void setIsBubble(@NonNull final Bubble b, final boolean isBubble) {
Objects.requireNonNull(b);
b.setIsBubble(isBubble);
- final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(b.getKey());
- if (entry != null) {
- // Updating the entry to be a bubble will trigger our normal update flow
- setIsBubble(entry, isBubble, b.shouldAutoExpand());
- } else if (isBubble) {
- // If bubble doesn't exist, it's a persisted bubble so we need to add it to the
- // stack ourselves
- Bubble bubble = mBubbleData.getOrCreateBubble(null, b /* persistedBubble */);
- inflateAndAdd(bubble, bubble.shouldAutoExpand() /* suppressFlyout */,
- !bubble.shouldAutoExpand() /* showInShade */);
- }
+ mSysuiProxy.getPendingOrActiveEntry(b.getKey(), (entry) -> {
+ mMainExecutor.execute(() -> {
+ if (entry != null) {
+ // Updating the entry to be a bubble will trigger our normal update flow
+ setIsBubble(entry, isBubble, b.shouldAutoExpand());
+ } else if (isBubble) {
+ // If bubble doesn't exist, it's a persisted bubble so we need to add it to the
+ // stack ourselves
+ Bubble bubble = mBubbleData.getOrCreateBubble(null, b /* persistedBubble */);
+ inflateAndAdd(bubble, bubble.shouldAutoExpand() /* suppressFlyout */,
+ !bubble.shouldAutoExpand() /* showInShade */);
+ }
+ });
+ });
}
@SuppressWarnings("FieldCanBeLocal")
@@ -992,14 +1003,17 @@ public class BubbleController {
}
}
- final BubbleEntry entry = mSysuiProxy.getPendingOrActiveEntry(bubble.getKey());
- if (entry != null) {
- final String groupKey = entry.getStatusBarNotification().getGroupKey();
- if (getBubblesInGroup(groupKey).isEmpty()) {
- // Time to potentially remove the summary
- mSysuiProxy.notifyMaybeCancelSummary(bubble.getKey());
- }
- }
+ mSysuiProxy.getPendingOrActiveEntry(bubble.getKey(), (entry) -> {
+ mMainExecutor.execute(() -> {
+ if (entry != null) {
+ final String groupKey = entry.getStatusBarNotification().getGroupKey();
+ if (getBubblesInGroup(groupKey).isEmpty()) {
+ // Time to potentially remove the summary
+ mSysuiProxy.notifyMaybeCancelSummary(bubble.getKey());
+ }
+ }
+ });
+ });
}
mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository);
@@ -1121,23 +1135,6 @@ public class BubbleController {
mStackView.updateContentDescription();
}
- /**
- * The task id of the expanded view, if the stack is expanded and not occluded by the
- * status bar, otherwise returns {@link ActivityTaskManager#INVALID_TASK_ID}.
- */
- private int getExpandedTaskId() {
- if (mStackView == null) {
- return INVALID_TASK_ID;
- }
- final BubbleViewProvider expandedViewProvider = mStackView.getExpandedBubble();
- if (expandedViewProvider != null && isStackExpanded()
- && !mStackView.isExpansionAnimating()
- && !mSysuiProxy.isNotificationShadeExpand()) {
- return expandedViewProvider.getTaskId();
- }
- return INVALID_TASK_ID;
- }
-
@VisibleForTesting
public BubbleStackView getStackView() {
return mStackView;
@@ -1343,9 +1340,10 @@ public class BubbleController {
}
@Override
- public void onRankingUpdated(RankingMap rankingMap) {
+ public void onRankingUpdated(RankingMap rankingMap,
+ HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey) {
mMainExecutor.execute(() -> {
- BubbleController.this.onRankingUpdated(rankingMap);
+ BubbleController.this.onRankingUpdated(rankingMap, entryDataByKey);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 29458ef70e2d..2f31acd41408 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -85,6 +85,19 @@ public class BubbleExpandedView extends LinearLayout {
private boolean mImeVisible;
private boolean mNeedsNewHeight;
+ /**
+ * Whether we want the TaskView's content to be visible (alpha = 1f). If
+ * {@link #mIsAlphaAnimating} is true, this may not reflect the TaskView's actual alpha value
+ * until the animation ends.
+ */
+ private boolean mIsContentVisible = false;
+
+ /**
+ * Whether we're animating the TaskView's alpha value. If so, we will hold off on applying alpha
+ * changes from {@link #setContentVisibility} until the animation ends.
+ */
+ private boolean mIsAlphaAnimating = false;
+
private int mMinHeight;
private int mOverflowHeight;
private int mSettingsIconHeight;
@@ -150,6 +163,7 @@ public class BubbleExpandedView extends LinearLayout {
// Apply flags to make behaviour match documentLaunchMode=always.
fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ fillInIntent.putExtra(Intent.EXTRA_IS_BUBBLED, true);
if (mBubble != null) {
mBubble.setIntentActive();
}
@@ -465,6 +479,29 @@ public class BubbleExpandedView extends LinearLayout {
}
/**
+ * Whether we are currently animating the TaskView's alpha value. If this is set to true, calls
+ * to {@link #setContentVisibility} will not be applied until this is set to false again.
+ */
+ void setAlphaAnimating(boolean animating) {
+ mIsAlphaAnimating = animating;
+
+ // If we're done animating, apply the correct
+ if (!animating) {
+ setContentVisibility(mIsContentVisible);
+ }
+ }
+
+ /**
+ * Sets the alpha of the underlying TaskView, since changing the expanded view's alpha does not
+ * affect the TaskView since it uses a Surface.
+ */
+ void setTaskViewAlpha(float alpha) {
+ if (mTaskView != null) {
+ mTaskView.setAlpha(alpha);
+ }
+ }
+
+ /**
* Set visibility of contents in the expanded state.
*
* @param visibility {@code true} if the contents should be visible on the screen.
@@ -477,16 +514,19 @@ public class BubbleExpandedView extends LinearLayout {
Log.d(TAG, "setContentVisibility: visibility=" + visibility
+ " bubble=" + getBubbleKey());
}
+ mIsContentVisible = visibility;
+
final float alpha = visibility ? 1f : 0f;
mPointerView.setAlpha(alpha);
- if (mTaskView != null) {
+ if (mTaskView != null && !mIsAlphaAnimating) {
mTaskView.setAlpha(alpha);
}
}
+
@Nullable
- View getTaskView() {
+ TaskView getTaskView() {
return mTaskView;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 16cd3cf3686c..51d63cff385a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -167,8 +167,12 @@ class BubbleOverflow(
return dotPath
}
- override fun setContentVisibility(visible: Boolean) {
- expandedView?.setContentVisibility(visible)
+ override fun setExpandedContentAlpha(alpha: Float) {
+ expandedView?.alpha = alpha
+ }
+
+ override fun setTaskViewVisibility(visible: Boolean) {
+ // Overflow does not have a TaskView.
}
override fun getIconView(): BadgedImageView? {
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 d54be0e62527..a3edc20e242a 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
@@ -43,7 +43,6 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Bundle;
-import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
import android.view.Choreographer;
@@ -123,6 +122,10 @@ public class BubbleStackView extends FrameLayout
@VisibleForTesting
static final int FLYOUT_HIDE_AFTER = 5000;
+ private static final float EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT = 0.1f;
+
+ private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
+
/**
* How long to wait to animate the stack temporarily invisible after a drag/flyout hide
* animation ends, if we are in fact temporarily invisible.
@@ -142,7 +145,7 @@ public class BubbleStackView extends FrameLayout
private final PhysicsAnimator.SpringConfig mTranslateSpringConfig =
new PhysicsAnimator.SpringConfig(
- SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY);
+ SpringForce.STIFFNESS_VERY_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY);
/**
* Handler to use for all delayed animations - this way, we can easily cancel them before
@@ -211,6 +214,9 @@ public class BubbleStackView extends FrameLayout
/** Container for the animating-out SurfaceView. */
private FrameLayout mAnimatingOutSurfaceContainer;
+ /** Animator for animating the alpha value of the animating out SurfaceView. */
+ private final ValueAnimator mAnimatingOutSurfaceAlphaAnimator = ValueAnimator.ofFloat(0f, 1f);
+
/**
* Buffer containing a screenshot of the animating-out bubble. This is drawn into the
* SurfaceView during animations.
@@ -261,6 +267,12 @@ public class BubbleStackView extends FrameLayout
/** Whether we're in the middle of dragging the stack around by touch. */
private boolean mIsDraggingStack = false;
+ /** Whether the expanded view has been hidden, because we are dragging out a bubble. */
+ private boolean mExpandedViewHidden = false;
+
+ /** Animator for animating the expanded view's alpha (including the TaskView inside it). */
+ private final ValueAnimator mExpandedViewAlphaAnimator = ValueAnimator.ofFloat(0f, 1f);
+
/**
* The pointer index of the ACTION_DOWN event we received prior to an ACTION_UP. We'll ignore
* touches from other pointer indices.
@@ -614,6 +626,12 @@ public class BubbleStackView extends FrameLayout
// Show the dismiss target, if we haven't already.
mDismissView.show();
+ if (mIsExpanded && mExpandedBubble != null && v.equals(mExpandedBubble.getIconView())) {
+ // Hide the expanded view if we're dragging out the expanded bubble, and we haven't
+ // already hidden it.
+ hideExpandedViewIfNeeded();
+ }
+
// First, see if the magnetized object consumes the event - if so, we shouldn't move the
// bubble since it's stuck to the target.
if (!passEventToMagnetizedObject(ev)) {
@@ -645,6 +663,9 @@ public class BubbleStackView extends FrameLayout
if (!passEventToMagnetizedObject(ev)) {
if (mBubbleData.isExpanded()) {
mExpandedAnimationController.snapBubbleBack(v, velX, velY);
+
+ // Re-show the expanded view if we hid it.
+ showExpandedViewIfNeeded();
} else {
// Fling the stack to the edge, and save whether or not it's going to end up on
// the left side of the screen.
@@ -937,6 +958,46 @@ public class BubbleStackView extends FrameLayout
animate()
.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED)
.setDuration(FADE_IN_DURATION);
+
+ mExpandedViewAlphaAnimator.setDuration(EXPANDED_VIEW_ALPHA_ANIMATION_DURATION);
+ mExpandedViewAlphaAnimator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
+ mExpandedViewAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ // We need to be Z ordered on top in order for alpha animations to work.
+ mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(true);
+ mExpandedBubble.getExpandedView().setAlphaAnimating(true);
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
+ mExpandedBubble.getExpandedView().setAlphaAnimating(false);
+ }
+ }
+ });
+ mExpandedViewAlphaAnimator.addUpdateListener(valueAnimator -> {
+ if (mExpandedBubble != null) {
+ mExpandedBubble.setExpandedContentAlpha((float) valueAnimator.getAnimatedValue());
+ }
+ });
+
+ mAnimatingOutSurfaceAlphaAnimator.setDuration(EXPANDED_VIEW_ALPHA_ANIMATION_DURATION);
+ mAnimatingOutSurfaceAlphaAnimator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
+ mAnimatingOutSurfaceAlphaAnimator.addUpdateListener(valueAnimator -> {
+ if (!mExpandedViewHidden) {
+ mAnimatingOutSurfaceView.setAlpha((float) valueAnimator.getAnimatedValue());
+ }
+ });
+ mAnimatingOutSurfaceAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ releaseAnimatingOutBubbleBuffer();
+ }
+ });
}
/**
@@ -1539,7 +1600,8 @@ public class BubbleStackView extends FrameLayout
// If we're expanded, screenshot the currently expanded bubble (before expanding the newly
// selected bubble) so we can animate it out.
- if (mIsExpanded && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ if (mIsExpanded && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null
+ && !mExpandedViewHidden) {
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
// Before screenshotting, have the real ActivityView show on top of other surfaces
// so that the screenshot doesn't flicker on top of it.
@@ -1575,7 +1637,7 @@ public class BubbleStackView extends FrameLayout
mExpandedViewContainer.setAlpha(0.0f);
mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
if (previouslySelected != null) {
- previouslySelected.setContentVisibility(false);
+ previouslySelected.setTaskViewVisibility(false);
}
updateExpandedBubble();
@@ -1656,6 +1718,58 @@ public class BubbleStackView extends FrameLayout
requestUpdate();
}
+ /** Animate the expanded view hidden. This is done while we're dragging out a bubble. */
+ private void hideExpandedViewIfNeeded() {
+ if (mExpandedViewHidden
+ || mExpandedBubble == null
+ || mExpandedBubble.getExpandedView() == null) {
+ return;
+ }
+
+ mExpandedViewHidden = true;
+
+ // Scale down.
+ PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
+ .spring(AnimatableScaleMatrix.SCALE_X,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
+ mScaleOutSpringConfig)
+ .spring(AnimatableScaleMatrix.SCALE_Y,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
+ mScaleOutSpringConfig)
+ .addUpdateListener((target, values) ->
+ mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix))
+ .start();
+
+ // Animate alpha from 1f to 0f.
+ mExpandedViewAlphaAnimator.reverse();
+ }
+
+ /**
+ * Animate the expanded view visible again. This is done when we're done dragging out a bubble.
+ */
+ private void showExpandedViewIfNeeded() {
+ if (!mExpandedViewHidden) {
+ return;
+ }
+
+ mExpandedViewHidden = false;
+
+ PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
+ .spring(AnimatableScaleMatrix.SCALE_X,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
+ mScaleOutSpringConfig)
+ .spring(AnimatableScaleMatrix.SCALE_Y,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
+ mScaleOutSpringConfig)
+ .addUpdateListener((target, values) ->
+ mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix))
+ .start();
+
+ mExpandedViewAlphaAnimator.start();
+ }
+
private void animateExpansion() {
cancelDelayedExpandCollapseSwitchAnimations();
final boolean showVertically = mPositioner.showBubblesVertically();
@@ -1714,7 +1828,7 @@ public class BubbleStackView extends FrameLayout
// Should not happen since we lay out before expanding, but just in case...
if (getWidth() > 0) {
startDelay = (long)
- (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION
+ (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION * 1.2f
+ (distanceAnimated / getWidth()) * 30);
}
@@ -1728,20 +1842,29 @@ public class BubbleStackView extends FrameLayout
pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding;
}
mExpandedViewContainerMatrix.setScale(
- 0f, 0f,
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
pivotX, pivotY);
} else {
mExpandedViewContainerMatrix.setScale(
- 0f, 0f,
- bubbleWillBeAt + mBubbleSize / 2f, getExpandedViewY());
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
+ bubbleWillBeAt + mBubbleSize / 2f,
+ getExpandedViewY());
}
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
- if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
+ if (mExpandedBubble.getExpandedView() != null) {
+ mExpandedBubble.setExpandedContentAlpha(0f);
+
+ // We'll be starting the alpha animation after a slight delay, so set this flag early
+ // here.
+ mExpandedBubble.getExpandedView().setAlphaAnimating(true);
}
mDelayedAnimationExecutor.executeDelayed(() -> {
+ mExpandedViewAlphaAnimator.start();
+
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
.spring(AnimatableScaleMatrix.SCALE_X,
@@ -1796,22 +1919,15 @@ public class BubbleStackView extends FrameLayout
// since we're about to animate collapsed.
mExpandedAnimationController.notifyPreparingToCollapse();
- final long startDelay =
- (long) (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION * 0.6f);
- mDelayedAnimationExecutor.executeDelayed(() -> {
- mExpandedAnimationController.collapseBackToStack(
- mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
- /* collapseTo */,
- () -> mBubbleContainer.setActiveController(mStackAnimationController));
- }, startDelay);
+ mExpandedAnimationController.collapseBackToStack(
+ mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
+ /* collapseTo */,
+ () -> mBubbleContainer.setActiveController(mStackAnimationController));
if (mTaskbarScrim.getVisibility() == VISIBLE) {
mTaskbarScrim.animate().alpha(0f).start();
}
- // We want to visually collapse into this bubble during the animation.
- final View expandingFromBubble = mExpandedBubble.getIconView();
-
int index;
if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) {
index = mBubbleData.getBubbles().size();
@@ -1840,31 +1956,25 @@ public class BubbleStackView extends FrameLayout
getExpandedViewY());
}
+ mExpandedViewAlphaAnimator.reverse();
+
+ // When the animation completes, we should no longer be showing the content.
+ if (mExpandedBubble.getExpandedView() != null) {
+ mExpandedBubble.getExpandedView().setContentVisibility(false);
+ }
+
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
- .spring(AnimatableScaleMatrix.SCALE_X, 0f, mScaleOutSpringConfig)
- .spring(AnimatableScaleMatrix.SCALE_Y, 0f, mScaleOutSpringConfig)
+ .spring(AnimatableScaleMatrix.SCALE_X,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
+ mScaleOutSpringConfig)
+ .spring(AnimatableScaleMatrix.SCALE_Y,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
+ mScaleOutSpringConfig)
.addUpdateListener((target, values) -> {
- if (expandingFromBubble != null) {
- // Follow the bubble as it translates!
- if (showVertically) {
- mExpandedViewContainerMatrix.postTranslate(
- 0f, expandingFromBubble.getTranslationY()
- - expandingFromBubbleAt);
- } else {
- mExpandedViewContainerMatrix.postTranslate(
- expandingFromBubble.getTranslationX()
- - expandingFromBubbleAt, 0f);
- }
- }
-
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
-
- // Hide early so we don't have a tiny little expanded view still visible at the
- // end of the scale animation.
- if (mExpandedViewContainerMatrix.getScaleX() < 0.05f) {
- mExpandedViewContainer.setVisibility(View.INVISIBLE);
- }
})
.withEndActions(() -> {
final BubbleViewProvider previouslySelected = mExpandedBubble;
@@ -1882,7 +1992,7 @@ public class BubbleStackView extends FrameLayout
updateBadgesAndZOrder(true /* setBadgeForCollapsedStack */);
afterExpandedViewAnimation();
if (previouslySelected != null) {
- previouslySelected.setContentVisibility(false);
+ previouslySelected.setTaskViewVisibility(false);
}
if (mPositioner.showingInTaskbar()) {
@@ -1903,22 +2013,21 @@ public class BubbleStackView extends FrameLayout
// The surface contains a screenshot of the animating out bubble, so we just need to animate
// it out (and then release the GraphicBuffer).
PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel();
- PhysicsAnimator animator = PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer)
- .spring(DynamicAnimation.SCALE_X, 0f, mScaleOutSpringConfig)
- .spring(DynamicAnimation.SCALE_Y, 0f, mScaleOutSpringConfig)
- .withEndActions(this::releaseAnimatingOutBubbleBuffer);
+
+ mAnimatingOutSurfaceAlphaAnimator.reverse();
+ mExpandedViewAlphaAnimator.start();
if (mPositioner.showBubblesVertically()) {
float translationX = mStackAnimationController.isStackOnLeftSide()
? mAnimatingOutSurfaceContainer.getTranslationX() + mBubbleSize * 2
: mAnimatingOutSurfaceContainer.getTranslationX();
- animator.spring(DynamicAnimation.TRANSLATION_X,
- translationX,
- mTranslateSpringConfig)
+ PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer)
+ .spring(DynamicAnimation.TRANSLATION_X, translationX, mTranslateSpringConfig)
.start();
} else {
- animator.spring(DynamicAnimation.TRANSLATION_Y,
- mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize * 2,
+ PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer)
+ .spring(DynamicAnimation.TRANSLATION_Y,
+ mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize,
mTranslateSpringConfig)
.start();
}
@@ -1947,7 +2056,8 @@ public class BubbleStackView extends FrameLayout
pivotX, pivotY);
} else {
mExpandedViewContainerMatrix.setScale(
- 0f, 0f,
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
expandingFromBubbleDestination + mBubbleSize / 2f,
getExpandedViewY());
}
@@ -1972,10 +2082,7 @@ public class BubbleStackView extends FrameLayout
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
})
.withEndActions(() -> {
- if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
- }
-
+ mExpandedViewHidden = false;
mIsBubbleSwitchAnimating = false;
})
.start();
@@ -2489,6 +2596,7 @@ public class BubbleStackView extends FrameLayout
&& mExpandedBubble.getExpandedView() != null) {
BubbleExpandedView bev = mExpandedBubble.getExpandedView();
bev.setContentVisibility(false);
+ bev.setAlphaAnimating(!mIsExpansionAnimating);
mExpandedViewContainerMatrix.setScaleX(0f);
mExpandedViewContainerMatrix.setScaleY(0f);
mExpandedViewContainerMatrix.setTranslate(0f, 0f);
@@ -2586,7 +2694,14 @@ public class BubbleStackView extends FrameLayout
mAnimatingOutBubbleBuffer.getHardwareBuffer(),
mAnimatingOutBubbleBuffer.getColorSpace());
- mSurfaceSynchronizer.syncSurfaceAndRun(() -> post(() -> onComplete.accept(true)));
+ mAnimatingOutSurfaceView.setAlpha(1f);
+ mExpandedViewContainer.setVisibility(View.GONE);
+
+ mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
+ post(() -> {
+ onComplete.accept(true);
+ });
+ });
});
}
@@ -2618,7 +2733,9 @@ public class BubbleStackView extends FrameLayout
}
}
mExpandedViewContainer.setPadding(leftPadding, 0, rightPadding, 0);
- mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
+ if (mIsExpansionAnimating) {
+ mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
+ }
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
mExpandedViewContainer.setTranslationY(getExpandedViewY());
mExpandedViewContainer.setTranslationX(0f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
index ec900be13658..da4259c42558 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
@@ -29,7 +29,16 @@ import androidx.annotation.Nullable;
public interface BubbleViewProvider {
@Nullable BubbleExpandedView getExpandedView();
- void setContentVisibility(boolean visible);
+ /**
+ * Sets the alpha of the expanded view content. This will be applied to both the expanded view
+ * container itself (the manage button, etc.) as well as the TaskView within it.
+ */
+ void setExpandedContentAlpha(float alpha);
+
+ /**
+ * Sets whether the contents of the bubble's TaskView should be visible.
+ */
+ void setTaskViewVisibility(boolean visible);
@Nullable View getIconView();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 6a1026bb24fe..8e061e9c9874 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -26,6 +26,7 @@ import android.os.Bundle;
import android.os.Looper;
import android.service.notification.NotificationListenerService.RankingMap;
import android.util.ArraySet;
+import android.util.Pair;
import android.view.View;
import androidx.annotation.IntDef;
@@ -37,6 +38,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
+import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
@@ -182,8 +184,11 @@ public interface Bubbles {
* permissions on the notification channel or the global setting.
*
* @param rankingMap the updated ranking map from NotificationListenerService
+ * @param entryDataByKey a map of ranking key to bubble entry and whether the entry should
+ * bubble up
*/
- void onRankingUpdated(RankingMap rankingMap);
+ void onRankingUpdated(RankingMap rankingMap,
+ HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey);
/**
* Called when the status bar has become visible or invisible (either permanently or
@@ -243,14 +248,10 @@ public interface Bubbles {
/** Callback to tell SysUi components execute some methods. */
interface SysuiProxy {
- @Nullable
- BubbleEntry getPendingOrActiveEntry(String key);
+ void getPendingOrActiveEntry(String key, Consumer<BubbleEntry> callback);
- List<BubbleEntry> getShouldRestoredEntries(ArraySet<String> savedBubbleKeys);
-
- boolean isNotificationShadeExpand();
-
- boolean shouldBubbleUp(String key);
+ void getShouldRestoredEntries(ArraySet<String> savedBubbleKeys,
+ Consumer<List<BubbleEntry>> callback);
void setNotificationInterruption(String key);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 73371e7eff20..56fe126f507e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -75,7 +75,7 @@ public class StackAnimationController extends
*/
public static final int SPRING_TO_TOUCH_STIFFNESS = 12000;
public static final float IME_ANIMATION_STIFFNESS = SpringForce.STIFFNESS_LOW;
- private static final int CHAIN_STIFFNESS = 600;
+ private static final int CHAIN_STIFFNESS = 800;
public static final float DEFAULT_BOUNCINESS = 0.9f;
private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfig =
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 616f24a874bc..fb70cbe502b0 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
@@ -40,9 +40,11 @@ import android.view.IWindowSessionCallback;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.view.SurfaceControlViewHost;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
import android.window.ClientWindowFrames;
@@ -251,6 +253,8 @@ public class SystemWindows {
public class SysUiWindowManager extends WindowlessWindowManager {
final int mDisplayId;
ContainerWindow mContainerWindow;
+ final HashMap<IBinder, SurfaceControl> mLeashForWindow =
+ new HashMap<IBinder, SurfaceControl>();
public SysUiWindowManager(int displayId, Context ctx, SurfaceControl rootSurface,
ContainerWindow container) {
super(ctx.getResources().getConfiguration(), rootSurface, null /* hostInputToken */);
@@ -263,7 +267,33 @@ public class SystemWindows {
}
SurfaceControl getSurfaceControlForWindow(View rootView) {
- return getSurfaceControl(rootView);
+ synchronized (this) {
+ return mLeashForWindow.get(getWindowBinder(rootView));
+ }
+ }
+
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ SurfaceControl leash = new SurfaceControl.Builder(new SurfaceSession())
+ .setContainerLayer()
+ .setName("SystemWindowLeash")
+ .setHidden(false)
+ .setParent(mRootSurface)
+ .setCallsite("SysUiWIndowManager#attachToParentSurface").build();
+ synchronized (this) {
+ mLeashForWindow.put(window.asBinder(), leash);
+ }
+ b.setParent(leash);
+ }
+
+ @Override
+ public void remove(android.view.IWindow window) throws RemoteException {
+ super.remove(window);
+ synchronized(this) {
+ IBinder token = window.asBinder();
+ new SurfaceControl.Transaction().remove(mLeashForWindow.get(token))
+ .apply();
+ mLeashForWindow.remove(token);
+ }
}
void setTouchableRegionForWindow(View rootView, Region region) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index 7f9c34f5df7a..87f0c25c93df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -39,6 +39,7 @@ import android.view.IWindow;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
@@ -55,6 +56,7 @@ public final class SplitWindowManager extends WindowlessWindowManager {
private final ParentContainerCallbacks mParentContainerCallbacks;
private Context mContext;
private SurfaceControlViewHost mViewHost;
+ private SurfaceControl mLeash;
private boolean mResizingSplits;
private final String mWindowName;
@@ -87,8 +89,16 @@ public final class SplitWindowManager extends WindowlessWindowManager {
}
@Override
- protected void attachToParentSurface(SurfaceControl.Builder b) {
- mParentContainerCallbacks.attachToParentSurface(b);
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ .setContainerLayer()
+ .setName(TAG)
+ .setHidden(false)
+ .setCallsite("SplitWindowManager#attachToParentSurface");
+ mParentContainerCallbacks.attachToParentSurface(builder);
+ mLeash = builder.build();
+ b.setParent(mLeash);
}
/** Inflates {@link DividerView} on to the root surface. */
@@ -118,9 +128,15 @@ public final class SplitWindowManager extends WindowlessWindowManager {
* hierarchy.
*/
void release() {
- if (mViewHost == null) return;
- mViewHost.release();
- mViewHost = null;
+ if (mViewHost != null){
+ mViewHost.release();
+ mViewHost = null;
+ }
+
+ if (mLeash != null) {
+ new SurfaceControl.Transaction().remove(mLeash).apply();
+ mLeash = null;
+ }
}
void setResizingSplits(boolean resizing) {
@@ -139,6 +155,6 @@ public final class SplitWindowManager extends WindowlessWindowManager {
*/
@Nullable
SurfaceControl getSurfaceControl() {
- return mViewHost == null ? null : getSurfaceControl(mViewHost.getWindowToken());
+ return mLeash;
}
}
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 c8938ad40aba..17709438baba 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
@@ -53,6 +53,7 @@ import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import java.util.Optional;
@@ -66,7 +67,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
private final Context mContext;
private final DisplayController mDisplayController;
- private SplitScreen mSplitScreen;
+ private SplitScreenController mSplitScreen;
private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
@@ -76,7 +77,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
mDisplayController = displayController;
}
- public void initialize(Optional<SplitScreen> splitscreen) {
+ public void initialize(Optional<SplitScreenController> splitscreen) {
mSplitScreen = splitscreen.orElse(null);
mDisplayController.addDisplayWindowListener(this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 35dcdd5923a8..6f5f2eb5723c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -64,7 +64,9 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreen.StagePosition;
+import com.android.wm.shell.splitscreen.SplitScreen.StageType;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -81,18 +83,18 @@ public class DragAndDropPolicy {
private final Context mContext;
private final ActivityTaskManager mActivityTaskManager;
private final Starter mStarter;
- private final SplitScreen mSplitScreen;
+ private final SplitScreenController mSplitScreen;
private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>();
private DragSession mSession;
- public DragAndDropPolicy(Context context, SplitScreen splitScreen) {
+ public DragAndDropPolicy(Context context, SplitScreenController splitScreen) {
this(context, ActivityTaskManager.getInstance(), splitScreen, new DefaultStarter(context));
}
@VisibleForTesting
DragAndDropPolicy(Context context, ActivityTaskManager activityTaskManager,
- SplitScreen splitScreen, Starter starter) {
+ SplitScreenController splitScreen, Starter starter) {
mContext = context;
mActivityTaskManager = activityTaskManager;
mSplitScreen = splitScreen;
@@ -200,8 +202,8 @@ public class DragAndDropPolicy {
final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT;
- @SplitScreen.StageType int stage = STAGE_TYPE_UNDEFINED;
- @SplitScreen.StagePosition int position = STAGE_POSITION_UNDEFINED;
+ @StageType int stage = STAGE_TYPE_UNDEFINED;
+ @StagePosition int position = STAGE_POSITION_UNDEFINED;
if (target.type != TYPE_FULLSCREEN && mSplitScreen != null) {
// Update launch options for the split side we are targeting.
position = leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT;
@@ -213,7 +215,28 @@ public class DragAndDropPolicy {
final ClipDescription description = data.getDescription();
final Intent dragData = mSession.dragData;
- mStarter.startClipDescription(description, dragData, stage, position);
+ startClipDescription(description, dragData, stage, position);
+ }
+
+ private void startClipDescription(ClipDescription description, Intent intent,
+ @StageType int stage, @StagePosition int position) {
+ final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
+ final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
+ final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
+ ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) : new Bundle();
+
+ if (isTask) {
+ final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
+ mStarter.startTask(taskId, stage, position, opts);
+ } else if (isShortcut) {
+ final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+ final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
+ final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
+ mStarter.startShortcut(packageName, id, stage, position, opts, user);
+ } else {
+ mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position,
+ opts);
+ }
}
/**
@@ -267,34 +290,13 @@ public class DragAndDropPolicy {
/**
* Interface for actually committing the task launches.
*/
- @VisibleForTesting
public interface Starter {
- default void startClipDescription(ClipDescription description, Intent intent,
- @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position) {
- final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
- final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
- final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
- ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) : new Bundle();
-
- if (isTask) {
- final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
- startTask(taskId, stage, position, opts);
- } else if (isShortcut) {
- final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
- final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
- final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
- startShortcut(packageName, id, stage, position, opts, user);
- } else {
- startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position, opts);
- }
- }
- void startTask(int taskId, @SplitScreen.StageType int stage,
- @SplitScreen.StagePosition int position, @Nullable Bundle options);
- void startShortcut(String packageName, String shortcutId,
- @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
- @Nullable Bundle options, UserHandle user);
- void startIntent(PendingIntent intent, @SplitScreen.StageType int stage,
- @SplitScreen.StagePosition int position, @Nullable Bundle options);
+ void startTask(int taskId, @StageType int stage, @StagePosition int position,
+ @Nullable Bundle options);
+ void startShortcut(String packageName, String shortcutId, @StageType int stage,
+ @StagePosition int position, @Nullable Bundle options, UserHandle user);
+ void startIntent(PendingIntent intent, @StageType int stage, @StagePosition int position,
+ @Nullable Bundle options);
void enterSplitScreen(int taskId, boolean leftOrTop);
void exitSplitScreen();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index 82c4e440fb15..b3423362347f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -42,7 +42,7 @@ import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import java.util.ArrayList;
@@ -61,7 +61,7 @@ public class DragLayout extends View {
private boolean mIsShowing;
private boolean mHasDropped;
- public DragLayout(Context context, SplitScreen splitscreen) {
+ public DragLayout(Context context, SplitScreenController splitscreen) {
super(context);
mPolicy = new DragAndDropPolicy(context, splitscreen);
mDisplayMargin = context.getResources().getDimensionPixelSize(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java
index 3a2f0da6bf03..60123ab97fd7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java
@@ -31,12 +31,6 @@ import java.io.PrintWriter;
public interface HideDisplayCutout {
/**
* Notifies {@link Configuration} changed.
- * @param newConfig
*/
void onConfigurationChanged(Configuration newConfig);
-
- /**
- * Dumps hide display cutout status.
- */
- void dump(@NonNull PrintWriter pw);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
index 12b8b87f1285..23f76ca5f6ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
@@ -44,20 +44,12 @@ public class HideDisplayCutoutController {
@VisibleForTesting
boolean mEnabled;
- HideDisplayCutoutController(Context context, HideDisplayCutoutOrganizer organizer,
- ShellExecutor mainExecutor) {
- mContext = context;
- mOrganizer = organizer;
- mMainExecutor = mainExecutor;
- updateStatus();
- }
-
/**
* Creates {@link HideDisplayCutoutController}, returns {@code null} if the feature is not
* supported.
*/
@Nullable
- public static HideDisplayCutout create(
+ public static HideDisplayCutoutController create(
Context context, DisplayController displayController, ShellExecutor mainExecutor) {
// The SystemProperty is set for devices that support this feature and is used to control
// whether to create the HideDisplayCutout instance.
@@ -68,7 +60,19 @@ public class HideDisplayCutoutController {
HideDisplayCutoutOrganizer organizer =
new HideDisplayCutoutOrganizer(context, displayController, mainExecutor);
- return new HideDisplayCutoutController(context, organizer, mainExecutor).mImpl;
+ return new HideDisplayCutoutController(context, organizer, mainExecutor);
+ }
+
+ HideDisplayCutoutController(Context context, HideDisplayCutoutOrganizer organizer,
+ ShellExecutor mainExecutor) {
+ mContext = context;
+ mOrganizer = organizer;
+ mMainExecutor = mainExecutor;
+ updateStatus();
+ }
+
+ public HideDisplayCutout asHideDisplayCutout() {
+ return mImpl;
}
@VisibleForTesting
@@ -94,7 +98,7 @@ public class HideDisplayCutoutController {
updateStatus();
}
- private void dump(@NonNull PrintWriter pw) {
+ public void dump(@NonNull PrintWriter pw) {
final String prefix = " ";
pw.print(TAG);
pw.println(" states: ");
@@ -111,14 +115,5 @@ public class HideDisplayCutoutController {
HideDisplayCutoutController.this.onConfigurationChanged(newConfig);
});
}
-
- @Override
- public void dump(@NonNull PrintWriter pw) {
- try {
- mMainExecutor.executeBlocking(() -> HideDisplayCutoutController.this.dump(pw));
- } catch (InterruptedException e) {
- Slog.e(TAG, "Failed to dump HideDisplayCutoutController in 2s");
- }
- }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
index e13a1dbe22d6..a18d106abea4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
@@ -862,14 +862,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
* assigned to it.
*/
private SurfaceControl getWindowSurfaceControl() {
- final ViewRootImpl root = getViewRootImpl();
- if (root == null) {
- return null;
- }
- SurfaceControl out = root.getSurfaceControl();
- if (out != null && out.isValid()) {
- return out;
- }
return mWindowManager.mSystemWindows.getViewSurface(this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
index bca6deb451c9..d25bef197359 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
@@ -115,21 +115,6 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
private volatile boolean mAdjustedForIme = false;
private boolean mHomeStackResizable = false;
- /**
- * Creates {@link SplitScreen}, returns {@code null} if the feature is not supported.
- */
- @Nullable
- public static LegacySplitScreen create(Context context,
- DisplayController displayController, SystemWindows systemWindows,
- DisplayImeController imeController, TransactionPool transactionPool,
- ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
- TaskStackListenerImpl taskStackListener, Transitions transitions,
- ShellExecutor mainExecutor, AnimationHandler sfVsyncAnimationHandler) {
- return new LegacySplitScreenController(context, displayController, systemWindows,
- imeController, transactionPool, shellTaskOrganizer, syncQueue, taskStackListener,
- transitions, mainExecutor, sfVsyncAnimationHandler).mImpl;
- }
-
public LegacySplitScreenController(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController imeController, TransactionPool transactionPool,
@@ -228,8 +213,12 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
});
}
+
+ public LegacySplitScreen asLegacySplitScreen() {
+ return mImpl;
+ }
- void onSplitScreenSupported() {
+ public void onSplitScreenSupported() {
// Set starting tile bounds based on middle target
final WindowContainerTransaction tct = new WindowContainerTransaction();
int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
@@ -237,7 +226,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
mTaskOrganizer.applyTransaction(tct);
}
- private void onKeyguardVisibilityChanged(boolean showing) {
+ public void onKeyguardVisibilityChanged(boolean showing) {
if (!isSplitActive() || mView == null) {
return;
}
@@ -293,19 +282,19 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- boolean isMinimized() {
+ public boolean isMinimized() {
return mMinimized;
}
- boolean isHomeStackResizable() {
+ public boolean isHomeStackResizable() {
return mHomeStackResizable;
}
- DividerView getDividerView() {
+ public DividerView getDividerView() {
return mView;
}
- boolean isDividerVisible() {
+ public boolean isDividerVisible() {
return mView != null && mView.getVisibility() == View.VISIBLE;
}
@@ -314,13 +303,13 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
* isDividerVisible because the divider is only visible once *everything* is in split mode
* while this only cares if some things are (eg. while entering/exiting as well).
*/
- private boolean isSplitActive() {
+ public boolean isSplitActive() {
return mSplits.mPrimary != null && mSplits.mSecondary != null
&& (mSplits.mPrimary.topActivityType != ACTIVITY_TYPE_UNDEFINED
|| mSplits.mSecondary.topActivityType != ACTIVITY_TYPE_UNDEFINED);
}
- private void addDivider(Configuration configuration) {
+ public void addDivider(Configuration configuration) {
Context dctx = mDisplayController.getDisplayContext(mContext.getDisplayId());
mView = (DividerView)
LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
@@ -338,14 +327,14 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
mWindowManager.add(mView, width, height, mContext.getDisplayId());
}
- private void removeDivider() {
+ public void removeDivider() {
if (mView != null) {
mView.onDividerRemoved();
}
mWindowManager.remove();
}
- private void update(Configuration configuration) {
+ public void update(Configuration configuration) {
final boolean isDividerHidden = mView != null && mIsKeyguardShowing;
removeDivider();
@@ -358,11 +347,11 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
mView.setHidden(isDividerHidden);
}
- void onTaskVanished() {
+ public void onTaskVanished() {
removeDivider();
}
- private void updateVisibility(final boolean visible) {
+ public void updateVisibility(final boolean visible) {
if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible);
if (mVisible != visible) {
mVisible = visible;
@@ -390,7 +379,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- private void setMinimized(final boolean minimized) {
+ public void setMinimized(final boolean minimized) {
if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
mMainExecutor.execute(() -> {
if (DEBUG) Slog.d(TAG, "run posted ext setMinimized " + minimized + " vis:" + mVisible);
@@ -401,7 +390,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
});
}
- private void setHomeMinimized(final boolean minimized) {
+ public void setHomeMinimized(final boolean minimized) {
if (DEBUG) {
Slog.d(TAG, "setHomeMinimized min:" + mMinimized + "->" + minimized + " hrsz:"
+ mHomeStackResizable + " split:" + isDividerVisible());
@@ -441,7 +430,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- void setAdjustedForIme(boolean adjustedForIme) {
+ public void setAdjustedForIme(boolean adjustedForIme) {
if (mAdjustedForIme == adjustedForIme) {
return;
}
@@ -449,30 +438,30 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
updateTouchable();
}
- private void updateTouchable() {
+ public void updateTouchable() {
mWindowManager.setTouchable(!mAdjustedForIme);
}
- private void onUndockingTask() {
+ public void onUndockingTask() {
if (mView != null) {
mView.onUndockingTask();
}
}
- private void onAppTransitionFinished() {
+ public void onAppTransitionFinished() {
if (mView == null) {
return;
}
mForcedResizableController.onAppTransitionFinished();
}
- private void dump(PrintWriter pw) {
+ public void dump(PrintWriter pw) {
pw.print(" mVisible="); pw.println(mVisible);
pw.print(" mMinimized="); pw.println(mMinimized);
pw.print(" mAdjustedForIme="); pw.println(mAdjustedForIme);
}
- long getAnimDuration() {
+ public long getAnimDuration() {
float transitionScale = Settings.Global.getFloat(mContext.getContentResolver(),
Settings.Global.TRANSITION_ANIMATION_SCALE,
mContext.getResources().getFloat(
@@ -482,14 +471,14 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
return (long) (transitionDuration * transitionScale);
}
- void registerInSplitScreenListener(Consumer<Boolean> listener) {
+ public void registerInSplitScreenListener(Consumer<Boolean> listener) {
listener.accept(isDividerVisible());
synchronized (mDockedStackExistsListeners) {
mDockedStackExistsListeners.add(new WeakReference<>(listener));
}
}
- void unregisterInSplitScreenListener(Consumer<Boolean> listener) {
+ public void unregisterInSplitScreenListener(Consumer<Boolean> listener) {
synchronized (mDockedStackExistsListeners) {
for (int i = mDockedStackExistsListeners.size() - 1; i >= 0; i--) {
if (mDockedStackExistsListeners.get(i) == listener) {
@@ -499,13 +488,13 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- private void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) {
+ public void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) {
synchronized (mBoundsChangedListeners) {
mBoundsChangedListeners.add(new WeakReference<>(listener));
}
}
- private boolean splitPrimaryTask() {
+ public boolean splitPrimaryTask() {
try {
if (ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED
|| isSplitActive()) {
@@ -538,12 +527,12 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
topRunningTask.taskId, true /* onTop */);
}
- private void dismissSplitToPrimaryTask() {
+ public void dismissSplitToPrimaryTask() {
startDismissSplit(true /* toPrimaryTask */);
}
/** Notifies the bounds of split screen changed. */
- void notifyBoundsChanged(Rect secondaryWindowBounds, Rect secondaryWindowInsets) {
+ public void notifyBoundsChanged(Rect secondaryWindowBounds, Rect secondaryWindowInsets) {
synchronized (mBoundsChangedListeners) {
mBoundsChangedListeners.removeIf(wf -> {
BiConsumer<Rect, Rect> l = wf.get();
@@ -553,19 +542,19 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- void startEnterSplit() {
+ public void startEnterSplit() {
update(mDisplayController.getDisplayContext(
mContext.getDisplayId()).getResources().getConfiguration());
// Set resizable directly here because applyEnterSplit already resizes home stack.
mHomeStackResizable = mWindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout);
}
- void prepareEnterSplitTransition(WindowContainerTransaction outWct) {
+ public void prepareEnterSplitTransition(WindowContainerTransaction outWct) {
// Set resizable directly here because buildEnterSplit already resizes home stack.
mHomeStackResizable = mWindowManagerProxy.buildEnterSplit(outWct, mSplits, mSplitLayout);
}
- void finishEnterSplitTransition(boolean minimized) {
+ public void finishEnterSplitTransition(boolean minimized) {
update(mDisplayController.getDisplayContext(
mContext.getDisplayId()).getResources().getConfiguration());
if (minimized) {
@@ -575,11 +564,11 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- void startDismissSplit(boolean toPrimaryTask) {
+ public void startDismissSplit(boolean toPrimaryTask) {
startDismissSplit(toPrimaryTask, false /* snapped */);
}
- void startDismissSplit(boolean toPrimaryTask, boolean snapped) {
+ public void startDismissSplit(boolean toPrimaryTask, boolean snapped) {
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
mSplits.getSplitTransitions().dismissSplit(
mSplits, mSplitLayout, !toPrimaryTask, snapped);
@@ -589,7 +578,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- void onDismissSplit() {
+ public void onDismissSplit() {
updateVisibility(false /* visible */);
mMinimized = false;
// Resets divider bar position to undefined, so new divider bar will apply default position
@@ -599,7 +588,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
mImePositionProcessor.reset();
}
- void ensureMinimizedSplit() {
+ public void ensureMinimizedSplit() {
setHomeMinimized(true /* minimized */);
if (mView != null && !isDividerVisible()) {
// Wasn't in split-mode yet, so enter now.
@@ -610,7 +599,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- void ensureNormalSplit() {
+ public void ensureNormalSplit() {
setHomeMinimized(false /* minimized */);
if (mView != null && !isDividerVisible()) {
// Wasn't in split-mode, so enter now.
@@ -621,15 +610,15 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays
}
}
- LegacySplitDisplayLayout getSplitLayout() {
+ public LegacySplitDisplayLayout getSplitLayout() {
return mSplitLayout;
}
- WindowManagerProxy getWmProxy() {
+ public WindowManagerProxy getWmProxy() {
return mWindowManagerProxy;
}
- WindowContainerToken getSecondaryRoot() {
+ public WindowContainerToken getSecondaryRoot() {
if (mSplits == null || mSplits.mSecondary == null) {
return null;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
index 5a493c234ce3..05526018d73f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
@@ -323,6 +323,14 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener {
}
@Override
+ public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ if (!mLeashByTaskId.contains(taskId)) {
+ throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
+ }
+ b.setParent(mLeashByTaskId.get(taskId));
+ }
+
+ @Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
final String childPrefix = innerPrefix + " ";
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 94c6f018b6ac..c8f89876222e 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
@@ -39,7 +39,6 @@ import android.view.WindowManagerGlobal;
import android.window.TaskOrganizer;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
-import android.window.WindowOrganizer;
import com.android.internal.annotations.GuardedBy;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -116,7 +115,7 @@ class WindowManagerProxy {
void applyResizeSplits(int position, LegacySplitDisplayLayout splitLayout) {
WindowContainerTransaction t = new WindowContainerTransaction();
splitLayout.resizeSplits(position, t);
- new WindowOrganizer().applyTransaction(t);
+ applySyncTransaction(t);
}
boolean getHomeAndRecentsTasks(List<ActivityManager.RunningTaskInfo> out,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
index e95864873c0c..4f31c370108d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -16,13 +16,11 @@
package com.android.wm.shell.onehanded;
-import androidx.annotation.NonNull;
+import android.content.res.Configuration;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
-import java.io.PrintWriter;
-
/**
* Interface to engage one handed feature.
*/
@@ -54,24 +52,33 @@ public interface OneHanded {
void stopOneHanded(int uiEvent);
/**
- * Set navigation 3 button mode enabled or disabled by users.
+ * Sets navigation 3 button mode enabled or disabled by users.
*/
void setThreeButtonModeEnabled(boolean enabled);
/**
- * Register callback to be notified after {@link OneHandedDisplayAreaOrganizer}
+ * Sets one handed feature temporary locked in enabled or disabled state, this won't change
+ * settings configuration.
+ *
+ * @param locked locked function in disabled(can not trigger) or enabled state.
+ * @param enabled function in disabled(can not trigger) or enabled state.
+ */
+ void setLockedDisabled(boolean locked, boolean enabled);
+
+ /**
+ * Registers callback to be notified after {@link OneHandedDisplayAreaOrganizer}
* transition start or finish
*/
void registerTransitionCallback(OneHandedTransitionCallback callback);
/**
- * Register callback for one handed gesture, this gesture callbcak will be activated on
+ * Registers callback for one handed gesture, this gesture callback will be activated on
* 3 button navigation mode only
*/
void registerGestureCallback(OneHandedGestureEventCallback callback);
/**
- * Dump one handed status.
+ * Receive onConfigurationChanged() events
*/
- void dump(@NonNull PrintWriter pw);
+ void onConfigChanged(Configuration newConfig);
}
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 eaa704f22410..a1b1de3faee2 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
@@ -23,6 +23,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
+import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.Point;
import android.os.Handler;
@@ -31,6 +32,7 @@ import android.os.ServiceManager;
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Slog;
+import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
@@ -65,6 +67,7 @@ public class OneHandedController {
private volatile boolean mIsOneHandedEnabled;
private volatile boolean mIsSwipeToNotificationEnabled;
private boolean mTaskChangeToExit;
+ private boolean mLockedDisabled;
private float mOffSetFraction;
private final Context mContext;
@@ -134,10 +137,10 @@ public class OneHandedController {
/**
- * Creates {@link OneHanded}, returns {@code null} if the feature is not supported.
+ * Creates {@link OneHandedController}, returns {@code null} if the feature is not supported.
*/
@Nullable
- public static OneHanded create(
+ public static OneHandedController create(
Context context, DisplayController displayController,
TaskStackListenerImpl taskStackListener, UiEventLogger uiEventLogger,
ShellExecutor mainExecutor, Handler mainHandler) {
@@ -154,7 +157,7 @@ public class OneHandedController {
OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
mainExecutor);
OneHandedGestureHandler gestureHandler = new OneHandedGestureHandler(
- context, displayController, mainExecutor);
+ context, displayController, ViewConfiguration.get(context), mainExecutor);
OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer =
new OneHandedBackgroundPanelOrganizer(context, displayController, mainExecutor);
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
@@ -166,7 +169,7 @@ public class OneHandedController {
return new OneHandedController(context, displayController,
oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
gestureHandler, timeoutHandler, oneHandedUiEventsLogger, overlayManager,
- taskStackListener, mainExecutor, mainHandler).mImpl;
+ taskStackListener, mainExecutor, mainHandler);
}
@VisibleForTesting
@@ -222,12 +225,15 @@ public class OneHandedController {
setupGesturalOverlay();
updateSettings();
- mAccessibilityManager = (AccessibilityManager)
- context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ mAccessibilityManager = AccessibilityManager.getInstance(context);
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityStateChangeListener);
}
+ public OneHanded asOneHanded() {
+ return mImpl;
+ }
+
/**
* Set one handed enabled or disabled when user update settings
*/
@@ -258,6 +264,10 @@ public class OneHandedController {
@VisibleForTesting
void startOneHanded() {
+ if (isLockedDisabled()) {
+ Slog.d(TAG, "Temporary lock disabled");
+ return;
+ }
if (!mDisplayAreaOrganizer.isInOneHanded()) {
final int yOffSet = Math.round(getDisplaySize().y * mOffSetFraction);
mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
@@ -341,7 +351,8 @@ public class OneHandedController {
};
}
- private void onEnabledSettingChanged() {
+ @VisibleForTesting
+ void onEnabledSettingChanged() {
final boolean enabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
mContext.getContentResolver());
mOneHandedUiEventLogger.writeEvent(enabled
@@ -356,7 +367,8 @@ public class OneHandedController {
mContext.getContentResolver()));
}
- private void onTimeoutSettingChanged() {
+ @VisibleForTesting
+ void onTimeoutSettingChanged() {
final int newTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout(
mContext.getContentResolver());
int metricsId = OneHandedUiEventLogger.OneHandedSettingsTogglesEvent.INVALID.getId();
@@ -384,7 +396,8 @@ public class OneHandedController {
}
}
- private void onTaskChangeExitSettingChanged() {
+ @VisibleForTesting
+ void onTaskChangeExitSettingChanged() {
final boolean enabled = OneHandedSettingsUtil.getSettingsTapsAppToExit(
mContext.getContentResolver());
mOneHandedUiEventLogger.writeEvent(enabled
@@ -394,7 +407,8 @@ public class OneHandedController {
setTaskChangeToExit(enabled);
}
- private void onSwipeToNotificationEnabledSettingChanged() {
+ @VisibleForTesting
+ void onSwipeToNotificationEnabledSettingChanged() {
final boolean enabled =
OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
mContext.getContentResolver());
@@ -424,21 +438,35 @@ public class OneHandedController {
return displaySize;
}
+ @VisibleForTesting
+ boolean isLockedDisabled() {
+ return mLockedDisabled;
+ }
+
private void updateOneHandedEnabled() {
if (mDisplayAreaOrganizer.isInOneHanded()) {
stopOneHanded();
}
- // TODO Be aware to unregisterOrganizer() after animation finished
- mDisplayAreaOrganizer.unregisterOrganizer();
- mBackgroundPanelOrganizer.unregisterOrganizer();
- if (mIsOneHandedEnabled) {
+
+ mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
+ mGestureHandler.onOneHandedEnabled(mIsOneHandedEnabled || mIsSwipeToNotificationEnabled);
+
+ if (!mIsOneHandedEnabled) {
+ mDisplayAreaOrganizer.unregisterOrganizer();
+ mBackgroundPanelOrganizer.unregisterOrganizer();
+ // Do NOT register + unRegister DA in the same call
+ return;
+ }
+
+ if (mDisplayAreaOrganizer.getDisplayAreaTokenMap().isEmpty()) {
mDisplayAreaOrganizer.registerOrganizer(
OneHandedDisplayAreaOrganizer.FEATURE_ONE_HANDED);
+ }
+
+ if (mBackgroundPanelOrganizer.getBackgroundSurface() == null) {
mBackgroundPanelOrganizer.registerOrganizer(
OneHandedBackgroundPanelOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL);
}
- mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
- mGestureHandler.onOneHandedEnabled(mIsOneHandedEnabled || mIsSwipeToNotificationEnabled);
}
private void setupGesturalOverlay() {
@@ -448,7 +476,6 @@ public class OneHandedController {
OverlayInfo info = null;
try {
- // TODO(b/157958539) migrate new RRO config file after S+
mOverlayManager.setHighestPriority(ONE_HANDED_MODE_GESTURAL_OVERLAY, USER_CURRENT);
info = mOverlayManager.getOverlayInfo(ONE_HANDED_MODE_GESTURAL_OVERLAY, USER_CURRENT);
} catch (RemoteException e) { /* Do nothing */ }
@@ -468,11 +495,31 @@ public class OneHandedController {
}
}
- private void dump(@NonNull PrintWriter pw) {
+ @VisibleForTesting
+ void setLockedDisabled(boolean locked, boolean enabled) {
+ if (enabled == mIsOneHandedEnabled) {
+ return;
+ }
+ mLockedDisabled = locked && !enabled;
+ }
+
+ private void onConfigChanged(Configuration newConfig) {
+ if (mTutorialHandler != null) {
+ if (!mIsOneHandedEnabled
+ || newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ return;
+ }
+ mTutorialHandler.onConfigurationChanged(newConfig);
+ }
+ }
+
+ public void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG + "states: ");
pw.print(innerPrefix + "mOffSetFraction=");
pw.println(mOffSetFraction);
+ pw.print(innerPrefix + "mLockedDisabled=");
+ pw.println(mLockedDisabled);
if (mDisplayAreaOrganizer != null) {
mDisplayAreaOrganizer.dump(pw);
@@ -549,6 +596,13 @@ public class OneHandedController {
}
@Override
+ public void setLockedDisabled(boolean locked, boolean enabled) {
+ mMainExecutor.execute(() -> {
+ OneHandedController.this.setLockedDisabled(locked, enabled);
+ });
+ }
+
+ @Override
public void registerTransitionCallback(OneHandedTransitionCallback callback) {
mMainExecutor.execute(() -> {
OneHandedController.this.registerTransitionCallback(callback);
@@ -563,9 +617,9 @@ public class OneHandedController {
}
@Override
- public void dump(@NonNull PrintWriter pw) {
+ public void onConfigChanged(Configuration newConfig) {
mMainExecutor.execute(() -> {
- OneHandedController.this.dump(pw);
+ OneHandedController.this.onConfigChanged(newConfig);
});
}
}
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 04d1264bdd9d..afc8a097dd05 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
@@ -66,8 +66,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
private boolean mIsInOneHanded;
private int mEnterExitAnimationDurationMs;
- @VisibleForTesting
- ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap();
+ private ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap();
private DisplayController mDisplayController;
private OneHandedAnimationController mAnimationController;
private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
@@ -298,6 +297,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
return new Rect(0, 0, realSize.x, realSize.y);
}
+ @VisibleForTesting
+ ArrayMap<WindowContainerToken, SurfaceControl> getDisplayAreaTokenMap() {
+ return mDisplayAreaTokenMap;
+ }
+
/**
* Register transition callback
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
index 49b7e050c48b..91e649f98292 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
@@ -87,6 +87,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback,
* @param displayController {@link DisplayController}
*/
public OneHandedGestureHandler(Context context, DisplayController displayController,
+ ViewConfiguration viewConfig,
ShellExecutor mainExecutor) {
mDisplayController = displayController;
mMainExecutor = mainExecutor;
@@ -95,7 +96,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback,
com.android.internal.R.dimen.navigation_bar_gesture_larger_height);
mDragDistThreshold = context.getResources().getDimensionPixelSize(
R.dimen.gestures_onehanded_drag_threshold);
- final float slop = ViewConfiguration.get(context).getScaledTouchSlop();
+ final float slop = viewConfig.getScaledTouchSlop();
mSquaredSlop = slop * slop;
updateIsEnabled();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index 492130bebb30..3f72b80a7dce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.onehanded;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -55,12 +56,14 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
private final AccessibilityManager mAccessibilityManager;
private final String mPackageName;
+ private Context mContext;
private View mTutorialView;
private Point mDisplaySize = new Point();
private ContentResolver mContentResolver;
private boolean mCanShowTutorial;
private String mStartOneHandedDescription;
private String mStopOneHandedDescription;
+ private boolean mIsOneHandedMode;
private enum ONE_HANDED_TRIGGER_STATE {
UNSET, ENTERING, EXITING
@@ -92,19 +95,19 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
mTriggerState = (startValue.top == 0)
? ONE_HANDED_TRIGGER_STATE.ENTERING : ONE_HANDED_TRIGGER_STATE.EXITING;
if (mCanShowTutorial && mTriggerState == ONE_HANDED_TRIGGER_STATE.ENTERING) {
- createTutorialTarget();
+ attachTurtorialTarget();
}
}
}
};
public OneHandedTutorialHandler(Context context, ShellExecutor mainExecutor) {
+ mContext = context;
context.getDisplay().getRealSize(mDisplaySize);
mPackageName = context.getPackageName();
mContentResolver = context.getContentResolver();
mWindowManager = context.getSystemService(WindowManager.class);
- mAccessibilityManager = (AccessibilityManager)
- context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ mAccessibilityManager = AccessibilityManager.getInstance(context);
mStartOneHandedDescription = context.getResources().getString(
R.string.accessibility_action_start_one_handed);
@@ -113,6 +116,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
mCanShowTutorial = (Settings.Secure.getInt(mContentResolver,
Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT)
? false : true;
+ mIsOneHandedMode = false;
final float offsetPercentageConfig = context.getResources().getFraction(
R.fraction.config_one_handed_offset, 1, 1);
final int sysPropPercentageConfig = SystemProperties.getInt(
@@ -120,11 +124,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
mTutorialAreaHeight = Math.round(mDisplaySize.y * (sysPropPercentageConfig / 100.0f));
mainExecutor.execute(() -> {
- mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial,
- null);
- mTargetViewContainer = new FrameLayout(context);
- mTargetViewContainer.setClipChildren(false);
- mTargetViewContainer.addView(mTutorialView);
+ recreateTutorialView(mContext);
});
}
@@ -144,10 +144,20 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
}
+ private void recreateTutorialView(Context context) {
+ mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial,
+ null);
+ mTargetViewContainer = new FrameLayout(context);
+ mTargetViewContainer.setClipChildren(false);
+ mTargetViewContainer.addView(mTutorialView);
+ mTargetViewContainer.setVisibility(mIsOneHandedMode ? View.VISIBLE : View.GONE);
+ }
+
private void updateFinished(int visible, float finalPosition) {
if (!canShowTutorial()) {
return;
}
+ mIsOneHandedMode = (finalPosition == 0f) ? true : false;
mTargetViewContainer.setVisibility(visible);
mTargetViewContainer.setTranslationY(finalPosition);
}
@@ -176,7 +186,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
* Adds the tutorial target view to the WindowManager and update its layout, so it's ready
* to be animated in.
*/
- private void createTutorialTarget() {
+ private void attachTurtorialTarget() {
if (!mTargetViewContainer.isAttachedToWindow()) {
try {
mWindowManager.addView(mTargetViewContainer, getTutorialTargetLayoutParams());
@@ -242,4 +252,17 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
mTargetViewContainer.setTransitionGroup(true);
mTargetViewContainer.setTranslationY(value - mTargetViewContainer.getHeight());
}
+
+ /**
+ * onConfigurationChanged events for updating tutorial text.
+ * @param newConfig
+ */
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (!mCanShowTutorial) {
+ return;
+ }
+ removeTutorialFromWindowManager();
+ recreateTutorialView(mContext.createConfigurationContext(newConfig));
+ attachTurtorialTarget();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 45aa3870ecb6..5ffa9885a143 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -16,6 +16,9 @@
package com.android.wm.shell.pip;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
import android.animation.AnimationHandler;
import android.animation.Animator;
import android.animation.RectEvaluator;
@@ -24,11 +27,13 @@ import android.annotation.IntDef;
import android.app.TaskInfo;
import android.graphics.Rect;
import android.view.Choreographer;
+import android.view.Surface;
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.DisplayLayout;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -132,15 +137,21 @@ public class PipAnimationController {
* leash bounds before transformation/any animation. This is so when we try to construct
* the different transformation matrices for the animation, we are constructing this based off
* the PiP original bounds, rather than the {@param startBounds}, which is post-transformed.
+ *
+ * If non-zero {@param rotationDelta} is given, it means that the display will be rotated by
+ * leaving PiP to fullscreen, and the {@param endBounds} is the fullscreen bounds before the
+ * rotation change.
*/
@VisibleForTesting
public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash,
Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect,
- @PipAnimationController.TransitionDirection int direction, float startingAngle) {
+ @PipAnimationController.TransitionDirection int direction, float startingAngle,
+ @Surface.Rotation int rotationDelta) {
if (mCurrentAnimator == null) {
mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds,
- endBounds, sourceHintRect, direction, 0 /* startingAngle */));
+ endBounds, sourceHintRect, direction, 0 /* startingAngle */,
+ rotationDelta));
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
&& mCurrentAnimator.isRunning()) {
// If we are still animating the fade into pip, then just move the surface and ensure
@@ -156,7 +167,7 @@ public class PipAnimationController {
mCurrentAnimator.cancel();
mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds,
- endBounds, sourceHintRect, direction, startingAngle));
+ endBounds, sourceHintRect, direction, startingAngle, rotationDelta));
}
return mCurrentAnimator;
}
@@ -410,7 +421,8 @@ public class PipAnimationController {
static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash,
Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect,
- @PipAnimationController.TransitionDirection int direction, float startingAngle) {
+ @PipAnimationController.TransitionDirection int direction, float startingAngle,
+ @Surface.Rotation int rotationDelta) {
// Just for simplicity we'll interpolate between the source rect hint insets and empty
// insets to calculate the window crop
final Rect initialSourceValue;
@@ -431,6 +443,16 @@ public class PipAnimationController {
}
final Rect sourceInsets = new Rect(0, 0, 0, 0);
+ final Rect rotatedEndRect;
+ if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) {
+ // Rotate the end bounds according to the rotation delta because the display will
+ // be rotated to the same orientation.
+ rotatedEndRect = new Rect(endValue);
+ DisplayLayout.rotateBounds(rotatedEndRect, endValue, rotationDelta);
+ } else {
+ rotatedEndRect = null;
+ }
+
// construct new Rect instances in case they are recycled
return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS,
endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue),
@@ -444,6 +466,12 @@ public class PipAnimationController {
final Rect base = getBaseValue();
final Rect start = getStartValue();
final Rect end = getEndValue();
+ if (rotatedEndRect != null) {
+ // Animate the bounds in a different orientation. It only happens when
+ // leaving PiP to fullscreen.
+ applyRotation(tx, leash, fraction, start, end, rotatedEndRect);
+ return;
+ }
Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
float angle = (1.0f - fraction) * startingAngle;
setCurrentValue(bounds);
@@ -469,6 +497,25 @@ public class PipAnimationController {
tx.apply();
}
+ private void applyRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
+ float fraction, Rect start, Rect end, Rect rotatedEndRect) {
+ final Rect bounds = mRectEvaluator.evaluate(fraction, start, rotatedEndRect);
+ setCurrentValue(bounds);
+ final float degree, x, y;
+ if (rotationDelta == ROTATION_90) {
+ degree = 90 * fraction;
+ x = fraction * (end.right - start.left) + start.left;
+ y = fraction * (end.top - start.top) + start.top;
+ } else {
+ degree = -90 * fraction;
+ x = fraction * (end.left - start.left) + start.left;
+ y = fraction * (end.bottom - start.top) + start.top;
+ }
+ getSurfaceTransactionHelper().rotateAndScaleWithCrop(tx, leash, bounds,
+ rotatedEndRect, degree, x, y);
+ tx.apply();
+ }
+
@Override
void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
getSurfaceTransactionHelper()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index a777a2766ee7..97aeda4b053f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -127,6 +127,32 @@ public class PipSurfaceTransactionHelper {
}
/**
+ * Operates the rotation according to the given degrees and scale (setMatrix) according to the
+ * source bounds and rotated destination bounds. The crop will be the unscaled source bounds.
+ * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+ */
+ public PipSurfaceTransactionHelper rotateAndScaleWithCrop(SurfaceControl.Transaction tx,
+ SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, float degrees,
+ float positionX, float positionY) {
+ mTmpDestinationRect.set(sourceBounds);
+ final int dw = destinationBounds.width();
+ final int dh = destinationBounds.height();
+ // Scale by the short side so there won't be empty area if the aspect ratio of source and
+ // destination are different.
+ final float scale = dw <= dh
+ ? (float) sourceBounds.width() / dw
+ : (float) sourceBounds.height() / dh;
+ // Inverse scale for crop to fit in screen coordinates.
+ mTmpDestinationRect.scale(1 / scale);
+ mTmpTransform.setRotate(degrees);
+ mTmpTransform.postScale(scale, scale);
+ mTmpTransform.postTranslate(positionX, positionY);
+ tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
+ .setWindowCrop(leash, mTmpDestinationRect.width(), mTmpDestinationRect.height());
+ return this;
+ }
+
+ /**
* Resets the scale (setMatrix) on a given transaction and leash if there's any
*
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
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 fb83006e8522..843e27f1b651 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
@@ -48,13 +48,12 @@ import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.util.Rational;
import android.view.Display;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TaskOrganizer;
import android.window.WindowContainerToken;
@@ -67,13 +66,11 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
@@ -134,8 +131,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private final PipUiEventLogger mPipUiEventLoggerLogger;
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
- private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
- private final Optional<LegacySplitScreen> mSplitScreenOptional;
+ private final Optional<LegacySplitScreenController> mSplitScreenOptional;
protected final ShellTaskOrganizer mTaskOrganizer;
protected final ShellExecutor mMainExecutor;
@@ -192,6 +188,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private boolean mWaitForFixedRotation;
/**
+ * The rotation that the display will apply after expanding PiP to fullscreen. This is only
+ * meaningful if {@link #mWaitForFixedRotation} is true.
+ */
+ private @Surface.Rotation int mNextRotation;
+
+ /**
* If set to {@code true}, no entering PiP transition would be kicked off and most likely
* it's due to the fact that Launcher is handling the transition directly when swiping
* auto PiP-able Activity to home.
@@ -205,7 +207,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@NonNull PipAnimationController pipAnimationController,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
@NonNull PipTransitionController pipTransitionController,
- Optional<LegacySplitScreen> splitScreenOptional,
+ Optional<LegacySplitScreenController> splitScreenOptional,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@@ -313,61 +315,40 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return;
}
- final Configuration initialConfig = mInitialState.remove(mToken.asBinder());
- if (initialConfig == null) {
- Log.wtf(TAG, "Token not in record, this should not happen mToken=" + mToken);
- return;
- }
mPipUiEventLoggerLogger.log(
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
- final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation()
- != mPipBoundsState.getDisplayLayout().rotation();
final WindowContainerTransaction wct = new WindowContainerTransaction();
- final Rect destinationBounds = initialConfig.windowConfiguration.getBounds();
+ final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
final int direction = syncWithSplitScreenBounds(destinationBounds)
? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
: TRANSITION_DIRECTION_LEAVE_PIP;
- if (orientationDiffers) {
- mState = State.EXITING_PIP;
- // Send started callback though animation is ignored.
- sendOnPipTransitionStarted(direction);
- // Don't bother doing an animation if the display rotation differs or if it's in
- // a non-supported windowing mode
- applyWindowingModeChangeOnExit(wct, direction);
- mTaskOrganizer.applyTransaction(wct);
- // Send finished callback though animation is ignored.
- sendOnPipTransitionFinished(direction);
- } else {
- final SurfaceControl.Transaction tx =
- mSurfaceControlTransactionFactory.getTransaction();
- mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds,
- mPipBoundsState.getBounds());
- tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
- // We set to fullscreen here for now, but later it will be set to UNDEFINED for
- // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
- wct.setActivityWindowingMode(mToken,
- direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
- ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- : WINDOWING_MODE_FULLSCREEN);
- wct.setBounds(mToken, destinationBounds);
- wct.setBoundsChangeTransaction(mToken, tx);
- mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
- @Override
- public void onTransactionReady(int id, SurfaceControl.Transaction t) {
- mMainExecutor.execute(() -> {
- t.apply();
- // Make sure to grab the latest source hint rect as it could have been
- // updated right after applying the windowing mode change.
- final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
- mPictureInPictureParams, destinationBounds);
- scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
- 0 /* startingAngle */, sourceHintRect, direction,
- animationDurationMs, null /* updateBoundsCallback */);
- mState = State.EXITING_PIP;
- });
- }
- });
- }
+ final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+ mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds());
+ tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
+ // We set to fullscreen here for now, but later it will be set to UNDEFINED for
+ // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
+ wct.setActivityWindowingMode(mToken,
+ direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
+ ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ : WINDOWING_MODE_FULLSCREEN);
+ wct.setBounds(mToken, destinationBounds);
+ wct.setBoundsChangeTransaction(mToken, tx);
+ mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
+ @Override
+ public void onTransactionReady(int id, SurfaceControl.Transaction t) {
+ mMainExecutor.execute(() -> {
+ t.apply();
+ // Make sure to grab the latest source hint rect as it could have been
+ // updated right after applying the windowing mode change.
+ final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
+ mPictureInPictureParams, destinationBounds);
+ scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
+ 0 /* startingAngle */, sourceHintRect, direction,
+ animationDurationMs, null /* updateBoundsCallback */);
+ mState = State.EXITING_PIP;
+ });
+ }
+ });
}
private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) {
@@ -399,7 +380,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration)
.start();
- mInitialState.remove(mToken.asBinder());
mState = State.EXITING_PIP;
}
@@ -424,7 +404,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mToken = mTaskInfo.token;
mState = State.TASK_APPEARED;
mLeash = leash;
- mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration));
mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams,
mTaskInfo.topActivityInfo);
@@ -605,7 +584,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
@Override
+ public boolean supportSizeCompatUI() {
+ // PIP doesn't support size compat.
+ return false;
+ }
+
+ @Override
public void onFixedRotationStarted(int displayId, int newRotation) {
+ mNextRotation = newRotation;
mWaitForFixedRotation = true;
}
@@ -645,7 +631,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipAnimationController.getCurrentAnimator();
if (animator == null || !animator.isRunning()
|| animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
- if (mState.isInPip() && fromRotation) {
+ if (mState.isInPip() && fromRotation && !mWaitForFixedRotation) {
// Update bounds state to final destination first. It's important to do this
// before finishing & cancelling the transition animation so that the MotionHelper
// bounds are synchronized to the destination bounds when the animation ends.
@@ -992,7 +978,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
finishResizeForMenu(destinationBounds);
}
- private void finishResizeForMenu(Rect destinationBounds) {
+ /** Moves the PiP menu to the destination bounds. */
+ public void finishResizeForMenu(Rect destinationBounds) {
mPipMenuController.movePipMenu(null, null, destinationBounds);
mPipMenuController.updateMenuBounds(destinationBounds);
}
@@ -1052,11 +1039,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Log.w(TAG, "Abort animation, invalid leash");
return;
}
+ final int rotationDelta = mWaitForFixedRotation
+ ? ((mNextRotation - mPipBoundsState.getDisplayLayout().rotation()) + 4) % 4
+ : Surface.ROTATION_0;
Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
? mPipBoundsState.getBounds() : currentBounds;
mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds,
- sourceHintRect, direction, startingAngle)
+ sourceHintRect, direction, startingAngle, rotationDelta)
.setTransitionDirection(direction)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs)
@@ -1064,7 +1054,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
/**
- * Sync with {@link LegacySplitScreen} on destination bounds if PiP is going to split screen.
+ * Sync with {@link LegacySplitScreenController} on destination bounds if PiP is going to split
+ * screen.
*
* @param destinationBoundsOut contain the updated destination bounds if applicable
* @return {@code true} if destinationBounds is altered for split screen
@@ -1074,7 +1065,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return false;
}
- LegacySplitScreen legacySplitScreen = mSplitScreenOptional.get();
+ LegacySplitScreenController legacySplitScreen = mSplitScreenOptional.get();
if (!legacySplitScreen.isDividerVisible()) {
// fail early if system is not in split screen mode
return false;
@@ -1101,11 +1092,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
pw.println(innerPrefix + "mState=" + mState);
pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
- pw.println(innerPrefix + "mInitialState:");
- for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) {
- pw.println(innerPrefix + " binder=" + e.getKey()
- + " winConfig=" + e.getValue().windowConfiguration);
- }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 91e8c9939244..9b6909b2bd51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -30,6 +30,7 @@ import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Rect;
import android.os.IBinder;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -112,7 +113,7 @@ public class PipTransition extends PipTransitionController {
taskInfo.pictureInPictureParams, currentBounds);
animator = mPipAnimationController.getAnimator(taskInfo, leash,
currentBounds, currentBounds, destinationBounds, sourceHintRect,
- TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */);
+ TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, Surface.ROTATION_0);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
t.setAlpha(leash, 0f);
t.apply();
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 a57eee83ef59..725f87d93e4e 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
@@ -198,8 +198,7 @@ public class PhonePipMenuController implements PipMenuController {
* {@code null}), it will get the leash that the WindowlessWM has assigned to it.
*/
public SurfaceControl getSurfaceControl() {
- SurfaceControl sf = mPipMenuView.getWindowSurfaceControl();
- return sf != null ? sf : mSystemWindows.getViewSurface(mPipMenuView);
+ return mSystemWindows.getViewSurface(mPipMenuView);
}
/**
@@ -376,17 +375,29 @@ public class PhonePipMenuController implements PipMenuController {
}
/**
- * Hides the menu activity.
+ * Hides the menu view.
*/
public void hideMenu() {
+ hideMenu(true /* animate */, true /* resize */);
+ }
+
+ /**
+ * Hides the menu view.
+ *
+ * @param animate whether to animate the menu fadeout
+ * @param resize whether or not to resize the PiP with the state change
+ */
+ public void hideMenu(boolean animate, boolean resize) {
final boolean isMenuVisible = isMenuVisible();
if (DEBUG) {
Log.d(TAG, "hideMenu() state=" + mMenuState
+ " isMenuVisible=" + isMenuVisible
+ + " animate=" + animate
+ + " resize=" + resize
+ " callers=\n" + Debug.getCallers(5, " "));
}
if (isMenuVisible) {
- mPipMenuView.hideMenu();
+ mPipMenuView.hideMenu(animate, resize);
}
}
@@ -405,15 +416,6 @@ public class PhonePipMenuController implements PipMenuController {
}
/**
- * Preemptively mark the menu as invisible, used when we are directly manipulating the pinned
- * stack and don't want to trigger a resize which can animate the stack in a conflicting way
- * (ie. when manually expanding or dismissing).
- */
- public void hideMenuWithoutResize() {
- onMenuStateChanged(MENU_STATE_NONE, false /* resize */, null /* callback */);
- }
-
- /**
* Sets the menu actions to the actions provided by the current PiP menu.
*/
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index c3970e33d736..4a2a032d8d1c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -552,6 +552,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
// mTouchHandler would rely on the bounds populated from mPipTaskOrganizer
mPipTaskOrganizer.onMovementBoundsChanged(outBounds, fromRotation, fromImeAdjustment,
fromShelfAdjustment, wct);
+ mPipTaskOrganizer.finishResizeForMenu(outBounds);
mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mPipBoundsState.getNormalBounds(),
outBounds, fromImeAdjustment, fromShelfAdjustment, rotation);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.java
new file mode 100644
index 000000000000..f11ae422e837
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.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.wm.shell.pip.phone;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.wm.shell.R;
+
+/**
+ * Container layout wraps single action image view drawn in PiP menu and can restrict the size of
+ * action image view (see pip_menu_action.xml).
+ */
+public class PipMenuActionView extends FrameLayout {
+ private ImageView mImageView;
+
+ public PipMenuActionView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mImageView = findViewById(R.id.image);
+ }
+
+ /** pass through to internal {@link #mImageView} */
+ public void setImageDrawable(Drawable drawable) {
+ mImageView.setImageDrawable(drawable);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
index 6d12752d9218..3eeba6eb5366 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
@@ -31,7 +31,6 @@ public class PipMenuIconsAlgorithm {
private static final String TAG = "PipMenuIconsAlgorithm";
- private boolean mFinishedLayout = false;
protected ViewGroup mViewRoot;
protected ViewGroup mTopEndContainer;
protected View mDragHandle;
@@ -51,27 +50,16 @@ public class PipMenuIconsAlgorithm {
mDragHandle = dragHandle;
mSettingsButton = settingsButton;
mDismissButton = dismissButton;
+
+ bindInitialViewState();
}
/**
* Updates the position of the drag handle based on where the PIP window is on the screen.
*/
public void onBoundsChanged(Rect bounds) {
- if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
- || mSettingsButton == null || mDismissButton == null) {
- Log.e(TAG, "One if the required views is null.");
- return;
- }
-
- //We only need to calculate the layout once since it does not change.
- if (!mFinishedLayout) {
- mTopEndContainer.removeView(mSettingsButton);
- mViewRoot.addView(mSettingsButton);
-
- setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
- setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
- mFinishedLayout = true;
- }
+ // On phones, the menu icons are always static and will never move based on the PIP window
+ // position. No need to do anything here.
}
/**
@@ -84,4 +72,22 @@ public class PipMenuIconsAlgorithm {
v.setLayoutParams(params);
}
}
+
+ /** Calculate the initial state of the menu icons. Called when the menu is first created. */
+ private void bindInitialViewState() {
+ if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
+ || mSettingsButton == null || mDismissButton == null) {
+ Log.e(TAG, "One of the required views is null.");
+ return;
+ }
+ // The menu view layout starts out with the settings button aligned at the top|end of the
+ // view group next to the dismiss button. On phones, the settings button should be aligned
+ // to the top|start of the view, so move it to parent view group to then align it to the
+ // top|start of the menu.
+ mTopEndContainer.removeView(mSettingsButton);
+ mViewRoot.addView(mSettingsButton);
+
+ setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
+ setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
+ }
}
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 962c4672644a..0d64407f649f 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
@@ -59,7 +59,6 @@ import android.view.ViewRootImpl;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
-import android.widget.ImageButton;
import android.widget.LinearLayout;
import com.android.wm.shell.R;
@@ -89,7 +88,6 @@ public class PipMenuView extends FrameLayout {
private static final boolean ENABLE_RESIZE_HANDLE = false;
private int mMenuState;
- private boolean mResize = true;
private boolean mAllowMenuTimeout = true;
private boolean mAllowTouches = true;
@@ -329,16 +327,21 @@ public class PipMenuView extends FrameLayout {
hideMenu(null);
}
+ void hideMenu(boolean animate, boolean resize) {
+ hideMenu(null, true /* notifyMenuVisibility */, animate, resize);
+ }
+
void hideMenu(Runnable animationEndCallback) {
- hideMenu(animationEndCallback, true /* notifyMenuVisibility */, true /* animate */);
+ hideMenu(animationEndCallback, true /* notifyMenuVisibility */, true /* animate */,
+ true /* resize */);
}
private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility,
- boolean animate) {
+ boolean animate, boolean resize) {
if (mMenuState != MENU_STATE_NONE) {
cancelDelayedHide();
if (notifyMenuVisibility) {
- notifyMenuStateChange(MENU_STATE_NONE, mResize, null);
+ notifyMenuStateChange(MENU_STATE_NONE, resize, null);
}
mMenuContainerAnimator = new AnimatorSet();
ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
@@ -403,7 +406,7 @@ public class PipMenuView extends FrameLayout {
// Ensure we have as many buttons as actions
final LayoutInflater inflater = LayoutInflater.from(mContext);
while (mActionsGroup.getChildCount() < mActions.size()) {
- final ImageButton actionView = (ImageButton) inflater.inflate(
+ final PipMenuActionView actionView = (PipMenuActionView) inflater.inflate(
R.layout.pip_menu_action, mActionsGroup, false);
mActionsGroup.addView(actionView);
}
@@ -420,7 +423,8 @@ public class PipMenuView extends FrameLayout {
&& (stackBounds.width() > stackBounds.height());
for (int i = 0; i < mActions.size(); i++) {
final RemoteAction action = mActions.get(i);
- final ImageButton actionView = (ImageButton) mActionsGroup.getChildAt(i);
+ final PipMenuActionView actionView =
+ (PipMenuActionView) mActionsGroup.getChildAt(i);
// TODO: Check if the action drawable has changed before we reload it
action.getIcon().loadDrawableAsync(mContext, d -> {
@@ -469,7 +473,8 @@ public class PipMenuView extends FrameLayout {
private void expandPip() {
// Do not notify menu visibility when hiding the menu, the controller will do this when it
// handles the message
- hideMenu(mController::onPipExpand, false /* notifyMenuVisibility */, true /* animate */);
+ hideMenu(mController::onPipExpand, false /* notifyMenuVisibility */, true /* animate */,
+ true /* resize */);
}
private void dismissPip() {
@@ -479,7 +484,8 @@ public class PipMenuView extends FrameLayout {
final boolean animate = mMenuState != MENU_STATE_CLOSE;
// Do not notify menu visibility when hiding the menu, the controller will do this when it
// handles the message
- hideMenu(mController::onPipDismiss, false /* notifyMenuVisibility */, animate);
+ hideMenu(mController::onPipDismiss, false /* notifyMenuVisibility */, animate,
+ true /* resize */);
}
private void showSettings() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index b19dcae2def8..eae8945ce6be 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -17,8 +17,7 @@
package com.android.wm.shell.pip.phone;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;
+import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -325,7 +324,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
+ " callers=\n" + Debug.getCallers(5, " "));
}
cancelPhysicsAnimation();
- mMenuController.hideMenuWithoutResize();
+ mMenuController.hideMenu(false /* animate */, false /* resize */);
mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION);
}
@@ -338,7 +337,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
Log.d(TAG, "removePip: callers=\n" + Debug.getCallers(5, " "));
}
cancelPhysicsAnimation();
- mMenuController.hideMenuWithoutResize();
+ mMenuController.hideMenu(true /* animate*/, false /* resize */);
mPipTaskOrganizer.removePip();
}
@@ -371,9 +370,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
/**
* Stash PiP to the closest edge. We set velocityY to 0 to limit pure horizontal motion.
*/
- void stashToEdge(float velocityX, @Nullable Runnable postBoundsUpdateCallback) {
- mPipBoundsState.setStashed(velocityX < 0 ? STASH_TYPE_LEFT : STASH_TYPE_RIGHT);
- movetoTarget(velocityX, 0 /* velocityY */, postBoundsUpdateCallback, true /* isStash */);
+ void stashToEdge(float velX, float velY, @Nullable Runnable postBoundsUpdateCallback) {
+ velY = mPipBoundsState.getStashedState() == STASH_TYPE_NONE ? 0 : velY;
+ movetoTarget(velX, velY, postBoundsUpdateCallback, true /* isStash */);
}
private void movetoTarget(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index b91ba34c4464..78ee1868eee7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -106,7 +106,8 @@ public class PipResizeGestureHandler {
// For pinch-resize
private boolean mThresholdCrossed0;
private boolean mThresholdCrossed1;
- private boolean mUsingPinchToZoom = false;
+ private boolean mUsingPinchToZoom = true;
+ private float mAngle = 0;
int mFirstIndex = -1;
int mSecondIndex = -1;
@@ -138,7 +139,7 @@ public class PipResizeGestureHandler {
mEnablePinchResize = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
PIP_PINCH_RESIZE,
- /* defaultValue = */ false);
+ /* defaultValue = */ true);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
mMainExecutor,
new DeviceConfig.OnPropertiesChangedListener() {
@@ -146,7 +147,7 @@ public class PipResizeGestureHandler {
public void onPropertiesChanged(DeviceConfig.Properties properties) {
if (properties.getKeyset().contains(PIP_PINCH_RESIZE)) {
mEnablePinchResize = properties.getBoolean(
- PIP_PINCH_RESIZE, /* defaultValue = */ false);
+ PIP_PINCH_RESIZE, /* defaultValue = */ true);
}
}
});
@@ -420,18 +421,25 @@ public class PipResizeGestureHandler {
float down1X = mDownSecondaryPoint.x;
float down1Y = mDownSecondaryPoint.y;
- // Top right + Bottom left pinch to zoom.
- if ((down0X > focusX && down0Y < focusY && down1X < focusX && down1Y > focusY)
- || (down1X > focusX && down1Y < focusY
- && down0X < focusX && down0Y > focusY)) {
+ if (down0X > focusX && down0Y < focusY && down1X < focusX && down1Y > focusY) {
+ // Top right + Bottom left pinch to zoom.
mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
mLastResizeBounds.centerY(), x0, y0, x1, y1, true);
- } else if ((down0X < focusX && down0Y < focusY
- && down1X > focusX && down1Y > focusY)
- || (down1X < focusX && down1Y < focusY
- && down0X > focusX && down0Y > focusY)) {
+ } else if (down1X > focusX && down1Y < focusY
+ && down0X < focusX && down0Y > focusY) {
+ // Top right + Bottom left pinch to zoom.
+ mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
+ mLastResizeBounds.centerY(), x1, y1, x0, y0, true);
+ } else if (down0X < focusX && down0Y < focusY
+ && down1X > focusX && down1Y > focusY) {
+ // Top left + bottom right pinch to zoom.
mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
mLastResizeBounds.centerY(), x0, y0, x1, y1, false);
+ } else if (down1X < focusX && down1Y < focusY
+ && down0X > focusX && down0Y > focusY) {
+ // Top left + bottom right pinch to zoom.
+ mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
+ mLastResizeBounds.centerY(), x1, y1, x0, y0, false);
}
mLastResizeBounds.set(PipPinchResizingAlgorithm.pinchResize(x0, y0, x1, y1,
@@ -445,21 +453,26 @@ public class PipResizeGestureHandler {
}
}
- private float mAngle = 0;
-
- private float calculateRotationAngle(int focusX, int focusY, float x0, float y0,
- float x1, float y1, boolean positive) {
+ private float calculateRotationAngle(int pivotX, int pivotY, float topX, float topY,
+ float bottomX, float bottomY, boolean positive) {
// The base angle is the angle formed by taking the angle between the center horizontal
// and one of the corners.
- double baseAngle = Math.toDegrees(Math.atan2(Math.abs(mLastResizeBounds.top - focusY),
- Math.abs(mLastResizeBounds.right - focusX)));
+ double baseAngle = Math.toDegrees(Math.atan2(Math.abs(mLastResizeBounds.top - pivotY),
+ Math.abs(mLastResizeBounds.right - pivotX)));
+
double angle0 = mThresholdCrossed0
- ? Math.toDegrees(Math.atan2(Math.abs(y0 - focusY), Math.abs(x0 - focusX)))
- : baseAngle;
- double angle1 = mThresholdCrossed1
- ? Math.toDegrees(Math.atan2(Math.abs(y1 - focusY), Math.abs(x1 - focusX)))
- : baseAngle;
+ ? Math.toDegrees(Math.atan2(pivotY - topY, topX - pivotX)) : baseAngle;
+ double angle1 = mThresholdCrossed0
+ ? Math.toDegrees(Math.atan2(pivotY - bottomY, bottomX - pivotX)) : baseAngle;
+
+
+ if (positive) {
+ angle1 = angle1 < 0 ? 180 + angle1 : angle1 - 180;
+ } else {
+ angle0 = angle0 < 0 ? -angle0 - 180 : 180 - angle0;
+ angle1 = -angle1;
+ }
// Calculate the percentage difference of [0, 90] compare to the base angle.
double diff0 = (Math.max(0, Math.min(angle0, 90)) - baseAngle) / 90;
@@ -503,8 +516,8 @@ public class PipResizeGestureHandler {
}
if (mThresholdCrossed) {
if (mPhonePipMenuController.isMenuVisible()) {
- mPhonePipMenuController.hideMenuWithoutResize();
- mPhonePipMenuController.hideMenu();
+ mPhonePipMenuController.hideMenu(false /* animate */,
+ false /* resize */);
}
final Rect currentPipBounds = mPipBoundsState.getBounds();
mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y,
@@ -545,8 +558,8 @@ public class PipResizeGestureHandler {
|| mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) {
mLastResizeBounds.set(0, 0, mMaxSize.x, mMaxSize.y);
}
- mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds,
- mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds()));
+ final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(mLastResizeBounds);
+ mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);
mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
PINCH_RESIZE_SNAP_DURATION, -mAngle, callback);
} else {
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 e69c6f2f47bc..afc7b5294a2f 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
@@ -19,7 +19,9 @@ package com.android.wm.shell.pip.phone;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASHING;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASH_MINIMUM_VELOCITY_THRESHOLD;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
+import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT;
import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
+import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_CLOSE;
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL;
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE;
@@ -63,8 +65,9 @@ import java.util.function.Consumer;
* the PIP.
*/
public class PipTouchHandler {
+ @VisibleForTesting static final float MINIMUM_SIZE_PERCENT = 0.4f;
+
private static final String TAG = "PipTouchHandler";
- private static final float MINIMUM_SIZE_PERCENT = 0.4f;
private static final float DEFAULT_STASH_VELOCITY_THRESHOLD = 18000.f;
// Allow PIP to resize to a slightly bigger state upon touch
@@ -809,7 +812,6 @@ public class PipTouchHandler {
}
if (touchState.startedDragging()) {
- mPipBoundsState.setStashed(STASH_TYPE_NONE);
mSavedSnapFraction = -1f;
mPipDismissTargetHandler.showDismissTargetMaybe();
}
@@ -862,10 +864,10 @@ public class PipTouchHandler {
// Reset the touch state on up before the fling settles
mTouchState.reset();
- if (mEnableStash && !mPipBoundsState.isStashed()
- && shouldStash(vel, getPossiblyMotionBounds())) {
- mMotionHelper.stashToEdge(vel.x, this::stashEndAction /* endAction */);
+ if (mEnableStash && shouldStash(vel, getPossiblyMotionBounds())) {
+ mMotionHelper.stashToEdge(vel.x, vel.y, this::stashEndAction /* endAction */);
} else {
+ mPipBoundsState.setStashed(STASH_TYPE_NONE);
mMotionHelper.flingToSnapTarget(vel.x, vel.y,
this::flingEndAction /* endAction */);
}
@@ -876,6 +878,9 @@ public class PipTouchHandler {
< mPipBoundsState.getMaxSize().x
&& mPipBoundsState.getBounds().height()
< mPipBoundsState.getMaxSize().y;
+ if (mMenuController.isMenuVisible()) {
+ mMenuController.hideMenu(false /* animate */, false /* resize */);
+ }
if (toExpand) {
mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
animateToMaximizedState(null);
@@ -916,6 +921,11 @@ public class PipTouchHandler {
&& mPipExclusionBoundsChangeListener.get() != null) {
mPipExclusionBoundsChangeListener.get().accept(mPipBoundsState.getBounds());
}
+ if (mPipBoundsState.getBounds().left < 0) {
+ mPipBoundsState.setStashed(STASH_TYPE_LEFT);
+ } else {
+ mPipBoundsState.setStashed(STASH_TYPE_RIGHT);
+ }
}
private void flingEndAction() {
@@ -933,12 +943,12 @@ public class PipTouchHandler {
private boolean shouldStash(PointF vel, Rect motionBounds) {
// If user flings the PIP window above the minimum velocity, stash PIP.
- // Only allow stashing to the edge if the user starts dragging the PIP from the
- // opposite edge.
+ // Only allow stashing to the edge if PIP wasn't previously stashed on the opposite
+ // edge.
final boolean stashFromFlingToEdge = ((vel.x < -mStashVelocityThreshold
- && mDownSavedFraction > 1f && mDownSavedFraction < 2f)
+ && mPipBoundsState.getStashedState() != STASH_TYPE_RIGHT)
|| (vel.x > mStashVelocityThreshold
- && mDownSavedFraction > 3f && mDownSavedFraction < 4f));
+ && mPipBoundsState.getStashedState() != STASH_TYPE_LEFT));
// If User releases the PIP window while it's out of the display bounds, put
// PIP into stashed mode.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 4f4e7dafe5c0..2b0a0cd3de20 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -26,9 +26,9 @@ import com.android.internal.protolog.common.IProtoLogGroup;
public enum ShellProtoLogGroup implements IProtoLogGroup {
// NOTE: Since we enable these from the same WM ShellCommand, these names should not conflict
// with those in the framework ProtoLogGroup
- WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+ WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
- WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+ WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
WM_SHELL_DRAG_AND_DROP(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
index 66ecf453c362..552ebde05274 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
@@ -28,6 +28,7 @@ import com.android.wm.shell.R;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
import org.json.JSONException;
@@ -109,10 +110,10 @@ public class ShellProtoLogImpl extends BaseProtoLogImpl {
return sServiceInstance;
}
- public int startTextLogging(Context context, String[] groups, PrintWriter pw) {
- try {
- mViewerConfig.loadViewerConfig(
- context.getResources().openRawResource(R.raw.wm_shell_protolog));
+ public int startTextLogging(String[] groups, PrintWriter pw) {
+ try (InputStream is =
+ getClass().getClassLoader().getResourceAsStream("wm_shell_protolog.json")){
+ mViewerConfig.loadViewerConfig(is);
return setLogging(true /* setTextLogging */, true, pw, groups);
} catch (IOException e) {
Log.i(TAG, "Unable to load log definitions: IOException while reading "
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
index e47e1ac71c73..9094d7de8d63 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
@@ -16,101 +16,80 @@
package com.android.wm.shell.sizecompatui;
-import android.app.ActivityClient;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.RippleDrawable;
-import android.os.IBinder;
-import android.util.Log;
-import android.view.Gravity;
+import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
+import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
/** Button to restart the size compat activity. */
-class SizeCompatRestartButton extends ImageButton implements View.OnClickListener,
+public class SizeCompatRestartButton extends FrameLayout implements View.OnClickListener,
View.OnLongClickListener {
- private static final String TAG = "SizeCompatRestartButton";
-
- final WindowManager.LayoutParams mWinParams;
- final boolean mShouldShowHint;
- final int mDisplayId;
- final int mPopupOffsetX;
- final int mPopupOffsetY;
- private IBinder mLastActivityToken;
- private PopupWindow mShowingHint;
+ private SizeCompatUILayout mLayout;
+ private ImageButton mRestartButton;
+ @VisibleForTesting
+ PopupWindow mShowingHint;
+ private WindowManager.LayoutParams mWinParams;
- SizeCompatRestartButton(Context context, int displayId, boolean hasShownHint) {
+ public SizeCompatRestartButton(@NonNull Context context) {
super(context);
- mDisplayId = displayId;
- mShouldShowHint = !hasShownHint;
- final Drawable drawable = context.getDrawable(R.drawable.size_compat_restart_button);
- setImageDrawable(drawable);
- setContentDescription(context.getString(R.string.restart_button_description));
+ }
- final int drawableW = drawable.getIntrinsicWidth();
- final int drawableH = drawable.getIntrinsicHeight();
- mPopupOffsetX = drawableW / 2;
- mPopupOffsetY = drawableH * 2;
+ public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
- final ColorStateList color = ColorStateList.valueOf(Color.LTGRAY);
- final GradientDrawable mask = new GradientDrawable();
- mask.setShape(GradientDrawable.OVAL);
- mask.setColor(color);
- setBackground(new RippleDrawable(color, null /* content */, mask));
- setOnClickListener(this);
- setOnLongClickListener(this);
-
- mWinParams = new WindowManager.LayoutParams();
- mWinParams.gravity = getGravity(getResources().getConfiguration().getLayoutDirection());
- mWinParams.width = drawableW * 2;
- mWinParams.height = drawableH * 2;
- mWinParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
- mWinParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- mWinParams.format = PixelFormat.TRANSLUCENT;
- mWinParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- mWinParams.setTitle(SizeCompatRestartButton.class.getSimpleName()
- + context.getDisplayId());
+ public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
}
- void updateLastTargetActivity(IBinder activityToken) {
- mLastActivityToken = activityToken;
+ public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
}
- /** @return {@code false} if the target display is invalid. */
- boolean show() {
- try {
- getContext().getSystemService(WindowManager.class).addView(this, mWinParams);
- } catch (WindowManager.InvalidDisplayException e) {
- // The target display may have been removed when the callback has just arrived.
- Log.w(TAG, "Cannot show on display " + getContext().getDisplayId(), e);
- return false;
- }
- return true;
+ void inject(SizeCompatUILayout layout) {
+ mLayout = layout;
+ mWinParams = layout.getWindowLayoutParams();
}
void remove() {
- if (mShowingHint != null) {
- mShowingHint.dismiss();
- }
- getContext().getSystemService(WindowManager.class).removeViewImmediate(this);
+ dismissHint();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mRestartButton = findViewById(R.id.size_compat_restart_button);
+ final ColorStateList color = ColorStateList.valueOf(Color.LTGRAY);
+ final GradientDrawable mask = new GradientDrawable();
+ mask.setShape(GradientDrawable.OVAL);
+ mask.setColor(color);
+ mRestartButton.setBackground(new RippleDrawable(color, null /* content */, mask));
+ mRestartButton.setOnClickListener(this);
+ mRestartButton.setOnLongClickListener(this);
}
@Override
public void onClick(View v) {
- ActivityClient.getInstance().restartActivityProcessIfVisible(mLastActivityToken);
+ mLayout.onRestartButtonClicked();
}
@Override
@@ -122,20 +101,26 @@ class SizeCompatRestartButton extends ImageButton implements View.OnClickListene
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- if (mShouldShowHint) {
+ if (mLayout.mShouldShowHint) {
+ mLayout.mShouldShowHint = false;
showHint();
}
}
@Override
+ public void setVisibility(@Visibility int visibility) {
+ if (visibility == View.GONE && mShowingHint != null) {
+ // Also dismiss the popup.
+ dismissHint();
+ }
+ super.setVisibility(visibility);
+ }
+
+ @Override
public void setLayoutDirection(int layoutDirection) {
- final int gravity = getGravity(layoutDirection);
+ final int gravity = SizeCompatUILayout.getGravity(layoutDirection);
if (mWinParams.gravity != gravity) {
mWinParams.gravity = gravity;
- if (mShowingHint != null) {
- mShowingHint.dismiss();
- showHint();
- }
getContext().getSystemService(WindowManager.class).updateViewLayout(this,
mWinParams);
}
@@ -147,8 +132,10 @@ class SizeCompatRestartButton extends ImageButton implements View.OnClickListene
return;
}
+ // TODO: popup is not attached to the button surface. Need to handle this differently for
+ // non-fullscreen task.
final View popupView = LayoutInflater.from(getContext()).inflate(
- R.layout.size_compat_mode_hint, null /* root */);
+ R.layout.size_compat_mode_hint, null);
final PopupWindow popupWindow = new PopupWindow(popupView,
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
popupWindow.setWindowLayoutType(mWinParams.type);
@@ -161,12 +148,15 @@ class SizeCompatRestartButton extends ImageButton implements View.OnClickListene
final Button gotItButton = popupView.findViewById(R.id.got_it);
gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY),
null /* content */, null /* mask */));
- gotItButton.setOnClickListener(view -> popupWindow.dismiss());
- popupWindow.showAtLocation(this, mWinParams.gravity, mPopupOffsetX, mPopupOffsetY);
+ gotItButton.setOnClickListener(view -> dismissHint());
+ popupWindow.showAtLocation(mRestartButton, mWinParams.gravity, mLayout.mPopupOffsetX,
+ mLayout.mPopupOffsetY);
}
- private static int getGravity(int layoutDirection) {
- return Gravity.BOTTOM
- | (layoutDirection == View.LAYOUT_DIRECTION_RTL ? Gravity.START : Gravity.END);
+ void dismissHint() {
+ if (mShowingHint != null) {
+ mShowingHint.dismiss();
+ mShowingHint = null;
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
deleted file mode 100644
index 11f22ed24a69..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java
+++ /dev/null
@@ -1,45 +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.wm.shell.sizecompatui;
-
-import android.annotation.Nullable;
-import android.graphics.Rect;
-import android.os.IBinder;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.annotations.ExternalThread;
-
-/**
- * Interface to engage size compat mode UI.
- */
-@ExternalThread
-public interface SizeCompatUI {
- /**
- * Called when the Task info changed. Creates and updates the restart button if there is an
- * activity in size compat, or removes the restart button if there is no size compat activity.
- *
- * @param displayId display the task and activity are in.
- * @param taskId task the activity is in.
- * @param taskBounds task bounds to place the restart button in.
- * @param sizeCompatActivity the size compat activity in the task. Can be {@code null} if the
- * top activity in this Task is not in size compat.
- * @param taskListener listener to handle the Task Surface placement.
- */
- void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds,
- @Nullable IBinder sizeCompatActivity,
- @Nullable ShellTaskOrganizer.TaskListener taskListener);
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
index 286c3b6a051e..a3880f497ff3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
@@ -18,144 +18,166 @@ package com.android.wm.shell.sizecompatui;
import android.annotation.Nullable;
import android.content.Context;
-import android.graphics.Rect;
+import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
+import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
-import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
/**
- * Shows a restart-activity button on Task when the foreground activity is in size compatibility
- * mode.
+ * Controls to show/update restart-activity buttons on Tasks based on whether the foreground
+ * activities are in size compatibility mode.
*/
public class SizeCompatUIController implements DisplayController.OnDisplaysChangedListener,
DisplayImeController.ImePositionProcessor {
- private static final String TAG = "SizeCompatUI";
+ private static final String TAG = "SizeCompatUIController";
+
+ /** Whether the IME is shown on display id. */
+ private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1);
/** The showing buttons by task id. */
- private final SparseArray<SizeCompatRestartButton> mActiveButtons = new SparseArray<>(1);
+ private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0);
+
/** Avoid creating display context frequently for non-default display. */
private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0);
- @VisibleForTesting
- final SizeCompatUI mImpl = new SizeCompatUIImpl();
private final Context mContext;
- private final ShellExecutor mMainExecutor;
private final DisplayController mDisplayController;
private final DisplayImeController mImeController;
+ private final SyncTransactionQueue mSyncQueue;
/** Only show once automatically in the process life. */
private boolean mHasShownHint;
- /** Creates the {@link SizeCompatUIController}. */
- public static SizeCompatUI create(Context context,
- DisplayController displayController,
- DisplayImeController imeController,
- ShellExecutor mainExecutor) {
- return new SizeCompatUIController(context, displayController, imeController, mainExecutor)
- .mImpl;
- }
-
- @VisibleForTesting
- SizeCompatUIController(Context context,
+ public SizeCompatUIController(Context context,
DisplayController displayController,
DisplayImeController imeController,
- ShellExecutor mainExecutor) {
+ SyncTransactionQueue syncQueue) {
mContext = context;
- mMainExecutor = mainExecutor;
mDisplayController = displayController;
mImeController = imeController;
+ mSyncQueue = syncQueue;
mDisplayController.addDisplayWindowListener(this);
mImeController.addPositionProcessor(this);
}
- private void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds,
- @Nullable IBinder sizeCompatActivity,
+ /**
+ * Called when the Task info changed. Creates and updates the restart button if there is an
+ * activity in size compat, or removes the restart button if there is no size compat activity.
+ *
+ * @param displayId display the task and activity are in.
+ * @param taskId task the activity is in.
+ * @param taskConfig task config to place the restart button with.
+ * @param sizeCompatActivity the size compat activity in the task. Can be {@code null} if the
+ * top activity in this Task is not in size compat.
+ * @param taskListener listener to handle the Task Surface placement.
+ */
+ public void onSizeCompatInfoChanged(int displayId, int taskId,
+ @Nullable Configuration taskConfig, @Nullable IBinder sizeCompatActivity,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
- // TODO Draw button on Task surface
- if (taskBounds == null || sizeCompatActivity == null || taskListener == null) {
+ if (taskConfig == null || sizeCompatActivity == null || taskListener == null) {
// Null token means the current foreground activity is not in size compatibility mode.
- removeRestartButton(taskId);
+ removeLayout(taskId);
+ } else if (mActiveLayouts.contains(taskId)) {
+ // Button already exists, update the button layout.
+ updateLayout(taskId, taskConfig, sizeCompatActivity, taskListener);
} else {
- updateRestartButton(displayId, taskId, sizeCompatActivity);
+ // Create a new restart button.
+ createLayout(displayId, taskId, taskConfig, sizeCompatActivity, taskListener);
}
}
@Override
public void onDisplayRemoved(int displayId) {
mDisplayContextCache.remove(displayId);
- for (int i = 0; i < mActiveButtons.size(); i++) {
- final int taskId = mActiveButtons.keyAt(i);
- final SizeCompatRestartButton button = mActiveButtons.get(taskId);
- if (button != null && button.mDisplayId == displayId) {
- removeRestartButton(taskId);
- }
+
+ // Remove all buttons on the removed display.
+ final List<Integer> toRemoveTaskIds = new ArrayList<>();
+ forAllLayoutsOnDisplay(displayId, layout -> toRemoveTaskIds.add(layout.getTaskId()));
+ for (int i = toRemoveTaskIds.size() - 1; i >= 0; i--) {
+ removeLayout(toRemoveTaskIds.get(i));
}
}
@Override
- public void onImeVisibilityChanged(int displayId, boolean isShowing) {
- final int newVisibility = isShowing ? View.GONE : View.VISIBLE;
- for (int i = 0; i < mActiveButtons.size(); i++) {
- final int taskId = mActiveButtons.keyAt(i);
- final SizeCompatRestartButton button = mActiveButtons.get(taskId);
- if (button == null || button.mDisplayId != displayId) {
- continue;
- }
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(displayId);
+ forAllLayoutsOnDisplay(displayId, layout -> layout.updateDisplayLayout(displayLayout));
+ }
- // Hide the button when input method is showing.
- if (button.getVisibility() != newVisibility) {
- button.setVisibility(newVisibility);
- }
+ @Override
+ public void onImeVisibilityChanged(int displayId, boolean isShowing) {
+ if (isShowing) {
+ mDisplaysWithIme.add(displayId);
+ } else {
+ mDisplaysWithIme.remove(displayId);
}
+
+ // Hide the button when input method is showing.
+ forAllLayoutsOnDisplay(displayId, layout -> layout.updateImeVisibility(isShowing));
}
- private void updateRestartButton(int displayId, int taskId, IBinder activityToken) {
- SizeCompatRestartButton restartButton = mActiveButtons.get(taskId);
- if (restartButton != null) {
- restartButton.updateLastTargetActivity(activityToken);
- return;
- }
+ private boolean isImeShowingOnDisplay(int displayId) {
+ return mDisplaysWithIme.contains(displayId);
+ }
+ private void createLayout(int displayId, int taskId, Configuration taskConfig,
+ IBinder activityToken, ShellTaskOrganizer.TaskListener taskListener) {
final Context context = getOrCreateDisplayContext(displayId);
if (context == null) {
- Log.i(TAG, "Cannot get context for display " + displayId);
+ Log.e(TAG, "Cannot get context for display " + displayId);
return;
}
- restartButton = createRestartButton(context, displayId);
- restartButton.updateLastTargetActivity(activityToken);
- if (restartButton.show()) {
- mActiveButtons.append(taskId, restartButton);
- } else {
- onDisplayRemoved(displayId);
- }
+ final SizeCompatUILayout layout = createLayout(context, displayId, taskId, taskConfig,
+ activityToken, taskListener);
+ mActiveLayouts.put(taskId, layout);
+ layout.createSizeCompatButton(isImeShowingOnDisplay(displayId));
}
@VisibleForTesting
- SizeCompatRestartButton createRestartButton(Context context, int displayId) {
- final SizeCompatRestartButton button = new SizeCompatRestartButton(context, displayId,
+ SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
+ Configuration taskConfig, IBinder activityToken,
+ ShellTaskOrganizer.TaskListener taskListener) {
+ final SizeCompatUILayout layout = new SizeCompatUILayout(mSyncQueue, context, taskConfig,
+ taskId, activityToken, taskListener, mDisplayController.getDisplayLayout(displayId),
mHasShownHint);
// Only show hint for the first time.
mHasShownHint = true;
- return button;
+ return layout;
}
- private void removeRestartButton(int taskId) {
- final SizeCompatRestartButton button = mActiveButtons.get(taskId);
- if (button != null) {
- button.remove();
- mActiveButtons.remove(taskId);
+ private void updateLayout(int taskId, Configuration taskConfig,
+ IBinder sizeCompatActivity,
+ ShellTaskOrganizer.TaskListener taskListener) {
+ final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
+ if (layout == null) {
+ return;
+ }
+ layout.updateSizeCompatInfo(taskConfig, sizeCompatActivity, taskListener,
+ isImeShowingOnDisplay(layout.getDisplayId()));
+ }
+
+ private void removeLayout(int taskId) {
+ final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
+ if (layout != null) {
+ layout.release();
+ mActiveLayouts.remove(taskId);
}
}
@@ -178,14 +200,13 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
return context;
}
- private class SizeCompatUIImpl implements SizeCompatUI {
- @Override
- public void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds,
- @Nullable IBinder sizeCompatActivity,
- @Nullable ShellTaskOrganizer.TaskListener taskListener) {
- mMainExecutor.execute(() ->
- SizeCompatUIController.this.onSizeCompatInfoChanged(displayId, taskId,
- taskBounds, sizeCompatActivity, taskListener));
+ private void forAllLayoutsOnDisplay(int displayId, Consumer<SizeCompatUILayout> callback) {
+ for (int i = 0; i < mActiveLayouts.size(); i++) {
+ final int taskId = mActiveLayouts.keyAt(i);
+ final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
+ if (layout != null && layout.getDisplayId() == displayId) {
+ callback.accept(layout);
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
new file mode 100644
index 000000000000..5924b53f822c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
@@ -0,0 +1,235 @@
+/*
+ * 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.wm.shell.sizecompatui;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import android.annotation.Nullable;
+import android.app.ActivityClient;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.IBinder;
+import android.view.Gravity;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+/**
+ * Records and handles layout of size compat UI on a task with size compat activity. Helps to
+ * calculate proper bounds when configuration or button position changes.
+ */
+class SizeCompatUILayout {
+ private static final String TAG = "SizeCompatUILayout";
+
+ private final SyncTransactionQueue mSyncQueue;
+ private Context mContext;
+ private Configuration mTaskConfig;
+ private final int mDisplayId;
+ private final int mTaskId;
+ private IBinder mActivityToken;
+ private ShellTaskOrganizer.TaskListener mTaskListener;
+ private DisplayLayout mDisplayLayout;
+ @VisibleForTesting
+ final SizeCompatUIWindowManager mWindowManager;
+
+ @VisibleForTesting
+ @Nullable
+ SizeCompatRestartButton mButton;
+ final int mButtonSize;
+ final int mPopupOffsetX;
+ final int mPopupOffsetY;
+ boolean mShouldShowHint;
+
+ SizeCompatUILayout(SyncTransactionQueue syncQueue, Context context, Configuration taskConfig,
+ int taskId, IBinder activityToken, ShellTaskOrganizer.TaskListener taskListener,
+ DisplayLayout displayLayout, boolean hasShownHint) {
+ mSyncQueue = syncQueue;
+ mContext = context.createConfigurationContext(taskConfig);
+ mTaskConfig = taskConfig;
+ mDisplayId = mContext.getDisplayId();
+ mTaskId = taskId;
+ mActivityToken = activityToken;
+ mTaskListener = taskListener;
+ mDisplayLayout = displayLayout;
+ mShouldShowHint = !hasShownHint;
+ mWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this);
+
+ mButtonSize =
+ mContext.getResources().getDimensionPixelSize(R.dimen.size_compat_button_size);
+ mPopupOffsetX = mButtonSize / 4;
+ mPopupOffsetY = mButtonSize;
+ }
+
+ /** Creates the button window. */
+ void createSizeCompatButton(boolean isImeShowing) {
+ if (isImeShowing || mButton != null) {
+ // When ime is showing, wait until ime is dismiss to create UI.
+ return;
+ }
+ mButton = mWindowManager.createSizeCompatUI();
+ updateSurfacePosition();
+ }
+
+ /** Releases the button window. */
+ void release() {
+ mButton.remove();
+ mButton = null;
+ mWindowManager.release();
+ }
+
+ /** Called when size compat info changed. */
+ void updateSizeCompatInfo(Configuration taskConfig, IBinder activityToken,
+ ShellTaskOrganizer.TaskListener taskListener, boolean isImeShowing) {
+ final Configuration prevTaskConfig = mTaskConfig;
+ final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
+ mTaskConfig = taskConfig;
+ mActivityToken = activityToken;
+ mTaskListener = taskListener;
+
+ // Update configuration.
+ mContext = mContext.createConfigurationContext(taskConfig);
+ mWindowManager.setConfiguration(taskConfig);
+
+ if (mButton == null || prevTaskListener != taskListener) {
+ // TaskListener changed, recreate the button for new surface parent.
+ release();
+ createSizeCompatButton(isImeShowing);
+ return;
+ }
+
+ if (!taskConfig.windowConfiguration.getBounds()
+ .equals(prevTaskConfig.windowConfiguration.getBounds())) {
+ // Reposition the button surface.
+ updateSurfacePosition();
+ }
+
+ if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
+ // Update layout for RTL.
+ mButton.setLayoutDirection(taskConfig.getLayoutDirection());
+ updateSurfacePosition();
+ }
+ }
+
+ /** Called when display layout changed. */
+ void updateDisplayLayout(DisplayLayout displayLayout) {
+ if (displayLayout == mDisplayLayout) {
+ return;
+ }
+
+ final Rect prevStableBounds = new Rect();
+ final Rect curStableBounds = new Rect();
+ mDisplayLayout.getStableBounds(prevStableBounds);
+ displayLayout.getStableBounds(curStableBounds);
+ mDisplayLayout = displayLayout;
+ if (!prevStableBounds.equals(curStableBounds)) {
+ // Stable bounds changed, update button surface position.
+ updateSurfacePosition();
+ }
+ }
+
+ /** Called when IME visibility changed. */
+ void updateImeVisibility(boolean isImeShowing) {
+ if (mButton == null) {
+ // Button may not be created because ime is previous showing.
+ createSizeCompatButton(isImeShowing);
+ return;
+ }
+
+ final int newVisibility = isImeShowing ? View.GONE : View.VISIBLE;
+ if (mButton.getVisibility() != newVisibility) {
+ mButton.setVisibility(newVisibility);
+ }
+ }
+
+ /** Gets the layout params for restart button. */
+ WindowManager.LayoutParams getWindowLayoutParams() {
+ final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
+ mButtonSize, mButtonSize,
+ TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
+ PixelFormat.TRANSLUCENT);
+ winParams.gravity = getGravity(getLayoutDirection());
+ winParams.token = new Binder();
+ winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + mContext.getDisplayId());
+ winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ return winParams;
+ }
+
+ /** Called when it is ready to be placed button surface button. */
+ void attachToParentSurface(SurfaceControl.Builder b) {
+ mTaskListener.attachChildSurfaceToTask(mTaskId, b);
+ }
+
+ /** Called when the restart button is clicked. */
+ void onRestartButtonClicked() {
+ ActivityClient.getInstance().restartActivityProcessIfVisible(mActivityToken);
+ }
+
+ @VisibleForTesting
+ void updateSurfacePosition() {
+ if (mButton == null || mWindowManager.getSurfaceControl() == null) {
+ return;
+ }
+ // The hint popup won't be at the correct position.
+ mButton.dismissHint();
+
+ // Use stable bounds to prevent the button from overlapping with system bars.
+ final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
+ final Rect stableBounds = new Rect();
+ mDisplayLayout.getStableBounds(stableBounds);
+ stableBounds.intersect(taskBounds);
+
+ // Position of the button in the container coordinate.
+ final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+ ? stableBounds.left - taskBounds.left
+ : stableBounds.right - taskBounds.left - mButtonSize;
+ final int positionY = stableBounds.bottom - taskBounds.top - mButtonSize;
+
+ mSyncQueue.runInSync(t ->
+ t.setPosition(mWindowManager.getSurfaceControl(), positionX, positionY));
+ }
+
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
+ int getTaskId() {
+ return mTaskId;
+ }
+
+ private int getLayoutDirection() {
+ return mContext.getResources().getConfiguration().getLayoutDirection();
+ }
+
+ static int getGravity(int layoutDirection) {
+ return Gravity.BOTTOM
+ | (layoutDirection == View.LAYOUT_DIRECTION_RTL ? Gravity.START : Gravity.END);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
new file mode 100644
index 000000000000..a7ad982a4736
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
@@ -0,0 +1,103 @@
+/*
+ * 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.wm.shell.sizecompatui;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.WindowlessWindowManager;
+
+import com.android.wm.shell.R;
+
+/**
+ * Holds view hierarchy of a root surface and helps to inflate {@link SizeCompatRestartButton}.
+ */
+class SizeCompatUIWindowManager extends WindowlessWindowManager {
+
+ private Context mContext;
+ private final SizeCompatUILayout mLayout;
+
+ @Nullable
+ private SurfaceControlViewHost mViewHost;
+ @Nullable
+ private SurfaceControl mLeash;
+
+ SizeCompatUIWindowManager(Context context, Configuration config, SizeCompatUILayout layout) {
+ super(config, null /* rootSurface */, null /* hostInputToken */);
+ mContext = context;
+ mLayout = layout;
+ }
+
+ @Override
+ public void setConfiguration(Configuration configuration) {
+ super.setConfiguration(configuration);
+ mContext = mContext.createConfigurationContext(configuration);
+ }
+
+ @Override
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ .setContainerLayer()
+ .setName("SizeCompatUILeash")
+ .setHidden(false)
+ .setCallsite("SizeCompatUIWindowManager#attachToParentSurface");
+ mLayout.attachToParentSurface(builder);
+ mLeash = builder.build();
+ b.setParent(mLeash);
+ }
+
+ /** Inflates {@link SizeCompatRestartButton} on to the root surface. */
+ SizeCompatRestartButton createSizeCompatUI() {
+ if (mViewHost == null) {
+ mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+ }
+
+ final SizeCompatRestartButton button = (SizeCompatRestartButton)
+ LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null);
+ button.inject(mLayout);
+ mViewHost.setView(button, mLayout.getWindowLayoutParams());
+ return button;
+ }
+
+ /** Releases the surface control and tears down the view hierarchy. */
+ void release() {
+ if (mViewHost != null) {
+ mViewHost.release();
+ mViewHost = null;
+ }
+
+ if (mLeash != null) {
+ new SurfaceControl.Transaction().remove(mLeash).apply();
+ mLeash = null;
+ }
+ }
+
+ /**
+ * Gets {@link SurfaceControl} of the surface holding size compat UI view. @return {@code null}
+ * if not feasible.
+ */
+ @Nullable
+ SurfaceControl getSurfaceControl() {
+ return mLeash;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 2c6809259459..7ca569349633 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -33,6 +33,7 @@ import java.io.PrintWriter;
/**
* Interface to engage split-screen feature.
+ * TODO: Figure out which of these are actually needed outside of the Shell
*/
@ExternalThread
public interface SplitScreen extends DragAndDropPolicy.Starter {
@@ -102,18 +103,13 @@ public interface SplitScreen extends DragAndDropPolicy.Starter {
void setSideStagePosition(@StagePosition int sideStagePosition);
/** Hides the side-stage if it is currently visible. */
void setSideStageVisibility(boolean visible);
- default void enterSplitScreen(int taskId, boolean leftOrTop) {
- moveToSideStage(taskId,
- leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT);
- }
+
/** Removes the split-screen stages. */
void exitSplitScreen();
+ /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */
+ void exitSplitScreenOnHide(boolean exitSplitScreenOnHide);
/** Gets the stage bounds. */
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds);
- /** Dumps current status of split-screen. */
- void dump(@NonNull PrintWriter pw, String prefix);
- /** Called when the shell organizer has been registered. */
- void onOrganizerRegistered();
void registerSplitScreenListener(SplitScreenListener listener);
void unregisterSplitScreenListener(SplitScreenListener listener);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 18dd53b90ff4..b0167afa2e4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -18,6 +18,13 @@ package com.android.wm.shell.splitscreen;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
+
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.PendingIntent;
@@ -35,7 +42,9 @@ import androidx.annotation.Nullable;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.draganddrop.DragAndDropPolicy;
import java.io.PrintWriter;
@@ -44,25 +53,33 @@ import java.io.PrintWriter;
* {@link SplitScreen}.
* @see StageCoordinator
*/
-public class SplitScreenController implements SplitScreen {
+public class SplitScreenController implements DragAndDropPolicy.Starter {
private static final String TAG = SplitScreenController.class.getSimpleName();
private final ShellTaskOrganizer mTaskOrganizer;
private final SyncTransactionQueue mSyncQueue;
private final Context mContext;
private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
+ private final ShellExecutor mMainExecutor;
+ private final SplitScreenImpl mImpl = new SplitScreenImpl();
+
private StageCoordinator mStageCoordinator;
public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, Context context,
- RootTaskDisplayAreaOrganizer rootTDAOrganizer) {
+ RootTaskDisplayAreaOrganizer rootTDAOrganizer,
+ ShellExecutor mainExecutor) {
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
mContext = context;
mRootTDAOrganizer = rootTDAOrganizer;
+ mMainExecutor = mainExecutor;
+ }
+
+ public SplitScreen asSplitScreen() {
+ return mImpl;
}
- @Override
public void onOrganizerRegistered() {
if (mStageCoordinator == null) {
// TODO: Multi-display
@@ -71,13 +88,11 @@ public class SplitScreenController implements SplitScreen {
}
}
- @Override
public boolean isSplitScreenVisible() {
return mStageCoordinator.isSplitScreenVisible();
}
- @Override
- public boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition) {
+ public boolean moveToSideStage(int taskId, @SplitScreen.StagePosition int sideStagePosition) {
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
if (task == null) {
throw new IllegalArgumentException("Unknown taskId" + taskId);
@@ -85,50 +100,50 @@ public class SplitScreenController implements SplitScreen {
return moveToSideStage(task, sideStagePosition);
}
- @Override
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @StagePosition int sideStagePosition) {
+ @SplitScreen.StagePosition int sideStagePosition) {
return mStageCoordinator.moveToSideStage(task, sideStagePosition);
}
- @Override
public boolean removeFromSideStage(int taskId) {
return mStageCoordinator.removeFromSideStage(taskId);
}
- @Override
- public void setSideStagePosition(@StagePosition int sideStagePosition) {
+ public void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
mStageCoordinator.setSideStagePosition(sideStagePosition);
}
- @Override
public void setSideStageVisibility(boolean visible) {
mStageCoordinator.setSideStageVisibility(visible);
}
- @Override
+ public void enterSplitScreen(int taskId, boolean leftOrTop) {
+ moveToSideStage(taskId,
+ leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT);
+ }
+
public void exitSplitScreen() {
mStageCoordinator.exitSplitScreen();
}
- @Override
+ public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+ mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
+ }
+
public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds);
}
- @Override
- public void registerSplitScreenListener(SplitScreenListener listener) {
+ public void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
mStageCoordinator.registerSplitScreenListener(listener);
}
- @Override
- public void unregisterSplitScreenListener(SplitScreenListener listener) {
+ public void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) {
mStageCoordinator.unregisterSplitScreenListener(listener);
}
- @Override
- public void startTask(int taskId,
- @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+ public void startTask(int taskId, @SplitScreen.StageType int stage,
+ @SplitScreen.StagePosition int position, @Nullable Bundle options) {
options = resolveStartStage(stage, position, options);
try {
@@ -138,9 +153,9 @@ public class SplitScreenController implements SplitScreen {
}
}
- @Override
- public void startShortcut(String packageName, String shortcutId, @StageType int stage,
- @StagePosition int position, @Nullable Bundle options, UserHandle user) {
+ public void startShortcut(String packageName, String shortcutId,
+ @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
+ @Nullable Bundle options, UserHandle user) {
options = resolveStartStage(stage, position, options);
try {
@@ -153,9 +168,8 @@ public class SplitScreenController implements SplitScreen {
}
}
- @Override
- public void startIntent(PendingIntent intent,
- @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+ public void startIntent(PendingIntent intent, @SplitScreen.StageType int stage,
+ @SplitScreen.StagePosition int position, @Nullable Bundle options) {
options = resolveStartStage(stage, position, options);
try {
@@ -165,8 +179,8 @@ public class SplitScreenController implements SplitScreen {
}
}
- private Bundle resolveStartStage(@StageType int stage, @StagePosition int position,
- @Nullable Bundle options) {
+ private Bundle resolveStartStage(@SplitScreen.StageType int stage,
+ @SplitScreen.StagePosition int position, @Nullable Bundle options) {
switch (stage) {
case STAGE_TYPE_UNDEFINED: {
// Use the stage of the specified position is valid.
@@ -216,7 +230,6 @@ public class SplitScreenController implements SplitScreen {
return options;
}
- @Override
public void dump(@NonNull PrintWriter pw, String prefix) {
pw.println(prefix + TAG);
if (mStageCoordinator != null) {
@@ -224,4 +237,120 @@ public class SplitScreenController implements SplitScreen {
}
}
+ private class SplitScreenImpl implements SplitScreen {
+ @Override
+ public boolean isSplitScreenVisible() {
+ return mMainExecutor.executeBlockingForResult(() -> {
+ return SplitScreenController.this.isSplitScreenVisible();
+ }, Boolean.class);
+ }
+
+ @Override
+ public boolean moveToSideStage(int taskId, int sideStagePosition) {
+ return mMainExecutor.executeBlockingForResult(() -> {
+ return SplitScreenController.this.moveToSideStage(taskId, sideStagePosition);
+ }, Boolean.class);
+ }
+
+ @Override
+ public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
+ int sideStagePosition) {
+ return mMainExecutor.executeBlockingForResult(() -> {
+ return SplitScreenController.this.moveToSideStage(task, sideStagePosition);
+ }, Boolean.class);
+ }
+
+ @Override
+ public boolean removeFromSideStage(int taskId) {
+ return mMainExecutor.executeBlockingForResult(() -> {
+ return SplitScreenController.this.removeFromSideStage(taskId);
+ }, Boolean.class);
+ }
+
+ @Override
+ public void setSideStagePosition(int sideStagePosition) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.setSideStagePosition(sideStagePosition);
+ });
+ }
+
+ @Override
+ public void setSideStageVisibility(boolean visible) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.setSideStageVisibility(visible);
+ });
+ }
+
+ @Override
+ public void enterSplitScreen(int taskId, boolean leftOrTop) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.enterSplitScreen(taskId, leftOrTop);
+ });
+ }
+
+ @Override
+ public void exitSplitScreen() {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.exitSplitScreen();
+ });
+ }
+
+ @Override
+ public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.exitSplitScreenOnHide(exitSplitScreenOnHide);
+ });
+ }
+
+ @Override
+ public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
+ try {
+ mMainExecutor.executeBlocking(() -> {
+ SplitScreenController.this.getStageBounds(outTopOrLeftBounds,
+ outBottomOrRightBounds);
+ });
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Failed to get stage bounds in 2s");
+ }
+ }
+
+ @Override
+ public void registerSplitScreenListener(SplitScreenListener listener) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.registerSplitScreenListener(listener);
+ });
+ }
+
+ @Override
+ public void unregisterSplitScreenListener(SplitScreenListener listener) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.unregisterSplitScreenListener(listener);
+ });
+ }
+
+ @Override
+ public void startTask(int taskId, int stage, int position, @Nullable Bundle options) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.startTask(taskId, stage, position, options);
+ });
+ }
+
+ @Override
+ public void startShortcut(String packageName, String shortcutId, int stage, int position,
+ @Nullable Bundle options, UserHandle user) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.startShortcut(packageName, shortcutId, stage, position,
+ options, user);
+ });
+ }
+
+ @Override
+ public void startIntent(PendingIntent intent, int stage, int position,
+ @Nullable Bundle options) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.startIntent(intent, stage, position, options);
+ });
+ }
+ }
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 176852b148fa..e44c820a656a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -79,6 +79,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
private DisplayAreaInfo mDisplayAreaInfo;
private final Context mContext;
private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
+ private boolean mExitSplitScreenOnHide = true;
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer) {
@@ -113,7 +114,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitScreen.StagePosition int sideStagePosition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
- mSideStagePosition = sideStagePosition;
+ setSideStagePosition(sideStagePosition);
mMainStage.activate(getMainStageBounds(), wct);
mSideStage.addTask(task, getSideStageBounds(), wct);
mTaskOrganizer.applyTransaction(wct);
@@ -144,14 +145,18 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
}
void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
+ if (mSideStagePosition == sideStagePosition) return;
+
mSideStagePosition = sideStagePosition;
if (mSideStageListener.mVisible) {
onStageVisibilityChanged(mSideStageListener);
}
+
+ sendOnStagePositionChanged();
}
void setSideStageVisibility(boolean visible) {
- if (!mSideStageListener.mVisible == visible) return;
+ if (mSideStageListener.mVisible == visible) return;
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSideStage.setVisibility(visible, wct);
@@ -162,6 +167,10 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
exitSplitScreen(null /* childrenToTop */);
}
+ void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+ mExitSplitScreenOnHide = exitSplitScreenOnHide;
+ }
+
private void exitSplitScreen(StageTaskListener childrenToTop) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
@@ -202,6 +211,14 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
mListeners.remove(listener);
}
+ private void sendOnStagePositionChanged() {
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ final SplitScreen.SplitScreenListener l = mListeners.get(i);
+ l.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition());
+ l.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition());
+ }
+ }
+
private void onStageChildTaskStatusChanged(
StageListenerImpl stageListener, int taskId, boolean present) {
@@ -251,7 +268,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
}
}
- if (!mainStageVisible && !sideStageVisible) {
+ if (mExitSplitScreenOnHide && !mainStageVisible && !sideStageVisible) {
// Exit split-screen if both stage are not visible.
// TODO: This is only a temporary request from UX and is likely to be removed soon...
exitSplitScreen();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 653299326cd0..10c742b69578 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -130,6 +130,17 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
}
+ @Override
+ public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ if (mRootTaskInfo.taskId == taskId) {
+ b.setParent(mRootLeash);
+ } else if (mChildrenLeashes.contains(taskId)) {
+ b.setParent(mChildrenLeashes.get(taskId));
+ } else {
+ throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
+ }
+ }
+
void setBounds(Rect bounds, WindowContainerTransaction wct) {
wct.setBounds(mRootTaskInfo.token, bounds);
}
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 f3f2fc3686b6..45d551528940 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,23 +31,21 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import android.util.Slog;
-import android.view.Gravity;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
+import android.view.Window;
+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.internal.policy.PhoneWindow;
import java.util.List;
/**
* Util class to create the view for a splash screen content.
+ * @hide
*/
-class SplashscreenContentDrawer {
+public class SplashscreenContentDrawer {
private static final String TAG = StartingSurfaceDrawer.TAG;
private static final boolean DEBUG = StartingSurfaceDrawer.DEBUG_SPLASH_SCREEN;
@@ -58,15 +56,24 @@ 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 int mIconSize;
+ private int mBrandingImageWidth;
+ private int mBrandingImageHeight;
- SplashscreenContentDrawer(Context context) {
+ SplashscreenContentDrawer(Context context, int maxIconAnimationDuration) {
mContext = context;
+ mMaxIconAnimationDuration = maxIconAnimationDuration;
}
private void updateDensity() {
mIconSize = mContext.getResources().getDimensionPixelSize(
com.android.wm.shell.R.dimen.starting_surface_icon_size);
+ mBrandingImageWidth = mContext.getResources().getDimensionPixelSize(
+ 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);
}
private int getSystemBGColor() {
@@ -83,48 +90,92 @@ class SplashscreenContentDrawer {
return new ColorDrawable(getSystemBGColor());
}
- View makeSplashScreenContentView(PhoneWindow win, Context context, int iconRes,
+ SplashScreenView makeSplashScreenContentView(Window win, Context context, int iconRes,
int splashscreenContentResId) {
updateDensity();
- win.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
// splash screen content will be deprecated after S.
- final View ssc = makeSplashscreenContentDrawable(win, context, splashscreenContentResId);
+ final SplashScreenView ssc =
+ makeSplashscreenContentDrawable(win, context, splashscreenContentResId);
if (ssc != null) {
return ssc;
}
- final TypedArray typedArray = context.obtainStyledAttributes(
- com.android.internal.R.styleable.Window);
- final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
- typedArray.recycle();
+ final SplashScreenWindowAttrs attrs =
+ SplashScreenWindowAttrs.createWindowAttrs(context);
+ final StartingWindowViewBuilder builder = new StartingWindowViewBuilder();
final Drawable themeBGDrawable;
- if (resId == 0) {
+
+ if (attrs.mWindowBgColor != 0) {
+ themeBGDrawable = new ColorDrawable(attrs.mWindowBgColor);
+ } else if (attrs.mWindowBgResId != 0) {
+ themeBGDrawable = context.getDrawable(attrs.mWindowBgResId);
+ } else {
Slog.w(TAG, "Window background not exist!");
themeBGDrawable = createDefaultBackgroundDrawable();
+ }
+ final int animationDuration;
+ final Drawable iconDrawable;
+ if (attrs.mReplaceIcon != null) {
+ iconDrawable = attrs.mReplaceIcon;
+ animationDuration = Math.max(0,
+ Math.min(attrs.mAnimationDuration, mMaxIconAnimationDuration));
} else {
- themeBGDrawable = context.getDrawable(resId);
+ iconDrawable = iconRes != 0 ? context.getDrawable(iconRes)
+ : context.getPackageManager().getDefaultActivityIcon();
+ animationDuration = 0;
}
- final Drawable iconDrawable = iconRes != 0 ? context.getDrawable(iconRes)
- : context.getPackageManager().getDefaultActivityIcon();
// TODO (b/173975965) Tracking the performance on improved splash screen.
- final StartingWindowViewBuilder builder = new StartingWindowViewBuilder();
return builder
- .setPhoneWindow(win)
+ .setWindow(win)
.setContext(context)
.setThemeDrawable(themeBGDrawable)
- .setIconDrawable(iconDrawable).build();
+ .setIconDrawable(iconDrawable)
+ .setIconAnimationDuration(animationDuration)
+ .setBrandingDrawable(attrs.mBrandingImage).build();
+ }
+
+ private static class SplashScreenWindowAttrs {
+ private int mWindowBgResId = 0;
+ private int mWindowBgColor = Color.TRANSPARENT;
+ private Drawable mReplaceIcon = null;
+ private Drawable mBrandingImage = null;
+ private int mAnimationDuration = 0;
+
+ static SplashScreenWindowAttrs createWindowAttrs(Context context) {
+ final SplashScreenWindowAttrs attrs = new SplashScreenWindowAttrs();
+ final TypedArray typedArray = context.obtainStyledAttributes(
+ com.android.internal.R.styleable.Window);
+ attrs.mWindowBgResId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
+ attrs.mWindowBgColor = typedArray.getColor(
+ R.styleable.Window_windowSplashScreenBackground, Color.TRANSPARENT);
+ attrs.mReplaceIcon = typedArray.getDrawable(
+ R.styleable.Window_windowSplashScreenAnimatedIcon);
+ attrs.mAnimationDuration = typedArray.getInt(
+ R.styleable.Window_windowSplashScreenAnimationDuration, 0);
+ attrs.mBrandingImage = typedArray.getDrawable(
+ R.styleable.Window_windowSplashScreenBrandingImage);
+ typedArray.recycle();
+ if (DEBUG) {
+ Slog.d(TAG, "window attributes color: "
+ + Integer.toHexString(attrs.mWindowBgColor)
+ + " icon " + attrs.mReplaceIcon + " duration " + attrs.mAnimationDuration
+ + " brandImage " + attrs.mBrandingImage);
+ }
+ return attrs;
+ }
}
private class StartingWindowViewBuilder {
- // materials
private Drawable mThemeBGDrawable;
private Drawable mIconDrawable;
- private PhoneWindow mPhoneWindow;
+ private Window mWindow;
+ private int mIconAnimationDuration;
private Context mContext;
+ private Drawable mBrandingDrawable;
// result
private boolean mBuildComplete = false;
- private View mCachedResult;
+ private SplashScreenView mCachedResult;
private int mThemeColor;
private Drawable mFinalIconDrawable;
private float mScale = 1f;
@@ -141,8 +192,20 @@ class SplashscreenContentDrawer {
return this;
}
- StartingWindowViewBuilder setPhoneWindow(PhoneWindow window) {
- mPhoneWindow = window;
+ StartingWindowViewBuilder setWindow(Window window) {
+ mWindow = window;
+ mBuildComplete = false;
+ return this;
+ }
+
+ StartingWindowViewBuilder setIconAnimationDuration(int iconAnimationDuration) {
+ mIconAnimationDuration = iconAnimationDuration;
+ mBuildComplete = false;
+ return this;
+ }
+
+ StartingWindowViewBuilder setBrandingDrawable(Drawable branding) {
+ mBrandingDrawable = branding;
mBuildComplete = false;
return this;
}
@@ -153,11 +216,11 @@ class SplashscreenContentDrawer {
return this;
}
- View build() {
+ SplashScreenView build() {
if (mBuildComplete) {
return mCachedResult;
}
- if (mPhoneWindow == null || mContext == null) {
+ if (mWindow == null || mContext == null) {
Slog.e(TAG, "Unable to create StartingWindowView, lack of materials!");
return null;
}
@@ -173,7 +236,7 @@ class SplashscreenContentDrawer {
mFinalIconDrawable = mIconDrawable;
}
final int iconSize = mFinalIconDrawable != null ? (int) (mIconSize * mScale) : 0;
- mCachedResult = fillViewWithIcon(mPhoneWindow, mContext, iconSize, mFinalIconDrawable);
+ mCachedResult = fillViewWithIcon(mWindow, mContext, iconSize, mFinalIconDrawable);
mBuildComplete = true;
return mCachedResult;
}
@@ -249,25 +312,26 @@ class SplashscreenContentDrawer {
return true;
}
- private View fillViewWithIcon(PhoneWindow win, Context context,
+ private SplashScreenView fillViewWithIcon(Window win, Context context,
int iconSize, Drawable iconDrawable) {
- final StartingSurfaceWindowView surfaceWindowView =
- new StartingSurfaceWindowView(context, iconSize);
- surfaceWindowView.setBackground(new ColorDrawable(mThemeColor));
+ final SplashScreenView.Builder builder = new SplashScreenView.Builder(context);
+ builder.setIconSize(iconSize).setBackgroundColor(mThemeColor);
if (iconDrawable != null) {
- surfaceWindowView.setIconDrawable(iconDrawable);
+ builder.setCenterViewDrawable(iconDrawable);
}
+ builder.setAnimationDuration(mIconAnimationDuration);
+ if (mBrandingDrawable != null) {
+ builder.setBrandingDrawable(mBrandingDrawable, mBrandingImageWidth,
+ mBrandingImageHeight);
+ }
+ final SplashScreenView splashScreenView = builder.build();
if (DEBUG) {
- Slog.d(TAG, "fillViewWithIcon surfaceWindowView " + surfaceWindowView);
+ Slog.d(TAG, "fillViewWithIcon surfaceWindowView " + splashScreenView);
}
- win.setContentView(surfaceWindowView);
- makeSystemUIColorsTransparent(win);
- return surfaceWindowView;
- }
-
- private void makeSystemUIColorsTransparent(PhoneWindow win) {
- win.setStatusBarColor(Color.TRANSPARENT);
- win.setNavigationBarColor(Color.TRANSPARENT);
+ win.setContentView(splashScreenView);
+ splashScreenView.cacheRootWindow(win);
+ splashScreenView.makeSystemUIColorsTransparent();
+ return splashScreenView;
}
}
@@ -298,8 +362,8 @@ class SplashscreenContentDrawer {
return root < 0.1;
}
- private static View makeSplashscreenContentDrawable(PhoneWindow win, Context ctx,
- int splashscreenContentResId) {
+ private static SplashScreenView makeSplashscreenContentDrawable(Window win,
+ Context ctx, int splashscreenContentResId) {
// doesn't support windowSplashscreenContent after S
// TODO add an allowlist to skip some packages if needed
final int targetSdkVersion = ctx.getApplicationInfo().targetSdkVersion;
@@ -316,7 +380,8 @@ class SplashscreenContentDrawer {
if (drawable == null) {
return null;
}
- View view = new View(ctx);
+ SplashScreenView view = new SplashScreenView(ctx);
+ view.setNotCopyable();
view.setBackground(drawable);
win.setContentView(view);
return view;
@@ -532,34 +597,4 @@ class SplashscreenContentDrawer {
}
}
}
-
- private static class StartingSurfaceWindowView extends FrameLayout {
- // TODO animate the icon view
- private final View mIconView;
-
- StartingSurfaceWindowView(Context context, int iconSize) {
- super(context);
-
- final boolean emptyIcon = iconSize == 0;
- if (emptyIcon) {
- mIconView = null;
- } else {
- mIconView = new View(context);
- FrameLayout.LayoutParams params =
- new FrameLayout.LayoutParams(iconSize, iconSize);
- params.gravity = Gravity.CENTER;
- addView(mIconView, params);
- }
- setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
- }
-
- // TODO support animatable icon
- void setIconDrawable(Drawable icon) {
- if (mIconView != null) {
- mIconView.setBackground(icon);
- }
- }
- }
}
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 8e24e0b516cb..5332291fd1bd 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
@@ -43,6 +43,8 @@ import android.util.SparseArray;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
+import android.window.SplashScreenView;
+import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.StartingWindowInfo;
import android.window.TaskOrganizer;
import android.window.TaskSnapshot;
@@ -63,7 +65,6 @@ import java.util.function.Consumer;
* class to remove the starting window of the Task.
* @hide
*/
-
public class StartingSurfaceDrawer {
static final String TAG = StartingSurfaceDrawer.class.getSimpleName();
static final boolean DEBUG_SPLASH_SCREEN = false;
@@ -81,7 +82,10 @@ public class StartingSurfaceDrawer {
mContext = context;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mMainExecutor = mainExecutor;
- mSplashscreenContentDrawer = new SplashscreenContentDrawer(context);
+
+ final int maxIconAnimDuration = context.getResources().getInteger(
+ com.android.wm.shell.R.integer.max_starting_window_intro_icon_anim_duration);
+ mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, maxIconAnimDuration);
}
private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
@@ -193,9 +197,8 @@ public class StartingSurfaceDrawer {
public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
final PreferredStartingTypeHelper helper =
new PreferredStartingTypeHelper(windowInfo);
- final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo;
if (helper.mPreferredType == PreferredStartingTypeHelper.STARTING_TYPE_SPLASH_SCREEN) {
- addSplashScreenStartingWindow(runningTaskInfo, appToken);
+ addSplashScreenStartingWindow(windowInfo, appToken);
} else if (helper.mPreferredType == PreferredStartingTypeHelper.STARTING_TYPE_SNAPSHOT) {
final TaskSnapshot snapshot = helper.mSnapshot;
makeTaskSnapshotWindow(windowInfo, appToken, snapshot);
@@ -203,11 +206,13 @@ public class StartingSurfaceDrawer {
// If prefer don't show, then don't show!
}
- private void addSplashScreenStartingWindow(RunningTaskInfo taskInfo, IBinder appToken) {
+ private void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
+ final RunningTaskInfo taskInfo = windowInfo.taskInfo;
final ActivityInfo activityInfo = taskInfo.topActivityInfo;
if (activityInfo == null) {
return;
}
+
final int displayId = taskInfo.displayId;
if (activityInfo.packageName == null) {
return;
@@ -222,11 +227,11 @@ public class StartingSurfaceDrawer {
}
Context context = mContext;
- int theme = activityInfo.getThemeResource();
- if (theme == 0) {
- // replace with the default theme if the application didn't set
- theme = com.android.internal.R.style.Theme_DeviceDefault_DayNight;
- }
+ // replace with the default theme if the application didn't set
+ final int theme = windowInfo.splashScreenThemeResId != 0
+ ? windowInfo.splashScreenThemeResId
+ : activityInfo.getThemeResource() != 0 ? activityInfo.getThemeResource()
+ : com.android.internal.R.style.Theme_DeviceDefault_DayNight;
if (DEBUG_SPLASH_SCREEN) {
Slog.d(TAG, "addSplashScreen " + activityInfo.packageName
+ ": nonLocalizedLabel=" + nonLocalizedLabel + " theme="
@@ -352,9 +357,10 @@ public class StartingSurfaceDrawer {
}
params.setTitle("Splash Screen " + activityInfo.packageName);
- final View contentView = mSplashscreenContentDrawer.makeSplashScreenContentView(win,
- context, iconRes, splashscreenContentResId[0]);
- if (contentView == null) {
+ final SplashScreenView splashScreenView =
+ mSplashscreenContentDrawer.makeSplashScreenContentView(win, context, iconRes,
+ splashscreenContentResId[0]);
+ if (splashScreenView == null) {
Slog.w(TAG, "Adding splash screen window for " + activityInfo.packageName + " failed!");
return;
}
@@ -366,7 +372,7 @@ public class StartingSurfaceDrawer {
+ activityInfo.packageName + " / " + appToken + ": " + view);
}
final WindowManager wm = context.getSystemService(WindowManager.class);
- postAddWindow(taskInfo.taskId, appToken, view, wm, params);
+ postAddWindow(taskInfo.taskId, appToken, view, wm, params, splashScreenView);
}
/**
@@ -377,12 +383,10 @@ public class StartingSurfaceDrawer {
final int taskId = startingWindowInfo.taskInfo.taskId;
final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken,
snapshot, mMainExecutor, () -> removeWindowSynced(taskId) /* clearWindow */);
- mMainExecutor.execute(() -> {
- mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
- final StartingWindowRecord tView =
- new StartingWindowRecord(null/* decorView */, surface);
- mStartingWindowRecords.put(taskId, tView);
- });
+ mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
+ final StartingWindowRecord tView =
+ new StartingWindowRecord(null/* decorView */, surface, null /* splashScreenView */);
+ mStartingWindowRecords.put(taskId, tView);
}
/**
@@ -392,11 +396,32 @@ public class StartingSurfaceDrawer {
if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
Slog.d(TAG, "Task start finish, remove starting surface for task " + taskId);
}
- mMainExecutor.execute(() -> removeWindowSynced(taskId));
+ removeWindowSynced(taskId);
+ }
+
+ /**
+ * Called when the Task wants to copy the splash screen.
+ * @param taskId
+ */
+ public void copySplashScreenView(int taskId) {
+ final StartingWindowRecord preView = mStartingWindowRecords.get(taskId);
+ SplashScreenViewParcelable parcelable;
+ if (preView != null && preView.mContentView != null
+ && preView.mContentView.isCopyable()) {
+ parcelable = new SplashScreenViewParcelable(preView.mContentView);
+ } else {
+ parcelable = null;
+ }
+ if (DEBUG_SPLASH_SCREEN) {
+ Slog.v(TAG, "Copying splash screen window view for task: " + taskId
+ + " parcelable? " + parcelable);
+ }
+ ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable);
}
protected void postAddWindow(int taskId, IBinder appToken,
- View view, WindowManager wm, WindowManager.LayoutParams params) {
+ View view, WindowManager wm, WindowManager.LayoutParams params,
+ SplashScreenView splashScreenView) {
mMainExecutor.execute(() -> {
boolean shouldSaveView = true;
try {
@@ -419,12 +444,12 @@ public class StartingSurfaceDrawer {
shouldSaveView = false;
}
}
-
if (shouldSaveView) {
removeWindowSynced(taskId);
mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
- final StartingWindowRecord tView =
- new StartingWindowRecord(view, null /* TaskSnapshotWindow */);
+ final StartingWindowRecord tView = new StartingWindowRecord(view,
+ null /* TaskSnapshotWindow */, splashScreenView);
+ splashScreenView.startIntroAnimation();
mStartingWindowRecords.put(taskId, tView);
}
});
@@ -445,7 +470,7 @@ public class StartingSurfaceDrawer {
if (DEBUG_TASK_SNAPSHOT) {
Slog.v(TAG, "Removing task snapshot window for " + taskId);
}
- record.mTaskSnapshotWindow.remove(mMainExecutor);
+ record.mTaskSnapshotWindow.remove();
}
mStartingWindowRecords.remove(taskId);
}
@@ -463,10 +488,13 @@ public class StartingSurfaceDrawer {
private static class StartingWindowRecord {
private final View mDecorView;
private final TaskSnapshotWindow mTaskSnapshotWindow;
+ private final SplashScreenView mContentView;
- StartingWindowRecord(View decorView, TaskSnapshotWindow taskSnapshotWindow) {
+ StartingWindowRecord(View decorView, TaskSnapshotWindow taskSnapshotWindow,
+ SplashScreenView splashScreenView) {
mDecorView = decorView;
mTaskSnapshotWindow = taskSnapshotWindow;
+ mContentView = splashScreenView;
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index b5e18960ff5c..b7fd3cb67b2b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -43,6 +43,7 @@ import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_AT
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.getNavigationBarRect;
+import android.annotation.BinderThread;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
@@ -82,6 +83,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.DecorView;
import com.android.internal.view.BaseIWindow;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ExternalThread;
/**
* This class represents a starting window that shows a snapshot.
@@ -121,6 +123,7 @@ public class TaskSnapshotWindow {
private final Window mWindow;
private final Surface mSurface;
private final Runnable mClearWindowHandler;
+ private final ShellExecutor mMainExecutor;
private SurfaceControl mSurfaceControl;
private SurfaceControl mChildSurfaceControl;
private final IWindowSession mSession;
@@ -213,7 +216,7 @@ public class TaskSnapshotWindow {
final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow(
surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance,
windowFlags, windowPrivateFlags, taskBounds, orientation, activityType,
- topWindowInsetsState, clearWindowHandler);
+ topWindowInsetsState, clearWindowHandler, mainExecutor);
final Window window = snapshotSurface.mWindow;
final InsetsState mTmpInsetsState = new InsetsState();
@@ -229,7 +232,7 @@ public class TaskSnapshotWindow {
} catch (RemoteException e) {
snapshotSurface.clearWindowSynced();
}
- window.setOuter(snapshotSurface, mainExecutor);
+ window.setOuter(snapshotSurface);
try {
session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
@@ -249,7 +252,8 @@ public class TaskSnapshotWindow {
TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds,
int currentOrientation, int activityType, InsetsState topWindowInsetsState,
- Runnable clearWindowHandler) {
+ Runnable clearWindowHandler, ShellExecutor mainExecutor) {
+ mMainExecutor = mainExecutor;
mSurface = new Surface();
mSession = WindowManagerGlobal.getWindowSession();
mWindow = new Window();
@@ -286,28 +290,26 @@ public class TaskSnapshotWindow {
mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
}
- void remove(ShellExecutor mainExecutor) {
+ void remove() {
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) {
final long delayTime = mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS - now;
- mainExecutor.executeDelayed(() -> remove(mainExecutor), delayTime);
+ mMainExecutor.executeDelayed(() -> remove(), delayTime);
if (DEBUG) {
Slog.d(TAG, "Defer removing snapshot surface in " + delayTime);
}
return;
}
- mainExecutor.execute(() -> {
- try {
- if (DEBUG) {
- Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn);
- }
- mSession.remove(mWindow);
- } catch (RemoteException e) {
- // nothing
+ try {
+ if (DEBUG) {
+ Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn);
}
- });
+ mSession.remove(mWindow);
+ } catch (RemoteException e) {
+ // nothing
+ }
}
/**
@@ -497,13 +499,12 @@ public class TaskSnapshotWindow {
}
}
+ @BinderThread
static class Window extends BaseIWindow {
private TaskSnapshotWindow mOuter;
- private ShellExecutor mMainExecutor;
- public void setOuter(TaskSnapshotWindow outer, ShellExecutor mainExecutor) {
+ public void setOuter(TaskSnapshotWindow outer) {
mOuter = outer;
- mMainExecutor = mainExecutor;
}
@Override
@@ -511,22 +512,20 @@ public class TaskSnapshotWindow {
MergedConfiguration mergedConfiguration, boolean forceLayout,
boolean alwaysConsumeSystemBars, int displayId) {
if (mOuter != null) {
- if (mergedConfiguration != null
- && mOuter.mOrientationOnCreation
- != mergedConfiguration.getMergedConfiguration().orientation) {
- // 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.
- mMainExecutor.execute(() -> {
+ mOuter.mMainExecutor.execute(() -> {
+ if (mergedConfiguration != null
+ && mOuter.mOrientationOnCreation
+ != mergedConfiguration.getMergedConfiguration().orientation) {
+ // 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.
mOuter.clearWindowSynced();
- });
- } else if (reportDraw) {
- mMainExecutor.execute(() -> {
+ } else if (reportDraw) {
if (mOuter.mHasDrawn) {
mOuter.reportDrawn();
}
- });
- }
+ }
+ });
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java
new file mode 100644
index 000000000000..85bbf74d56b9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java
@@ -0,0 +1,40 @@
+/*
+ * 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.wm.shell.transition;
+
+import android.annotation.NonNull;
+import android.window.IRemoteTransition;
+import android.window.TransitionFilter;
+
+import com.android.wm.shell.common.annotations.ExternalThread;
+
+/**
+ * Interface to manage remote transitions.
+ */
+@ExternalThread
+public interface RemoteTransitions {
+ /**
+ * Registers a remote transition.
+ */
+ void registerRemote(@NonNull TransitionFilter filter,
+ @NonNull IRemoteTransition remoteTransition);
+
+ /**
+ * Unregisters a remote transition.
+ */
+ void unregisterRemote(@NonNull IRemoteTransition remoteTransition);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index d8687bd899de..10344892e766 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -67,6 +67,7 @@ public class Transitions {
private final ShellExecutor mAnimExecutor;
private final TransitionPlayerImpl mPlayerImpl;
private final RemoteTransitionHandler mRemoteTransitionHandler;
+ private final RemoteTransitionImpl mImpl = new RemoteTransitionImpl();
/** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
@@ -78,6 +79,10 @@ public class Transitions {
/** Keeps track of currently tracked transitions and all the animations associated with each */
private final ArrayMap<IBinder, ActiveTransition> mActiveTransitions = new ArrayMap<>();
+ public static RemoteTransitions asRemoteTransitions(Transitions transitions) {
+ return transitions.mImpl;
+ }
+
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
@NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
mOrganizer = organizer;
@@ -101,8 +106,20 @@ public class Transitions {
/** Create an empty/non-registering transitions object for system-ui tests. */
@VisibleForTesting
- public static Transitions createEmptyForTesting() {
- return new Transitions();
+ public static RemoteTransitions createEmptyForTesting() {
+ return new RemoteTransitions() {
+ @Override
+ public void registerRemote(@androidx.annotation.NonNull TransitionFilter filter,
+ @androidx.annotation.NonNull IRemoteTransition remoteTransition) {
+ // Do nothing
+ }
+
+ @Override
+ public void unregisterRemote(
+ @androidx.annotation.NonNull IRemoteTransition remoteTransition) {
+ // Do nothing
+ }
+ };
}
/** Register this transition handler with Core */
@@ -134,16 +151,14 @@ public class Transitions {
}
/** Register a remote transition to be used when `filter` matches an incoming transition */
- @ExternalThread
public void registerRemote(@NonNull TransitionFilter filter,
@NonNull IRemoteTransition remoteTransition) {
- mMainExecutor.execute(() -> mRemoteTransitionHandler.addFiltered(filter, remoteTransition));
+ mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
}
/** Unregisters a remote transition and all associated filters */
- @ExternalThread
public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) {
- mMainExecutor.execute(() -> mRemoteTransitionHandler.removeFiltered(remoteTransition));
+ mRemoteTransitionHandler.removeFiltered(remoteTransition);
}
/** @return true if the transition was triggered by opening something vs closing something */
@@ -371,4 +386,22 @@ public class Transitions {
mMainExecutor.execute(() -> Transitions.this.requestStartTransition(iBinder, request));
}
}
+
+ @ExternalThread
+ private class RemoteTransitionImpl implements RemoteTransitions {
+ @Override
+ public void registerRemote(@NonNull TransitionFilter filter,
+ @NonNull IRemoteTransition remoteTransition) {
+ mMainExecutor.execute(() -> {
+ Transitions.this.registerRemote(filter, remoteTransition);
+ });
+ }
+
+ @Override
+ public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) {
+ mMainExecutor.execute(() -> {
+ Transitions.this.unregisterRemote(remoteTransition);
+ });
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index 2c6c7b358e3b..d80699de8a2d 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -1,2 +1,3 @@
+# Bug component: 909476
# includes OWNERS from parent directories
natanieljr@google.com
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index 8258630a9502..f06d57c6c789 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -25,8 +25,6 @@
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.wm.shell.flicker"/>
- <option name="include-annotation" value="androidx.test.filters.RequiresDevice" />
- <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
<option name="shell-timeout" value="6600s" />
<option name="test-timeout" value="6000s" />
<option name="hidden-api-checks" value="false" />
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 7ad75532eced..3282ece999ac 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -18,6 +18,7 @@ package com.android.wm.shell.flicker
import android.graphics.Region
import android.view.Surface
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
@@ -25,6 +26,109 @@ import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.traces.layers.getVisibleBounds
@JvmOverloads
+fun LayersAssertionBuilder.appPairsDividerIsVisible(bugId: Int = 0) {
+ end("appPairsDividerIsVisible", bugId) {
+ this.isVisible(APP_PAIR_SPLIT_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsDividerIsInvisible(bugId: Int = 0) {
+ end("appPairsDividerIsInVisible", bugId) {
+ this.notExists(APP_PAIR_SPLIT_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsDividerBecomesVisible(bugId: Int = 0) {
+ all("dividerLayerBecomesVisible", bugId) {
+ this.hidesLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .showsLayer(DOCKED_STACK_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerIsVisible(bugId: Int = 0) {
+ end("dockedStackDividerIsVisible", bugId) {
+ this.isVisible(DOCKED_STACK_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerBecomesVisible(bugId: Int = 0) {
+ all("dividerLayerBecomesVisible", bugId) {
+ this.hidesLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .showsLayer(DOCKED_STACK_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerBecomesInvisible(bugId: Int = 0) {
+ all("dividerLayerBecomesInvisible", bugId) {
+ this.showsLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .hidesLayer(DOCKED_STACK_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerIsInvisible(bugId: Int = 0) {
+ end("dockedStackDividerIsInvisible", bugId) {
+ this.notExists(DOCKED_STACK_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsPrimaryBoundsIsVisible(
+ rotation: Int,
+ primaryLayerName: String,
+ bugId: Int = 0
+) {
+ end("PrimaryAppBounds", bugId) {
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+ this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsSecondaryBoundsIsVisible(
+ rotation: Int,
+ secondaryLayerName: String,
+ bugId: Int = 0
+) {
+ end("SecondaryAppBounds", bugId) {
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+ this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackPrimaryBoundsIsVisible(
+ rotation: Int,
+ primaryLayerName: String,
+ bugId: Int = 0
+) {
+ end("PrimaryAppBounds", bugId) {
+ val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
+ this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackSecondaryBoundsIsVisible(
+ rotation: Int,
+ secondaryLayerName: String,
+ bugId: Int = 0
+) {
+ end("SecondaryAppBounds", bugId) {
+ val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
+ this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
+ }
+}
+
+@JvmOverloads
fun LayersAssertionBuilderLegacy.appPairsDividerIsVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -51,8 +155,8 @@ fun LayersAssertionBuilderLegacy.appPairsDividerBecomesVisible(
) {
all("dividerLayerBecomesVisible", bugId, enabled) {
this.hidesLayer(DOCKED_STACK_DIVIDER)
- .then()
- .showsLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .showsLayer(DOCKED_STACK_DIVIDER)
}
}
@@ -73,8 +177,8 @@ fun LayersAssertionBuilderLegacy.dockedStackDividerBecomesVisible(
) {
all("dividerLayerBecomesVisible", bugId, enabled) {
this.hidesLayer(DOCKED_STACK_DIVIDER)
- .then()
- .showsLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .showsLayer(DOCKED_STACK_DIVIDER)
}
}
@@ -85,8 +189,8 @@ fun LayersAssertionBuilderLegacy.dockedStackDividerBecomesInvisible(
) {
all("dividerLayerBecomesInvisible", bugId, enabled) {
this.showsLayer(DOCKED_STACK_DIVIDER)
- .then()
- .hidesLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .hidesLayer(DOCKED_STACK_DIVIDER)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt
new file mode 100644
index 000000000000..1869d833fbc4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/Extensions.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+@file:JvmName("Utils")
+package com.android.wm.shell.flicker
+
+import android.app.ActivityTaskManager
+import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT
+import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
+
+fun removeAllTasksButHome() {
+ val ALL_ACTIVITY_TYPE_BUT_HOME = intArrayOf(
+ ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS,
+ ACTIVITY_TYPE_UNDEFINED)
+ val atm = ActivityTaskManager.getService()
+ atm.removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME)
+}
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 d25774935e86..c3fd66395366 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
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.apppairs
import android.os.Bundle
-import android.platform.test.annotations.Presubmit
import android.os.SystemClock
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -40,7 +39,6 @@ import org.junit.runners.Parameterized
* Test cold launch app from launcher.
* To run this test: `atest WMShellFlickerTests:AppPairsTestCannotPairNonResizeableApps`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -51,10 +49,9 @@ class AppPairsTestCannotPairNonResizeableApps(
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val testTag = "testAppPairs_cannotPairNonResizeableApps"
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
withTestName {
- buildTestTag(testTag, configuration)
+ buildTestTag(configuration)
}
transitions {
nonResizeableApp?.launchViaIntent(wmHelper)
@@ -64,17 +61,20 @@ class AppPairsTestCannotPairNonResizeableApps(
SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
}
assertions {
- layersTrace {
- appPairsDividerIsInvisible()
- }
- windowManagerTrace {
- end("onlyResizeableAppWindowVisible") {
+ presubmit {
+ layersTrace {
+ appPairsDividerIsInvisible()
+ }
+ windowManagerTrace {
val nonResizeableApp = nonResizeableApp
require(nonResizeableApp != null) {
"Non resizeable app not initialized"
}
- isVisible(nonResizeableApp.defaultWindowName)
- isInvisible(primaryApp.defaultWindowName)
+
+ end("onlyResizeableAppWindowVisible") {
+ isVisible(nonResizeableApp.defaultWindowName)
+ isInvisible(primaryApp.defaultWindowName)
+ }
}
}
}
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 257350b6950b..7a2a5e482d98 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
@@ -18,7 +18,6 @@ package com.android.wm.shell.flicker.apppairs
import android.os.Bundle
import android.os.SystemClock
-import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
@@ -38,7 +37,6 @@ import org.junit.runners.Parameterized
* Test cold launch app from launcher.
* To run this test: `atest WMShellFlickerTests:AppPairsTestPairPrimaryAndSecondaryApps`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -49,10 +47,9 @@ class AppPairsTestPairPrimaryAndSecondaryApps(
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val testTag = "testAppPairs_pairPrimaryAndSecondaryApps"
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
withTestName {
- buildTestTag(testTag, configuration)
+ buildTestTag(configuration)
}
transitions {
// TODO pair apps through normal UX flow
@@ -61,20 +58,27 @@ class AppPairsTestPairPrimaryAndSecondaryApps(
SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
}
assertions {
- layersTrace {
- appPairsDividerIsVisible()
- end("appsEndingBounds", enabled = false) {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- this.hasVisibleRegion(primaryApp.defaultWindowName,
- appPairsHelper.getPrimaryBounds(dividerRegion))
- .hasVisibleRegion(secondaryApp.defaultWindowName,
- appPairsHelper.getSecondaryBounds(dividerRegion))
+ presubmit {
+ layersTrace {
+ appPairsDividerIsVisible()
+ }
+ windowManagerTrace {
+ end("bothAppWindowsVisible") {
+ isVisible(primaryApp.defaultWindowName)
+ isVisible(secondaryApp.defaultWindowName)
+ }
}
}
- windowManagerTrace {
- end("bothAppWindowsVisible") {
- isVisible(primaryApp.defaultWindowName)
- isVisible(secondaryApp.defaultWindowName)
+
+ flaky {
+ layersTrace {
+ end("appsEndingBounds") {
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+ this.hasVisibleRegion(primaryApp.defaultWindowName,
+ appPairsHelper.getPrimaryBounds(dividerRegion))
+ .hasVisibleRegion(secondaryApp.defaultWindowName,
+ appPairsHelper.getSecondaryBounds(dividerRegion))
+ }
}
}
}
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 0b001f5ac1b6..d8dc4c2b56f6 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
@@ -18,7 +18,6 @@ package com.android.wm.shell.flicker.apppairs
import android.os.Bundle
import android.os.SystemClock
-import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
@@ -38,7 +37,6 @@ import org.junit.runners.Parameterized
* Test cold launch app from launcher.
* To run this test: `atest WMShellFlickerTests:AppPairsTestUnpairPrimaryAndSecondaryApps`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -49,10 +47,9 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val testTag = "testAppPairs_unpairPrimaryAndSecondaryApps"
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
withTestName {
- buildTestTag(testTag, configuration)
+ buildTestTag(configuration)
}
setup {
executeShellCommand(
@@ -66,24 +63,31 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
}
assertions {
- layersTrace {
- appPairsDividerIsInvisible()
- start("appsStartingBounds", enabled = false) {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- this.hasVisibleRegion(primaryApp.defaultWindowName,
- appPairsHelper.getPrimaryBounds(dividerRegion))
- .hasVisibleRegion(secondaryApp.defaultWindowName,
- appPairsHelper.getSecondaryBounds(dividerRegion))
+ presubmit {
+ layersTrace {
+ appPairsDividerIsInvisible()
}
- end("appsEndingBounds", enabled = false) {
- this.notExists(primaryApp.defaultWindowName)
- .notExists(secondaryApp.defaultWindowName)
+ windowManagerTrace {
+ end("bothAppWindowsInvisible") {
+ isInvisible(primaryApp.defaultWindowName)
+ isInvisible(secondaryApp.defaultWindowName)
+ }
}
}
- windowManagerTrace {
- end("bothAppWindowsInvisible") {
- isInvisible(primaryApp.defaultWindowName)
- isInvisible(secondaryApp.defaultWindowName)
+
+ flaky {
+ layersTrace {
+ start("appsStartingBounds") {
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+ this.hasVisibleRegion(primaryApp.defaultWindowName,
+ appPairsHelper.getPrimaryBounds(dividerRegion))
+ .hasVisibleRegion(secondaryApp.defaultWindowName,
+ appPairsHelper.getSecondaryBounds(dividerRegion))
+ }
+ end("appsEndingBounds") {
+ this.notExists(primaryApp.defaultWindowName)
+ .notExists(secondaryApp.defaultWindowName)
+ }
}
}
}
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 aafa9bfbd676..8aee005b7513 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
@@ -18,7 +18,6 @@ package com.android.wm.shell.flicker.apppairs
import android.os.Bundle
import android.os.SystemClock
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -46,7 +45,6 @@ import org.junit.runners.Parameterized
* Test open apps to app pairs and rotate.
* To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppsInAppPairsMode`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -60,7 +58,7 @@ class RotateTwoLaunchedAppsInAppPairsMode(
fun getParams(): Collection<Array<Any>> {
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
withTestName {
- buildTestTag("testRotateTwoLaunchedAppsInAppPairsMode", configuration)
+ buildTestTag(configuration)
}
transitions {
executeShellCommand(composePairsCommand(
@@ -69,23 +67,28 @@ class RotateTwoLaunchedAppsInAppPairsMode(
setRotation(configuration.endRotation)
}
assertions {
- layersTrace {
- navBarLayerRotatesAndScales(Surface.ROTATION_0, configuration.endRotation,
- enabled = false)
- statusBarLayerRotatesScales(Surface.ROTATION_0, configuration.endRotation,
- enabled = false)
- appPairsDividerIsVisible(enabled = false)
- appPairsPrimaryBoundsIsVisible(configuration.endRotation,
- primaryApp.defaultWindowName, bugId = 172776659)
- appPairsSecondaryBoundsIsVisible(configuration.endRotation,
- secondaryApp.defaultWindowName, bugId = 172776659)
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ end("bothAppWindowsVisible") {
+ isVisible(primaryApp.defaultWindowName)
+ .isVisible(secondaryApp.defaultWindowName)
+ }
+ }
}
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("bothAppWindowsVisible") {
- isVisible(primaryApp.defaultWindowName)
- .isVisible(secondaryApp.defaultWindowName)
+
+ flaky {
+ layersTrace {
+ appPairsDividerIsVisible()
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ appPairsPrimaryBoundsIsVisible(configuration.endRotation,
+ primaryApp.defaultWindowName, bugId = 172776659)
+ appPairsSecondaryBoundsIsVisible(configuration.endRotation,
+ secondaryApp.defaultWindowName, bugId = 172776659)
}
}
}
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 19ca31fbee4a..bc99c9430f13 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
@@ -62,7 +62,7 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
fun getParams(): Collection<Array<Any>> {
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
withTestName {
- buildTestTag("testRotateAndEnterAppPairsMode", configuration)
+ buildTestTag(configuration)
}
transitions {
this.setRotation(configuration.endRotation)
@@ -71,22 +71,37 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
}
assertions {
- layersTrace {
- navBarLayerRotatesAndScales(Surface.ROTATION_0, configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(Surface.ROTATION_0, configuration.endRotation)
- appPairsDividerIsVisible()
- appPairsPrimaryBoundsIsVisible(configuration.endRotation,
- primaryApp.defaultWindowName, 172776659)
- appPairsSecondaryBoundsIsVisible(configuration.endRotation,
- secondaryApp.defaultWindowName, 172776659)
+ val isRotated = configuration.startRotation.isRotated()
+ presubmit {
+ layersTrace {
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ appPairsDividerIsVisible()
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
+ }
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ end("bothAppWindowsVisible") {
+ isVisible(primaryApp.defaultWindowName)
+ isVisible(secondaryApp.defaultWindowName)
+ }
+ }
}
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("bothAppWindowsVisible") {
- isVisible(primaryApp.defaultWindowName)
- isVisible(secondaryApp.defaultWindowName)
+ flaky {
+ layersTrace {
+ appPairsPrimaryBoundsIsVisible(configuration.endRotation,
+ primaryApp.defaultWindowName, 172776659)
+ appPairsSecondaryBoundsIsVisible(configuration.endRotation,
+ secondaryApp.defaultWindowName, 172776659)
+
+ if (isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
index 7ec22bb9db1c..cac46fe676b3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
@@ -32,13 +32,12 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
/**
* Opens the IME and wait for it to be displayed
*
- * @param device UIDevice instance to interact with the device
* @param wmHelper Helper used to wait for WindowManager states
*/
@JvmOverloads
- open fun openIME(device: UiDevice, wmHelper: WindowManagerStateHelper? = null) {
+ open fun openIME(wmHelper: WindowManagerStateHelper? = null) {
if (!isTelevision) {
- val editText = device.wait(
+ val editText = uiDevice.wait(
Until.findObject(By.res(getPackage(), "plain_text_input")),
FIND_TIMEOUT)
@@ -47,7 +46,7 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
"was left in an unknown state (e.g. in split screen)"
}
editText.click()
- waitAndAssertIMEShown(device, wmHelper)
+ waitAndAssertIMEShown(uiDevice, wmHelper)
} else {
// If we do the same thing as above - editText.click() - on TV, that's going to force TV
// into the touch mode. We really don't want that.
@@ -69,16 +68,15 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
/**
* Opens the IME and wait for it to be gone
*
- * @param device UIDevice instance to interact with the device
* @param wmHelper Helper used to wait for WindowManager states
*/
@JvmOverloads
- open fun closeIME(device: UiDevice, wmHelper: WindowManagerStateHelper? = null) {
+ open fun closeIME(wmHelper: WindowManagerStateHelper? = null) {
if (!isTelevision) {
- device.pressBack()
+ uiDevice.pressBack()
// Using only the AccessibilityInfo it is not possible to identify if the IME is active
if (wmHelper == null) {
- device.waitForIdle()
+ uiDevice.waitForIdle()
} else {
require(wmHelper.waitImeWindowGone()) { "IME did did not close" }
}
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 b90e865de691..ecc066be734f 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,17 +17,19 @@
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
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
-import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow
import com.android.wm.shell.flicker.pip.tv.isFocusedOrHasFocusedChild
+import com.android.wm.shell.flicker.pip.waitPipWindowGone
+import com.android.wm.shell.flicker.pip.waitPipWindowShown
import com.android.wm.shell.flicker.testapp.Components
-import org.junit.Assert.fail
class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
instrumentation,
@@ -55,6 +57,17 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
}
}
+ /** {@inheritDoc} */
+ override fun launchViaIntent(
+ wmHelper: WindowManagerStateHelper,
+ expectedWindowName: String,
+ action: String?,
+ stringExtras: Map<String, String>
+ ) {
+ super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
+ wmHelper.waitPipWindowShown()
+ }
+
private fun focusOnObject(selector: BySelector): Boolean {
// We expect all the focusable UI elements to be arranged in a way so that it is possible
// to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top"
@@ -69,16 +82,12 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
return false
}
- fun clickEnterPipButton() {
+ @JvmOverloads
+ fun clickEnterPipButton(wmHelper: WindowManagerStateHelper? = null) {
clickObject(ENTER_PIP_BUTTON_ID)
- // TODO(b/172321238): remove this check once hasPipWindow is fixed on TVs
- if (!isTelevision) {
- uiDevice.hasPipWindow()
- } else {
- // Simply wait for 3 seconds
- SystemClock.sleep(3_000)
- }
+ // Wait on WMHelper or simply wait for 3 seconds
+ wmHelper?.waitPipWindowShown() ?: SystemClock.sleep(3_000)
}
fun clickStartMediaSessionButton() {
@@ -97,16 +106,75 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
fun stopMedia() = mediaController?.transportControls?.stop()
?: error("No active media session found")
+ @Deprecated("Use PipAppHelper.closePipWindow(wmHelper) instead",
+ ReplaceWith("closePipWindow(wmHelper)"))
fun closePipWindow() {
if (isTelevision) {
uiDevice.closeTvPipWindow()
} else {
- uiDevice.closePipWindow()
+ closePipWindow(WindowManagerStateHelper(mInstrumentation))
+ }
+ }
+
+ /**
+ * Expands the pip window and dismisses it by clicking on the X button.
+ *
+ * Note, currently the View coordinates reported by the accessibility are relative to
+ * the window, so the correct coordinates need to be calculated
+ *
+ * For example, in a PIP window located at Rect(508, 1444 - 1036, 1741), the
+ * dismiss button coordinates are shown as Rect(650, 0 - 782, 132), with center in
+ * Point(716, 66), instead of Point(970, 1403)
+ *
+ * See b/179337864
+ */
+ fun closePipWindow(wmHelper: WindowManagerStateHelper) {
+ if (isTelevision) {
+ uiDevice.closeTvPipWindow()
+ } else {
+ 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)
+ }
+
+ // Wait for animation to complete.
+ wmHelper.waitPipWindowGone()
+ wmHelper.waitForHomeActivityVisible()
+ }
+
+ /**
+ * Click once on the PIP window to expand it
+ */
+ fun expandPipWindow(wmHelper: WindowManagerStateHelper) {
+ val windowRegion = wmHelper.getWindowRegion(component)
+ require(!windowRegion.isEmpty) {
+ "Unable to find a PIP window in the current state"
}
+ val windowRect = windowRegion.bounds
+ uiDevice.click(windowRect.centerX(), windowRect.centerY())
+ // Ensure WindowManagerService wait until all animations have completed
+ wmHelper.waitForAppTransitionIdle()
+ mInstrumentation.uiAutomation.syncInputTransactions()
+ }
- if (!waitUntilClosed()) {
- fail("Couldn't close Pip")
+ /**
+ * Double click on the PIP window to reopen to app
+ */
+ fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) {
+ val windowRegion = wmHelper.getWindowRegion(component)
+ require(!windowRegion.isEmpty) {
+ "Unable to find a PIP window in the current state"
}
+ val windowRect = windowRegion.bounds
+ uiDevice.click(windowRect.centerX(), windowRect.centerY())
+ uiDevice.click(windowRect.centerX(), windowRect.centerY())
+ wmHelper.waitPipWindowGone()
+ wmHelper.waitForAppTransitionIdle()
}
companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt
index 2015f4941cea..bc42d5ed04ce 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt
@@ -16,11 +16,6 @@
package com.android.wm.shell.flicker.pip
-import android.app.ActivityTaskManager
-import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT
-import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS
-import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
-import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
import android.os.SystemClock
import com.android.wm.shell.flicker.NonRotationTestBase
@@ -29,14 +24,6 @@ abstract class AppTestBase(
rotation: Int
) : NonRotationTestBase(rotationName, rotation) {
companion object {
- fun removeAllTasksButHome() {
- val ALL_ACTIVITY_TYPE_BUT_HOME = intArrayOf(
- ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS,
- ACTIVITY_TYPE_UNDEFINED)
- val atm = ActivityTaskManager.getService()
- atm.removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME)
- }
-
fun waitForAnimationComplete() {
// TODO: UiDevice doesn't have reliable way to wait for the completion of animation.
// Consider to introduce WindowManagerStateHelper to access Activity state.
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 5a3d18d9feef..d56ed02972fb 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
@@ -16,21 +16,19 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.runFlicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.wm.shell.flicker.helpers.PipAppHelper
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 com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -39,82 +37,66 @@ import org.junit.runners.Parameterized
* Test Pip launch and exit.
* To run this test: `atest WMShellFlickerTests:EnterExitPipTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class EnterExitPipTest(
- rotationName: String,
- rotation: Int
-) : AppTestBase(rotationName, rotation) {
- private val pipApp = PipAppHelper(instrumentation)
- private val testApp = FixedAppHelper(instrumentation)
-
- @Test
- fun testDisplayMetricsPinUnpin() {
- runFlicker(instrumentation) {
- withTestName { "testDisplayMetricsPinUnpin" }
- setup {
- test {
- removeAllTasksButHome()
- device.wakeUpAndGoToHomeScreen()
- pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true"))
- testApp.launchViaIntent()
- waitForAnimationComplete()
- }
- }
- transitions {
- // This will bring PipApp to fullscreen
- pipApp.launchViaIntent()
- waitForAnimationComplete()
- }
- teardown {
- test {
- removeAllTasksButHome()
- }
- }
- assertions {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- windowManagerTrace {
- all("pipApp must remain inside visible bounds") {
- coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
- }
- all("Initially shows both app windows then pipApp hides testApp") {
- showsAppWindow(testApp.defaultWindowName)
- .showsAppWindowOnTop(pipApp.defaultWindowName)
- .then()
- .hidesAppWindow(testApp.defaultWindowName)
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<Array<Any>> {
+ val testApp = FixedAppHelper(instrumentation)
+ val testSpec = getTransition(eachRun = true) { configuration ->
+ setup {
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
}
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
}
- layersTrace {
- all("Initially shows both app layers then pipApp hides testApp") {
- showsLayer(testApp.defaultWindowName)
- .showsLayer(pipApp.defaultWindowName)
- .then()
- .hidesLayer(testApp.defaultWindowName)
- }
- start("testApp covers the fullscreen, pipApp remains inside display") {
- hasVisibleRegion(testApp.defaultWindowName, displayBounds)
- coversAtMostRegion(displayBounds, pipApp.defaultWindowName)
- }
- end("pipApp covers the fullscreen") {
- hasVisibleRegion(pipApp.defaultWindowName, displayBounds)
+ transitions {
+ // This will bring PipApp to fullscreen
+ pipApp.launchViaIntent(wmHelper)
+ }
+ assertions {
+ val displayBounds = WindowUtils.getDisplayBounds(configuration.startRotation)
+ presubmit {
+ windowManagerTrace {
+ all("pipApp must remain inside visible bounds") {
+ coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
+ }
+ all("Initially shows both app windows then pipApp hides testApp") {
+ showsAppWindow(testApp.defaultWindowName)
+ .showsAppWindowOnTop(pipApp.defaultWindowName)
+ .then()
+ .hidesAppWindow(testApp.defaultWindowName)
+ }
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ }
+ layersTrace {
+ all("Initially shows both app layers then pipApp hides testApp") {
+ showsLayer(testApp.defaultWindowName)
+ .showsLayer(pipApp.defaultWindowName)
+ .then()
+ .hidesLayer(testApp.defaultWindowName)
+ }
+ start("testApp covers the fullscreen, pipApp remains inside display") {
+ hasVisibleRegion(testApp.defaultWindowName, displayBounds)
+ coversAtMostRegion(displayBounds, pipApp.defaultWindowName)
+ }
+ end("pipApp covers the fullscreen") {
+ hasVisibleRegion(pipApp.defaultWindowName, displayBounds)
+ }
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ }
}
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
}
}
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ testSpec, supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5)
}
}
} \ No newline at end of file
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 af62eb9ae40d..ff31ba7d2c01 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
@@ -17,17 +17,10 @@
package com.android.wm.shell.flicker.pip
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.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.expandPipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
-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.statusBarLayerIsAlwaysVisible
@@ -35,9 +28,7 @@ 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.repetitions
import com.android.server.wm.flicker.startRotation
-import com.android.wm.shell.flicker.helpers.PipAppHelper
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -50,80 +41,57 @@ import org.junit.runners.Parameterized
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 152738416)
class EnterPipTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
- companion object {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = PipAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
- withTestName { buildTestTag("enterPip", testApp, configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- device.pressHome()
- testApp.launchViaIntent(wmHelper)
- this.setRotation(configuration.startRotation)
- }
- }
- teardown {
- eachRun {
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- }
- }
- transitions {
- testApp.clickEnterPipButton()
- device.expandPipWindow()
- }
- assertions {
+ val testSpec = getTransition(eachRun = true,
+ stringExtras = emptyMap()) { configuration ->
+ transitions {
+ pipApp.clickEnterPipButton()
+ pipApp.expandPipWindow(wmHelper)
+ }
+ assertions {
+ presubmit {
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
all("pipWindowBecomesVisible") {
- this.showsAppWindow(testApp.`package`)
- .then()
- .showsAppWindow(PIP_WINDOW_TITLE)
+ this.showsAppWindow(pipApp.defaultWindowName)
}
}
layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
- enabled = false)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0, bugId = 140855415)
statusBarLayerRotatesScales(configuration.startRotation,
Surface.ROTATION_0)
}
layersTrace {
all("pipLayerBecomesVisible") {
- this.showsLayer(testApp.launcherName)
- .then()
- .showsLayer(PIP_WINDOW_TITLE)
+ this.showsLayer(pipApp.launcherName)
}
}
}
+
+ flaky {
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0, bugId = 140855415)
+ }
+ }
}
+ }
+
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ testSpec, supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5)
}
}
} \ No newline at end of file
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
new file mode 100644
index 000000000000..eaaa2f6390be
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.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.wm.shell.flicker.pip
+
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.statusBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
+import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
+import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
+import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip with orientation changes.
+ * To run this test: `atest WMShellFlickerTests:PipOrientationTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class EnterPipToOtherOrientationTest(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
+ private val testApp = FixedAppHelper(instrumentation)
+
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5) { configuration ->
+ setupAndTeardown(this, configuration)
+
+ setup {
+ eachRun {
+ // Launch a portrait only app on the fullscreen stack
+ testApp.launchViaIntent(wmHelper, stringExtras = mapOf(
+ EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString()))
+ // Launch the PiP activity fixed as landscape
+ pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
+ EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()))
+ }
+ }
+ teardown {
+ eachRun {
+ pipApp.exit()
+ testApp.exit()
+ }
+ }
+ transitions {
+ // Enter PiP, and assert that the PiP is within bounds now that the device is back
+ // in portrait
+ broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
+ wmHelper.waitPipWindowShown()
+ wmHelper.waitForAppTransitionIdle()
+ }
+ assertions {
+ val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
+ val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
+
+ presubmit {
+ windowManagerTrace {
+ all("pipApp window is always on top") {
+ showsAppWindowOnTop(pipApp.defaultWindowName)
+ }
+ start("pipApp window hides testApp") {
+ isInvisible(testApp.defaultWindowName)
+ }
+ end("testApp windows is shown") {
+ isVisible(testApp.defaultWindowName)
+ }
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ }
+
+ layersTrace {
+ start("pipApp layer hides testApp") {
+ hasVisibleRegion(pipApp.defaultWindowName, startingBounds)
+ isInvisible(testApp.defaultWindowName)
+ }
+ }
+ }
+
+ flaky {
+ layersTrace {
+ end("testApp layer covers fullscreen") {
+ hasVisibleRegion(testApp.defaultWindowName, endingBounds)
+ }
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible(bugId = 140855415)
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt
new file mode 100644
index 000000000000..707d28d9c4c0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip
+
+import android.app.WindowConfiguration
+import android.content.ComponentName
+import com.android.server.wm.flicker.traces.windowmanager.WindowManagerStateSubject
+import com.android.server.wm.traces.common.windowmanager.WindowManagerState
+import com.android.server.wm.traces.parser.toWindowName
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.google.common.truth.Truth
+
+inline val WindowManagerState.pinnedWindows
+ get() = visibleWindows
+ .filter { it.windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED }
+
+/**
+ * Checks if the state has any window in PIP mode
+ */
+fun WindowManagerState.hasPipWindow(): Boolean = pinnedWindows.isNotEmpty()
+
+/**
+ * Checks that an activity [activity] is in PIP mode
+ */
+fun WindowManagerState.isInPipMode(activity: ComponentName): Boolean {
+ val windowName = activity.toWindowName()
+ return pinnedWindows.any { it.title == windowName }
+}
+
+/**
+ * Asserts that an activity [activity] exists and is in PIP mode
+ */
+fun WindowManagerStateSubject.isInPipMode(
+ activity: ComponentName
+): WindowManagerStateSubject = apply {
+ val windowName = activity.toWindowName()
+ hasWindow(windowName)
+ val pinnedWindows = wmState.pinnedWindows
+ .map { it.title }
+ Truth.assertWithMessage("Window not in PIP mode")
+ .that(pinnedWindows)
+ .contains(windowName)
+}
+
+/**
+ * Waits until the state has a window in PIP mode, i.e., with
+ * windowingMode = WindowConfiguration.WINDOWING_MODE_PINNED
+ */
+fun WindowManagerStateHelper.waitPipWindowShown(): Boolean =
+ waitFor("PIP window shown") {
+ val result = it.wmState.hasPipWindow()
+ result
+ }
+
+/**
+ * Waits until the state doesn't have a window in PIP mode, i.e., with
+ * windowingMode = WindowConfiguration.WINDOWING_MODE_PINNED
+ */
+fun WindowManagerStateHelper.waitPipWindowGone(): Boolean =
+ waitFor("PIP window gone") {
+ val result = !it.wmState.hasPipWindow()
+ result
+ }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index c21b594246b9..f054e6412080 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -16,21 +16,17 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.dsl.runWithFlicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.startRotation
import com.android.wm.shell.flicker.IME_WINDOW_NAME
import com.android.wm.shell.flicker.helpers.ImeAppHelper
-import com.android.wm.shell.flicker.testapp.Components
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -39,114 +35,60 @@ import org.junit.runners.Parameterized
* Test Pip launch.
* To run this test: `atest WMShellFlickerTests:PipKeyboardTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipKeyboardTest(
- rotationName: String,
- rotation: Int
-) : PipTestBase(rotationName, rotation) {
- private val keyboardApp = ImeAppHelper(instrumentation)
- private val keyboardComponent = Components.ImeActivity.COMPONENT
- private val helper = WindowManagerStateHelper()
+class PipKeyboardTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTestRunner(testSpec) {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
+ private const val TAG_IME_VISIBLE = "imeIsVisible"
- private val keyboardScenario: FlickerBuilder
- get() = FlickerBuilder(instrumentation).apply {
- repeat { TEST_REPETITIONS }
- // disable layer tracing
- withLayerTracing { null }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- device.pressHome()
- // launch our target pip app
- testApp.launchViaIntent(wmHelper)
- this.setRotation(rotation)
- testApp.clickEnterPipButton()
- // open an app with an input field and a keyboard
- // UiAutomator doesn't support to launch the multiple Activities in a task.
- // So use launchActivity() for the Keyboard Activity.
- keyboardApp.launchViaIntent()
- helper.waitForAppTransitionIdle()
- helper.waitForFullScreenApp(keyboardComponent)
- }
- }
- teardown {
- test {
- keyboardApp.exit()
-
- if (device.hasPipWindow()) {
- device.closePipWindow()
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val imeApp = ImeAppHelper(instrumentation)
+ val testSpec = getTransition(eachRun = false) { configuration ->
+ setup {
+ test {
+ imeApp.launchViaIntent(wmHelper)
+ setRotation(configuration.startRotation)
}
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
}
- }
- }
-
- /** Ensure the pip window remains visible throughout any keyboard interactions. */
- @Test
- fun pipWindow_doesNotLeaveTheScreen_onKeyboardOpenClose() {
- val testTag = "pipWindow_doesNotLeaveTheScreen_onKeyboardOpenClose"
- runWithFlicker(keyboardScenario) {
- withTestName { testTag }
- transitions {
- // open the soft keyboard
- keyboardApp.openIME(device, wmHelper)
- helper.waitImeWindowShown()
-
- // then close it again
- keyboardApp.closeIME(device, wmHelper)
- helper.waitImeWindowGone()
- }
- assertions {
- windowManagerTrace {
- all("PiP window must remain inside visible bounds") {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- coversAtMostRegion(testApp.defaultWindowName, displayBounds)
+ teardown {
+ test {
+ imeApp.exit()
+ setRotation(Surface.ROTATION_0)
}
}
- }
- }
- }
+ transitions {
+ // open the soft keyboard
+ imeApp.openIME(wmHelper)
+ createTag(TAG_IME_VISIBLE)
- /** Ensure the pip window does not obscure the keyboard. */
- @Test
- fun pipWindow_doesNotObscure_keyboard() {
- val testTag = "pipWindow_doesNotObscure_keyboard"
- runWithFlicker(keyboardScenario) {
- withTestName { testTag }
- transitions {
- // open the soft keyboard
- keyboardApp.openIME(device, wmHelper)
- helper.waitImeWindowShown()
- }
- teardown {
- eachRun {
- // close the keyboard
- keyboardApp.closeIME(device, wmHelper)
- helper.waitImeWindowGone()
+ // then close it again
+ imeApp.closeIME(wmHelper)
}
- }
- assertions {
- windowManagerTrace {
- end("imeWindowAboveApp") {
- isAboveWindow(IME_WINDOW_NAME, testApp.defaultWindowName)
+ assertions {
+ presubmit {
+ windowManagerTrace {
+ // Ensure the pip window remains visible throughout
+ // any keyboard interactions
+ all("pipInVisibleBounds") {
+ val displayBounds = WindowUtils.getDisplayBounds(
+ configuration.startRotation)
+ coversAtMostRegion(pipApp.defaultWindowName, displayBounds)
+ }
+ // Ensure that the pip window does not obscure the keyboard
+ tag(TAG_IME_VISIBLE) {
+ isAboveWindow(IME_WINDOW_NAME, pipApp.defaultWindowName)
+ }
+ }
}
}
}
- }
- }
-
- companion object {
- private const val TEST_REPETITIONS = 5
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ testSpec, supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5)
}
}
}
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 e5790962c025..f10bd7f1e45a 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
@@ -32,6 +32,7 @@ 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.wm.shell.flicker.removeAllTasksButHome
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
import org.junit.FixMethodOrder
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt
deleted file mode 100644
index 5e0760ceeda7..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt
+++ /dev/null
@@ -1,205 +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.wm.shell.flicker.pip
-
-import android.content.Intent
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.runFlicker
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.wm.shell.flicker.helpers.PipAppHelper
-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.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_PIP_ORIENTATION
-import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
-import org.junit.Assert.assertEquals
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test Pip with orientation changes.
- * To run this test: `atest WMShellFlickerTests:PipOrientationTest`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipOrientationTest(
- rotationName: String,
- rotation: Int
-) : AppTestBase(rotationName, rotation) {
- // Helper class to process test actions by broadcast.
- private inner class BroadcastActionTrigger {
- private fun createIntentWithAction(broadcastAction: String): Intent {
- return Intent(broadcastAction).setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- }
- fun doAction(broadcastAction: String) {
- instrumentation.getContext().sendBroadcast(createIntentWithAction(broadcastAction))
- }
- fun requestOrientationForPip(orientation: Int) {
- instrumentation.getContext()
- .sendBroadcast(createIntentWithAction(ACTION_SET_REQUESTED_ORIENTATION)
- .putExtra(EXTRA_PIP_ORIENTATION, orientation.toString()))
- }
- }
- private val broadcastActionTrigger = BroadcastActionTrigger()
-
- // Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
- private val ORIENTATION_LANDSCAPE = 0
- // Corresponds to ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
- private val ORIENTATION_PORTRAIT = 1
-
- private val testApp = FixedAppHelper(instrumentation)
- private val pipApp = PipAppHelper(instrumentation)
-
- @Test
- fun testEnterPipToOtherOrientation() {
- runFlicker(instrumentation) {
- withTestName { "testEnterPipToOtherOrientation" }
- setup {
- test {
- removeAllTasksButHome()
- device.wakeUpAndGoToHomeScreen()
- // Launch a portrait only app on the fullscreen stack
- testApp.launchViaIntent(stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString()))
- waitForAnimationComplete()
- // Launch the PiP activity fixed as landscape
- pipApp.launchViaIntent(stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()))
- waitForAnimationComplete()
- }
- }
- transitions {
- // Enter PiP, and assert that the PiP is within bounds now that the device is back
- // in portrait
- broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
- waitForAnimationComplete()
- }
- teardown {
- test {
- removeAllTasksButHome()
- }
- }
- assertions {
- windowManagerTrace {
- all("pipApp window is always on top") {
- showsAppWindowOnTop(pipApp.defaultWindowName)
- }
- start("pipApp window hides testApp") {
- isInvisible(testApp.defaultWindowName)
- }
- end("testApp windows is shown") {
- isVisible(testApp.defaultWindowName)
- }
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
- layersTrace {
- val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
- val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
- start("pipApp layer hides testApp") {
- hasVisibleRegion(pipApp.defaultWindowName, startingBounds)
- isInvisible(testApp.defaultWindowName)
- }
- end("testApp layer covers fullscreen", enabled = false) {
- hasVisibleRegion(testApp.defaultWindowName, endingBounds)
- }
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- }
- }
- }
- }
-
- @Test
- fun testSetRequestedOrientationWhilePinned() {
- runFlicker(instrumentation) {
- withTestName { "testSetRequestedOrientationWhilePinned" }
- setup {
- test {
- removeAllTasksButHome()
- device.wakeUpAndGoToHomeScreen()
- // Launch the PiP activity fixed as landscape
- pipApp.launchViaIntent(stringExtras = mapOf(
- EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(),
- EXTRA_ENTER_PIP to "true"))
- waitForAnimationComplete()
- assertEquals(Surface.ROTATION_0, device.displayRotation)
- }
- }
- transitions {
- // Request that the orientation is set to landscape
- broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE)
-
- // Launch the activity back into fullscreen and ensure that it is now in landscape
- pipApp.launchViaIntent()
- waitForAnimationComplete()
- assertEquals(Surface.ROTATION_90, device.displayRotation)
- }
- teardown {
- test {
- removeAllTasksButHome()
- }
- }
- assertions {
- val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
- val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
- windowManagerTrace {
- start("PIP window must remain inside display") {
- coversAtMostRegion(pipApp.defaultWindowName, startingBounds)
- }
- end("pipApp shows on top") {
- showsAppWindowOnTop(pipApp.defaultWindowName)
- }
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
- layersTrace {
- start("PIP layer must remain inside display") {
- coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
- }
- end("pipApp layer covers fullscreen") {
- hasVisibleRegion(pipApp.defaultWindowName, endingBounds)
- }
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- }
- }
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
- }
- }
-} \ No newline at end of file
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 a00c5f463a50..ade65ac8aa63 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
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -24,13 +23,9 @@ import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.buildTestTag
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.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.wm.shell.flicker.helpers.PipAppHelper
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
@@ -38,7 +33,6 @@ 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
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -48,80 +42,75 @@ import org.junit.runners.Parameterized
* Test Pip Stack in bounds after rotations.
* To run this test: `atest WMShellFlickerTests:PipRotationTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class PipRotationTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
- companion object {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = FixedAppHelper(instrumentation)
- val pipApp = PipAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance().buildRotationTest(instrumentation,
- supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)) {
- configuration ->
- withTestName { buildTestTag("PipRotationTest", testApp, configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- AppTestBase.removeAllTasksButHome()
- device.wakeUpAndGoToHomeScreen()
- pipApp.launchViaIntent(stringExtras = mapOf(
- EXTRA_ENTER_PIP to "true"))
- testApp.launchViaIntent()
- AppTestBase.waitForAnimationComplete()
- }
- eachRun {
- setRotation(configuration.startRotation)
- }
- }
- transitions {
- setRotation(configuration.endRotation)
+ val fixedApp = FixedAppHelper(instrumentation)
+ val testSpec = getTransition(eachRun = false) { configuration ->
+ setup {
+ test {
+ fixedApp.launchViaIntent(wmHelper)
+ }
+ eachRun {
+ setRotation(configuration.startRotation)
+ }
+ }
+ transitions {
+ setRotation(configuration.endRotation)
+ }
+ teardown {
+ eachRun {
+ setRotation(Surface.ROTATION_0)
+ }
+ }
+ assertions {
+ val startingBounds = WindowUtils.getDisplayBounds(configuration.startRotation)
+ val endingBounds = WindowUtils.getDisplayBounds(configuration.endRotation)
+
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
}
- teardown {
- eachRun {
- setRotation(Surface.ROTATION_0)
- }
- test {
- AppTestBase.removeAllTasksButHome()
- }
+
+ layersTrace {
+ noUncoveredRegions(configuration.startRotation,
+ configuration.endRotation, allStates = false)
}
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- noUncoveredRegions(configuration.startRotation,
- configuration.endRotation, allStates = false)
- navBarLayerRotatesAndScales(configuration.startRotation,
- configuration.endRotation, bugId = 140855415)
- statusBarLayerRotatesScales(configuration.startRotation,
- configuration.endRotation, bugId = 140855415)
+ }
+
+ flaky {
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible(bugId = 140855415)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ configuration.endRotation, bugId = 140855415)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ configuration.endRotation, bugId = 140855415)
+
+ start("appLayerRotates_StartingBounds", bugId = 140855415) {
+ hasVisibleRegion(fixedApp.defaultWindowName, startingBounds)
+ coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
}
- layersTrace {
- val startingBounds = WindowUtils.getDisplayBounds(
- configuration.startRotation)
- val endingBounds = WindowUtils.getDisplayBounds(
- configuration.endRotation)
- start("appLayerRotates_StartingBounds", bugId = 140855415) {
- hasVisibleRegion(testApp.defaultWindowName, startingBounds)
- coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
- }
- end("appLayerRotates_EndingBounds", bugId = 140855415) {
- hasVisibleRegion(testApp.defaultWindowName, endingBounds)
- coversAtMostRegion(endingBounds, pipApp.defaultWindowName)
- }
+ end("appLayerRotates_EndingBounds", bugId = 140855415) {
+ hasVisibleRegion(fixedApp.defaultWindowName, endingBounds)
+ coversAtMostRegion(endingBounds, pipApp.defaultWindowName)
}
}
}
+ }
+ }
+
+ return FlickerTestRunnerFactory.getInstance().buildRotationTest(instrumentation,
+ testSpec, supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90),
+ repetitions = 5)
}
}
} \ No newline at end of file
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 3e7eb134e627..f2d58997d1f2 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
@@ -17,28 +17,20 @@
package com.android.wm.shell.flicker.pip
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.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.focusChanges
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.expandPipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
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 org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -51,48 +43,29 @@ import org.junit.runners.Parameterized
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 152738416)
class PipToAppTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
- companion object {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = PipAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
- withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- device.pressHome()
- testApp.launchViaIntent(wmHelper)
- }
- eachRun {
- this.setRotation(configuration.startRotation)
- testApp.clickEnterPipButton()
- device.hasPipWindow()
- }
+ val testSpec = getTransition(eachRun = true) { configuration ->
+ setup {
+ eachRun {
+ this.setRotation(configuration.startRotation)
}
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- testApp.exit()
- }
- }
- transitions {
- device.expandPipWindow()
- device.waitForIdle()
+ }
+ teardown {
+ eachRun {
+ this.setRotation(Surface.ROTATION_0)
}
- assertions {
+ }
+ transitions {
+ pipApp.expandPipWindowToApp(wmHelper)
+ }
+ assertions {
+ presubmit {
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
@@ -100,34 +73,42 @@ class PipToAppTest(
all("appReplacesPipWindow") {
this.showsAppWindow(PIP_WINDOW_TITLE)
.then()
- .showsAppWindowOnTop(testApp.launcherName)
+ .showsAppWindowOnTop(pipApp.launcherName)
}
}
layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
- enabled = false)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0, bugId = 140855415)
statusBarLayerRotatesScales(configuration.startRotation,
Surface.ROTATION_0)
all("appReplacesPipLayer") {
this.showsLayer(PIP_WINDOW_TITLE)
.then()
- .showsLayer(testApp.launcherName)
+ .showsLayer(pipApp.launcherName)
}
}
+ }
+
+ flaky {
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0, bugId = 140855415)
+ }
eventLog {
focusChanges(
- "NexusLauncherActivity", testApp.launcherName,
+ "NexusLauncherActivity", pipApp.launcherName,
"NexusLauncherActivity", bugId = 151179149)
}
}
}
+ }
+
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
index 5d3bc1388686..1b44377425db 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
@@ -17,27 +17,20 @@
package com.android.wm.shell.flicker.pip
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.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.focusChanges
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.closePipWindow
-import com.android.server.wm.flicker.helpers.hasPipWindow
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 org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -50,50 +43,29 @@ import org.junit.runners.Parameterized
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 152738416)
class PipToHomeTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
- companion object {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = PipAppHelper(instrumentation)
- return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
- supportedRotations = listOf(Surface.ROTATION_0)) { configuration ->
- withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- device.pressHome()
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- this.setRotation(configuration.startRotation)
- testApp.clickEnterPipButton()
- device.hasPipWindow()
- }
+ val testSpec = getTransition(eachRun = true) { configuration ->
+ setup {
+ eachRun {
+ this.setRotation(configuration.startRotation)
}
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- }
- test {
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- testApp.exit()
- }
- }
- transitions {
- testApp.closePipWindow()
+ }
+ teardown {
+ eachRun {
+ this.setRotation(Surface.ROTATION_0)
}
- assertions {
+ }
+ transitions {
+ pipApp.closePipWindow(wmHelper)
+ }
+ assertions {
+ presubmit {
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
@@ -106,12 +78,7 @@ class PipToHomeTest(
}
layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
- enabled = false)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0, bugId = 140855415)
statusBarLayerRotatesScales(configuration.startRotation,
Surface.ROTATION_0)
@@ -121,13 +88,28 @@ class PipToHomeTest(
.hidesLayer(PIP_WINDOW_TITLE)
}
}
+ }
+
+ postsubmit {
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ }
+ }
+ flaky {
eventLog {
- focusChanges(testApp.launcherName, "NexusLauncherActivity",
+ focusChanges(pipApp.launcherName, "NexusLauncherActivity",
bugId = 151179149)
}
}
}
+ }
+
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
}
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt
new file mode 100644
index 000000000000..b1e404e4c8e6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt
@@ -0,0 +1,138 @@
+/*
+ * 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.wm.shell.flicker.pip
+
+import android.app.Instrumentation
+import android.content.Intent
+import android.os.Bundle
+import android.view.Surface
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.buildTestTag
+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.wm.shell.flicker.helpers.PipAppHelper
+import com.android.wm.shell.flicker.removeAllTasksButHome
+import com.android.wm.shell.flicker.testapp.Components
+
+abstract class PipTransitionBase(protected val instrumentation: Instrumentation) {
+ // Helper class to process test actions by broadcast.
+ protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) {
+ private fun createIntentWithAction(broadcastAction: String): Intent {
+ return Intent(broadcastAction).setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ }
+
+ fun doAction(broadcastAction: String) {
+ instrumentation.context
+ .sendBroadcast(createIntentWithAction(broadcastAction))
+ }
+
+ fun requestOrientationForPip(orientation: Int) {
+ instrumentation.context.sendBroadcast(
+ createIntentWithAction(Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION)
+ .putExtra(Components.PipActivity.EXTRA_PIP_ORIENTATION, orientation.toString())
+ )
+ }
+
+ companion object {
+ // Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+ @JvmStatic
+ val ORIENTATION_LANDSCAPE = 0
+
+ // Corresponds to ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+ @JvmStatic
+ val ORIENTATION_PORTRAIT = 1
+ }
+ }
+
+ protected val pipApp = PipAppHelper(instrumentation)
+ protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
+
+ /**
+ * Gets a configuration that handles basic setup and teardown of pip tests
+ */
+ protected val setupAndTeardown: FlickerBuilder.(Bundle) -> Unit
+ get() = { configuration ->
+ withTestName { buildTestTag(configuration) }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ removeAllTasksButHome()
+ device.wakeUpAndGoToHomeScreen()
+ }
+ }
+ teardown {
+ eachRun {
+ setRotation(Surface.ROTATION_0)
+ }
+ test {
+ removeAllTasksButHome()
+ pipApp.exit()
+ }
+ }
+ }
+
+ /**
+ * Gets a configuration that handles basic setup and teardown of pip tests and that
+ * launches the Pip app for test
+ *
+ * @param eachRun If the pip app should be launched in each run (otherwise only 1x per test)
+ * @param stringExtras Arguments to pass to the PIP launch intent
+ * @param extraSpec Addicional segment of flicker specification
+ */
+ @JvmOverloads
+ open fun getTransition(
+ eachRun: Boolean,
+ stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true"),
+ extraSpec: FlickerBuilder.(Bundle) -> Unit = {}
+ ): FlickerBuilder.(Bundle) -> Unit {
+ return { configuration ->
+ setupAndTeardown(this, configuration)
+
+ setup {
+ test {
+ removeAllTasksButHome()
+ if (!eachRun) {
+ pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
+ wmHelper.waitPipWindowShown()
+ }
+ }
+ eachRun {
+ if (eachRun) {
+ pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
+ wmHelper.waitPipWindowShown()
+ }
+ }
+ }
+ teardown {
+ eachRun {
+ if (eachRun) {
+ pipApp.exit()
+ }
+ }
+ test {
+ if (!eachRun) {
+ pipApp.exit()
+ }
+ removeAllTasksButHome()
+ }
+ }
+
+ extraSpec(this, configuration)
+ }
+ }
+} \ 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
new file mode 100644
index 000000000000..c01bc94151e9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -0,0 +1,115 @@
+/*
+ * 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.pip
+
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+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.PipTransitionBase.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
+import org.junit.Assert.assertEquals
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip with orientation changes.
+ * To run this test: `atest WMShellFlickerTests:PipOrientationTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SetRequestedOrientationWhilePinnedTest(
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation,
+ supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 1) { configuration ->
+ setupAndTeardown(this, configuration)
+
+ setup {
+ eachRun {
+ // Launch the PiP activity fixed as landscape
+ pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
+ EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(),
+ EXTRA_ENTER_PIP to "true"))
+ }
+ }
+ teardown {
+ eachRun {
+ pipApp.exit()
+ }
+ }
+ transitions {
+ // Request that the orientation is set to landscape
+ broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE)
+
+ // Launch the activity back into fullscreen and
+ // ensure that it is now in landscape
+ pipApp.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(pipApp.component)
+ wmHelper.waitForRotation(Surface.ROTATION_90)
+ assertEquals(Surface.ROTATION_90, device.displayRotation)
+ }
+ assertions {
+ val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
+ val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
+ presubmit {
+ windowManagerTrace {
+ start("PIP window must remain inside display") {
+ coversAtMostRegion(pipApp.defaultWindowName, startingBounds)
+ }
+ end("pipApp shows on top") {
+ showsAppWindowOnTop(pipApp.defaultWindowName)
+ }
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ }
+ layersTrace {
+ start("PIP layer must remain inside display") {
+ coversAtMostRegion(startingBounds, pipApp.defaultWindowName)
+ }
+ end("pipApp layer covers fullscreen") {
+ hasVisibleRegion(pipApp.defaultWindowName, endingBounds)
+ }
+ }
+ }
+
+ flaky {
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible(bugId = 140855415)
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index c0ab20d9249f..fb53e5355c4a 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "WMShellUnitTests",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 80ea9b9e177e..a0e9f43218f2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -31,6 +31,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -51,7 +52,8 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
+import com.android.wm.shell.sizecompatui.SizeCompatUIController;
+import com.android.wm.shell.startingsurface.StartingSurfaceDrawer;
import org.junit.Before;
import org.junit.Test;
@@ -76,7 +78,9 @@ public class ShellTaskOrganizerTests {
@Mock
private Context mContext;
@Mock
- private SizeCompatUI mSizeCompatUI;
+ private SizeCompatUIController mSizeCompatUI;
+ @Mock
+ private StartingSurfaceDrawer mStartingSurfaceDrawer;
ShellTaskOrganizer mOrganizer;
private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class);
@@ -112,7 +116,7 @@ public class ShellTaskOrganizerTests {
.when(mTaskOrganizerController).registerTaskOrganizer(any());
} catch (RemoteException e) {}
mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext,
- mSizeCompatUI));
+ mSizeCompatUI, mStartingSurfaceDrawer));
}
@Test
@@ -279,10 +283,10 @@ public class ShellTaskOrganizerTests {
// sizeCompatActivity is null if top activity is not in size compat.
verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- taskInfo1.configuration.windowConfiguration.getBounds(),
- null /* sizeCompatActivity*/ , taskListener);
+ null /* taskConfig */, null /* sizeCompatActivity*/, null /* taskListener */);
// sizeCompatActivity is non-null if top activity is in size compat.
+ clearInvocations(mSizeCompatUI);
final RunningTaskInfo taskInfo2 =
createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
taskInfo2.displayId = taskInfo1.displayId;
@@ -290,14 +294,12 @@ public class ShellTaskOrganizerTests {
taskInfo2.topActivityInSizeCompat = true;
mOrganizer.onTaskInfoChanged(taskInfo2);
verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- taskInfo1.configuration.windowConfiguration.getBounds(),
- taskInfo1.topActivityToken,
- taskListener);
+ taskInfo1.configuration, taskInfo1.topActivityToken, taskListener);
+ clearInvocations(mSizeCompatUI);
mOrganizer.onTaskVanished(taskInfo1);
verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- null /* taskConfig */, null /* sizeCompatActivity*/,
- null /* taskListener */);
+ null /* taskConfig */, null /* sizeCompatActivity*/, null /* taskListener */);
}
private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 25721066b713..19ecc49513e5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -44,6 +44,7 @@ import static org.mockito.ArgumentMatchers.eq;
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.verify;
import android.app.ActivityManager;
@@ -65,7 +66,7 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import org.junit.Before;
import org.junit.Test;
@@ -92,7 +93,7 @@ public class DragAndDropPolicyTest {
// Both the split-screen and start interface.
@Mock
- private SplitScreen mSplitScreenStarter;
+ private SplitScreenController mSplitScreenStarter;
private DisplayLayout mLandscapeDisplayLayout;
private DisplayLayout mPortraitDisplayLayout;
@@ -127,8 +128,8 @@ public class DragAndDropPolicyTest {
mPortraitDisplayLayout = new DisplayLayout(info2, res, false, false);
mInsets = Insets.of(0, 0, 0, 0);
- mPolicy = new DragAndDropPolicy(
- mContext, mActivityTaskManager, mSplitScreenStarter, mSplitScreenStarter);
+ mPolicy = spy(new DragAndDropPolicy(
+ mContext, mActivityTaskManager, mSplitScreenStarter, mSplitScreenStarter));
mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
setClipDataResizeable(mNonResizeableActivityClipData, false);
@@ -204,8 +205,8 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
}
@Test
@@ -216,13 +217,13 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
@@ -233,13 +234,13 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
@@ -250,8 +251,8 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
}
@Test
@@ -262,8 +263,8 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
}
@Test
@@ -275,14 +276,14 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
@@ -294,14 +295,14 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
index 8d5139b182f0..a8feb04f31d4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
@@ -47,7 +47,6 @@ public class OneHandedAnimationControllerTest extends OneHandedTestCase {
private static final int TEST_BOUNDS_HEIGHT = 1000;
OneHandedAnimationController mOneHandedAnimationController;
- OneHandedTutorialHandler mTutorialHandler;
@Mock
private SurfaceControl mMockLeash;
@@ -60,8 +59,6 @@ public class OneHandedAnimationControllerTest extends OneHandedTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
-
- mTutorialHandler = new OneHandedTutorialHandler(mContext, mMainExecutor);
mOneHandedAnimationController = new OneHandedAnimationController(mContext);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
index e9c4af12a0d6..b0f52cf656f8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
@@ -59,7 +59,7 @@ public class OneHandedBackgroundPanelOrganizerTest extends OneHandedTestCase {
DisplayController mMockDisplayController;
@Before
- public void setUp() throws Exception {
+ public void setUp() {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
mToken = new WindowContainerToken(mMockRealToken);
@@ -82,15 +82,6 @@ public class OneHandedBackgroundPanelOrganizerTest extends OneHandedTestCase {
}
@Test
- public void testUnregisterOrganizer() {
- mBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
- mTestableLooper.processAllMessages();
- mBackgroundPanelOrganizer.unregisterOrganizer();
-
- assertThat(mBackgroundPanelOrganizer.getBackgroundSurface()).isNull();
- }
-
- @Test
public void testShowBackgroundLayer() {
mBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
mBackgroundPanelOrganizer.showBackgroundPanelLayer();
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 f141167178a1..1ad8fd3e7298 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,19 +16,24 @@
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.anyInt;
import static org.mockito.Mockito.atLeastOnce;
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.om.IOverlayManager;
import android.os.Handler;
-import android.provider.Settings;
import android.testing.AndroidTestingRunner;
+import android.util.ArrayMap;
import android.view.Display;
+import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
@@ -37,19 +42,17 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class OneHandedControllerTest extends OneHandedTestCase {
Display mDisplay;
- OneHandedController mOneHandedController;
- OneHandedTimeoutHandler mTimeoutHandler;
+ OneHandedController mSpiedOneHandedController;
+ OneHandedTimeoutHandler mSpiedTimeoutHandler;
@Mock
DisplayController mMockDisplayController;
@@ -64,8 +67,6 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Mock
OneHandedGestureHandler mMockGestureHandler;
@Mock
- OneHandedTimeoutHandler mMockTimeoutHandler;
- @Mock
OneHandedUiEventLogger mMockUiEventLogger;
@Mock
IOverlayManager mMockOverlayManager;
@@ -74,14 +75,30 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Mock
ShellExecutor mMockShellMainExecutor;
@Mock
+ SurfaceControl mMockLeash;
+ @Mock
Handler mMockShellMainHandler;
+ final boolean mDefaultEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
+ getTestContext().getContentResolver());
+ final boolean mDefaultSwipeToNotificationEnabled =
+ OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+ getTestContext().getContentResolver());
+ final boolean mDefaultTapAppToExitEnabled = OneHandedSettingsUtil.getSettingsTapsAppToExit(
+ getTestContext().getContentResolver());
+
@Before
- public void setUp() throws Exception {
+ public void setUp() {
MockitoAnnotations.initMocks(this);
mDisplay = mContext.getDisplay();
- mTimeoutHandler = Mockito.spy(new OneHandedTimeoutHandler(mMockShellMainExecutor));
- OneHandedController oneHandedController = new OneHandedController(
+ mSpiedTimeoutHandler = spy(new OneHandedTimeoutHandler(mMockShellMainExecutor));
+
+ when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
+ when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
+ when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
+ when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash);
+
+ mSpiedOneHandedController = spy(new OneHandedController(
mContext,
mMockDisplayController,
mMockBackgroundOrganizer,
@@ -89,16 +106,13 @@ public class OneHandedControllerTest extends OneHandedTestCase {
mMockTouchHandler,
mMockTutorialHandler,
mMockGestureHandler,
- mTimeoutHandler,
+ mSpiedTimeoutHandler,
mMockUiEventLogger,
mMockOverlayManager,
mMockTaskStackListener,
mMockShellMainExecutor,
- mMockShellMainHandler);
- mOneHandedController = Mockito.spy(oneHandedController);
-
- when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
- when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
+ mMockShellMainHandler)
+ );
}
@Test
@@ -113,21 +127,36 @@ public class OneHandedControllerTest extends OneHandedTestCase {
}
@Test
- public void testRegisterOrganizer() {
- verify(mMockDisplayAreaOrganizer, atLeastOnce()).registerOrganizer(anyInt());
+ public void testNoRegisterAndUnregisterInSameCall() {
+ if (mDefaultEnabled) {
+ verify(mMockDisplayAreaOrganizer, never()).unregisterOrganizer();
+ } else {
+ verify(mMockDisplayAreaOrganizer, never()).registerOrganizer(FEATURE_ONE_HANDED);
+ }
}
@Test
- public void testStartOneHanded() {
- mOneHandedController.startOneHanded();
+ public void testStartOneHandedShouldTriggerScheduleOffset() {
+ when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
+ mSpiedOneHandedController.setOneHandedEnabled(true);
+ mSpiedOneHandedController.startOneHanded();
verify(mMockDisplayAreaOrganizer).scheduleOffset(anyInt(), anyInt());
}
@Test
+ public void testStartOneHandedShouldNotTriggerScheduleOffset() {
+ mSpiedOneHandedController.setOneHandedEnabled(true);
+ when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(true);
+ mSpiedOneHandedController.startOneHanded();
+
+ verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt());
+ }
+
+ @Test
public void testStopOneHanded() {
when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
- mOneHandedController.stopOneHanded();
+ mSpiedOneHandedController.stopOneHanded();
verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt());
}
@@ -141,71 +170,121 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Test
public void testRegisterTransitionCallback() {
- OneHandedTransitionCallback callback = new OneHandedTransitionCallback() {};
- mOneHandedController.registerTransitionCallback(callback);
+ OneHandedTransitionCallback callback = new OneHandedTransitionCallback() {
+ };
+ mSpiedOneHandedController.registerTransitionCallback(callback);
verify(mMockDisplayAreaOrganizer).registerTransitionCallback(callback);
}
+ @Test
+ public void testStopOneHandedShouldRemoveTimer() {
+ when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(true);
+ mSpiedOneHandedController.stopOneHanded();
+
+ verify(mSpiedTimeoutHandler, atLeastOnce()).removeTimer();
+ }
@Test
- public void testStopOneHanded_shouldRemoveTimer() {
- mOneHandedController.stopOneHanded();
+ public void testUpdateEnabled() {
+ mSpiedOneHandedController.setOneHandedEnabled(true);
- verify(mTimeoutHandler).removeTimer();
+ verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(mDefaultEnabled);
+ verify(mMockGestureHandler, atLeastOnce()).onOneHandedEnabled(
+ mDefaultEnabled || mDefaultSwipeToNotificationEnabled);
}
@Test
- public void testUpdateIsEnabled() {
- final boolean enabled = true;
- mOneHandedController.setOneHandedEnabled(enabled);
+ public void testUpdateSwipeToNotification() {
+ mSpiedOneHandedController.setSwipeToNotificationEnabled(mDefaultSwipeToNotificationEnabled);
- verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(enabled);
+ verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(mDefaultEnabled);
+ verify(mMockGestureHandler, atLeastOnce()).onOneHandedEnabled(
+ mDefaultEnabled || mDefaultSwipeToNotificationEnabled);
}
@Test
- public void testUpdateSwipeToNotificationIsEnabled() {
- final boolean enabled = true;
- mOneHandedController.setSwipeToNotificationEnabled(enabled);
+ public void testSettingsObserverUpdateTapAppToExit() {
+ mSpiedOneHandedController.onTaskChangeExitSettingChanged();
+ if (mDefaultTapAppToExitEnabled) {
+ verify(mMockTaskStackListener, atLeastOnce()).addListener(any());
+ } else {
+ verify(mMockTaskStackListener, atLeastOnce()).removeListener(any());
+ }
+ }
- verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(enabled);
+ @Test
+ public void testSettingsObserverUpdateEnabled() {
+ mSpiedOneHandedController.onEnabledSettingChanged();
+
+ verify(mSpiedOneHandedController).setOneHandedEnabled(mDefaultEnabled);
}
- @Ignore("b/167943723, refactor it and fix it")
@Test
- public void tesSettingsObserver_updateTapAppToExit() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.TAPS_APP_TO_EXIT, 1);
+ public void testSettingsObserverUpdateTimeout() {
+ mSpiedOneHandedController.onTimeoutSettingChanged();
- verify(mOneHandedController).setTaskChangeToExit(true);
+ verify(mSpiedTimeoutHandler, atLeastOnce()).setTimeout(anyInt());
}
- @Ignore("b/167943723, refactor it and fix it")
@Test
- public void tesSettingsObserver_updateEnabled() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.ONE_HANDED_MODE_ENABLED, 1);
+ 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);
+ }
+ }
+
+ @Test
+ public void testLockedOneHandedDisabled() {
+ // Default mLockDisabled is false
+ assertThat(mSpiedOneHandedController.isLockedDisabled()).isFalse();
+
+ mSpiedOneHandedController.setOneHandedEnabled(true);
+ mSpiedOneHandedController.setLockedDisabled(false /* locked */, true /* enabled */);
+
+ // If mOneHandedEnabled == enabled, then keep unlocked
+ assertThat(mSpiedOneHandedController.isLockedDisabled()).isFalse();
- verify(mOneHandedController).setOneHandedEnabled(true);
+ // If prefer locked enabled state and 'mOneHandedEnabled == enabled', then unlocked
+ mSpiedOneHandedController.setLockedDisabled(true /* locked */, true /* enabled */);
+
+ assertThat(mSpiedOneHandedController.isLockedDisabled()).isFalse();
+
+ // If prefer locked disabled state and 'mOneHandedEnabled != enabled', then locked disabled
+ mSpiedOneHandedController.setLockedDisabled(true /* locked */, false /* enabled */);
+
+ assertThat(mSpiedOneHandedController.isLockedDisabled()).isTrue();
+
+ // If prefer unlock disabled state and 'mOneHandedEnabled != enabled', then unlocked
+ mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
+
+ assertThat(mSpiedOneHandedController.isLockedDisabled()).isFalse();
}
- @Ignore("b/167943723, refactor it and fix it")
@Test
- public void tesSettingsObserver_updateTimeout() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
- OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
+ public void testKeyguardShowingLockOneHandedDisabled() {
+ when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
+ mSpiedOneHandedController.setOneHandedEnabled(true);
+ mSpiedOneHandedController.setLockedDisabled(true /* locked */, false /* enabled */);
+ mSpiedOneHandedController.startOneHanded();
- verify(mMockTimeoutHandler).setTimeout(
- OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
+ verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt());
}
- @Ignore("b/167943723, refactor it and fix it")
@Test
- public void tesSettingsObserver_updateSwipeToNotification() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1);
+ public void testResetKeyguardShowingLockOneHandedDisabled() {
+ when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
+ mSpiedOneHandedController.setOneHandedEnabled(true);
+ mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
+ mSpiedOneHandedController.startOneHanded();
- verify(mOneHandedController).setSwipeToNotificationEnabled(true);
+ verify(mMockDisplayAreaOrganizer).scheduleOffset(anyInt(), anyInt());
}
}
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 01162b5c0b83..7a826c1be4d3 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
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -37,6 +38,7 @@ import android.testing.TestableLooper;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.window.DisplayAreaAppearedInfo;
import android.window.DisplayAreaInfo;
import android.window.IWindowContainerToken;
import android.window.WindowContainerToken;
@@ -53,15 +55,19 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
+ static final int DISPLAYAREA_INFO_COUNT = 3;
static final int DISPLAY_WIDTH = 1000;
static final int DISPLAY_HEIGHT = 1000;
DisplayAreaInfo mDisplayAreaInfo;
Display mDisplay;
- OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
+ OneHandedDisplayAreaOrganizer mSpiedDisplayAreaOrganizer;
OneHandedTutorialHandler mTutorialHandler;
OneHandedAnimationController.OneHandedTransitionAnimator mFakeAnimator;
WindowContainerToken mToken;
@@ -86,6 +92,8 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
@Mock
ShellExecutor mMockShellMainExecutor;
+ List<DisplayAreaAppearedInfo> mDisplayAreaAppearedInfoList = new ArrayList<>();
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -112,170 +120,195 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
when(mMockLeash.getWidth()).thenReturn(DISPLAY_WIDTH);
when(mMockLeash.getHeight()).thenReturn(DISPLAY_HEIGHT);
- mDisplayAreaOrganizer = spy(new OneHandedDisplayAreaOrganizer(mContext,
+ mSpiedDisplayAreaOrganizer = spy(new OneHandedDisplayAreaOrganizer(mContext,
mMockDisplayController,
mMockAnimationController,
mTutorialHandler,
mMockBackgroundOrganizer,
mMockShellMainExecutor));
+
+ for (int i = 0; i < DISPLAYAREA_INFO_COUNT; i++) {
+ mDisplayAreaAppearedInfoList.add(getDummyDisplayAreaInfo());
+ }
+ doReturn(mDisplayAreaAppearedInfoList).when(mSpiedDisplayAreaOrganizer).registerOrganizer(
+ FEATURE_ONE_HANDED);
+ }
+
+ private DisplayAreaAppearedInfo getDummyDisplayAreaInfo() {
+ return new DisplayAreaAppearedInfo(mDisplayAreaInfo, mMockLeash);
+ }
+
+ @Test
+ public void testRegisterDisplayAreaOrganizer() {
+ assertThat(mSpiedDisplayAreaOrganizer.registerOrganizer(FEATURE_ONE_HANDED)).isNotNull();
}
@Test
public void testOnDisplayAreaAppeared() {
- mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
+ mDisplayAreaAppearedInfoList.forEach(
+ (info) -> mSpiedDisplayAreaOrganizer.onDisplayAreaAppeared(
+ info.getDisplayAreaInfo(),
+ info.getLeash()));
verify(mMockAnimationController, never()).getAnimator(any(), any(), any(), any());
}
@Test
public void testOnDisplayAreaVanished() {
- mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
- mDisplayAreaOrganizer.onDisplayAreaVanished(mDisplayAreaInfo);
+ mDisplayAreaAppearedInfoList.forEach(
+ (info) -> mSpiedDisplayAreaOrganizer.onDisplayAreaAppeared(
+ info.getDisplayAreaInfo(),
+ info.getLeash()));
+
+ mDisplayAreaAppearedInfoList.forEach(
+ (info) -> mSpiedDisplayAreaOrganizer.onDisplayAreaVanished(
+ info.getDisplayAreaInfo()));
- assertThat(mDisplayAreaOrganizer.mDisplayAreaTokenMap).isEmpty();
+ verify(mSpiedDisplayAreaOrganizer, times(DISPLAYAREA_INFO_COUNT)).onDisplayAreaVanished(
+ any());
}
@Test
public void testRotation_portrait_0_to_landscape_90() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 0 -> 90
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_portrait_0_to_seascape_270() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 0 -> 270
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_270,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_270,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_portrait_180_to_landscape_90() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 180 -> 90
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_90,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_90,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_portrait_180_to_seascape_270() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 180 -> 270
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_270,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_270,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_landscape_90_to_portrait_0() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 90 -> 0
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_0,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_0,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_landscape_90_to_portrait_180() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 90 -> 180
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_180,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_180,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_Seascape_270_to_portrait_0() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 270 -> 0
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_0,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_0,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_seascape_90_to_portrait_180() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 270 -> 180
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_180,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_180,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_portrait_0_to_portrait_0() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 0 -> 0
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_0,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_0,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_portrait_0_to_portrait_180() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 0 -> 180
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_180,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_180,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_portrait_180_to_portrait_180() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 180 -> 180
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_180,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_180,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_portrait_180_to_portrait_0() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 180 -> 0
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_0,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_0,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_landscape_90_to_landscape_90() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 90 -> 90
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_90,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_90,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_landscape_90_to_seascape_270() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 90 -> 270
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_270,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_270,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_seascape_270_to_seascape_270() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 270 -> 270
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_270,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_270,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
public void testRotation_seascape_90_to_landscape_90() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 270 -> 90
- mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_90,
+ mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_90,
mMockWindowContainerTransaction);
- verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
+ verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java
index e5f2ff717e37..b275b701f87a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java
@@ -18,10 +18,8 @@ package com.android.wm.shell.onehanded;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
+import android.view.ViewConfiguration;
import androidx.test.filters.SmallTest;
@@ -29,7 +27,6 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -38,7 +35,6 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class OneHandedGestureHandlerTest extends OneHandedTestCase {
- OneHandedTutorialHandler mTutorialHandler;
OneHandedGestureHandler mGestureHandler;
@Mock
DisplayController mMockDisplayController;
@@ -46,11 +42,10 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase {
ShellExecutor mMockShellMainExecutor;
@Before
- public void setUp() throws Exception {
+ public void setUp() {
MockitoAnnotations.initMocks(this);
- mTutorialHandler = new OneHandedTutorialHandler(mContext, mMockShellMainExecutor);
mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController,
- mMockShellMainExecutor);
+ ViewConfiguration.get(mTestContext), mMockShellMainExecutor);
}
@Test
@@ -68,16 +63,6 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase {
assertThat(mGestureHandler.mGestureEventCallback).isEqualTo(callback);
}
- @Ignore("b/167943723, refactor it and fix it")
- @Test
- public void testReceiveNewConfig_whenThreeButtonModeEnabled() {
- mGestureHandler.onOneHandedEnabled(true);
- mGestureHandler.onThreeButtonModeEnabled(true);
-
- assertThat(mGestureHandler.mInputMonitor).isNotNull();
- assertThat(mGestureHandler.mInputEventReceiver).isNotNull();
- }
-
@Test
public void testOneHandedDisabled_shouldDisposeInputChannel() {
mGestureHandler.onOneHandedEnabled(false);
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 f8c9d535ba94..61643d86c8d9 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
@@ -28,7 +28,6 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java
index 73a95345e1c9..32a188d02cf0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java
@@ -19,88 +19,47 @@ package com.android.wm.shell.onehanded;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.wm.shell.onehanded.OneHandedController.SUPPORT_ONE_HANDED_MODE;
-import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
import static org.junit.Assume.assumeTrue;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.SystemProperties;
-import android.provider.Settings;
+import android.testing.TestableContext;
import androidx.test.platform.app.InstrumentationRegistry;
-import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
+import org.mockito.Answers;
+import org.mockito.Mock;
/**
* Base class that does One Handed specific setup.
*/
public abstract class OneHandedTestCase {
- static boolean sOrigEnabled;
- static boolean sOrigTapsAppToExitEnabled;
- static int sOrigTimeout;
- static boolean sOrigSwipeToNotification;
-
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
protected Context mContext;
+ @Rule
+ public TestableContext mTestContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
@Before
- public void setupSettings() {
+ public void setUpContext() {
assumeTrue(SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false));
- final Context testContext =
- InstrumentationRegistry.getInstrumentation().getTargetContext();
- final DisplayManager dm = testContext.getSystemService(DisplayManager.class);
- mContext = testContext.createDisplayContext(dm.getDisplay(DEFAULT_DISPLAY));
-
- InstrumentationRegistry
- .getInstrumentation()
- .getUiAutomation()
- .adoptShellPermissionIdentity();
-
- sOrigEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
- getContext().getContentResolver());
- sOrigTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout(
- getContext().getContentResolver());
- sOrigTapsAppToExitEnabled = OneHandedSettingsUtil.getSettingsTapsAppToExit(
- getContext().getContentResolver());
- sOrigSwipeToNotification = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
- getContext().getContentResolver());
- Settings.Secure.putInt(getContext().getContentResolver(),
- Settings.Secure.ONE_HANDED_MODE_ENABLED, 1);
- Settings.Secure.putInt(getContext().getContentResolver(),
- Settings.Secure.ONE_HANDED_MODE_TIMEOUT, ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
- Settings.Secure.putInt(getContext().getContentResolver(),
- Settings.Secure.TAPS_APP_TO_EXIT, 1);
- Settings.Secure.putInt(getContext().getContentResolver(),
- Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1);
+ final DisplayManager dm = getTestContext().getSystemService(DisplayManager.class);
+ mContext = getTestContext().createDisplayContext(dm.getDisplay(DEFAULT_DISPLAY));
}
- @After
- public void restoreSettings() {
- if (mContext == null) {
- // Return early if one-handed mode is not supported
- return;
- }
-
- Settings.Secure.putInt(getContext().getContentResolver(),
- Settings.Secure.ONE_HANDED_MODE_ENABLED, sOrigEnabled ? 1 : 0);
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.ONE_HANDED_MODE_TIMEOUT, sOrigTimeout);
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.TAPS_APP_TO_EXIT, sOrigTapsAppToExitEnabled ? 1 : 0);
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
- sOrigSwipeToNotification ? 1 : 0);
-
- InstrumentationRegistry
- .getInstrumentation()
- .getUiAutomation()
- .dropShellPermissionIdentity();
+ /** return testable context */
+ protected TestableContext getTestContext() {
+ return mTestContext;
}
+ /** return display context */
protected Context getContext() {
return mContext;
}
}
-
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
index bbe8891817d6..98f240a209a6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
@@ -27,24 +27,18 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
-import android.os.Looper;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.TestShellExecutor;
-import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.ArrayList;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class OneHandedTimeoutHandlerTest extends OneHandedTestCase {
@@ -52,7 +46,7 @@ public class OneHandedTimeoutHandlerTest extends OneHandedTestCase {
private TestShellExecutor mMainExecutor;
@Before
- public void setUp() throws Exception {
+ public void setUp() {
MockitoAnnotations.initMocks(this);
mMainExecutor = new TestShellExecutor();
mTimeoutHandler = Mockito.spy(new OneHandedTimeoutHandler(mMainExecutor));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java
index d3b02caf8b65..8660e0e9f7f6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java
@@ -18,8 +18,9 @@ package com.android.wm.shell.onehanded;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
+
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
@@ -35,18 +36,20 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class OneHandedTouchHandlerTest extends OneHandedTestCase {
- private OneHandedTouchHandler mTouchHandler;
-
- @Mock
- private OneHandedTimeoutHandler mTimeoutHandler;
+ boolean mIsEventCallback = false;
+ private OneHandedTouchHandler mTouchHandler;
+ private OneHandedTimeoutHandler mSpiedTimeoutHandler;
+ private OneHandedTouchHandler.OneHandedTouchEventCallback mTouchEventCallback =
+ () -> mIsEventCallback = true;
@Mock
- private ShellExecutor mMainExecutor;
+ private ShellExecutor mMockShellMainExecutor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mTouchHandler = new OneHandedTouchHandler(mTimeoutHandler, mMainExecutor);
+ mSpiedTimeoutHandler = spy(new OneHandedTimeoutHandler(mMockShellMainExecutor));
+ mTouchHandler = new OneHandedTouchHandler(mSpiedTimeoutHandler, mMockShellMainExecutor);
}
@Test
@@ -55,7 +58,7 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase {
};
mTouchHandler.registerTouchEventListener(callback);
- assertThat(mTouchHandler.mTouchEventCallback).isEqualTo(callback);
+ assertThat(mIsEventCallback).isFalse();
}
@Test
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 c3e6bf376bda..024cf7ffc1f3 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
@@ -17,10 +17,12 @@
package com.android.wm.shell.onehanded;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.om.IOverlayManager;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
+import android.util.ArrayMap;
import androidx.test.filters.SmallTest;
@@ -37,12 +39,15 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
- @Mock
- OneHandedTouchHandler mTouchHandler;
- OneHandedTutorialHandler mTutorialHandler;
- OneHandedGestureHandler mGestureHandler;
OneHandedTimeoutHandler mTimeoutHandler;
OneHandedController mOneHandedController;
+
+ @Mock
+ OneHandedGestureHandler mMockGestureHandler;
+ @Mock
+ OneHandedTouchHandler mMockTouchHandler;
+ @Mock
+ OneHandedTutorialHandler mMockTutorialHandler;
@Mock
DisplayController mMockDisplayController;
@Mock
@@ -64,18 +69,17 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mTutorialHandler = new OneHandedTutorialHandler(mContext, mMockShellMainExecutor);
mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
- mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController,
- mMockShellMainExecutor);
+
+ when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
mOneHandedController = new OneHandedController(
getContext(),
mMockDisplayController,
mMockBackgroundOrganizer,
mMockDisplayAreaOrganizer,
- mTouchHandler,
- mTutorialHandler,
- mGestureHandler,
+ mMockTouchHandler,
+ mMockTutorialHandler,
+ mMockGestureHandler,
mTimeoutHandler,
mMockUiEventLogger,
mMockOverlayManager,
@@ -86,6 +90,6 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
@Test
public void testRegisterForDisplayAreaOrganizer() {
- verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mTutorialHandler);
+ verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockTutorialHandler);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 0087d917f007..3147dab1a0f8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -16,6 +16,9 @@
package com.android.wm.shell.pip;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
+
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
@@ -34,6 +37,7 @@ import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
import org.junit.Before;
import org.junit.Test;
@@ -84,7 +88,7 @@ public class PipAnimationControllerTest extends ShellTestCase {
public void getAnimator_withBounds_returnBoundsAnimator() {
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, new Rect(), new Rect(), new Rect(), null,
- TRANSITION_DIRECTION_TO_PIP, 0);
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
assertEquals("Expect ANIM_TYPE_BOUNDS animation",
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
@@ -98,13 +102,13 @@ public class PipAnimationControllerTest extends ShellTestCase {
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
- TRANSITION_DIRECTION_TO_PIP, 0);
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
oldAnimator.start();
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue2, null,
- TRANSITION_DIRECTION_TO_PIP, 0);
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
assertEquals("getAnimator with same type returns same animator",
oldAnimator, newAnimator);
@@ -128,6 +132,21 @@ public class PipAnimationControllerTest extends ShellTestCase {
}
@Test
+ public void pipTransitionAnimator_rotatedEndValue() {
+ final Rect startBounds = new Rect(200, 700, 400, 800);
+ final Rect endBounds = new Rect(0, 0, 500, 1000);
+ final PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController
+ .getAnimator(mTaskInfo, mLeash, null, startBounds, endBounds, null,
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_90);
+ // Apply fraction 1 to compute the end value.
+ animator.applySurfaceControlTransaction(mLeash, new DummySurfaceControlTx(), 1);
+ final Rect rotatedEndBounds = new Rect(endBounds);
+ DisplayLayout.rotateBounds(rotatedEndBounds, endBounds, ROTATION_90);
+
+ assertEquals("Expect 90 degree rotated bounds", rotatedEndBounds, animator.mCurrentValue);
+ }
+
+ @Test
@SuppressWarnings("unchecked")
public void pipTransitionAnimator_updateEndValue() {
final Rect baseValue = new Rect(0, 0, 100, 100);
@@ -136,7 +155,7 @@ public class PipAnimationControllerTest extends ShellTestCase {
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
- TRANSITION_DIRECTION_TO_PIP, 0);
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
animator.updateEndValue(endValue2);
@@ -150,7 +169,7 @@ public class PipAnimationControllerTest extends ShellTestCase {
final Rect endValue = new Rect(100, 100, 200, 200);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
- TRANSITION_DIRECTION_TO_PIP, 0);
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
animator.setPipAnimationCallback(mPipAnimationCallback);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 9430af946899..d10c03677d30 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -44,6 +44,7 @@ import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import org.junit.Before;
@@ -70,7 +71,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Mock private PipTransitionController mMockPipTransitionController;
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger;
- @Mock private Optional<LegacySplitScreen> mMockOptionalSplitScreen;
+ @Mock private Optional<LegacySplitScreenController> mMockOptionalSplitScreen;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
private TestShellExecutor mMainExecutor;
private PipBoundsState mPipBoundsState;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 449ad88f6532..19930485047c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -22,16 +22,14 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.graphics.Point;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.Size;
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
@@ -59,6 +57,9 @@ import org.mockito.MockitoAnnotations;
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class PipTouchHandlerTest extends ShellTestCase {
+ private static final int INSET = 10;
+ private static final int PIP_LENGTH = 100;
+
private PipTouchHandler mPipTouchHandler;
@Mock
@@ -85,8 +86,9 @@ public class PipTouchHandlerTest extends ShellTestCase {
private PipMotionHelper mMotionHelper;
private PipResizeGestureHandler mPipResizeGestureHandler;
+ private DisplayLayout mDisplayLayout;
private Rect mInsetBounds;
- private Rect mMinBounds;
+ private Rect mPipBounds;
private Rect mCurBounds;
private boolean mFromImeAdjustment;
private boolean mFromShelfAdjustment;
@@ -109,12 +111,18 @@ public class PipTouchHandlerTest extends ShellTestCase {
mPipTouchHandler.setPipMotionHelper(mMotionHelper);
mPipTouchHandler.setPipResizeGestureHandler(mPipResizeGestureHandler);
- // Assume a display of 1000 x 1000
- // inset of 10
- mInsetBounds = new Rect(10, 10, 990, 990);
+ mDisplayLayout = new DisplayLayout(mContext, mContext.getDisplay());
+ mPipBoundsState.setDisplayLayout(mDisplayLayout);
+ mInsetBounds = new Rect(mPipBoundsState.getDisplayBounds().left + INSET,
+ mPipBoundsState.getDisplayBounds().top + INSET,
+ mPipBoundsState.getDisplayBounds().right - INSET,
+ mPipBoundsState.getDisplayBounds().bottom - INSET);
// minBounds of 100x100 bottom right corner
- mMinBounds = new Rect(890, 890, 990, 990);
- mCurBounds = new Rect(mMinBounds);
+ mPipBounds = new Rect(mPipBoundsState.getDisplayBounds().right - INSET - PIP_LENGTH,
+ mPipBoundsState.getDisplayBounds().bottom - INSET - PIP_LENGTH,
+ mPipBoundsState.getDisplayBounds().right - INSET,
+ mPipBoundsState.getDisplayBounds().bottom - INSET);
+ mCurBounds = new Rect(mPipBounds);
mFromImeAdjustment = false;
mFromShelfAdjustment = false;
mDisplayRotation = 0;
@@ -122,37 +130,23 @@ public class PipTouchHandlerTest extends ShellTestCase {
}
@Test
- public void updateMovementBounds_minBounds() {
- Rect expectedMinMovementBounds = new Rect();
- mPipBoundsAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds,
+ public void updateMovementBounds_minMaxBounds() {
+ final int shorterLength = Math.min(mPipBoundsState.getDisplayBounds().width(),
+ mPipBoundsState.getDisplayBounds().height());
+ Rect expectedMovementBounds = new Rect();
+ mPipBoundsAlgorithm.getMovementBounds(mPipBounds, mInsetBounds, expectedMovementBounds,
0);
- mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
+ mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mPipBounds, mCurBounds,
mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
- assertEquals(expectedMinMovementBounds, mPipBoundsState.getNormalMovementBounds());
+ assertEquals(expectedMovementBounds, mPipBoundsState.getNormalMovementBounds());
verify(mPipResizeGestureHandler, times(1))
- .updateMinSize(mMinBounds.width(), mMinBounds.height());
- }
-
- @Test
- public void updateMovementBounds_maxBounds() {
- Point displaySize = new Point();
- mContext.getDisplay().getRealSize(displaySize);
- Size maxSize = mPipBoundsAlgorithm.getSizeForAspectRatio(1,
- mContext.getResources().getDimensionPixelSize(
- R.dimen.pip_expanded_shortest_edge_size), displaySize.x, displaySize.y);
- Rect maxBounds = new Rect(0, 0, maxSize.getWidth(), maxSize.getHeight());
- Rect expectedMaxMovementBounds = new Rect();
- mPipBoundsAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds,
- 0);
-
- mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
- mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
+ .updateMinSize(mPipBounds.width(), mPipBounds.height());
- assertEquals(expectedMaxMovementBounds, mPipBoundsState.getExpandedMovementBounds());
verify(mPipResizeGestureHandler, times(1))
- .updateMaxSize(maxBounds.width(), maxBounds.height());
+ .updateMaxSize(shorterLength - 2 * mInsetBounds.left,
+ shorterLength - 2 * mInsetBounds.left);
}
@Test
@@ -160,7 +154,7 @@ public class PipTouchHandlerTest extends ShellTestCase {
mFromImeAdjustment = true;
mPipTouchHandler.onImeVisibilityChanged(true /* imeVisible */, mImeHeight);
- mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
+ mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mPipBounds, mCurBounds,
mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
verify(mMotionHelper, times(1)).animateToOffset(any(), anyInt());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
new file mode 100644
index 000000000000..d9086a6ccdc1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.wm.shell.sizecompatui;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link SizeCompatRestartButton}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:SizeCompatRestartButtonTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SizeCompatRestartButtonTest extends ShellTestCase {
+
+ @Mock private SyncTransactionQueue mSyncTransactionQueue;
+ @Mock private IBinder mActivityToken;
+ @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
+ @Mock private DisplayLayout mDisplayLayout;
+
+ private SizeCompatUILayout mLayout;
+ private SizeCompatRestartButton mButton;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ final int taskId = 1;
+ mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mContext, new Configuration(),
+ taskId, mActivityToken, mTaskListener, mDisplayLayout, false /* hasShownHint*/);
+ mButton = (SizeCompatRestartButton)
+ LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null);
+ mButton.inject(mLayout);
+
+ spyOn(mLayout);
+ spyOn(mButton);
+ doNothing().when(mButton).showHint();
+ }
+
+ @Test
+ public void testOnClick() {
+ doNothing().when(mLayout).onRestartButtonClicked();
+
+ mButton.onClick(mButton);
+
+ verify(mLayout).onRestartButtonClicked();
+ }
+
+ @Test
+ public void testOnLongClick() {
+ verify(mButton, never()).showHint();
+
+ mButton.onLongClick(mButton);
+
+ verify(mButton).showHint();
+ }
+
+ @Test
+ public void testOnAttachedToWindow_showHint() {
+ mLayout.mShouldShowHint = false;
+ mButton.onAttachedToWindow();
+
+ verify(mButton, never()).showHint();
+
+ mLayout.mShouldShowHint = true;
+ mButton.onAttachedToWindow();
+
+ verify(mButton).showHint();
+ }
+
+ @Test
+ public void testRemove() {
+ mButton.remove();
+
+ verify(mButton).dismissHint();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
index 98f01ff08deb..806a90b7832a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
@@ -16,23 +16,28 @@
package com.android.wm.shell.sizecompatui;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+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 android.content.Context;
-import android.graphics.Rect;
+import android.content.res.Configuration;
import android.os.IBinder;
import android.testing.AndroidTestingRunner;
-import android.view.View;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
import org.junit.Before;
import org.junit.Test;
@@ -50,28 +55,34 @@ import org.mockito.MockitoAnnotations;
@SmallTest
public class SizeCompatUIControllerTest extends ShellTestCase {
private static final int DISPLAY_ID = 0;
-
- private final TestShellExecutor mShellMainExecutor = new TestShellExecutor();
+ private static final int TASK_ID = 12;
private SizeCompatUIController mController;
private @Mock DisplayController mMockDisplayController;
+ private @Mock DisplayLayout mMockDisplayLayout;
private @Mock DisplayImeController mMockImeController;
- private @Mock SizeCompatRestartButton mMockButton;
private @Mock IBinder mMockActivityToken;
private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
+ private @Mock SyncTransactionQueue mMockSyncQueue;
+ private @Mock SizeCompatUILayout mMockLayout;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- doReturn(true).when(mMockButton).show();
+ doReturn(mMockDisplayLayout).when(mMockDisplayController).getDisplayLayout(anyInt());
+ doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
+ doReturn(TASK_ID).when(mMockLayout).getTaskId();
mController = new SizeCompatUIController(mContext, mMockDisplayController,
- mMockImeController, mShellMainExecutor) {
+ mMockImeController, mMockSyncQueue) {
@Override
- SizeCompatRestartButton createRestartButton(Context context, int displayId) {
- return mMockButton;
+ SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
+ Configuration taskConfig, IBinder activityToken,
+ ShellTaskOrganizer.TaskListener taskListener) {
+ return mMockLayout;
}
};
+ spyOn(mController);
}
@Test
@@ -82,42 +93,72 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
@Test
public void testOnSizeCompatInfoChanged() {
- final int taskId = 12;
- final Rect taskBounds = new Rect(0, 0, 1000, 2000);
+ final Configuration taskConfig = new Configuration();
+
+ // Verify that the restart button is added with non-null size compat info.
+ mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+ mMockActivityToken, mMockTaskListener);
+
+ verify(mController).createLayout(any(), eq(DISPLAY_ID), eq(TASK_ID), eq(taskConfig),
+ eq(mMockActivityToken), eq(mMockTaskListener));
- // Verify that the restart button is added with non-null size compat activity.
- mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds,
+ // Verify that the restart button is updated with non-null new size compat info.
+ final Configuration newTaskConfig = new Configuration();
+ mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig,
mMockActivityToken, mMockTaskListener);
- mShellMainExecutor.flushAll();
- verify(mMockButton).show();
- verify(mMockButton).updateLastTargetActivity(eq(mMockActivityToken));
+ verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockActivityToken, mMockTaskListener,
+ false /* isImeShowing */);
+
+ // Verify that the restart button is removed with null size compat info.
+ mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, null, null, mMockTaskListener);
+
+ verify(mMockLayout).release();
+ }
+
+ @Test
+ public void testOnDisplayRemoved() {
+ final Configuration taskConfig = new Configuration();
+ mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+ mMockActivityToken, mMockTaskListener);
+
+ mController.onDisplayRemoved(DISPLAY_ID + 1);
+
+ verify(mMockLayout, never()).release();
+
+ mController.onDisplayRemoved(DISPLAY_ID);
+
+ verify(mMockLayout).release();
+ }
+
+ @Test
+ public void testOnDisplayConfigurationChanged() {
+ final Configuration taskConfig = new Configuration();
+ mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+ mMockActivityToken, mMockTaskListener);
+
+ final Configuration newTaskConfig = new Configuration();
+ mController.onDisplayConfigurationChanged(DISPLAY_ID + 1, newTaskConfig);
+
+ verify(mMockLayout, never()).updateDisplayLayout(any());
- // Verify that the restart button is removed with null size compat activity.
- mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, null, null, null);
+ mController.onDisplayConfigurationChanged(DISPLAY_ID, newTaskConfig);
- mShellMainExecutor.flushAll();
- verify(mMockButton).remove();
+ verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
}
@Test
public void testChangeButtonVisibilityOnImeShowHide() {
- final int taskId = 12;
- final Rect taskBounds = new Rect(0, 0, 1000, 2000);
- mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds,
+ final Configuration taskConfig = new Configuration();
+ mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
mMockActivityToken, mMockTaskListener);
- mShellMainExecutor.flushAll();
- // Verify that the restart button is hidden when IME is visible.
- doReturn(View.VISIBLE).when(mMockButton).getVisibility();
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
- verify(mMockButton).setVisibility(eq(View.GONE));
+ verify(mMockLayout).updateImeVisibility(true);
- // Verify that the restart button is visible when IME is hidden.
- doReturn(View.GONE).when(mMockButton).getVisibility();
mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
- verify(mMockButton).setVisibility(eq(View.VISIBLE));
+ verify(mMockLayout).updateImeVisibility(false);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
new file mode 100644
index 000000000000..236db44bdce0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
@@ -0,0 +1,206 @@
+/*
+ * 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.wm.shell.sizecompatui;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityClient;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.testing.AndroidTestingRunner;
+import android.view.DisplayInfo;
+import android.view.SurfaceControl;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link SizeCompatUILayout}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:SizeCompatUILayoutTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SizeCompatUILayoutTest extends ShellTestCase {
+
+ private static final int TASK_ID = 1;
+
+ @Mock private SyncTransactionQueue mSyncTransactionQueue;
+ @Mock private IBinder mActivityToken;
+ @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
+ @Mock private DisplayLayout mDisplayLayout;
+ @Mock private SizeCompatRestartButton mButton;
+ private Configuration mTaskConfig;
+
+ private SizeCompatUILayout mLayout;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mTaskConfig = new Configuration();
+
+ mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mContext, new Configuration(),
+ TASK_ID, mActivityToken, mTaskListener, mDisplayLayout, false /* hasShownHint*/);
+
+ spyOn(mLayout);
+ spyOn(mLayout.mWindowManager);
+ doReturn(mButton).when(mLayout.mWindowManager).createSizeCompatUI();
+ }
+
+ @Test
+ public void testCreateSizeCompatButton() {
+ // Not create button if IME is showing.
+ mLayout.createSizeCompatButton(true /* isImeShowing */);
+
+ verify(mLayout.mWindowManager, never()).createSizeCompatUI();
+ assertNull(mLayout.mButton);
+
+ mLayout.createSizeCompatButton(false /* isImeShowing */);
+
+ verify(mLayout.mWindowManager).createSizeCompatUI();
+ assertNotNull(mLayout.mButton);
+ }
+
+ @Test
+ public void testRelease() {
+ mLayout.createSizeCompatButton(false /* isImeShowing */);
+
+ mLayout.release();
+
+ assertNull(mLayout.mButton);
+ verify(mButton).remove();
+ verify(mLayout.mWindowManager).release();
+ }
+
+ @Test
+ public void testUpdateSizeCompatInfo() {
+ mLayout.createSizeCompatButton(false /* isImeShowing */);
+
+ // No diff
+ clearInvocations(mLayout);
+ mLayout.updateSizeCompatInfo(mTaskConfig, mActivityToken, mTaskListener,
+ false /* isImeShowing */);
+
+ verify(mLayout, never()).updateSurfacePosition();
+ verify(mLayout, never()).release();
+ verify(mLayout, never()).createSizeCompatButton(anyBoolean());
+
+ // Change task listener, recreate button.
+ clearInvocations(mLayout);
+ final ShellTaskOrganizer.TaskListener newTaskListener = mock(
+ ShellTaskOrganizer.TaskListener.class);
+ mLayout.updateSizeCompatInfo(mTaskConfig, mActivityToken, newTaskListener,
+ false /* isImeShowing */);
+
+ verify(mLayout).release();
+ verify(mLayout).createSizeCompatButton(anyBoolean());
+
+ // Change task bounds, update position.
+ clearInvocations(mLayout);
+ final Configuration newTaskConfiguration = new Configuration();
+ newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
+ mLayout.updateSizeCompatInfo(newTaskConfiguration, mActivityToken, newTaskListener,
+ false /* isImeShowing */);
+
+ verify(mLayout).updateSurfacePosition();
+ }
+
+ @Test
+ public void testUpdateDisplayLayout() {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 1000;
+ displayInfo.logicalHeight = 2000;
+ final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo,
+ mContext.getResources(), false, false);
+
+ mLayout.updateDisplayLayout(displayLayout1);
+ verify(mLayout).updateSurfacePosition();
+
+ // No update if the display bounds is the same.
+ clearInvocations(mLayout);
+ final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
+ mContext.getResources(), false, false);
+ mLayout.updateDisplayLayout(displayLayout2);
+ verify(mLayout, never()).updateSurfacePosition();
+ }
+
+ @Test
+ public void testUpdateImeVisibility() {
+ // Create button if it is not created.
+ mLayout.mButton = null;
+ mLayout.updateImeVisibility(false /* isImeShowing */);
+
+ verify(mLayout).createSizeCompatButton(false /* isImeShowing */);
+
+ // Hide button if ime is shown.
+ clearInvocations(mLayout);
+ doReturn(View.VISIBLE).when(mButton).getVisibility();
+ mLayout.updateImeVisibility(true /* isImeShowing */);
+
+ verify(mLayout, never()).createSizeCompatButton(anyBoolean());
+ verify(mButton).setVisibility(View.GONE);
+
+ // Show button if ime is not shown.
+ doReturn(View.GONE).when(mButton).getVisibility();
+ mLayout.updateImeVisibility(false /* isImeShowing */);
+
+ verify(mLayout, never()).createSizeCompatButton(anyBoolean());
+ verify(mButton).setVisibility(View.VISIBLE);
+ }
+
+ @Test
+ public void testAttachToParentSurface() {
+ final SurfaceControl.Builder b = new SurfaceControl.Builder();
+ mLayout.attachToParentSurface(b);
+
+ verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b);
+ }
+
+ @Test
+ public void testOnRestartButtonClicked() {
+ spyOn(ActivityClient.getInstance());
+ doNothing().when(ActivityClient.getInstance()).restartActivityProcessIfVisible(any());
+
+ mLayout.onRestartButtonClicked();
+
+ verify(ActivityClient.getInstance()).restartActivityProcessIfVisible(mActivityToken);
+ }
+}
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 c9537afa37ef..de7d6c74bb06 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
@@ -40,6 +40,7 @@ import android.testing.TestableContext;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowMetrics;
+import android.window.SplashScreenView;
import android.window.StartingWindowInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -79,7 +80,8 @@ public class StartingSurfaceDrawerTests {
@Override
protected void postAddWindow(int taskId, IBinder appToken,
- View view, WindowManager wm, WindowManager.LayoutParams params) {
+ View view, WindowManager wm, WindowManager.LayoutParams params,
+ SplashScreenView splashScreenView) {
// listen for addView
mAddWindowForTask = taskId;
mViewThemeResId = view.getContext().getThemeResId();
@@ -125,7 +127,8 @@ public class StartingSurfaceDrawerTests {
createWindowInfo(taskId, android.R.style.Theme);
mStartingSurfaceDrawer.addStartingWindow(windowInfo, mBinder);
waitHandlerIdle(mainLoop);
- verify(mStartingSurfaceDrawer).postAddWindow(eq(taskId), eq(mBinder), any(), any(), any());
+ verify(mStartingSurfaceDrawer).postAddWindow(
+ eq(taskId), eq(mBinder), any(), any(), any(), any());
assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId);
mStartingSurfaceDrawer.removeStartingWindow(windowInfo.taskInfo.taskId);
@@ -142,7 +145,8 @@ public class StartingSurfaceDrawerTests {
createWindowInfo(taskId, 0);
mStartingSurfaceDrawer.addStartingWindow(windowInfo, mBinder);
waitHandlerIdle(mainLoop);
- verify(mStartingSurfaceDrawer).postAddWindow(eq(taskId), eq(mBinder), any(), any(), any());
+ verify(mStartingSurfaceDrawer).postAddWindow(
+ eq(taskId), eq(mBinder), any(), any(), any(), any());
assertNotEquals(mStartingSurfaceDrawer.mViewThemeResId, 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 414a0a778d93..27e5f51d88b8 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
@@ -47,6 +47,7 @@ import android.window.TaskSnapshot;
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;
@@ -83,7 +84,7 @@ public class TaskSnapshotWindowTest {
createTaskDescription(Color.WHITE, Color.RED, Color.BLUE),
0 /* appearance */, windowFlags /* windowFlags */, 0 /* privateWindowFlags */,
taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD, new InsetsState(),
- null /* clearWindow */);
+ null /* clearWindow */, new TestShellExecutor());
}
private TaskSnapshot createTaskSnapshot(int width, int height, Point taskSize,
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 4b4284a0c745..aba0f1b47673 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -14,6 +14,23 @@
// libandroidfw is partially built for the host (used by obbtool, aapt, and others)
+package {
+ default_applicable_licenses: ["frameworks_base_libs_androidfw_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_libs_androidfw_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
cc_defaults {
name: "libandroidfw_defaults",
cflags: [
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index ca5981c0dd5c..9c743cea592a 100755
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -83,8 +83,16 @@ std::unique_ptr<ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
return {};
}
+ std::unique_ptr<AssetsProvider> overlay_assets;
const std::string overlay_path(loaded_idmap->OverlayApkPath());
- auto overlay_assets = ZipAssetsProvider::Create(overlay_path);
+ if (IsFabricatedOverlay(overlay_path)) {
+ // Fabricated overlays do not contain resource definitions. All of the overlay resource values
+ // are defined inline in the idmap.
+ overlay_assets = EmptyAssetsProvider::Create();
+ } else {
+ // The overlay should be an APK.
+ overlay_assets = ZipAssetsProvider::Create(overlay_path);
+ }
if (overlay_assets == nullptr) {
return {};
}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 03ab62f48870..36bde5ccf267 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -102,9 +102,8 @@ AssetManager2::AssetManager2() {
memset(&configuration_, 0, sizeof(configuration_));
}
-bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets,
- bool invalidate_caches) {
- apk_assets_ = apk_assets;
+bool AssetManager2::SetApkAssets(std::vector<const ApkAssets*> apk_assets, bool invalidate_caches) {
+ apk_assets_ = std::move(apk_assets);
BuildDynamicRefTable();
RebuildFilterList();
if (invalidate_caches) {
@@ -137,6 +136,36 @@ void AssetManager2::BuildDynamicRefTable() {
// 0x01 is reserved for the android package.
int next_package_id = 0x02;
for (const ApkAssets* apk_assets : sorted_apk_assets) {
+ std::shared_ptr<OverlayDynamicRefTable> overlay_ref_table;
+ if (auto loaded_idmap = apk_assets->GetLoadedIdmap(); loaded_idmap != nullptr) {
+ // The target package must precede the overlay package in the apk assets paths in order
+ // to take effect.
+ auto iter = apk_assets_package_ids.find(std::string(loaded_idmap->TargetApkPath()));
+ if (iter == apk_assets_package_ids.end()) {
+ LOG(INFO) << "failed to find target package for overlay "
+ << loaded_idmap->OverlayApkPath();
+ } else {
+ uint8_t target_package_id = iter->second;
+
+ // Create a special dynamic reference table for the overlay to rewrite references to
+ // overlay resources as references to the target resources they overlay.
+ overlay_ref_table = std::make_shared<OverlayDynamicRefTable>(
+ loaded_idmap->GetOverlayDynamicRefTable(target_package_id));
+
+ // Add the overlay resource map to the target package's set of overlays.
+ const uint8_t target_idx = package_ids_[target_package_id];
+ CHECK(target_idx != 0xff) << "overlay target '" << loaded_idmap->TargetApkPath()
+ << "'added to apk_assets_package_ids but does not have an"
+ << " assigned package group";
+
+ PackageGroup& target_package_group = package_groups_[target_idx];
+ target_package_group.overlays_.push_back(
+ ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id,
+ overlay_ref_table.get()),
+ apk_assets_cookies[apk_assets]});
+ }
+ }
+
const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc();
for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) {
// Get the package ID or assign one if a shared library.
@@ -147,50 +176,25 @@ void AssetManager2::BuildDynamicRefTable() {
package_id = package->GetPackageId();
}
- // Add the mapping for package ID to index if not present.
uint8_t idx = package_ids_[package_id];
if (idx == 0xff) {
+ // Add the mapping for package ID to index if not present.
package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size());
- package_groups_.push_back({});
-
- if (apk_assets->IsOverlay()) {
- // The target package must precede the overlay package in the apk assets paths in order
- // to take effect.
- const auto& loaded_idmap = apk_assets->GetLoadedIdmap();
- auto target_package_iter = apk_assets_package_ids.find(
- std::string(loaded_idmap->TargetApkPath()));
- if (target_package_iter == apk_assets_package_ids.end()) {
- LOG(INFO) << "failed to find target package for overlay "
- << loaded_idmap->OverlayApkPath();
- } else {
- const uint8_t target_package_id = target_package_iter->second;
- const uint8_t target_idx = package_ids_[target_package_id];
- CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not"
- << " have an assigned package group";
-
- PackageGroup& target_package_group = package_groups_[target_idx];
-
- // Create a special dynamic reference table for the overlay to rewrite references to
- // overlay resources as references to the target resources they overlay.
- auto overlay_table = std::make_shared<OverlayDynamicRefTable>(
- loaded_idmap->GetOverlayDynamicRefTable(target_package_id));
- package_groups_.back().dynamic_ref_table = overlay_table;
-
- // Add the overlay resource map to the target package's set of overlays.
- target_package_group.overlays_.push_back(
- ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id,
- overlay_table.get()),
- apk_assets_cookies[apk_assets]});
- }
+ PackageGroup& new_group = package_groups_.emplace_back();
+
+ if (overlay_ref_table != nullptr) {
+ // If this package is from an overlay, use a dynamic reference table that can rewrite
+ // overlay resource ids to their corresponding target resource ids.
+ new_group.dynamic_ref_table = overlay_ref_table;
}
- DynamicRefTable* ref_table = package_groups_.back().dynamic_ref_table.get();
+ DynamicRefTable* ref_table = new_group.dynamic_ref_table.get();
ref_table->mAssignedPackageId = package_id;
ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
}
- PackageGroup* package_group = &package_groups_[idx];
// Add the package and to the set of packages with the same ID.
+ PackageGroup* package_group = &package_groups_[idx];
package_group->packages_.push_back(ConfiguredPackage{package.get(), {}});
package_group->cookies_.push_back(apk_assets_cookies[apk_assets]);
@@ -578,7 +582,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
const PackageGroup& package_group = package_groups_[package_idx];
auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
- stop_at_first_match, ignore_configuration);
+ stop_at_first_match, ignore_configuration);
if (UNLIKELY(!result.has_value())) {
return base::unexpected(result.error());
}
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
index 23cacf88a6db..f3c48f7f9fc8 100644
--- a/libs/androidfw/AssetsProvider.cpp
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -84,7 +84,7 @@ const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const {
return value_;
}
-ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path,
+ZipAssetsProvider::ZipAssetsProvider(ZipArchiveHandle handle, PathOrDebugName&& path,
time_t last_mod_time)
: zip_handle_(handle, ::CloseArchive),
name_(std::forward<PathOrDebugName>(path)),
@@ -93,7 +93,7 @@ ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path,
std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path) {
ZipArchiveHandle handle;
if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) {
- LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
+ LOG(ERROR) << "Failed to open APK '" << path << "': " << ::ErrorCodeString(result);
CloseArchive(handle);
return {};
}
@@ -253,6 +253,14 @@ bool ZipAssetsProvider::ForEachFile(const std::string& root_path,
return result == -1;
}
+std::optional<uint32_t> ZipAssetsProvider::GetCrc(std::string_view path) const {
+ ::ZipEntry entry;
+ if (FindEntry(zip_handle_.get(), path, &entry) != 0) {
+ return {};
+ }
+ return entry.crc32;
+}
+
const std::string& ZipAssetsProvider::GetDebugName() const {
return name_.GetDebugName();
}
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index f216f55771c2..efd1f6a25786 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -54,12 +54,6 @@ struct Idmap_header {
};
struct Idmap_data_header {
- uint8_t target_package_id;
- uint8_t overlay_package_id;
-
- // Padding to ensure 4 byte alignment for target_entry_count
- uint16_t p0;
-
uint32_t target_entry_count;
uint32_t target_inline_entry_count;
uint32_t overlay_entry_count;
@@ -158,19 +152,19 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
return {};
}
- // The resource ids encoded within the idmap are build-time resource ids.
- target_res_id = (0x00FFFFFFU & target_res_id)
- | (((uint32_t) data_header_->target_package_id) << 24U);
+ // The resource ids encoded within the idmap are build-time resource ids so do not consider the
+ // package id when determining if the resource in the target package is overlaid.
+ target_res_id &= 0x00FFFFFFU;
// Check if the target resource is mapped to an overlay resource.
auto first_entry = entries_;
auto end_entry = entries_ + dtohl(data_header_->target_entry_count);
auto entry = std::lower_bound(first_entry, end_entry, target_res_id,
- [](const Idmap_target_entry &e, const uint32_t target_id) {
- return dtohl(e.target_id) < target_id;
+ [](const Idmap_target_entry& e, const uint32_t target_id) {
+ return (0x00FFFFFFU & dtohl(e.target_id)) < target_id;
});
- if (entry != end_entry && dtohl(entry->target_id) == target_res_id) {
+ if (entry != end_entry && (0x00FFFFFFU & dtohl(entry->target_id)) == target_res_id) {
uint32_t overlay_resource_id = dtohl(entry->overlay_id);
// Lookup the resource without rewriting the overlay resource id back to the target resource id
// being looked up.
@@ -182,12 +176,13 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
auto first_inline_entry = inline_entries_;
auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count);
auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id,
- [](const Idmap_target_entry_inline &e,
+ [](const Idmap_target_entry_inline& e,
const uint32_t target_id) {
- return dtohl(e.target_id) < target_id;
+ return (0x00FFFFFFU & dtohl(e.target_id)) < target_id;
});
- if (inline_entry != end_inline_entry && dtohl(inline_entry->target_id) == target_res_id) {
+ if (inline_entry != end_inline_entry &&
+ (0x00FFFFFFU & dtohl(inline_entry->target_id)) == target_res_id) {
return Result(inline_entry->value);
}
return {};
@@ -235,7 +230,7 @@ std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size
}
return std::string_view(data, *len);
}
-}
+} // namespace
LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
const Idmap_header* header,
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 223382731bc0..30500abc39c0 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -25,6 +25,7 @@
#include <string.h>
#include <algorithm>
+#include <fstream>
#include <limits>
#include <map>
#include <memory>
@@ -44,6 +45,7 @@
#ifdef __ANDROID__
#include <binder/TextOutput.h>
+
#endif
#ifndef INT32_MAX
@@ -233,6 +235,15 @@ void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs
fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData));
}
+bool IsFabricatedOverlay(const std::string& path) {
+ std::ifstream fin(path);
+ uint32_t magic;
+ if (fin.read(reinterpret_cast<char*>(&magic), sizeof(uint32_t))) {
+ return magic == kFabricatedOverlayMagic;
+ }
+ return false;
+}
+
static bool assertIdmapHeader(const void* idmap, size_t size) {
if (reinterpret_cast<uintptr_t>(idmap) & 0x03) {
ALOGE("idmap: header is not word aligned");
diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp
index 77ef8dfb9725..b511244c4a30 100644
--- a/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp
+++ b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_libs_androidfw_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_libs_androidfw_license"],
+}
+
cc_fuzz {
name: "resourcefile_fuzzer",
srcs: [
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 6fbd6aa0df7b..2255973f1039 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -101,7 +101,7 @@ class AssetManager2 {
// Only pass invalidate_caches=false when it is known that the structure
// change in ApkAssets is due to a safe addition of resources with completely
// new resource IDs.
- bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true);
+ bool SetApkAssets(std::vector<const ApkAssets*> apk_assets, bool invalidate_caches = true);
inline const std::vector<const ApkAssets*> GetApkAssets() const {
return apk_assets_;
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
index 7b06947f45aa..6f16ff453905 100644
--- a/libs/androidfw/include/androidfw/AssetsProvider.h
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -88,6 +88,8 @@ struct ZipAssetsProvider : public AssetsProvider {
WARN_UNUSED const std::string& GetDebugName() const override;
WARN_UNUSED bool IsUpToDate() const override;
+ WARN_UNUSED std::optional<uint32_t> GetCrc(std::string_view path) const;
+
~ZipAssetsProvider() override = default;
protected:
std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index 0ded79309bc1..6804472b3d17 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -168,15 +168,14 @@ class LoadedIdmap {
}
// Returns a mapping from target resource ids to overlay values.
- const IdmapResMap GetTargetResourcesMap(
- uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const {
+ const IdmapResMap GetTargetResourcesMap(uint8_t target_assigned_package_id,
+ const OverlayDynamicRefTable* overlay_ref_table) const {
return IdmapResMap(data_header_, target_entries_, target_inline_entries_,
target_assigned_package_id, overlay_ref_table);
}
// Returns a dynamic reference table for a loaded overlay package.
- const OverlayDynamicRefTable GetOverlayDynamicRefTable(
- uint8_t target_assigned_package_id) const {
+ const OverlayDynamicRefTable GetOverlayDynamicRefTable(uint8_t target_assigned_package_id) const {
return OverlayDynamicRefTable(data_header_, overlay_entries_, target_assigned_package_id);
}
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index bfd564c258ee..168a863df2bc 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -43,8 +43,19 @@
namespace android {
-constexpr const static uint32_t kIdmapMagic = 0x504D4449u;
-constexpr const static uint32_t kIdmapCurrentVersion = 0x00000007u;
+constexpr const uint32_t kIdmapMagic = 0x504D4449u;
+constexpr const uint32_t kIdmapCurrentVersion = 0x00000008u;
+
+// This must never change.
+constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endian)
+
+// The version should only be changed when a backwards-incompatible change must be made to the
+// fabricated overlay file format. Old fabricated overlays must be migrated to the new file format
+// to prevent losing fabricated overlay data.
+constexpr const uint32_t kFabricatedOverlayCurrentVersion = 1;
+
+// Returns whether or not the path represents a fabricated overlay.
+bool IsFabricatedOverlay(const std::string& path);
/**
* In C++11, char16_t is defined as *at least* 16 bits. We do a lot of
diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap
index 723413c3cea8..88eadccb38cf 100644
--- a/libs/androidfw/tests/data/overlay/overlay.idmap
+++ b/libs/androidfw/tests/data/overlay/overlay.idmap
Binary files differ
diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp
index 3a99d41448f2..f166fdeafa41 100644
--- a/libs/hostgraphics/Android.bp
+++ b/libs/hostgraphics/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_host_static {
name: "libhostgraphics",
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ce1d96c167d7..77ceda91898e 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -1,3 +1,33 @@
+package {
+ default_applicable_licenses: ["frameworks_base_libs_hwui_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_libs_hwui_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ "SPDX-license-identifier-BSD",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
cc_defaults {
name: "hwui_defaults",
defaults: [
@@ -335,7 +365,6 @@ cc_defaults {
"jni/YuvToJpegEncoder.cpp",
"jni/fonts/Font.cpp",
"jni/fonts/FontFamily.cpp",
- "jni/fonts/NativeFont.cpp",
"jni/text/LineBreaker.cpp",
"jni/text/MeasuredText.cpp",
"jni/text/TextShaper.cpp",
@@ -358,12 +387,13 @@ cc_defaults {
"libharfbuzz_ng",
"liblog",
"libminikin",
- "libnativehelper",
"libz",
"libziparchive",
"libjpeg",
],
+ static_libs: ["libnativehelper_lazy"],
+
target: {
android: {
srcs: [ // sources that depend on android only libraries
@@ -445,6 +475,7 @@ cc_defaults {
"renderthread/TimeLord.cpp",
"hwui/AnimatedImageDrawable.cpp",
"hwui/Bitmap.cpp",
+ "hwui/BlurDrawLooper.cpp",
"hwui/Canvas.cpp",
"hwui/ImageDecoder.cpp",
"hwui/MinikinSkia.cpp",
@@ -481,6 +512,8 @@ cc_defaults {
target: {
android: {
+ header_libs: ["libandroid_headers_private" ],
+
srcs: [
"hwui/AnimatedImageThread.cpp",
"pipeline/skia/ATraceMemoryDump.cpp",
@@ -568,6 +601,7 @@ cc_defaults {
name: "hwui_test_defaults",
defaults: ["hwui_defaults"],
test_suites: ["device-tests"],
+ header_libs: ["libandroid_headers_private"],
target: {
android: {
shared_libs: [
@@ -605,7 +639,6 @@ cc_test {
shared_libs: [
"libmemunreachable",
],
-
srcs: [
"tests/unit/main.cpp",
"tests/unit/ABitmapTests.cpp",
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 74cf1fda1b75..20a8a8c71a0f 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -160,10 +160,6 @@ void BaseRenderNodeAnimator::pushStaging(AnimationContext& context) {
}
}
- if (!mHasStartValue) {
- doSetStartValue(getValue(mTarget));
- }
-
if (!mStagingRequests.empty()) {
// No interpolator was set, use the default
if (mPlayState == PlayState::NotStarted && !mInterpolator) {
@@ -270,9 +266,11 @@ bool BaseRenderNodeAnimator::updatePlayTime(nsecs_t playTime) {
// to call setValue even if the animation isn't yet running or is still
// being delayed as we need to override the staging value
if (playTime < 0) {
- setValue(mTarget, mFromValue);
return false;
}
+ if (!this->mHasStartValue) {
+ doSetStartValue(getValue(mTarget));
+ }
float fraction = 1.0f;
if ((mPlayState == PlayState::Running || mPlayState == PlayState::Reversing) && mDuration > 0) {
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index 27be62269959..d5fee3f667a9 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -35,7 +35,6 @@ class DeviceInfo {
public:
static DeviceInfo* get();
- static float getMaxRefreshRate() { return get()->mMaxRefreshRate; }
static int32_t getWidth() { return get()->mWidth; }
static int32_t getHeight() { return get()->mHeight; }
// Gets the density in density-independent pixels
@@ -45,7 +44,6 @@ public:
static int64_t getAppOffset() { return get()->mAppVsyncOffsetNanos; }
// Sets the density in density-independent pixels
static void setDensity(float density) { sDensity.store(density); }
- static void setMaxRefreshRate(float refreshRate) { get()->mMaxRefreshRate = refreshRate; }
static void setWidth(int32_t width) { get()->mWidth = width; }
static void setHeight(int32_t height) { get()->mHeight = height; }
static void setRefreshRate(float refreshRate) {
@@ -91,7 +89,6 @@ private:
SkColorType mWideColorType = SkColorType::kN32_SkColorType;
int mDisplaysSize = 0;
int mPhysicalDisplayIndex = -1;
- float mMaxRefreshRate = 60.0;
int32_t mWidth = 1080;
int32_t mHeight = 1920;
int64_t mVsyncPeriod = 16666666;
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index 8b20492543f7..5d3f6f2f28c9 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -40,9 +40,10 @@ const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> Fram
"DequeueBufferDuration",
"QueueBufferDuration",
"GpuCompleted",
+ "SwapBuffersCompleted"
};
-static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 19,
+static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 20,
"Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)");
void FrameInfo::importUiThreadInfo(int64_t* info) {
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index ee7d15a2fbce..45a367f525da 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -55,6 +55,7 @@ enum class FrameInfoIndex {
QueueBufferDuration,
GpuCompleted,
+ SwapBuffersCompleted,
// Must be the last value!
// Also must be kept in sync with FrameMetrics.java#FRAME_STATS_COUNT
@@ -120,6 +121,10 @@ public:
void markSwapBuffers() { set(FrameInfoIndex::SwapBuffers) = systemTime(SYSTEM_TIME_MONOTONIC); }
+ void markSwapBuffersCompleted() {
+ set(FrameInfoIndex::SwapBuffersCompleted) = systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+
void markFrameCompleted() { set(FrameInfoIndex::FrameCompleted) = systemTime(SYSTEM_TIME_MONOTONIC); }
void addFlag(int frameInfoFlag) {
diff --git a/libs/hwui/FrameMetricsReporter.h b/libs/hwui/FrameMetricsReporter.h
index 75b8038c5040..0643e790d00b 100644
--- a/libs/hwui/FrameMetricsReporter.h
+++ b/libs/hwui/FrameMetricsReporter.h
@@ -16,14 +16,16 @@
#pragma once
+#include <utils/Mutex.h>
#include <utils/Log.h>
#include <utils/RefBase.h>
+#include <ui/FatVector.h>
+
#include "FrameInfo.h"
#include "FrameMetricsObserver.h"
#include <string.h>
-#include <vector>
namespace android {
namespace uirenderer {
@@ -32,9 +34,13 @@ class FrameMetricsReporter {
public:
FrameMetricsReporter() {}
- void addObserver(FrameMetricsObserver* observer) { mObservers.push_back(observer); }
+ void addObserver(FrameMetricsObserver* observer) {
+ std::lock_guard lock(mObserversLock);
+ mObservers.push_back(observer);
+ }
bool removeObserver(FrameMetricsObserver* observer) {
+ std::lock_guard lock(mObserversLock);
for (size_t i = 0; i < mObservers.size(); i++) {
if (mObservers[i].get() == observer) {
mObservers.erase(mObservers.begin() + i);
@@ -44,16 +50,28 @@ public:
return false;
}
- bool hasObservers() { return mObservers.size() > 0; }
+ bool hasObservers() {
+ std::lock_guard lock(mObserversLock);
+ return mObservers.size() > 0;
+ }
void reportFrameMetrics(const int64_t* stats) {
- for (size_t i = 0; i < mObservers.size(); i++) {
- mObservers[i]->notify(stats);
+ FatVector<sp<FrameMetricsObserver>, 10> copy;
+ {
+ std::lock_guard lock(mObserversLock);
+ copy.reserve(mObservers.size());
+ for (size_t i = 0; i < mObservers.size(); i++) {
+ copy.push_back(mObservers[i]);
+ }
+ }
+ for (size_t i = 0; i < copy.size(); i++) {
+ copy[i]->notify(stats);
}
}
private:
- std::vector<sp<FrameMetricsObserver> > mObservers;
+ FatVector<sp<FrameMetricsObserver>, 10> mObservers GUARDED_BY(mObserversLock);
+ std::mutex mObserversLock;
};
} // namespace uirenderer
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index ccce403ecfac..4a2e30dd38f2 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -79,7 +79,9 @@ static const int64_t EXEMPT_FRAMES_FLAGS = FrameInfoFlags::SurfaceCanvas;
// and filter it out of the frame profile data
static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync;
-JankTracker::JankTracker(ProfileDataContainer* globalData) {
+JankTracker::JankTracker(ProfileDataContainer* globalData)
+ : mData(globalData->getDataMutex())
+ , mDataMutex(globalData->getDataMutex()) {
mGlobalData = globalData;
nsecs_t frameIntervalNanos = DeviceInfo::getVsyncPeriod();
nsecs_t sfOffset = DeviceInfo::getCompositorOffset();
@@ -107,6 +109,8 @@ void JankTracker::setFrameInterval(nsecs_t frameInterval) {
}
void JankTracker::finishFrame(const FrameInfo& frame) {
+ std::lock_guard lock(mDataMutex);
+
// Fast-path for jank-free frames
int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted);
if (mDequeueTimeForgiveness && frame[FrameInfoIndex::DequeueBufferDuration] > 500_us) {
@@ -125,7 +129,11 @@ void JankTracker::finishFrame(const FrameInfo& frame) {
}
}
- LOG_ALWAYS_FATAL_IF(totalDuration <= 0, "Impossible totalDuration %" PRId64, totalDuration);
+ LOG_ALWAYS_FATAL_IF(totalDuration <= 0, "Impossible totalDuration %" PRId64 " start=%" PRIi64
+ " gpuComplete=%" PRIi64, totalDuration,
+ frame[FrameInfoIndex::IntendedVsync],
+ frame[FrameInfoIndex::GpuCompleted]);
+
mData->reportFrame(totalDuration);
(*mGlobalData)->reportFrame(totalDuration);
@@ -188,6 +196,7 @@ void JankTracker::finishFrame(const FrameInfo& frame) {
void JankTracker::dumpData(int fd, const ProfileDataDescription* description,
const ProfileData* data) {
+
if (description) {
switch (description->type) {
case JankTrackerType::Generic:
@@ -227,6 +236,7 @@ void JankTracker::dumpFrames(int fd) {
}
void JankTracker::reset() {
+ std::lock_guard lock(mDataMutex);
mFrames.clear();
mData->reset();
(*mGlobalData)->reset();
@@ -235,6 +245,7 @@ void JankTracker::reset() {
}
void JankTracker::finishGpuDraw(const FrameInfo& frame) {
+ std::lock_guard lock(mDataMutex);
int64_t totalGPUDrawTime = frame.gpuDrawTime();
if (totalGPUDrawTime >= 0) {
mData->reportGPUFrame(totalGPUDrawTime);
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index b3fbbfe98669..096455372923 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -84,12 +84,15 @@ private:
// This is only used if we are in pipelined mode and are using HWC2,
// otherwise it's 0.
nsecs_t mDequeueTimeForgiveness = 0;
- ProfileDataContainer mData;
- ProfileDataContainer* mGlobalData;
+ ProfileDataContainer mData GUARDED_BY(mDataMutex);
+ ProfileDataContainer* mGlobalData GUARDED_BY(mDataMutex);
ProfileDataDescription mDescription;
// Ring buffer large enough for 2 seconds worth of frames
RingBuffer<FrameInfo, 120> mFrames;
+
+ // Mutex to protect acccess to mData and mGlobalData obtained from mGlobalData->getDataMutex
+ std::mutex& mDataMutex;
};
} /* namespace uirenderer */
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index ca2ada9e8141..b14ade97ca5f 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -54,7 +54,7 @@ SkBlendMode Layer::getMode() const {
}
static inline SkScalar isIntegerAligned(SkScalar x) {
- return fabsf(roundf(x) - x) <= NON_ZERO_EPSILON;
+ return MathUtils::isZero(roundf(x) - x);
}
// Disable filtering when there is no scaling in screen coordinates and the corners have the same
diff --git a/libs/hwui/ProfileDataContainer.cpp b/libs/hwui/ProfileDataContainer.cpp
index 38e0f0aa8d83..41afc0e04c8b 100644
--- a/libs/hwui/ProfileDataContainer.cpp
+++ b/libs/hwui/ProfileDataContainer.cpp
@@ -38,6 +38,8 @@ void ProfileDataContainer::freeData() {
}
void ProfileDataContainer::rotateStorage() {
+ std::lock_guard lock(mJankDataMutex);
+
// If we are mapped we want to stop using the ashmem backend and switch to malloc
// We are expecting a switchStorageToAshmem call to follow this, but it's not guaranteed
// If we aren't sitting on top of ashmem then just do a reset() as it's functionally
@@ -50,6 +52,7 @@ void ProfileDataContainer::rotateStorage() {
}
void ProfileDataContainer::switchStorageToAshmem(int ashmemfd) {
+ std::lock_guard lock(mJankDataMutex);
int regionSize = ashmem_get_size_region(ashmemfd);
if (regionSize < 0) {
int err = errno;
@@ -70,7 +73,9 @@ void ProfileDataContainer::switchStorageToAshmem(int ashmemfd) {
return;
}
- newData->mergeWith(*mData);
+ if (mData != nullptr) {
+ newData->mergeWith(*mData);
+ }
freeData();
mData = newData;
mIsMapped = true;
diff --git a/libs/hwui/ProfileDataContainer.h b/libs/hwui/ProfileDataContainer.h
index a39869491ede..a61b8dcf390e 100644
--- a/libs/hwui/ProfileDataContainer.h
+++ b/libs/hwui/ProfileDataContainer.h
@@ -19,6 +19,9 @@
#include "ProfileData.h"
#include "utils/Macros.h"
+#include <mutex>
+#include <utils/Mutex.h>
+
namespace android {
namespace uirenderer {
@@ -26,7 +29,8 @@ class ProfileDataContainer {
PREVENT_COPY_AND_ASSIGN(ProfileDataContainer);
public:
- explicit ProfileDataContainer() {}
+ explicit ProfileDataContainer(std::mutex& jankDataMutex)
+ : mData(new ProfileData()), mJankDataMutex(jankDataMutex) {}
~ProfileDataContainer() { freeData(); }
@@ -36,13 +40,16 @@ public:
ProfileData* get() { return mData; }
ProfileData* operator->() { return mData; }
+ std::mutex& getDataMutex() { return mJankDataMutex; }
+
private:
void freeData();
// By default this will use malloc memory. It may be moved later to ashmem
// if there is shared space for it and a request comes in to do that.
- ProfileData* mData = new ProfileData;
+ ProfileData* mData GUARDED_BY(mJankDataMutex);
bool mIsMapped = false;
+ std::mutex& mJankDataMutex;
};
} /* namespace uirenderer */
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index e798f2a2bc69..971a53a8b2dc 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -79,7 +79,6 @@ bool Properties::debuggingEnabled = false;
bool Properties::isolatedProcess = false;
int Properties::contextPriority = 0;
-int Properties::defaultRenderAhead = -1;
float Properties::defaultSdrWhitePoint = 200.f;
bool Properties::load() {
@@ -129,10 +128,6 @@ bool Properties::load() {
runningInEmulator = base::GetBoolProperty(PROPERTY_QEMU_KERNEL, false);
- defaultRenderAhead = std::max(
- -1,
- std::min(2, base::GetIntProperty(PROPERTY_RENDERAHEAD, render_ahead().value_or(-1))));
-
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
}
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 1639143ef87c..dcb79babad24 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -162,8 +162,6 @@ enum DebugLevel {
*/
#define PROPERTY_QEMU_KERNEL "ro.kernel.qemu"
-#define PROPERTY_RENDERAHEAD "debug.hwui.render_ahead"
-
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -247,8 +245,6 @@ public:
static int contextPriority;
- static int defaultRenderAhead;
-
static float defaultSdrWhitePoint;
private:
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 8fddf713f1fa..1fddac4cd05d 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -463,9 +463,7 @@ void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint,
}
void SkiaCanvas::drawPoint(float x, float y, const Paint& paint) {
- apply_looper(&paint, [&](const SkPaint& p) {
- mCanvas->drawPoint(x, y, p);
- });
+ apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawPoint(x, y, p); });
}
void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint) {
@@ -493,9 +491,7 @@ void SkiaCanvas::drawRect(float left, float top, float right, float bottom, cons
void SkiaCanvas::drawRegion(const SkRegion& region, const Paint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
- apply_looper(&paint, [&](const SkPaint& p) {
- mCanvas->drawRegion(region, p);
- });
+ apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawRegion(region, p); });
}
void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
@@ -509,24 +505,18 @@ void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom,
void SkiaCanvas::drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
const Paint& paint) {
- apply_looper(&paint, [&](const SkPaint& p) {
- mCanvas->drawDRRect(outer, inner, p);
- });
+ apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawDRRect(outer, inner, p); });
}
void SkiaCanvas::drawCircle(float x, float y, float radius, const Paint& paint) {
if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
- apply_looper(&paint, [&](const SkPaint& p) {
- mCanvas->drawCircle(x, y, radius, p);
- });
+ apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawCircle(x, y, radius, p); });
}
void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const Paint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
- apply_looper(&paint, [&](const SkPaint& p) {
- mCanvas->drawOval(oval, p);
- });
+ apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawOval(oval, p); });
}
void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle,
@@ -547,9 +537,7 @@ void SkiaCanvas::drawPath(const SkPath& path, const Paint& paint) {
if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) {
return;
}
- apply_looper(&paint, [&](const SkPaint& p) {
- mCanvas->drawPath(path, p);
- });
+ apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawPath(path, p); });
}
void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const Paint& paint) {
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 155f6df7b703..eac3f2217bd8 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -208,29 +208,21 @@ protected:
*/
PaintCoW&& filterPaint(PaintCoW&& paint) const;
+ // proc(const SkPaint& modifiedPaint)
template <typename Proc> void apply_looper(const Paint* paint, Proc proc) {
SkPaint skp;
- SkDrawLooper* looper = nullptr;
+ BlurDrawLooper* looper = nullptr;
if (paint) {
skp = *filterPaint(paint);
looper = paint->getLooper();
}
if (looper) {
- SkSTArenaAlloc<256> alloc;
- SkDrawLooper::Context* ctx = looper->makeContext(&alloc);
- if (ctx) {
- SkDrawLooper::Context::Info info;
- for (;;) {
- SkPaint p = skp;
- if (!ctx->next(&info, &p)) {
- break;
- }
- mCanvas->save();
- mCanvas->translate(info.fTranslate.fX, info.fTranslate.fY);
- proc(p);
- mCanvas->restore();
- }
- }
+ looper->apply(skp, [&](SkPoint offset, const SkPaint& modifiedPaint) {
+ mCanvas->save();
+ mCanvas->translate(offset.fX, offset.fY);
+ proc(modifiedPaint);
+ mCanvas->restore();
+ });
} else {
proc(skp);
}
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index 0fad2d58cc8a..e1f5abd786bf 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -69,7 +69,6 @@ extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env
extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
extern int register_android_graphics_fonts_Font(JNIEnv* env);
extern int register_android_graphics_fonts_FontFamily(JNIEnv* env);
-extern int register_android_graphics_fonts_NativeFont(JNIEnv* env);
extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
@@ -136,7 +135,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_graphics_drawable_VectorDrawable),
REG_JNI(register_android_graphics_fonts_Font),
REG_JNI(register_android_graphics_fonts_FontFamily),
- REG_JNI(register_android_graphics_fonts_NativeFont),
REG_JNI(register_android_graphics_pdf_PdfDocument),
REG_JNI(register_android_graphics_pdf_PdfEditor),
REG_JNI(register_android_graphics_pdf_PdfRenderer),
diff --git a/libs/hwui/hwui/BlurDrawLooper.cpp b/libs/hwui/hwui/BlurDrawLooper.cpp
new file mode 100644
index 000000000000..27a038d4598e
--- /dev/null
+++ b/libs/hwui/hwui/BlurDrawLooper.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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 "BlurDrawLooper.h"
+#include <SkMaskFilter.h>
+
+namespace android {
+
+BlurDrawLooper::BlurDrawLooper(SkColor4f color, float blurSigma, SkPoint offset)
+ : mColor(color), mBlurSigma(blurSigma), mOffset(offset) {}
+
+BlurDrawLooper::~BlurDrawLooper() = default;
+
+SkPoint BlurDrawLooper::apply(SkPaint* paint) const {
+ paint->setColor(mColor);
+ if (mBlurSigma > 0) {
+ paint->setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, mBlurSigma, true));
+ }
+ return mOffset;
+}
+
+sk_sp<BlurDrawLooper> BlurDrawLooper::Make(SkColor4f color, SkColorSpace* cs, float blurSigma,
+ SkPoint offset) {
+ if (cs) {
+ SkPaint tmp;
+ tmp.setColor(color, cs); // converts color to sRGB
+ color = tmp.getColor4f();
+ }
+ return sk_sp<BlurDrawLooper>(new BlurDrawLooper(color, blurSigma, offset));
+}
+
+} // namespace android
diff --git a/libs/hwui/hwui/BlurDrawLooper.h b/libs/hwui/hwui/BlurDrawLooper.h
new file mode 100644
index 000000000000..7e6786f7dfbc
--- /dev/null
+++ b/libs/hwui/hwui/BlurDrawLooper.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_GRAPHICS_BLURDRAWLOOPER_H_
+#define ANDROID_GRAPHICS_BLURDRAWLOOPER_H_
+
+#include <SkPaint.h>
+#include <SkRefCnt.h>
+
+class SkColorSpace;
+
+namespace android {
+
+class BlurDrawLooper : public SkRefCnt {
+public:
+ static sk_sp<BlurDrawLooper> Make(SkColor4f, SkColorSpace*, float blurSigma, SkPoint offset);
+
+ ~BlurDrawLooper() override;
+
+ // proc(SkPoint offset, const SkPaint& modifiedPaint)
+ template <typename DrawProc>
+ void apply(const SkPaint& paint, DrawProc proc) const {
+ SkPaint p(paint);
+ proc(this->apply(&p), p); // draw the shadow
+ proc({0, 0}, paint); // draw the original (on top)
+ }
+
+private:
+ const SkColor4f mColor;
+ const float mBlurSigma;
+ const SkPoint mOffset;
+
+ SkPoint apply(SkPaint* paint) const;
+
+ BlurDrawLooper(SkColor4f, float, SkPoint);
+};
+
+} // namespace android
+
+#endif // ANDROID_GRAPHICS_BLURDRAWLOOPER_H_
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 0e338f35b8e7..2db3ace1cd43 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -30,10 +30,11 @@
namespace android {
-MinikinFontSkia::MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize,
- std::string_view filePath, int ttcIndex,
+MinikinFontSkia::MinikinFontSkia(sk_sp<SkTypeface> typeface, int sourceId, const void* fontData,
+ size_t fontSize, std::string_view filePath, int ttcIndex,
const std::vector<minikin::FontVariation>& axes)
: mTypeface(std::move(typeface))
+ , mSourceId(sourceId)
, mFontData(fontData)
, mFontSize(fontSize)
, mTtcIndex(ttcIndex)
@@ -141,8 +142,8 @@ std::shared_ptr<minikin::MinikinFont> MinikinFontSkia::createFontWithVariation(
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
sk_sp<SkTypeface> face(fm->makeFromStream(std::move(stream), args));
- return std::make_shared<MinikinFontSkia>(std::move(face), mFontData, mFontSize, mFilePath,
- ttcIndex, variations);
+ return std::make_shared<MinikinFontSkia>(std::move(face), mSourceId, mFontData, mFontSize,
+ mFilePath, ttcIndex, variations);
}
// hinting<<16 | edging<<8 | bools:5bits
diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h
index 77a21428f36a..de9a5c2af0aa 100644
--- a/libs/hwui/hwui/MinikinSkia.h
+++ b/libs/hwui/hwui/MinikinSkia.h
@@ -30,7 +30,7 @@ namespace android {
class ANDROID_API MinikinFontSkia : public minikin::MinikinFont {
public:
- MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize,
+ MinikinFontSkia(sk_sp<SkTypeface> typeface, int sourceId, const void* fontData, size_t fontSize,
std::string_view filePath, int ttcIndex,
const std::vector<minikin::FontVariation>& axes);
@@ -62,6 +62,7 @@ public:
const std::vector<minikin::FontVariation>& GetAxes() const;
std::shared_ptr<minikin::MinikinFont> createFontWithVariation(
const std::vector<minikin::FontVariation>&) const;
+ int GetSourceId() const override { return mSourceId; }
static uint32_t packFontFlags(const SkFont&);
static void unpackFontFlags(SkFont*, uint32_t fontFlags);
@@ -73,6 +74,7 @@ public:
private:
sk_sp<SkTypeface> mTypeface;
+ int mSourceId;
// A raw pointer to the font data - it should be owned by some other object with
// lifetime at least as long as this object.
const void* mFontData;
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 05bae5c9f778..d9c9eeed03e9 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -17,11 +17,11 @@
#ifndef ANDROID_GRAPHICS_PAINT_H_
#define ANDROID_GRAPHICS_PAINT_H_
+#include "BlurDrawLooper.h"
#include "Typeface.h"
#include <cutils/compiler.h>
-#include <SkDrawLooper.h>
#include <SkFont.h>
#include <SkPaint.h>
#include <string>
@@ -59,8 +59,8 @@ public:
SkFont& getSkFont() { return mFont; }
const SkFont& getSkFont() const { return mFont; }
- SkDrawLooper* getLooper() const { return mLooper.get(); }
- void setLooper(sk_sp<SkDrawLooper> looper) { mLooper = std::move(looper); }
+ BlurDrawLooper* getLooper() const { return mLooper.get(); }
+ void setLooper(sk_sp<BlurDrawLooper> looper) { mLooper = std::move(looper); }
// These shadow the methods on SkPaint, but we need to so we can keep related
// attributes in-sync.
@@ -155,7 +155,7 @@ public:
private:
SkFont mFont;
- sk_sp<SkDrawLooper> mLooper;
+ sk_sp<BlurDrawLooper> mLooper;
float mLetterSpacing = 0;
float mWordSpacing = 0;
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index 03f1d62625f1..5a9d2508230e 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -185,9 +185,9 @@ void Typeface::setRobotoTypefaceForTest() {
sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(std::move(fontData));
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", kRobotoFont);
- std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
- std::move(typeface), data, st.st_size, kRobotoFont, 0,
- std::vector<minikin::FontVariation>());
+ std::shared_ptr<minikin::MinikinFont> font =
+ std::make_shared<MinikinFontSkia>(std::move(typeface), 0, data, st.st_size, kRobotoFont,
+ 0, std::vector<minikin::FontVariation>());
std::vector<std::shared_ptr<minikin::Font>> fonts;
fonts.push_back(minikin::Font::Builder(font).build());
diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp
index 2e85840cad99..ce5ac382aeff 100644
--- a/libs/hwui/jni/FontFamily.cpp
+++ b/libs/hwui/jni/FontFamily.cpp
@@ -17,15 +17,16 @@
#undef LOG_TAG
#define LOG_TAG "Minikin"
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include "FontUtils.h"
+#include "GraphicsJNI.h"
#include "SkData.h"
#include "SkFontMgr.h"
#include "SkRefCnt.h"
#include "SkTypeface.h"
-#include "GraphicsJNI.h"
-#include <nativehelper/ScopedPrimitiveArray.h>
-#include <nativehelper/ScopedUtfChars.h>
#include "Utils.h"
-#include "FontUtils.h"
+#include "fonts/Font.h"
#include <hwui/MinikinSkia.h>
#include <hwui/Typeface.h>
@@ -35,6 +36,12 @@
#include <memory>
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// The following JNI methods are kept only for compatibility reasons due to hidden API accesses.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
namespace android {
struct NativeFamilyBuilder {
@@ -125,8 +132,8 @@ static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, in
return false;
}
std::shared_ptr<minikin::MinikinFont> minikinFont =
- std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, "", ttcIndex,
- builder->axes);
+ std::make_shared<MinikinFontSkia>(std::move(face), fonts::getNewSourceId(), fontPtr,
+ fontSize, "", ttcIndex, builder->axes);
minikin::Font::Builder fontBuilder(minikinFont);
if (weight != RESOLVE_BY_FONT_TABLE) {
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index 3c86b28262b0..bcec0fa8a1cc 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -25,7 +25,6 @@
#include <nativehelper/ScopedUtfChars.h>
#include <nativehelper/ScopedPrimitiveArray.h>
-#include "SkBlurDrawLooper.h"
#include "SkColorFilter.h"
#include "SkFont.h"
#include "SkFontMetrics.h"
@@ -39,6 +38,7 @@
#include "unicode/ushape.h"
#include "utils/Blur.h"
+#include <hwui/BlurDrawLooper.h>
#include <hwui/MinikinSkia.h>
#include <hwui/MinikinUtils.h>
#include <hwui/Paint.h>
@@ -964,13 +964,13 @@ namespace PaintGlue {
}
else {
SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
- paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy));
+ paint->setLooper(BlurDrawLooper::Make(color, cs.get(), sigma, {dx, dy}));
}
}
static jboolean hasShadowLayer(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr);
+ return paint->getLooper() != nullptr;
}
static jboolean equalsForTextMeasurement(CRITICAL_JNI_PARAMS_COMMA jlong lPaint, jlong rPaint) {
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
index 97c40d695f97..a48d7f734e29 100644
--- a/libs/hwui/jni/RenderEffect.cpp
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -64,8 +64,8 @@ static jlong createBitmapEffect(
sk_sp<SkImage> image = android::bitmap::toBitmap(bitmapHandle).makeImage();
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- sk_sp<SkImageFilter> bitmapFilter =
- SkImageFilters::Image(image, srcRect, dstRect, kLow_SkFilterQuality);
+ sk_sp<SkImageFilter> bitmapFilter = SkImageFilters::Image(
+ image, srcRect, dstRect, SkSamplingOptions(SkFilterMode::kLinear));
return reinterpret_cast<jlong>(bitmapFilter.release());
}
@@ -115,6 +115,18 @@ static jlong createChainEffect(
return reinterpret_cast<jlong>(composeFilter.release());
}
+static jlong createShaderEffect(
+ JNIEnv* env,
+ jobject,
+ jlong shaderHandle
+) {
+ auto* shader = reinterpret_cast<const SkShader*>(shaderHandle);
+ sk_sp<SkImageFilter> shaderFilter = SkImageFilters::Shader(
+ sk_ref_sp(shader), nullptr
+ );
+ return reinterpret_cast<jlong>(shaderFilter.release());
+}
+
static void RenderEffect_safeUnref(SkImageFilter* filter) {
SkSafeUnref(filter);
}
@@ -130,11 +142,12 @@ static const JNINativeMethod gRenderEffectMethods[] = {
{"nativeCreateBitmapEffect", "(JFFFFFFFF)J", (void*)createBitmapEffect},
{"nativeCreateColorFilterEffect", "(JJ)J", (void*)createColorFilterEffect},
{"nativeCreateBlendModeEffect", "(JJI)J", (void*)createBlendModeEffect},
- {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect}
+ {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect},
+ {"nativeCreateShaderEffect", "(J)J", (void*)createShaderEffect}
};
int register_android_graphics_RenderEffect(JNIEnv* env) {
android::RegisterMethodsOrDie(env, "android/graphics/RenderEffect",
gRenderEffectMethods, NELEM(gRenderEffectMethods));
return 0;
-} \ No newline at end of file
+}
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 1dc5cd99eed1..2e4d7f62f671 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -239,14 +239,12 @@ static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr,
static jlong RuntimeShader_createShaderBuilder(JNIEnv* env, jobject, jstring sksl) {
ScopedUtfChars strSksl(env, sksl);
- auto result = SkRuntimeEffect::Make(SkString(strSksl.c_str()));
- sk_sp<SkRuntimeEffect> effect = std::get<0>(result);
- if (effect.get() == nullptr) {
- const auto& err = std::get<1>(result);
- doThrowIAE(env, err.c_str());
+ auto result = SkRuntimeEffect::Make(SkString(strSksl.c_str()), SkRuntimeEffect::Options{});
+ if (result.effect.get() == nullptr) {
+ doThrowIAE(env, result.errorText.c_str());
return 0;
}
- return reinterpret_cast<jlong>(new SkRuntimeShaderBuilder(std::move(effect)));
+ return reinterpret_cast<jlong>(new SkRuntimeShaderBuilder(std::move(result.effect)));
}
static void SkRuntimeShaderBuilder_delete(SkRuntimeShaderBuilder* builder) {
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index 8f455fe4ab43..251323d34422 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -355,29 +355,48 @@ static void Typeface_forceSetStaticFinalField(JNIEnv *env, jclass cls, jstring f
env->SetStaticObjectField(cls, fid, typeface);
}
+// Critical Native
+static jint Typeface_getFamilySize(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
+ return toTypeface(faceHandle)->fFontCollection->getFamilies().size();
+}
+
+// Critical Native
+static jlong Typeface_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle, jint index) {
+ std::shared_ptr<minikin::FontFamily> family =
+ toTypeface(faceHandle)->fFontCollection->getFamilies()[index];
+ return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
+}
+
+// Regular JNI
+static void Typeface_warmUpCache(JNIEnv* env, jobject, jstring jFilePath) {
+ ScopedUtfChars filePath(env, jFilePath);
+ makeSkDataCached(filePath.c_str(), false /* fs verity */);
+}
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gTypefaceMethods[] = {
- { "nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface },
- { "nativeCreateFromTypefaceWithExactStyle", "(JIZ)J",
- (void*)Typeface_createFromTypefaceWithExactStyle },
- { "nativeCreateFromTypefaceWithVariation", "(JLjava/util/List;)J",
- (void*)Typeface_createFromTypefaceWithVariation },
- { "nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias },
- { "nativeGetReleaseFunc", "()J", (void*)Typeface_getReleaseFunc },
- { "nativeGetStyle", "(J)I", (void*)Typeface_getStyle },
- { "nativeGetWeight", "(J)I", (void*)Typeface_getWeight },
- { "nativeCreateFromArray", "([JJII)J",
- (void*)Typeface_createFromArray },
- { "nativeSetDefault", "(J)V", (void*)Typeface_setDefault },
- { "nativeGetSupportedAxes", "(J)[I", (void*)Typeface_getSupportedAxes },
- { "nativeRegisterGenericFamily", "(Ljava/lang/String;J)V",
- (void*)Typeface_registerGenericFamily },
- { "nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces},
- { "nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces},
- { "nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V",
- (void*)Typeface_forceSetStaticFinalField },
+ {"nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface},
+ {"nativeCreateFromTypefaceWithExactStyle", "(JIZ)J",
+ (void*)Typeface_createFromTypefaceWithExactStyle},
+ {"nativeCreateFromTypefaceWithVariation", "(JLjava/util/List;)J",
+ (void*)Typeface_createFromTypefaceWithVariation},
+ {"nativeCreateWeightAlias", "(JI)J", (void*)Typeface_createWeightAlias},
+ {"nativeGetReleaseFunc", "()J", (void*)Typeface_getReleaseFunc},
+ {"nativeGetStyle", "(J)I", (void*)Typeface_getStyle},
+ {"nativeGetWeight", "(J)I", (void*)Typeface_getWeight},
+ {"nativeCreateFromArray", "([JJII)J", (void*)Typeface_createFromArray},
+ {"nativeSetDefault", "(J)V", (void*)Typeface_setDefault},
+ {"nativeGetSupportedAxes", "(J)[I", (void*)Typeface_getSupportedAxes},
+ {"nativeRegisterGenericFamily", "(Ljava/lang/String;J)V",
+ (void*)Typeface_registerGenericFamily},
+ {"nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces},
+ {"nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces},
+ {"nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V",
+ (void*)Typeface_forceSetStaticFinalField},
+ {"nativeGetFamilySize", "(J)I", (void*)Typeface_getFamilySize},
+ {"nativeGetFamily", "(JI)J", (void*)Typeface_getFamily},
+ {"nativeWarmUpCache", "(Ljava/lang/String;)V", (void*)Typeface_warmUpCache},
};
int register_android_graphics_Typeface(JNIEnv* env)
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index a146b64e29cc..df66981853bb 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -189,6 +189,13 @@ static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz,
}
}
+static void android_view_ThreadedRenderer_setSurfaceControl(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jlong surfaceControlPtr) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ ASurfaceControl* surfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControlPtr);
+ proxy->setSurfaceControl(surfaceControl);
+}
+
static jboolean android_view_ThreadedRenderer_pause(JNIEnv* env, jobject clazz,
jlong proxyPtr) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -603,14 +610,12 @@ static void android_view_ThreadedRenderer_setDisplayDensityDpi(JNIEnv*, jclass,
static void android_view_ThreadedRenderer_initDisplayInfo(JNIEnv*, jclass, jint physicalWidth,
jint physicalHeight, jfloat refreshRate,
- jfloat maxRefreshRate,
jint wideColorDataspace,
jlong appVsyncOffsetNanos,
jlong presentationDeadlineNanos) {
DeviceInfo::setWidth(physicalWidth);
DeviceInfo::setHeight(physicalHeight);
DeviceInfo::setRefreshRate(refreshRate);
- DeviceInfo::setMaxRefreshRate(maxRefreshRate);
DeviceInfo::setWideColorDataspace(static_cast<ADataSpace>(wideColorDataspace));
DeviceInfo::setAppVsyncOffsetNanos(appVsyncOffsetNanos);
DeviceInfo::setPresentationDeadlineNanos(presentationDeadlineNanos);
@@ -673,6 +678,8 @@ static const JNINativeMethod gMethods[] = {
{"nSetName", "(JLjava/lang/String;)V", (void*)android_view_ThreadedRenderer_setName},
{"nSetSurface", "(JLandroid/view/Surface;Z)V",
(void*)android_view_ThreadedRenderer_setSurface},
+ {"nSetSurfaceControl", "(JJ)V",
+ (void*)android_view_ThreadedRenderer_setSurfaceControl},
{"nPause", "(J)Z", (void*)android_view_ThreadedRenderer_pause},
{"nSetStopped", "(JZ)V", (void*)android_view_ThreadedRenderer_setStopped},
{"nSetLightAlpha", "(JFF)V", (void*)android_view_ThreadedRenderer_setLightAlpha},
@@ -735,7 +742,7 @@ static const JNINativeMethod gMethods[] = {
{"nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark},
{"nSetDisplayDensityDpi", "(I)V",
(void*)android_view_ThreadedRenderer_setDisplayDensityDpi},
- {"nInitDisplayInfo", "(IIFFIJJ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo},
+ {"nInitDisplayInfo", "(IIFIJJ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo},
{"preload", "()V", (void*)android_view_ThreadedRenderer_preload},
};
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index b769d40238a4..5a972f56ea87 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -34,6 +34,7 @@
#include <hwui/Typeface.h>
#include <minikin/FontFamily.h>
#include <minikin/FontFileParser.h>
+#include <minikin/LocaleList.h>
#include <ui/FatVector.h>
#include <memory>
@@ -136,12 +137,9 @@ static jlong Font_Builder_clone(JNIEnv* env, jobject clazz, jlong fontPtr, jlong
sk_sp<SkTypeface> newTypeface = minikinSkia->GetSkTypeface()->makeClone(args);
std::shared_ptr<minikin::MinikinFont> newMinikinFont = std::make_shared<MinikinFontSkia>(
- std::move(newTypeface),
- minikinSkia->GetFontData(),
- minikinSkia->GetFontSize(),
- minikinSkia->getFilePath(),
- minikinSkia->GetFontIndex(),
- builder->axes);
+ std::move(newTypeface), minikinSkia->GetSourceId(), minikinSkia->GetFontData(),
+ minikinSkia->GetFontSize(), minikinSkia->getFilePath(), minikinSkia->GetFontIndex(),
+ builder->axes);
std::shared_ptr<minikin::Font> newFont = minikin::Font::Builder(newMinikinFont)
.setWeight(weight)
.setSlant(static_cast<minikin::FontStyle::Slant>(italic))
@@ -149,12 +147,8 @@ static jlong Font_Builder_clone(JNIEnv* env, jobject clazz, jlong fontPtr, jlong
return reinterpret_cast<jlong>(new FontWrapper(std::move(newFont)));
}
-// Critical Native
-static jlong Font_Builder_getReleaseNativeFont(CRITICAL_JNI_PARAMS) {
- return reinterpret_cast<jlong>(releaseFont);
-}
-
///////////////////////////////////////////////////////////////////////////////
+// Font JNI functions
// Fast Native
static jfloat Font_getGlyphBounds(JNIEnv* env, jobject, jlong fontHandle, jint glyphId,
@@ -195,51 +189,98 @@ static jfloat Font_getFontMetrics(JNIEnv* env, jobject, jlong fontHandle, jlong
}
// Critical Native
-static jlong Font_getNativeFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
- FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
- return reinterpret_cast<jlong>(font->font.get());
+static jlong Font_getMinikinFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ return reinterpret_cast<jlong>(font->font->typeface().get());
}
// Critical Native
-static jlong Font_GetBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
- FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
- const void* bufferPtr = font->font->typeface()->GetFontData();
- return reinterpret_cast<jlong>(bufferPtr);
+static jlong Font_cloneFont(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ std::shared_ptr<minikin::Font> ref = font->font;
+ return reinterpret_cast<jlong>(new FontWrapper(std::move(ref)));
}
-///////////////////////////////////////////////////////////////////////////////
-
-struct FontBufferWrapper {
- FontBufferWrapper(const std::shared_ptr<minikin::MinikinFont>& font) : minikinFont(font) {}
- // MinikinFont holds a shared pointer of SkTypeface which has reference to font data.
- std::shared_ptr<minikin::MinikinFont> minikinFont;
-};
+// Fast Native
+static jobject Font_newByteBuffer(JNIEnv* env, jobject, jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+ return env->NewDirectByteBuffer(const_cast<void*>(minikinFont->GetFontData()),
+ minikinFont->GetFontSize());
+}
-static void unrefBuffer(jlong nativePtr) {
- FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr);
- delete wrapper;
+// Critical Native
+static jlong Font_getBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ return reinterpret_cast<jlong>(font->font->typeface()->GetFontData());
}
// Critical Native
-static jlong FontBufferHelper_refFontBuffer(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
- const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
- return reinterpret_cast<jlong>(new FontBufferWrapper(font->typeface()));
+static jlong Font_getReleaseNativeFontFunc(CRITICAL_JNI_PARAMS) {
+ return reinterpret_cast<jlong>(releaseFont);
}
// Fast Native
-static jobject FontBufferHelper_wrapByteBuffer(JNIEnv* env, jobject, jlong nativePtr) {
- FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr);
- return env->NewDirectByteBuffer(
- const_cast<void*>(wrapper->minikinFont->GetFontData()),
- wrapper->minikinFont->GetFontSize());
+static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+ const std::string& path = minikinFont->GetFontPath();
+ if (path.empty()) {
+ return nullptr;
+ }
+ return env->NewStringUTF(path.c_str());
+}
+
+// Fast Native
+static jstring Font_getLocaleList(JNIEnv* env, jobject, jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ uint32_t localeListId = font->font->getLocaleListId();
+ if (localeListId == 0) {
+ return nullptr;
+ }
+ std::string langTags = minikin::getLocaleString(localeListId);
+ if (langTags.empty()) {
+ return nullptr;
+ }
+ return env->NewStringUTF(langTags.c_str());
}
// Critical Native
-static jlong FontBufferHelper_getReleaseFunc(CRITICAL_JNI_PARAMS) {
- return reinterpret_cast<jlong>(unrefBuffer);
+static jint Font_getPackedStyle(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ uint32_t weight = font->font->style().weight();
+ uint32_t isItalic = font->font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 1 : 0;
+ return (isItalic << 16) | weight;
}
-///////////////////////////////////////////////////////////////////////////////
+// Critical Native
+static jint Font_getIndex(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+ return minikinFont->GetFontIndex();
+}
+
+// Critical Native
+static jint Font_getAxisCount(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+ return minikinFont->GetAxes().size();
+}
+
+// Critical Native
+static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr, jint index) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+ minikin::FontVariation var = minikinFont->GetAxes().at(index);
+ uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value);
+ return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary);
+}
+
+// Critical Native
+static jint Font_getSourceId(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ return font->font->typeface()->GetSourceId();
+}
// Fast Native
static jlong FontFileUtil_getFontRevision(JNIEnv* env, jobject, jobject buffer, jint index) {
@@ -314,20 +355,24 @@ static const JNINativeMethod gFontBuilderMethods[] = {
{"nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;Ljava/lang/String;IZI)J",
(void*)Font_Builder_build},
{"nClone", "(JJIZI)J", (void*)Font_Builder_clone},
- {"nGetReleaseNativeFont", "()J", (void*)Font_Builder_getReleaseNativeFont},
};
static const JNINativeMethod gFontMethods[] = {
- { "nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*) Font_getGlyphBounds },
- { "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics },
- { "nGetNativeFontPtr", "(J)J", (void*) Font_getNativeFontPtr },
- { "nGetFontBufferAddress", "(J)J", (void*) Font_GetBufferAddress },
-};
-
-static const JNINativeMethod gFontBufferHelperMethods[] = {
- { "nRefFontBuffer", "(J)J", (void*) FontBufferHelper_refFontBuffer },
- { "nWrapByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void*) FontBufferHelper_wrapByteBuffer },
- { "nGetReleaseFunc", "()J", (void*) FontBufferHelper_getReleaseFunc },
+ {"nGetMinikinFontPtr", "(J)J", (void*)Font_getMinikinFontPtr},
+ {"nCloneFont", "(J)J", (void*)Font_cloneFont},
+ {"nNewByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void*)Font_newByteBuffer},
+ {"nGetBufferAddress", "(J)J", (void*)Font_getBufferAddress},
+ {"nGetReleaseNativeFont", "()J", (void*)Font_getReleaseNativeFontFunc},
+ {"nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*)Font_getGlyphBounds},
+ {"nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F",
+ (void*)Font_getFontMetrics},
+ {"nGetFontPath", "(J)Ljava/lang/String;", (void*)Font_getFontPath},
+ {"nGetLocaleList", "(J)Ljava/lang/String;", (void*)Font_getLocaleList},
+ {"nGetPackedStyle", "(J)I", (void*)Font_getPackedStyle},
+ {"nGetIndex", "(J)I", (void*)Font_getIndex},
+ {"nGetAxisCount", "(J)I", (void*)Font_getAxisCount},
+ {"nGetAxisInfo", "(JI)J", (void*)Font_getAxisInfo},
+ {"nGetSourceId", "(J)I", (void*)Font_getSourceId},
};
static const JNINativeMethod gFontFileUtilMethods[] = {
@@ -343,8 +388,6 @@ int register_android_graphics_fonts_Font(JNIEnv* env) {
NELEM(gFontBuilderMethods)) +
RegisterMethodsOrDie(env, "android/graphics/fonts/Font", gFontMethods,
NELEM(gFontMethods)) +
- RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFontBufferHelper",
- gFontBufferHelperMethods, NELEM(gFontBufferHelperMethods)) +
RegisterMethodsOrDie(env, "android/graphics/fonts/FontFileUtil", gFontFileUtilMethods,
NELEM(gFontFileUtilMethods));
}
@@ -370,10 +413,15 @@ std::shared_ptr<minikin::MinikinFont> createMinikinFontSkia(
if (face == nullptr) {
return nullptr;
}
- return std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize,
+ return std::make_shared<MinikinFontSkia>(std::move(face), getNewSourceId(), fontPtr, fontSize,
fontPath, ttcIndex, axes);
}
+int getNewSourceId() {
+ static std::atomic<int> sSourceId = {0};
+ return sSourceId++;
+}
+
} // namespace fonts
} // namespace android
diff --git a/libs/hwui/jni/fonts/Font.h b/libs/hwui/jni/fonts/Font.h
index b5d20bf8cc3c..4bf60ee85657 100644
--- a/libs/hwui/jni/fonts/Font.h
+++ b/libs/hwui/jni/fonts/Font.h
@@ -33,6 +33,8 @@ std::shared_ptr<minikin::MinikinFont> createMinikinFontSkia(
sk_sp<SkData>&& data, std::string_view fontPath, const void *fontPtr, size_t fontSize,
int ttcIndex, const std::vector<minikin::FontVariation>& axes);
+int getNewSourceId();
+
} // namespace fonts
} // namespace android
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index a07723f39b0c..b68213549938 100644
--- a/libs/hwui/jni/fonts/FontFamily.cpp
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -95,22 +95,36 @@ static jstring FontFamily_getLangTags(JNIEnv* env, jobject, jlong familyPtr) {
}
// CriticalNative
-static jint FontFamily_getVariant(jlong familyPtr) {
+static jint FontFamily_getVariant(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr) {
FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
return static_cast<jint>(family->family->variant());
}
+// CriticalNative
+static jint FontFamily_getFontSize(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr) {
+ FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+ return family->family->getNumFonts();
+}
+
+// CriticalNative
+static jlong FontFamily_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr, jint index) {
+ FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+ std::shared_ptr<minikin::Font> font = family->family->getFontRef(index);
+ return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
+}
+
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gFontFamilyBuilderMethods[] = {
{ "nInitBuilder", "()J", (void*) FontFamily_Builder_initBuilder },
{ "nAddFont", "(JJ)V", (void*) FontFamily_Builder_addFont },
{ "nBuild", "(JLjava/lang/String;IZ)J", (void*) FontFamily_Builder_build },
-
{ "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
};
static const JNINativeMethod gFontFamilyMethods[] = {
+ {"nGetFontSize", "(J)I", (void*)FontFamily_getFontSize},
+ {"nGetFont", "(JI)J", (void*)FontFamily_getFont},
{"nGetLangTags", "(J)Ljava/lang/String;", (void*)FontFamily_getLangTags},
{"nGetVariant", "(J)I", (void*)FontFamily_getVariant},
};
diff --git a/libs/hwui/jni/fonts/NativeFont.cpp b/libs/hwui/jni/fonts/NativeFont.cpp
deleted file mode 100644
index c5c5d464ccac..000000000000
--- a/libs/hwui/jni/fonts/NativeFont.cpp
+++ /dev/null
@@ -1,125 +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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "Minikin"
-
-#include "Font.h"
-#include "SkData.h"
-#include "SkFont.h"
-#include "SkFontMetrics.h"
-#include "SkFontMgr.h"
-#include "SkRefCnt.h"
-#include "SkTypeface.h"
-#include "GraphicsJNI.h"
-#include <nativehelper/ScopedUtfChars.h>
-#include "Utils.h"
-#include "FontUtils.h"
-
-#include <hwui/MinikinSkia.h>
-#include <hwui/Paint.h>
-#include <hwui/Typeface.h>
-#include <minikin/FontFamily.h>
-#include <minikin/LocaleList.h>
-#include <ui/FatVector.h>
-
-#include <memory>
-
-namespace android {
-
-// Critical Native
-static jint NativeFont_getFamilyCount(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle) {
- Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle);
- return tf->fFontCollection->getFamilies().size();
-}
-
-// Critical Native
-static jlong NativeFont_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle, jint index) {
- Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle);
- return reinterpret_cast<jlong>(tf->fFontCollection->getFamilies()[index].get());
-
-}
-
-// Fast Native
-static jstring NativeFont_getLocaleList(JNIEnv* env, jobject, jlong familyHandle) {
- minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle);
- uint32_t localeListId = family->localeListId();
- return env->NewStringUTF(minikin::getLocaleString(localeListId).c_str());
-}
-
-// Critical Native
-static jint NativeFont_getFontCount(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle) {
- minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle);
- return family->getNumFonts();
-}
-
-// Critical Native
-static jlong NativeFont_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle, jint index) {
- minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle);
- return reinterpret_cast<jlong>(family->getFont(index));
-}
-
-// Critical Native
-static jlong NativeFont_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
- const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
- MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
-
- uint64_t result = font->style().weight();
- result |= font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000;
- result |= ((static_cast<uint64_t>(minikinSkia->GetFontIndex())) << 32);
- result |= ((static_cast<uint64_t>(minikinSkia->GetAxes().size())) << 48);
- return result;
-}
-
-// Critical Native
-static jlong NativeFont_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) {
- const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
- MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
- const minikin::FontVariation& var = minikinSkia->GetAxes().at(index);
- uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value);
- return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary);
-}
-
-// FastNative
-static jstring NativeFont_getFontPath(JNIEnv* env, jobject, jlong fontHandle) {
- const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
- MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
- const std::string& filePath = minikinSkia->getFilePath();
- if (filePath.empty()) {
- return nullptr;
- }
- return env->NewStringUTF(filePath.c_str());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static const JNINativeMethod gNativeFontMethods[] = {
- { "nGetFamilyCount", "(J)I", (void*) NativeFont_getFamilyCount },
- { "nGetFamily", "(JI)J", (void*) NativeFont_getFamily },
- { "nGetLocaleList", "(J)Ljava/lang/String;", (void*) NativeFont_getLocaleList },
- { "nGetFontCount", "(J)I", (void*) NativeFont_getFontCount },
- { "nGetFont", "(JI)J", (void*) NativeFont_getFont },
- { "nGetFontInfo", "(J)J", (void*) NativeFont_getFontInfo },
- { "nGetAxisInfo", "(JI)J", (void*) NativeFont_getAxisInfo },
- { "nGetFontPath", "(J)Ljava/lang/String;", (void*) NativeFont_getFontPath },
-};
-
-int register_android_graphics_fonts_NativeFont(JNIEnv* env) {
- return RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFont", gNativeFontMethods,
- NELEM(gNativeFontMethods));
-}
-
-} // namespace android
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index 9785aa537f65..a6fb95832c03 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -23,13 +23,14 @@
#include <set>
#include <algorithm>
-#include "SkPaint.h"
-#include "SkTypeface.h"
#include <hwui/MinikinSkia.h>
#include <hwui/MinikinUtils.h>
#include <hwui/Paint.h>
-#include <minikin/MinikinPaint.h>
#include <minikin/MinikinFont.h>
+#include <minikin/MinikinPaint.h>
+#include "FontUtils.h"
+#include "SkPaint.h"
+#include "SkTypeface.h"
namespace android {
@@ -149,7 +150,8 @@ static jfloat TextShaper_Result_getY(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i
// CriticalNative
static jlong TextShaper_Result_getFont(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
- return reinterpret_cast<jlong>(layout->layout.getFont(i));
+ std::shared_ptr<minikin::Font> fontRef = layout->layout.getFontRef(i);
+ return reinterpret_cast<jlong>(new FontWrapper(std::move(fontRef)));
}
// CriticalNative
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 34df5ddbb210..471a7f7af3b1 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -34,7 +34,7 @@ void LayerDrawable::onDraw(SkCanvas* canvas) {
}
static inline SkScalar isIntegerAligned(SkScalar x) {
- return fabsf(roundf(x) - x) <= NON_ZERO_EPSILON;
+ return MathUtils::isZero(roundf(x) - x);
}
// Disable filtering when there is no scaling in screen coordinates and the corners have the same
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index 3b8caeb3aab1..7cfccb56382c 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -52,7 +52,7 @@ void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
RenderNodeDrawable* childNode = mChildren[drawIndex];
SkASSERT(childNode);
const float casterZ = childNode->getNodeProperties().getZ();
- if (casterZ >= -NON_ZERO_EPSILON) { // draw only children with negative Z
+ if (casterZ >= -MathUtils::NON_ZERO_EPSILON) { // draw only children with negative Z
return;
}
SkAutoCanvasRestore acr(canvas, true);
@@ -86,7 +86,7 @@ void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
const size_t endIndex = zChildren.size();
while (drawIndex < endIndex // draw only children with positive Z
- && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON)
+ && zChildren[drawIndex]->getNodeProperties().getZ() <= MathUtils::NON_ZERO_EPSILON)
drawIndex++;
size_t shadowIndex = drawIndex;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index ee7c4d8bb54a..b2884023a83d 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -194,28 +194,20 @@ SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint) {
return filterPaint(std::move(paint));
}
-static SkDrawLooper* get_looper(const Paint* paint) {
+static BlurDrawLooper* get_looper(const Paint* paint) {
return paint ? paint->getLooper() : nullptr;
}
template <typename Proc>
-void applyLooper(SkDrawLooper* looper, const SkPaint* paint, Proc proc) {
+void applyLooper(BlurDrawLooper* looper, const SkPaint* paint, Proc proc) {
if (looper) {
- SkSTArenaAlloc<256> alloc;
- SkDrawLooper::Context* ctx = looper->makeContext(&alloc);
- if (ctx) {
- SkDrawLooper::Context::Info info;
- for (;;) {
- SkPaint p;
- if (paint) {
- p = *paint;
- }
- if (!ctx->next(&info, &p)) {
- break;
- }
- proc(info.fTranslate.fX, info.fTranslate.fY, &p);
- }
+ SkPaint p;
+ if (paint) {
+ p = *paint;
}
+ looper->apply(p, [&](SkPoint offset, const SkPaint& modifiedPaint) {
+ proc(offset.fX, offset.fY, &modifiedPaint);
+ });
} else {
proc(0, 0, paint);
}
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 85924c5e8939..d998e5031984 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -101,7 +101,8 @@ void CacheManager::trimMemory(TrimMemoryMode mode) {
return;
}
- mGrContext->flushAndSubmit();
+ // flush and submit all work to the gpu and wait for it to finish
+ mGrContext->flushAndSubmit(/*syncCpu=*/true);
switch (mode) {
case TrimMemoryMode::Complete:
@@ -119,11 +120,6 @@ void CacheManager::trimMemory(TrimMemoryMode mode) {
SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
break;
}
-
- // We must sync the cpu to make sure deletions of resources still queued up on the GPU actually
- // happen.
- mGrContext->flush({});
- mGrContext->submit(true);
}
void CacheManager::trimStaleResources() {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index e6ff74ddee21..b760db287bcb 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -108,7 +108,6 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode*
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
mProfiler.setDensity(DeviceInfo::getDensity());
- setRenderAheadDepth(Properties::defaultRenderAhead);
}
CanvasContext::~CanvasContext() {
@@ -134,6 +133,7 @@ void CanvasContext::removeRenderNode(RenderNode* node) {
void CanvasContext::destroy() {
stopDrawing();
setSurface(nullptr);
+ setSurfaceControl(nullptr);
freePrefetchedLayers();
destroyHardwareResources();
mAnimationContext->destroy();
@@ -157,30 +157,40 @@ static void setBufferCount(ANativeWindow* window) {
void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
ATRACE_CALL();
- if (mFixedRenderAhead) {
- mRenderAheadCapacity = mRenderAheadDepth;
- } else {
- if (DeviceInfo::get()->getMaxRefreshRate() > 66.6f) {
- mRenderAheadCapacity = 1;
- } else {
- mRenderAheadCapacity = 0;
- }
- }
-
if (window) {
+ int extraBuffers = 0;
+ native_window_get_extra_buffer_count(window, &extraBuffers);
+
mNativeSurface = std::make_unique<ReliableSurface>(window);
mNativeSurface->init();
if (enableTimeout) {
// TODO: Fix error handling & re-shorten timeout
ANativeWindow_setDequeueTimeout(window, 4000_ms);
}
- mNativeSurface->setExtraBufferCount(mRenderAheadCapacity);
+ mNativeSurface->setExtraBufferCount(extraBuffers);
} else {
mNativeSurface = nullptr;
}
setupPipelineSurface();
}
+void CanvasContext::setSurfaceControl(ASurfaceControl* surfaceControl) {
+ if (surfaceControl == mSurfaceControl) return;
+
+ auto funcs = mRenderThread.getASurfaceControlFunctions();
+
+ if (mSurfaceControl != nullptr) {
+ funcs.unregisterListenerFunc(this, &onSurfaceStatsAvailable);
+ funcs.releaseFunc(mSurfaceControl);
+ }
+ mSurfaceControl = surfaceControl;
+ mExpectSurfaceStats = surfaceControl != nullptr;
+ if (mSurfaceControl != nullptr) {
+ funcs.acquireFunc(mSurfaceControl);
+ funcs.registerListenerFunc(surfaceControl, this, &onSurfaceStatsAvailable);
+ }
+}
+
void CanvasContext::setupPipelineSurface() {
bool hasSurface = mRenderPipeline->setSurface(
mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior);
@@ -326,8 +336,8 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
// just keep using the previous frame's structure instead
if (!wasSkipped(mCurrentFrameInfo)) {
mCurrentFrameInfo = mJankTracker.startFrame();
- mLast4FrameInfos.next().first = mCurrentFrameInfo;
}
+
mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued;
mCurrentFrameInfo->markSyncStart();
@@ -441,39 +451,6 @@ void CanvasContext::notifyFramePending() {
mRenderThread.pushBackFrameCallback(this);
}
-void CanvasContext::setPresentTime() {
- int64_t presentTime = NATIVE_WINDOW_TIMESTAMP_AUTO;
- int renderAhead = 0;
- const auto frameIntervalNanos = mRenderThread.timeLord().frameIntervalNanos();
- if (mFixedRenderAhead) {
- renderAhead = std::min(mRenderAheadDepth, mRenderAheadCapacity);
- } else if (frameIntervalNanos < 15_ms) {
- renderAhead = std::min(1, static_cast<int>(mRenderAheadCapacity));
- }
-
- if (renderAhead) {
- presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) +
- (frameIntervalNanos * (renderAhead + 1)) - DeviceInfo::get()->getAppOffset() +
- (frameIntervalNanos / 2);
- }
-
- // Add code to support pre-rendering feature
- int mode = DEFAULT_MODE;
- if (mNativeSurface) {
- ANativeWindow* anw = mNativeSurface->getNativeWindow();
- anw->query(anw, NATIVE_WINDOW_PRESENT_TIME_MODE, &mode);
- }
- // If pre-rendering is disable, the mode will be DEFAULT_MODE and
- // we will not change the presentTime.
- if (mode == AUTO_TIME_MODE) {
- presentTime = NATIVE_WINDOW_TIMESTAMP_AUTO;
- } else if (mode == SET_CURRENT_TIME_MODE) {
- presentTime = systemTime(SYSTEM_TIME_MONOTONIC);
- }
-
- native_window_set_buffers_timestamp(mNativeSurface->getNativeWindow(), presentTime);
-}
-
void CanvasContext::draw() {
SkRect dirty;
mDamageAccumulator.finish(&dirty);
@@ -493,8 +470,6 @@ void CanvasContext::draw() {
mCurrentFrameInfo->markIssueDrawCommandsStart();
Frame frame = mRenderPipeline->getFrame();
- setPresentTime();
-
SkRect windowDirty = computeDirtyRect(frame, &dirty);
bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
@@ -567,17 +542,14 @@ void CanvasContext::draw() {
}
mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = swap.dequeueDuration;
mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = swap.queueDuration;
- mLast4FrameInfos[-1].second = frameCompleteNr;
mHaveNewSurface = false;
mFrameNumber = -1;
} else {
mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = 0;
mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = 0;
- mLast4FrameInfos[-1].second = -1;
}
- // TODO: Use a fence for real completion?
- mCurrentFrameInfo->markFrameCompleted();
+ mCurrentFrameInfo->markSwapBuffersCompleted();
#if LOG_FRAMETIME_MMA
float thisFrame = mCurrentFrameInfo->duration(FrameInfoIndex::IssueDrawCommandsStart,
@@ -601,30 +573,73 @@ void CanvasContext::draw() {
mFrameCompleteCallbacks.clear();
}
- mJankTracker.finishFrame(*mCurrentFrameInfo);
- if (CC_UNLIKELY(mFrameMetricsReporter.get() != nullptr)) {
- mFrameMetricsReporter->reportFrameMetrics(mCurrentFrameInfo->data());
- }
-
- if (mLast4FrameInfos.size() == mLast4FrameInfos.capacity()) {
- // By looking 4 frames back, we guarantee all SF stats are available. There are at
- // most 3 buffers in BufferQueue. Surface object keeps stats for the last 8 frames.
- FrameInfo* forthBehind = mLast4FrameInfos.front().first;
- int64_t composedFrameId = mLast4FrameInfos.front().second;
- nsecs_t acquireTime = -1;
- if (mNativeSurface) {
- native_window_get_frame_timestamps(mNativeSurface->getNativeWindow(), composedFrameId,
- nullptr, &acquireTime, nullptr, nullptr, nullptr,
- nullptr, nullptr, nullptr, nullptr);
+ if (requireSwap) {
+ if (mExpectSurfaceStats) {
+ std::lock_guard lock(mLast4FrameInfosMutex);
+ std::pair<FrameInfo*, int64_t>& next = mLast4FrameInfos.next();
+ next.first = mCurrentFrameInfo;
+ next.second = frameCompleteNr;
+ } else {
+ mCurrentFrameInfo->markFrameCompleted();
+ mCurrentFrameInfo->set(FrameInfoIndex::GpuCompleted)
+ = mCurrentFrameInfo->get(FrameInfoIndex::FrameCompleted);
+ finishFrame(mCurrentFrameInfo);
}
- // Ignore default -1, NATIVE_WINDOW_TIMESTAMP_INVALID and NATIVE_WINDOW_TIMESTAMP_PENDING
- forthBehind->set(FrameInfoIndex::GpuCompleted) = acquireTime > 0 ? acquireTime : -1;
- mJankTracker.finishGpuDraw(*forthBehind);
}
mRenderThread.cacheManager().onFrameCompleted();
}
+void CanvasContext::finishFrame(FrameInfo* frameInfo) {
+
+ // TODO (b/169858044): Consolidate this into a single call.
+ mJankTracker.finishFrame(*frameInfo);
+ mJankTracker.finishGpuDraw(*frameInfo);
+
+ // TODO (b/169858044): Move this into JankTracker to adjust deadline when queue is
+ // double-stuffed.
+ if (CC_UNLIKELY(mFrameMetricsReporter.get() != nullptr)) {
+ mFrameMetricsReporter->reportFrameMetrics(frameInfo->data());
+ }
+}
+
+void CanvasContext::onSurfaceStatsAvailable(void* context, ASurfaceControl* control,
+ ASurfaceControlStats* stats) {
+
+ CanvasContext* instance = static_cast<CanvasContext*>(context);
+
+ const ASurfaceControlFunctions& functions =
+ instance->mRenderThread.getASurfaceControlFunctions();
+
+ nsecs_t gpuCompleteTime = functions.getAcquireTimeFunc(stats);
+ uint64_t frameNumber = functions.getFrameNumberFunc(stats);
+
+ FrameInfo* frameInfo = nullptr;
+ {
+ std::lock_guard(instance->mLast4FrameInfosMutex);
+ for (size_t i = 0; i < instance->mLast4FrameInfos.size(); i++) {
+ if (instance->mLast4FrameInfos[i].second == frameNumber) {
+ frameInfo = instance->mLast4FrameInfos[i].first;
+ break;
+ }
+ }
+ }
+ if (frameInfo != nullptr) {
+ if (gpuCompleteTime == -1) {
+ gpuCompleteTime = frameInfo->get(FrameInfoIndex::SwapBuffersCompleted);
+ }
+ if (gpuCompleteTime < frameInfo->get(FrameInfoIndex::SwapBuffers)) {
+ // TODO (b/180488606): Investigate why this can happen for first frames.
+ ALOGW("Impossible GPU complete time swapBuffers=%" PRIi64 " gpuComplete=%" PRIi64,
+ frameInfo->get(FrameInfoIndex::SwapBuffers), gpuCompleteTime);
+ gpuCompleteTime = frameInfo->get(FrameInfoIndex::SwapBuffersCompleted);
+ }
+ frameInfo->set(FrameInfoIndex::FrameCompleted) = gpuCompleteTime;
+ frameInfo->set(FrameInfoIndex::GpuCompleted) = gpuCompleteTime;
+ instance->finishFrame(frameInfo);
+ }
+}
+
// Called by choreographer to do an RT-driven animation
void CanvasContext::doFrame() {
if (!mRenderPipeline->isSurfaceReady()) return;
@@ -780,19 +795,6 @@ bool CanvasContext::surfaceRequiresRedraw() {
return width != mLastFrameWidth || height != mLastFrameHeight;
}
-void CanvasContext::setRenderAheadDepth(int renderAhead) {
- if (renderAhead > 2 || renderAhead < -1 || mNativeSurface) {
- return;
- }
- if (renderAhead == -1) {
- mFixedRenderAhead = false;
- mRenderAheadDepth = 0;
- } else {
- mFixedRenderAhead = true;
- mRenderAheadDepth = static_cast<uint32_t>(renderAhead);
- }
-}
-
SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) {
if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) {
// can't rely on prior content of window if viewport size changes
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 03a46a6ab5a4..974c984f3120 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -37,6 +37,7 @@
#include <SkSize.h>
#include <cutils/compiler.h>
#include <utils/Functor.h>
+#include <utils/Mutex.h>
#include <functional>
#include <future>
@@ -118,6 +119,7 @@ public:
void setSwapBehavior(SwapBehavior swapBehavior);
void setSurface(ANativeWindow* window, bool enableTimeout = true);
+ void setSurfaceControl(ASurfaceControl* surfaceControl);
bool pauseSurface();
void setStopped(bool stopped);
bool hasSurface() const { return mNativeSurface.get(); }
@@ -199,11 +201,12 @@ public:
return mUseForceDark;
}
- // Must be called before setSurface
- void setRenderAheadDepth(int renderAhead);
-
SkISize getNextFrameSize() const;
+ // Called when SurfaceStats are available.
+ static void onSurfaceStatsAvailable(void* context, ASurfaceControl* control,
+ ASurfaceControlStats* stats);
+
private:
CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
@@ -217,10 +220,10 @@ private:
bool isSwapChainStuffed();
bool surfaceRequiresRedraw();
- void setPresentTime();
void setupPipelineSurface();
SkRect computeDirtyRect(const Frame& frame, SkRect* dirty);
+ void finishFrame(FrameInfo* frameInfo);
// The same type as Frame.mWidth and Frame.mHeight
int32_t mLastFrameWidth = 0;
@@ -228,6 +231,9 @@ private:
RenderThread& mRenderThread;
std::unique_ptr<ReliableSurface> mNativeSurface;
+ // The SurfaceControl reference is passed from ViewRootImpl, can be set to
+ // NULL to remove the reference
+ ASurfaceControl* mSurfaceControl = nullptr;
// stopped indicates the CanvasContext will reject actual redraw operations,
// and defer repaint until it is un-stopped
bool mStopped = false;
@@ -238,9 +244,6 @@ private:
// painted onto its surface.
bool mIsDirty = false;
SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default;
- bool mFixedRenderAhead = false;
- uint32_t mRenderAheadDepth = 0;
- uint32_t mRenderAheadCapacity = 0;
struct SwapHistory {
SkRect damage;
nsecs_t vsyncTime;
@@ -270,7 +273,12 @@ private:
std::vector<sp<RenderNode>> mRenderNodes;
FrameInfo* mCurrentFrameInfo = nullptr;
- RingBuffer<std::pair<FrameInfo*, int64_t>, 4> mLast4FrameInfos;
+
+ // List of frames that are awaiting GPU completion reporting
+ RingBuffer<std::pair<FrameInfo*, int64_t>, 4> mLast4FrameInfos
+ GUARDED_BY(mLast4FrameInfosMutex);
+ std::mutex mLast4FrameInfosMutex;
+
std::string mName;
JankTracker mJankTracker;
FrameInfoVisualizer mProfiler;
@@ -285,6 +293,9 @@ private:
std::unique_ptr<IRenderPipeline> mRenderPipeline;
std::vector<std::function<void(int64_t)>> mFrameCompleteCallbacks;
+
+ // If set to true, we expect that callbacks into onSurfaceStatsAvailable
+ bool mExpectSurfaceStats = false;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index b51f6dcfc66f..b9568fcf8e66 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -84,6 +84,19 @@ void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) {
});
}
+void RenderProxy::setSurfaceControl(ASurfaceControl* surfaceControl) {
+ auto funcs = mRenderThread.getASurfaceControlFunctions();
+ if (surfaceControl) {
+ funcs.acquireFunc(surfaceControl);
+ }
+ mRenderThread.queue().post([this, control = surfaceControl, funcs]() mutable {
+ mContext->setSurfaceControl(control);
+ if (control) {
+ funcs.releaseFunc(control);
+ }
+ });
+}
+
void RenderProxy::allocateBuffers() {
mRenderThread.queue().post([=]() { mContext->allocateBuffers(); });
}
@@ -202,6 +215,7 @@ void RenderProxy::notifyFramePending() {
void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) {
mRenderThread.queue().runSync([&]() {
+ std::lock_guard lock(mRenderThread.getJankDataMutex());
mContext->profiler().dumpData(fd);
if (dumpFlags & DumpFlags::FrameStats) {
mContext->dumpFrames(fd);
@@ -221,6 +235,7 @@ void RenderProxy::resetProfileInfo() {
uint32_t RenderProxy::frameTimePercentile(int percentile) {
return mRenderThread.queue().runSync([&]() -> auto {
+ std::lock_guard lock(mRenderThread.globalProfileData().getDataMutex());
return mRenderThread.globalProfileData()->findPercentile(percentile);
});
}
@@ -295,11 +310,6 @@ void RenderProxy::setForceDark(bool enable) {
mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); });
}
-void RenderProxy::setRenderAheadDepth(int renderAhead) {
- mRenderThread.queue().post(
- [context = mContext, renderAhead] { context->setRenderAheadDepth(renderAhead); });
-}
-
int RenderProxy::copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom,
SkBitmap* bitmap) {
auto& thread = RenderThread::getInstance();
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 33dabc9895b1..366d6b5a172c 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -20,6 +20,7 @@
#include <SkBitmap.h>
#include <android/native_window.h>
#include <cutils/compiler.h>
+#include <android/surface_control.h>
#include <utils/Functor.h>
#include "../FrameMetricsObserver.h"
@@ -72,6 +73,7 @@ public:
void setName(const char* name);
void setSurface(ANativeWindow* window, bool enableTimeout = true);
+ void setSurfaceControl(ASurfaceControl* surfaceControl);
void allocateBuffers();
bool pause();
void setStopped(bool stopped);
@@ -123,23 +125,6 @@ public:
void removeFrameMetricsObserver(FrameMetricsObserver* observer);
void setForceDark(bool enable);
- /**
- * Sets a render-ahead depth on the backing renderer. This will increase latency by
- * <swapInterval> * renderAhead and increase memory usage by (3 + renderAhead) * <resolution>.
- * In return the renderer will be less susceptible to jitter, resulting in a smoother animation.
- *
- * Not recommended to use in response to anything touch driven, but for canned animations
- * where latency is not a concern careful use may be beneficial.
- *
- * Note that when increasing this there will be a frame gap of N frames where N is
- * renderAhead - <current renderAhead>. When decreasing this if there are any pending
- * frames they will retain their prior renderAhead value, so it will take a few frames
- * for the decrease to flush through.
- *
- * @param renderAhead How far to render ahead, must be in the range [0..2]
- */
- void setRenderAheadDepth(int renderAhead);
-
static int copySurfaceInto(ANativeWindow* window, int left, int top, int right,
int bottom, SkBitmap* bitmap);
static void prepareToDraw(Bitmap& bitmap);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 7750a31b817f..5dc02e8454ac 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -33,6 +33,7 @@
#include <GrContextOptions.h>
#include <gl/GrGLInterface.h>
+#include <dlfcn.h>
#include <sys/resource.h>
#include <utils/Condition.h>
#include <utils/Log.h>
@@ -49,6 +50,37 @@ static bool gHasRenderThreadInstance = false;
static JVMAttachHook gOnStartHook = nullptr;
+ASurfaceControlFunctions::ASurfaceControlFunctions() {
+ void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
+ acquireFunc = (ASC_acquire) dlsym(handle_, "ASurfaceControl_acquire");
+ LOG_ALWAYS_FATAL_IF(acquireFunc == nullptr,
+ "Failed to find required symbol ASurfaceControl_acquire!");
+
+ releaseFunc = (ASC_release) dlsym(handle_, "ASurfaceControl_release");
+ LOG_ALWAYS_FATAL_IF(releaseFunc == nullptr,
+ "Failed to find required symbol ASurfaceControl_release!");
+
+ registerListenerFunc = (ASC_registerSurfaceStatsListener) dlsym(handle_,
+ "ASurfaceControl_registerSurfaceStatsListener");
+ LOG_ALWAYS_FATAL_IF(registerListenerFunc == nullptr,
+ "Failed to find required symbol ASurfaceControl_registerSurfaceStatsListener!");
+
+ unregisterListenerFunc = (ASC_unregisterSurfaceStatsListener) dlsym(handle_,
+ "ASurfaceControl_unregisterSurfaceStatsListener");
+ LOG_ALWAYS_FATAL_IF(unregisterListenerFunc == nullptr,
+ "Failed to find required symbol ASurfaceControl_unregisterSurfaceStatsListener!");
+
+ getAcquireTimeFunc = (ASCStats_getAcquireTime) dlsym(handle_,
+ "ASurfaceControlStats_getAcquireTime");
+ LOG_ALWAYS_FATAL_IF(getAcquireTimeFunc == nullptr,
+ "Failed to find required symbol ASurfaceControlStats_getAcquireTime!");
+
+ getFrameNumberFunc = (ASCStats_getFrameNumber) dlsym(handle_,
+ "ASurfaceControlStats_getFrameNumber");
+ LOG_ALWAYS_FATAL_IF(getFrameNumberFunc == nullptr,
+ "Failed to find required symbol ASurfaceControlStats_getFrameNumber!");
+}
+
void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) {
RenderThread* rt = reinterpret_cast<RenderThread*>(data);
int64_t vsyncId = AChoreographer_getVsyncId(rt->mChoreographer);
@@ -134,7 +166,8 @@ RenderThread::RenderThread()
, mFrameCallbackTaskPending(false)
, mRenderState(nullptr)
, mEglManager(nullptr)
- , mFunctorManager(WebViewFunctorManager::instance()) {
+ , mFunctorManager(WebViewFunctorManager::instance())
+ , mGlobalProfileData(mJankDataMutex) {
Properties::load();
start("RenderThread");
}
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 4fbb07168ac0..a7d1ba8dafd7 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -17,6 +17,7 @@
#ifndef RENDERTHREAD_H_
#define RENDERTHREAD_H_
+#include <surface_control_private.h>
#include <GrDirectContext.h>
#include <SkBitmap.h>
#include <cutils/compiler.h>
@@ -78,6 +79,27 @@ struct VsyncSource {
virtual ~VsyncSource() {}
};
+typedef void (*ASC_acquire)(ASurfaceControl* control);
+typedef void (*ASC_release)(ASurfaceControl* control);
+
+typedef void (*ASC_registerSurfaceStatsListener)(ASurfaceControl* control, void* context,
+ ASurfaceControl_SurfaceStatsListener func);
+typedef void (*ASC_unregisterSurfaceStatsListener)(void* context,
+ ASurfaceControl_SurfaceStatsListener func);
+
+typedef int64_t (*ASCStats_getAcquireTime)(ASurfaceControlStats* stats);
+typedef uint64_t (*ASCStats_getFrameNumber)(ASurfaceControlStats* stats);
+
+struct ASurfaceControlFunctions {
+ ASurfaceControlFunctions();
+ ASC_acquire acquireFunc;
+ ASC_release releaseFunc;
+ ASC_registerSurfaceStatsListener registerListenerFunc;
+ ASC_unregisterSurfaceStatsListener unregisterListenerFunc;
+ ASCStats_getAcquireTime getAcquireTimeFunc;
+ ASCStats_getFrameNumber getFrameNumberFunc;
+};
+
class ChoreographerSource;
class DummyVsyncSource;
@@ -104,6 +126,7 @@ public:
RenderState& renderState() const { return *mRenderState; }
EglManager& eglManager() const { return *mEglManager; }
ProfileDataContainer& globalProfileData() { return mGlobalProfileData; }
+ std::mutex& getJankDataMutex() { return mJankDataMutex; }
Readback& readback();
GrDirectContext* getGrContext() const { return mGrContext.get(); }
@@ -121,6 +144,10 @@ public:
void preload();
+ const ASurfaceControlFunctions& getASurfaceControlFunctions() {
+ return mASurfaceControlFunctions;
+ }
+
/**
* isCurrent provides a way to query, if the caller is running on
* the render thread.
@@ -189,6 +216,9 @@ private:
sk_sp<GrDirectContext> mGrContext;
CacheManager* mCacheManager;
sp<VulkanManager> mVkManager;
+
+ ASurfaceControlFunctions mASurfaceControlFunctions;
+ std::mutex mJankDataMutex;
};
} /* namespace renderthread */
diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index 06f158f25fc5..6a9a98d6743b 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -40,9 +40,9 @@ const DisplayInfo& getDisplayInfo() {
return info;
}
-const DisplayConfig& getActiveDisplayConfig() {
- static DisplayConfig config = [] {
- DisplayConfig config;
+const ui::DisplayMode& getActiveDisplayMode() {
+ static ui::DisplayMode config = [] {
+ ui::DisplayMode config;
#if HWUI_NULL_GPU
config.resolution = ui::Size(1080, 1920);
config.xDpi = config.yDpi = 320.f;
@@ -51,7 +51,7 @@ const DisplayConfig& getActiveDisplayConfig() {
const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken();
LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__);
- const status_t status = SurfaceComposerClient::getActiveDisplayConfig(token, &config);
+ const status_t status = SurfaceComposerClient::getActiveDisplayMode(token, &config);
LOG_ALWAYS_FATAL_IF(status, "%s: Failed to get active display config", __FUNCTION__);
#endif
return config;
diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h
index a012ecb1a1d3..7d2f6d8ea731 100644
--- a/libs/hwui/tests/common/TestContext.h
+++ b/libs/hwui/tests/common/TestContext.h
@@ -23,8 +23,8 @@
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
-#include <ui/DisplayConfig.h>
#include <ui/DisplayInfo.h>
+#include <ui/DisplayMode.h>
#include <utils/Looper.h>
#include <atomic>
@@ -37,10 +37,10 @@ namespace uirenderer {
namespace test {
const DisplayInfo& getDisplayInfo();
-const DisplayConfig& getActiveDisplayConfig();
+const ui::DisplayMode& getActiveDisplayMode();
inline const ui::Size& getActiveDisplayResolution() {
- return getActiveDisplayConfig().resolution;
+ return getActiveDisplayMode().resolution;
}
class TestContext {
diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h
index 74a039b3d090..91022cfe734b 100644
--- a/libs/hwui/tests/common/TestScene.h
+++ b/libs/hwui/tests/common/TestScene.h
@@ -38,7 +38,6 @@ public:
int count = 0;
int reportFrametimeWeight = 0;
bool renderOffscreen = true;
- int renderAhead = 0;
};
template <class T>
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index eda5d2266dcf..8c7d2612f39b 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -153,11 +153,6 @@ void run(const TestScene::Info& info, const TestScene::Options& opts,
proxy->resetProfileInfo();
proxy->fence();
- if (opts.renderAhead) {
- usleep(33000);
- }
- proxy->setRenderAheadDepth(opts.renderAhead);
-
ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight);
nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC);
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index 88d33c315a09..174a14080eff 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -69,7 +69,6 @@ OPTIONS:
are offscreen rendered
--benchmark_format Set output format. Possible values are tabular, json, csv
--renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk
- --render-ahead=NUM Sets how far to render-ahead. Must be 0 (default), 1, or 2.
)");
}
@@ -171,7 +170,6 @@ enum {
Onscreen,
Offscreen,
Renderer,
- RenderAhead,
};
}
@@ -187,7 +185,6 @@ static const struct option LONG_OPTIONS[] = {
{"onscreen", no_argument, nullptr, LongOpts::Onscreen},
{"offscreen", no_argument, nullptr, LongOpts::Offscreen},
{"renderer", required_argument, nullptr, LongOpts::Renderer},
- {"render-ahead", required_argument, nullptr, LongOpts::RenderAhead},
{0, 0, 0, 0}};
static const char* SHORT_OPTIONS = "c:r:h";
@@ -286,16 +283,6 @@ void parseOptions(int argc, char* argv[]) {
gOpts.renderOffscreen = true;
break;
- case LongOpts::RenderAhead:
- if (!optarg) {
- error = true;
- }
- gOpts.renderAhead = atoi(optarg);
- if (gOpts.renderAhead < 0 || gOpts.renderAhead > 2) {
- error = true;
- }
- break;
-
case 'h':
printHelp();
exit(EXIT_SUCCESS);
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index 7951537e1525..a1ba70a22581 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -16,7 +16,6 @@
#include "tests/common/TestUtils.h"
-#include <SkBlurDrawLooper.h>
#include <SkColorMatrixFilter.h>
#include <SkColorSpace.h>
#include <SkImagePriv.h>
@@ -85,15 +84,3 @@ TEST(SkiaBehavior, srgbColorSpaceIsSingleton) {
ASSERT_EQ(sRGB1.get(), sRGB2.get());
}
-TEST(SkiaBehavior, blurDrawLooper) {
- sk_sp<SkDrawLooper> looper = SkBlurDrawLooper::Make(SK_ColorRED, 5.0f, 3.0f, 4.0f);
-
- SkDrawLooper::BlurShadowRec blur;
- bool success = looper->asABlurShadow(&blur);
- ASSERT_TRUE(success);
-
- ASSERT_EQ(SK_ColorRED, blur.fColor);
- ASSERT_EQ(5.0f, blur.fSigma);
- ASSERT_EQ(3.0f, blur.fOffset.fX);
- ASSERT_EQ(4.0f, blur.fOffset.fY);
-}
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index f77ca2a8c06c..dae3c9435712 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -17,7 +17,6 @@
#include "tests/common/TestUtils.h"
#include <hwui/Paint.h>
-#include <SkBlurDrawLooper.h>
#include <SkCanvasStateUtils.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
@@ -37,7 +36,7 @@ TEST(SkiaCanvas, drawShadowLayer) {
// it is transparent to ensure that we still draw the rect since it has a looper
paint.setColor(SK_ColorTRANSPARENT);
// this is how view's shadow layers are implemented
- paint.setLooper(SkBlurDrawLooper::Make(0xF0000000, 6.0f, 0, 10));
+ paint.setLooper(BlurDrawLooper::Make({0, 0, 0, 240.0f / 255}, nullptr, 6.0f, {0, 10}));
canvas.drawRect(3, 3, 7, 7, paint);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 5d2aa2ff83c9..ab23448ab93f 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -57,7 +57,7 @@ std::shared_ptr<minikin::FontFamily> buildFamily(const char* fileName) {
sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData)));
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
std::shared_ptr<minikin::MinikinFont> font =
- std::make_shared<MinikinFontSkia>(std::move(typeface), data, st.st_size, fileName, 0,
+ std::make_shared<MinikinFontSkia>(std::move(typeface), 0, data, st.st_size, fileName, 0,
std::vector<minikin::FontVariation>());
std::vector<std::shared_ptr<minikin::Font>> fonts;
fonts.push_back(minikin::Font::Builder(font).build());
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 62bf39ca8a7a..1d3f9d701eed 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -22,11 +22,11 @@
namespace android {
namespace uirenderer {
-#define NON_ZERO_EPSILON (0.001f)
-#define ALPHA_EPSILON (0.001f)
-
class MathUtils {
public:
+ static constexpr float NON_ZERO_EPSILON = 0.001f;
+ static constexpr float ALPHA_EPSILON = 0.001f;
+
/**
* Check for floats that are close enough to zero.
*/
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index e2fdf2fcb5a5..09c6a4fdf50d 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -20,7 +20,6 @@
#include <utils/Blur.h>
#include <SkColorFilter.h>
-#include <SkDrawLooper.h>
#include <SkPaint.h>
#include <SkShader.h>
diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp
index d291ec001daf..f322bff8e6ee 100644
--- a/libs/incident/Android.bp
+++ b/libs/incident/Android.bp
@@ -13,6 +13,15 @@
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_defaults {
name: "libincidentpriv_defaults",
@@ -95,7 +104,7 @@ cc_test {
name: "libincident_test",
test_config: "AndroidTest.xml",
defaults: ["libincidentpriv_defaults"],
- test_suites: ["device-tests", "mts"],
+ test_suites: ["device-tests", "mts-statsd"],
compile_multilib: "both",
multilib: {
lib64: {
@@ -125,6 +134,3 @@ cc_test {
"libgmock",
],
}
-
-
-
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index dca35012cbdd..55f932dffff1 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_shared {
name: "libinputservice",
srcs: [
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 213b3adfb2a8..4eabfb238f4a 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_test {
name: "libinputservice_test",
srcs: [
diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp
index d2b7d5c75d58..132d71eb6db8 100644
--- a/libs/protoutil/Android.bp
+++ b/libs/protoutil/Android.bp
@@ -12,6 +12,15 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_defaults {
name: "libprotoutil_defaults",
diff --git a/libs/services/Android.bp b/libs/services/Android.bp
index 1e621079b532..bf2e764aae6a 100644
--- a/libs/services/Android.bp
+++ b/libs/services/Android.bp
@@ -14,6 +14,15 @@
// Provides C++ wrappers for system services.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_shared {
name: "libservices",
srcs: [
diff --git a/libs/storage/Android.bp b/libs/storage/Android.bp
index c19933e39c96..e8c6c071bc06 100644
--- a/libs/storage/Android.bp
+++ b/libs/storage/Android.bp
@@ -1,3 +1,20 @@
+package {
+ default_applicable_licenses: ["frameworks_base_libs_storage_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_libs_storage_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
cc_library_static {
name: "libstorage",
diff --git a/libs/tracingproxy/Android.bp b/libs/tracingproxy/Android.bp
new file mode 100644
index 000000000000..7126bfac773d
--- /dev/null
+++ b/libs/tracingproxy/Android.bp
@@ -0,0 +1,53 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Provides C++ wrappers for system services.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-MIT
+ // SPDX-license-identifier-Unicode-DFS
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+cc_library_shared {
+ name: "libtracingproxy",
+
+ aidl: {
+ export_aidl_headers: true,
+ include_dirs: [
+ "frameworks/base/core/java",
+ ],
+ },
+
+ srcs: [
+ ":ITracingServiceProxy.aidl",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/libs/usb/Android.bp b/libs/usb/Android.bp
index e752b55f5ef7..edc847487dcb 100644
--- a/libs/usb/Android.bp
+++ b/libs/usb/Android.bp
@@ -14,6 +14,16 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-GPL-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_sdk_library {
name: "com.android.future.usb.accessory",
srcs: ["src/**/*.java"],
diff --git a/libs/usb/tests/AccessoryChat/Android.bp b/libs/usb/tests/AccessoryChat/Android.bp
index 19ed3d3ef52e..72090fb692e0 100644
--- a/libs/usb/tests/AccessoryChat/Android.bp
+++ b/libs/usb/tests/AccessoryChat/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AccessoryChat",
diff --git a/libs/usb/tests/AccessoryChat/accessorychat/Android.bp b/libs/usb/tests/AccessoryChat/accessorychat/Android.bp
index 5613745e966b..108649aa92ae 100644
--- a/libs/usb/tests/AccessoryChat/accessorychat/Android.bp
+++ b/libs/usb/tests/AccessoryChat/accessorychat/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_binary {
name: "accessorychat",
host_supported: true,
diff --git a/libs/usb/tests/accessorytest/Android.bp b/libs/usb/tests/accessorytest/Android.bp
index c6340e32e60c..69761aeb46fe 100644
--- a/libs/usb/tests/accessorytest/Android.bp
+++ b/libs/usb/tests/accessorytest/Android.bp
@@ -1,3 +1,13 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-GPL-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_binary_host {
name: "accessorytest",
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 65721cc33aed..adf58da6a072 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -36,6 +36,7 @@ import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationRequest;
import android.location.LocationTime;
+import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.os.Bundle;
import android.os.ICancellationSignal;
@@ -91,6 +92,9 @@ interface ILocationManager
void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, @nullable String attributionTag);
void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
+ void addProviderRequestListener(in IProviderRequestListener listener);
+ void removeProviderRequestListener(in IProviderRequestListener listener);
+
int getGnssBatchSize();
void startGnssBatch(long periodNanos, in ILocationListener listener, String packageName, @nullable String attributionTag, @nullable String listenerId);
void flushGnssBatch();
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index a7e9a0de538a..5e2e5595ba9c 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -569,7 +569,12 @@ public class Location implements Parcelable {
/** @hide */
public long getElapsedRealtimeAgeMillis() {
- return NANOSECONDS.toMillis(getElapsedRealtimeAgeNanos());
+ return getElapsedRealtimeAgeMillis(SystemClock.elapsedRealtime());
+ }
+
+ /** @hide */
+ public long getElapsedRealtimeAgeMillis(long referenceRealtimeMs) {
+ return referenceRealtimeMs - NANOSECONDS.toMillis(mElapsedRealtimeNanos);
}
/**
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 8f6a26ca7fc9..70d79e5d44bb 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -49,7 +49,10 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
+import android.location.provider.ProviderRequest.Listener;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -436,6 +439,9 @@ public class LocationManager {
new GnssNavigationTransportManager();
}
+ private static final ProviderRequestTransportManager sProviderRequestListeners =
+ new ProviderRequestTransportManager();
+
private final Context mContext;
private final ILocationManager mService;
@@ -2790,6 +2796,37 @@ public class LocationManager {
}
/**
+ * Registers a {@link ProviderRequest.Listener} to all providers.
+ *
+ * @param executor the executor that the callback runs on
+ * @param listener the listener to register
+ * @return {@code true} always
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public boolean registerProviderRequestListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Listener listener) {
+ sProviderRequestListeners.addListener(listener,
+ new ProviderRequestTransport(executor, listener));
+ return true;
+ }
+
+ /**
+ * Unregisters a {@link ProviderRequest.Listener}.
+ *
+ * @param listener the listener to remove.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public void unregisterProviderRequestListener(
+ @NonNull Listener listener) {
+ sProviderRequestListeners.removeListener(listener);
+ }
+
+ /**
* Returns the batch size (in number of Location objects) that are supported by the batching
* interface.
*
@@ -2978,6 +3015,22 @@ public class LocationManager {
}
}
+ private static class ProviderRequestTransportManager extends
+ ListenerTransportManager<ProviderRequestTransport> {
+
+ @Override
+ protected void registerTransport(ProviderRequestTransport transport)
+ throws RemoteException {
+ getService().addProviderRequestListener(transport);
+ }
+
+ @Override
+ protected void unregisterTransport(ProviderRequestTransport transport)
+ throws RemoteException {
+ getService().removeProviderRequestListener(transport);
+ }
+ }
+
private static class GetCurrentLocationTransport extends ILocationCallback.Stub implements
ListenerExecutor, CancellationSignal.OnCancelListener {
@@ -3377,6 +3430,36 @@ public class LocationManager {
}
}
+ private static class ProviderRequestTransport extends IProviderRequestListener.Stub
+ implements ListenerTransport<ProviderRequest.Listener> {
+
+ private final Executor mExecutor;
+
+ private volatile @Nullable ProviderRequest.Listener mListener;
+
+ ProviderRequestTransport(Executor executor, ProviderRequest.Listener listener) {
+ Preconditions.checkArgument(executor != null, "invalid null executor");
+ Preconditions.checkArgument(listener != null, "invalid null callback");
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void unregister() {
+ mListener = null;
+ }
+
+ @Override
+ public @Nullable ProviderRequest.Listener getListener() {
+ return mListener;
+ }
+
+ @Override
+ public void onProviderRequestChanged(String provider, ProviderRequest request) {
+ execute(mExecutor, listener -> listener.onProviderRequestChanged(provider, request));
+ }
+ }
+
private static class BatchedLocationCallbackWrapper implements LocationListener {
private final BatchedLocationCallback mCallback;
diff --git a/location/java/android/location/provider/IProviderRequestListener.aidl b/location/java/android/location/provider/IProviderRequestListener.aidl
new file mode 100644
index 000000000000..89d454aafe2c
--- /dev/null
+++ b/location/java/android/location/provider/IProviderRequestListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.location.provider;
+
+import android.location.provider.ProviderRequest;
+
+/**
+ * {@hide}
+ */
+oneway interface IProviderRequestListener {
+ void onProviderRequestChanged(String provider, in ProviderRequest request);
+}
diff --git a/location/java/android/location/provider/ProviderRequest.java b/location/java/android/location/provider/ProviderRequest.java
index e543b040a2d4..b6ec32309b08 100644
--- a/location/java/android/location/provider/ProviderRequest.java
+++ b/location/java/android/location/provider/ProviderRequest.java
@@ -53,6 +53,17 @@ public final class ProviderRequest implements Parcelable {
private final boolean mLocationSettingsIgnored;
private final WorkSource mWorkSource;
+ /**
+ * Listener to be invoked when a new request is set to the provider.
+ */
+ public interface Listener {
+
+ /**
+ * Invoked when a new request is set.
+ */
+ void onProviderRequestChanged(@NonNull String provider, @NonNull ProviderRequest request);
+ }
+
private ProviderRequest(
long intervalMillis,
@Quality int quality,
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
index c0188c0df497..696125ca586a 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_sdk_library {
name: "com.android.location.provider",
srcs: ["java/**/*.java"],
diff --git a/lowpan/tests/Android.bp b/lowpan/tests/Android.bp
index ad2bc27d1b39..590892926ec1 100644
--- a/lowpan/tests/Android.bp
+++ b/lowpan/tests/Android.bp
@@ -14,6 +14,15 @@
// Make test APK
// ============================================================
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksLowpanApiTests",
srcs: ["**/*.java"],
diff --git a/media/Android.bp b/media/Android.bp
index 8b06bb26fcf7..9268b22a929a 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
aidl_interface {
name: "audio_common-aidl",
unstable: true,
diff --git a/media/OWNERS b/media/OWNERS
index e74149019b11..abfc8bfa976e 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -26,3 +26,7 @@ olly@google.com
# SEO
sungsoo@google.com
+
+# SEA/KIR/BVE
+jtinker@google.com
+robertshih@google.com
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index cf2f0f0da2b7..a7ed09118817 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -10,7 +10,7 @@
"include-filter": "com.google.android.media.gts.WidevineGenericOpsTests"
},
{
- "include-filter": "com.google.android.media.gts.WidevineYouTubePerformanceTests"
+ "include-filter": "com.google.android.media.gts.WidevineH264PlaybackTests"
}
]
}
diff --git a/media/java/Android.bp b/media/java/Android.bp
index 0810699abf0a..aea63a073e95 100644
--- a/media/java/Android.bp
+++ b/media/java/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "IMidiDeviceServer.aidl",
srcs: ["android/media/midi/IMidiDeviceServer.aidl"],
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index d40de76431e4..205c1f4b4057 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -354,7 +354,7 @@ public final class AudioDeviceInfo {
/**
* @hide
- * @return the internal device tyoe
+ * @return the internal device type
*/
public int getInternalType() {
return mPort.type();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 71bea348409f..e0c86b7b9d92 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1,5 +1,4 @@
/*
-/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -568,6 +567,25 @@ public class AudioManager {
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int FLAG_FROM_KEY = 1 << 12;
+ /** @hide */
+ @IntDef(flag = false, prefix = "FLAG", value = {
+ FLAG_SHOW_UI,
+ FLAG_ALLOW_RINGER_MODES,
+ FLAG_PLAY_SOUND,
+ FLAG_REMOVE_SOUND_AND_VIBRATE,
+ FLAG_VIBRATE,
+ FLAG_FIXED_VOLUME,
+ FLAG_BLUETOOTH_ABS_VOLUME,
+ FLAG_SHOW_SILENT_HINT,
+ FLAG_HDMI_SYSTEM_AUDIO_VOLUME,
+ FLAG_ACTIVE_MEDIA_ONLY,
+ FLAG_SHOW_UI_WARNINGS,
+ FLAG_SHOW_VIBRATE_HINT,
+ FLAG_FROM_KEY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Flags {}
+
// The iterator of TreeMap#entrySet() returns the entries in ascending key order.
private static final TreeMap<Integer, String> FLAG_NAMES = new TreeMap<>();
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 812dac97bcd3..ede1dbf47d36 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -20,6 +20,7 @@ import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_ALL;
import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -180,6 +181,21 @@ public final class AudioPlaybackConfiguration implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface PlayerState {}
+ /** @hide */
+ public static String playerStateToString(@PlayerState int state) {
+ switch (state) {
+ case PLAYER_STATE_UNKNOWN: return "PLAYER_STATE_UNKNOWN";
+ case PLAYER_STATE_RELEASED: return "PLAYER_STATE_RELEASED";
+ case PLAYER_STATE_IDLE: return "PLAYER_STATE_IDLE";
+ case PLAYER_STATE_STARTED: return "PLAYER_STATE_STARTED";
+ case PLAYER_STATE_PAUSED: return "PLAYER_STATE_PAUSED";
+ case PLAYER_STATE_STOPPED: return "PLAYER_STATE_STOPPED";
+ case PLAYER_UPDATE_DEVICE_ID: return "PLAYER_UPDATE_DEVICE_ID";
+ default:
+ return "invalid state " + state;
+ }
+ }
+
// immutable data
private final int mPlayerIId;
@@ -195,6 +211,8 @@ public final class AudioPlaybackConfiguration implements Parcelable {
private int mDeviceId;
+ private int mSessionId;
+
/**
* Never use without initializing parameters afterwards
*/
@@ -207,7 +225,10 @@ public final class AudioPlaybackConfiguration implements Parcelable {
* @hide
*/
public AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid) {
- if (DEBUG) { Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer); }
+ if (DEBUG) {
+ Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer
+ + " sessionId=" + pic.mSessionId);
+ }
mPlayerIId = piid;
mPlayerType = pic.mPlayerType;
mClientUid = uid;
@@ -220,6 +241,7 @@ public final class AudioPlaybackConfiguration implements Parcelable {
} else {
mIPlayerShell = null;
}
+ mSessionId = pic.mSessionId;
}
/**
@@ -259,6 +281,7 @@ public final class AudioPlaybackConfiguration implements Parcelable {
anonymCopy.mClientUid = PLAYER_UPID_INVALID;
anonymCopy.mClientPid = PLAYER_UPID_INVALID;
anonymCopy.mIPlayerShell = null;
+ anonymCopy.mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE;
return anonymCopy;
}
@@ -303,6 +326,17 @@ public final class AudioPlaybackConfiguration implements Parcelable {
/**
* @hide
+ * Return the audio session ID associated with this player.
+ * See {@link AudioManager#generateAudioSessionId()}.
+ * @return an audio session ID
+ */
+ @SystemApi
+ public @IntRange(from = 0) int getSessionId() {
+ return mSessionId;
+ }
+
+ /**
+ * @hide
* Return the type of player linked to this configuration.
* <br>Note that player types not exposed in the system API will be represented as
* {@link #PLAYER_TYPE_UNKNOWN}.
@@ -381,6 +415,17 @@ public final class AudioPlaybackConfiguration implements Parcelable {
/**
* @hide
+ * Handle a change of audio session Id
+ * @param sessionId the audio session ID
+ */
+ public boolean handleSessionIdEvent(int sessionId) {
+ final boolean changed = sessionId != mSessionId;
+ mSessionId = sessionId;
+ return changed;
+ }
+
+ /**
+ * @hide
* Handle a player state change
* @param event
* @param deviceId active device id or {@Code PLAYER_DEVICEID_INVALID}
@@ -476,7 +521,8 @@ public final class AudioPlaybackConfiguration implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid);
+ return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid,
+ mSessionId);
}
@Override
@@ -498,6 +544,7 @@ public final class AudioPlaybackConfiguration implements Parcelable {
ips = mIPlayerShell;
}
dest.writeStrongInterface(ips == null ? null : ips.getIPlayer());
+ dest.writeInt(mSessionId);
}
private AudioPlaybackConfiguration(Parcel in) {
@@ -510,6 +557,7 @@ public final class AudioPlaybackConfiguration implements Parcelable {
mPlayerAttr = AudioAttributes.CREATOR.createFromParcel(in);
final IPlayer p = IPlayer.Stub.asInterface(in.readStrongBinder());
mIPlayerShell = (p == null) ? null : new IPlayerShell(null, p);
+ mSessionId = in.readInt();
}
@Override
@@ -523,7 +571,8 @@ public final class AudioPlaybackConfiguration implements Parcelable {
&& (mDeviceId == that.mDeviceId)
&& (mPlayerType == that.mPlayerType)
&& (mClientUid == that.mClientUid)
- && (mClientPid == that.mClientPid));
+ && (mClientPid == that.mClientPid))
+ && (mSessionId == that.mSessionId);
}
@Override
@@ -533,7 +582,8 @@ public final class AudioPlaybackConfiguration implements Parcelable {
+ " type:" + toLogFriendlyPlayerType(mPlayerType)
+ " u/pid:" + mClientUid + "/" + mClientPid
+ " state:" + toLogFriendlyPlayerState(mPlayerState)
- + " attr:" + mPlayerAttr;
+ + " attr:" + mPlayerAttr
+ + " sessionId:" + mSessionId;
}
//=====================================================================
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 175d36fedb1f..7fb83f17a9d4 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -836,7 +836,7 @@ public class AudioTrack extends PlayerBase
mState = STATE_INITIALIZED;
}
- baseRegisterPlayer();
+ baseRegisterPlayer(mSessionId);
}
/**
@@ -866,7 +866,7 @@ public class AudioTrack extends PlayerBase
// other initialization...
if (nativeTrackInJavaObj != 0) {
- baseRegisterPlayer();
+ baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);
deferred_connect(nativeTrackInJavaObj);
} else {
mState = STATE_UNINITIALIZED;
@@ -2726,8 +2726,10 @@ public class AudioTrack extends PlayerBase
}
}
synchronized(mPlayStateLock) {
+ baseStart(0); // unknown device at this point
native_start();
- baseStart(native_getRoutedDeviceId());
+ // FIXME see b/179218630
+ //baseStart(native_getRoutedDeviceId());
if (mPlayState == PLAYSTATE_PAUSED_STOPPING) {
mPlayState = PLAYSTATE_STOPPING;
} else {
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index 065dac94ec37..cfbf8ea456fc 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -119,9 +119,14 @@ public class CamcorderProfile
*/
public static final int QUALITY_2K = 12;
+ /**
+ * Quality level corresponding to 8K UHD (7680 x 4320) resolution
+ */
+ public static final int QUALITY_8KUHD = 13;
+
// Start and end of quality list
private static final int QUALITY_LIST_START = QUALITY_LOW;
- private static final int QUALITY_LIST_END = QUALITY_2K;
+ private static final int QUALITY_LIST_END = QUALITY_8KUHD;
/**
* Time lapse quality level corresponding to the lowest available resolution.
@@ -188,10 +193,14 @@ public class CamcorderProfile
*/
public static final int QUALITY_TIME_LAPSE_2K = 1012;
+ /**
+ * Time lapse quality level corresponding to the 8K UHD (7680 x 4320) resolution.
+ */
+ public static final int QUALITY_TIME_LAPSE_8KUHD = 1013;
// Start and end of timelapse quality list
private static final int QUALITY_TIME_LAPSE_LIST_START = QUALITY_TIME_LAPSE_LOW;
- private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_2K;
+ private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_8KUHD;
/**
* High speed ( >= 100fps) quality level corresponding to the lowest available resolution.
diff --git a/media/java/android/media/DrmInitData.java b/media/java/android/media/DrmInitData.java
index 85b4ba54de63..3c48f8f602df 100644
--- a/media/java/android/media/DrmInitData.java
+++ b/media/java/android/media/DrmInitData.java
@@ -19,6 +19,7 @@ import android.annotation.NonNull;
import android.media.MediaDrm;
import java.util.Arrays;
+import java.util.Objects;
import java.util.UUID;
/**
@@ -94,9 +95,9 @@ public abstract class DrmInitData {
* @param data The initialization data.
*/
public SchemeInitData(@NonNull UUID uuid, @NonNull String mimeType, @NonNull byte[] data) {
- this.uuid = uuid;
- this.mimeType = mimeType;
- this.data = data;
+ this.uuid = Objects.requireNonNull(uuid);
+ this.mimeType = Objects.requireNonNull(mimeType);
+ this.data = Objects.requireNonNull(data);
}
@Override
diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java
index bbf632a406ec..e339ae8ef706 100644
--- a/media/java/android/media/HwAudioSource.java
+++ b/media/java/android/media/HwAudioSource.java
@@ -54,7 +54,7 @@ public class HwAudioSource extends PlayerBase {
Preconditions.checkArgument(device.isSource(), "Requires a source device");
mAudioDeviceInfo = device;
mAudioAttributes = attributes;
- baseRegisterPlayer();
+ baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);
}
/**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index f9975a9c1d4e..f276b6607429 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -73,6 +73,8 @@ interface IAudioService {
oneway void releaseRecorder(in int riid);
+ oneway void playerSessionId(in int piid, in int sessionId);
+
// Java-only methods below.
oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
diff --git a/media/java/android/media/IRingtonePlayer.aidl b/media/java/android/media/IRingtonePlayer.aidl
index 02fa94c845a6..5a7ff7fbd1ae 100644
--- a/media/java/android/media/IRingtonePlayer.aidl
+++ b/media/java/android/media/IRingtonePlayer.aidl
@@ -33,7 +33,8 @@ interface IRingtonePlayer {
float volume, boolean looping, in @nullable VolumeShaper.Configuration volumeShaperConfig);
oneway void stop(IBinder token);
boolean isPlaying(IBinder token);
- oneway void setPlaybackProperties(IBinder token, float volume, boolean looping);
+ oneway void setPlaybackProperties(IBinder token, float volume, boolean looping,
+ boolean hapticGeneratorEnabled);
/** Used for Notification sound playback. */
oneway void playAsync(in Uri uri, in UserHandle user, boolean looping, in AudioAttributes aa);
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index 92db946ab5ba..44f8385b715e 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -23,6 +23,7 @@ import android.graphics.ImageFormat;
import android.graphics.ImageFormat.Format;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.SurfaceUtils;
import android.hardware.HardwareBuffer;
import android.os.Handler;
@@ -202,6 +203,25 @@ public class ImageWriter implements AutoCloseable {
if (format == ImageFormat.UNKNOWN) {
format = SurfaceUtils.getSurfaceFormat(surface);
}
+ // Several public formats use the same native HAL_PIXEL_FORMAT_BLOB. The native
+ // allocation estimation sequence depends on the public formats values. To avoid
+ // possible errors, convert where necessary.
+ if (format == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) {
+ int surfaceDataspace = SurfaceUtils.getSurfaceDataspace(surface);
+ switch (surfaceDataspace) {
+ case StreamConfigurationMap.HAL_DATASPACE_DEPTH:
+ format = ImageFormat.DEPTH_POINT_CLOUD;
+ break;
+ case StreamConfigurationMap.HAL_DATASPACE_DYNAMIC_DEPTH:
+ format = ImageFormat.DEPTH_JPEG;
+ break;
+ case StreamConfigurationMap.HAL_DATASPACE_HEIF:
+ format = ImageFormat.HEIC;
+ break;
+ default:
+ format = ImageFormat.JPEG;
+ }
+ }
// Estimate the native buffer allocation size and register it so it gets accounted for
// during GC. Note that this doesn't include the buffers required by the buffer queue
// itself and the buffers requested by the producer.
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 607c8f11e01a..cf31e4141a6d 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2414,37 +2414,44 @@ final public class MediaCodec implements PlaybackComponent {
* This indicates that the requested key was not found when trying to
* perform a decrypt operation. The operation can be retried after adding
* the correct decryption key.
+ * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_NO_KEY}.
*/
- public static final int ERROR_NO_KEY = 1;
+ public static final int ERROR_NO_KEY = MediaDrm.ErrorCodes.ERROR_NO_KEY;
/**
* This indicates that the key used for decryption is no longer
* valid due to license term expiration. The operation can be retried
* after updating the expired keys.
+ * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_KEY_EXPIRED}.
*/
- public static final int ERROR_KEY_EXPIRED = 2;
+ public static final int ERROR_KEY_EXPIRED = MediaDrm.ErrorCodes.ERROR_KEY_EXPIRED;
/**
* This indicates that a required crypto resource was not able to be
* allocated while attempting the requested operation. The operation
* can be retried if the app is able to release resources.
+ * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_RESOURCE_BUSY}
*/
- public static final int ERROR_RESOURCE_BUSY = 3;
+ public static final int ERROR_RESOURCE_BUSY = MediaDrm.ErrorCodes.ERROR_RESOURCE_BUSY;
/**
* This indicates that the output protection levels supported by the
* device are not sufficient to meet the requirements set by the
* content owner in the license policy.
+ * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_INSUFFICIENT_OUTPUT_PROTECTION}
*/
- public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4;
+ public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION =
+ MediaDrm.ErrorCodes.ERROR_INSUFFICIENT_OUTPUT_PROTECTION;
/**
* This indicates that decryption was attempted on a session that is
* not opened, which could be due to a failure to open the session,
* closing the session prematurely, or the session being reclaimed
* by the resource manager.
+ * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_SESSION_NOT_OPENED}
*/
- public static final int ERROR_SESSION_NOT_OPENED = 5;
+ public static final int ERROR_SESSION_NOT_OPENED =
+ MediaDrm.ErrorCodes.ERROR_SESSION_NOT_OPENED;
/**
* This indicates that an operation was attempted that could not be
@@ -2453,23 +2460,28 @@ final public class MediaCodec implements PlaybackComponent {
* device security features that aren't supported by the device,
* or due to an internal error in the crypto system that prevents
* the specified security policy from being met.
+ * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_UNSUPPORTED_OPERATION}
*/
- public static final int ERROR_UNSUPPORTED_OPERATION = 6;
+ public static final int ERROR_UNSUPPORTED_OPERATION =
+ MediaDrm.ErrorCodes.ERROR_UNSUPPORTED_OPERATION;
/**
* This indicates that the security level of the device is not
* sufficient to meet the requirements set by the content owner
* in the license policy.
+ * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_INSUFFICIENT_SECURITY}
*/
- public static final int ERROR_INSUFFICIENT_SECURITY = 7;
+ public static final int ERROR_INSUFFICIENT_SECURITY =
+ MediaDrm.ErrorCodes.ERROR_INSUFFICIENT_SECURITY;
/**
* This indicates that the video frame being decrypted exceeds
* the size of the device's protected output buffers. When
* encountering this error the app should try playing content
* of a lower resolution.
+ * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_FRAME_TOO_LARGE}
*/
- public static final int ERROR_FRAME_TOO_LARGE = 8;
+ public static final int ERROR_FRAME_TOO_LARGE = MediaDrm.ErrorCodes.ERROR_FRAME_TOO_LARGE;
/**
* This error indicates that session state has been
@@ -2477,26 +2489,37 @@ final public class MediaCodec implements PlaybackComponent {
* of retaining crypto session state across device
* suspend/resume. The session must be closed and a new
* session opened to resume operation.
+ * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_LOST_STATE}
*/
- public static final int ERROR_LOST_STATE = 9;
+ public static final int ERROR_LOST_STATE = MediaDrm.ErrorCodes.ERROR_LOST_STATE;
/** @hide */
@IntDef({
- ERROR_NO_KEY,
- ERROR_KEY_EXPIRED,
- ERROR_RESOURCE_BUSY,
- ERROR_INSUFFICIENT_OUTPUT_PROTECTION,
- ERROR_SESSION_NOT_OPENED,
- ERROR_UNSUPPORTED_OPERATION,
- ERROR_INSUFFICIENT_SECURITY,
- ERROR_FRAME_TOO_LARGE,
- ERROR_LOST_STATE
+ MediaDrm.ErrorCodes.ERROR_NO_KEY,
+ MediaDrm.ErrorCodes.ERROR_KEY_EXPIRED,
+ MediaDrm.ErrorCodes.ERROR_RESOURCE_BUSY,
+ MediaDrm.ErrorCodes.ERROR_INSUFFICIENT_OUTPUT_PROTECTION,
+ MediaDrm.ErrorCodes.ERROR_SESSION_NOT_OPENED,
+ MediaDrm.ErrorCodes.ERROR_UNSUPPORTED_OPERATION,
+ MediaDrm.ErrorCodes.ERROR_INSUFFICIENT_SECURITY,
+ MediaDrm.ErrorCodes.ERROR_FRAME_TOO_LARGE,
+ MediaDrm.ErrorCodes.ERROR_LOST_STATE,
+ MediaDrm.ErrorCodes.ERROR_GENERIC_OEM,
+ MediaDrm.ErrorCodes.ERROR_GENERIC_PLUGIN,
+ MediaDrm.ErrorCodes.ERROR_LICENSE_PARSE,
+ MediaDrm.ErrorCodes.ERROR_MEDIA_FRAMEWORK,
+ MediaDrm.ErrorCodes.ERROR_ZERO_SUBSAMPLES
})
@Retention(RetentionPolicy.SOURCE)
public @interface CryptoErrorCode {}
/**
- * Retrieve the error code associated with a CryptoException
+ * Returns error code associated with this {@link CryptoException}.
+ * <p>
+ * Please refer to {@link MediaDrm.ErrorCodes} for the general error
+ * handling strategy and details about each possible return value.
+ *
+ * @return an error code defined in {@link MediaDrm.ErrorCodes}.
*/
@CryptoErrorCode
public int getErrorCode() {
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 49f9d6612341..f7467a636024 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -23,7 +23,11 @@ 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;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
import android.media.metrics.PlaybackComponent;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -38,7 +42,11 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
+import java.time.Instant;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
+import java.util.Base64;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -143,6 +151,7 @@ public final class MediaDrm implements AutoCloseable {
private static final String PERMISSION = android.Manifest.permission.ACCESS_DRM_CERTIFICATES;
private long mNativeContext;
+ private final String mAppPackageName;
/**
* Specify no certificate type
@@ -280,16 +289,371 @@ public final class MediaDrm implements AutoCloseable {
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
*/
+ mAppPackageName = ActivityThread.currentOpPackageName();
native_setup(new WeakReference<MediaDrm>(this),
- getByteArrayFromUUID(uuid), ActivityThread.currentOpPackageName());
+ getByteArrayFromUUID(uuid), mAppPackageName);
mCloseGuard.open("release");
}
/**
- * Thrown when an unrecoverable failure occurs during a MediaDrm operation.
- * Extends java.lang.IllegalStateException with the addition of an error
+ * Error codes that may be returned from {@link
+ * MediaDrmStateException#getErrorCode()} and {@link
+ * MediaCodec.CryptoException#getErrorCode()}
+ * <p>
+ * The description of each error code includes steps that may be taken to
+ * resolve the error condition. For some errors however, a recovery action
+ * cannot be predetermined. The description of those codes refers to a
+ * general strategy for handling the error condition programmatically, which
+ * is to try the following in listed order until successful:
+ * <ol>
+ * <li> retry the operation </li>
+ * <li> if the operation is related to a session, {@link
+ * #closeSession(byte[]) close} the session, {@link #openSession() open} a
+ * new session, and retry the operation </li>
+ * <li> {@link #close() close} the {@link MediaDrm} instance and any other
+ * related components such as the {@link MediaCodec codec} and retry
+ * playback, or </li>
+ * <li> try using a different configuration of the {@link MediaDrm} plugin,
+ * such as a different {@link #openSession(int) security level}. </li>
+ * </ol>
+ * <p>
+ * If the problem still persists after all the aforementioned steps, please
+ * report the failure to the {@link MediaDrm} plugin vendor along with the
+ * {@link LogMessage log messages} returned by {@link
+ * MediaDrm#getLogMessages()}, and a bugreport if possible.
+ */
+ public final static class ErrorCodes {
+ private ErrorCodes() {}
+
+ /**
+ * ERROR_UNKNOWN is used where no other defined error code is applicable
+ * to the current failure.
+ * <p>
+ * Please see the general error handling strategy for unexpected errors
+ * described in {@link ErrorCodes}.
+ */
+ public static final int ERROR_UNKNOWN = 0;
+
+ /**
+ * The requested key was not found when trying to perform a decrypt
+ * operation.
+ * <p>
+ * The operation can be retried after adding the correct decryption key.
+ */
+ public static final int ERROR_NO_KEY = 1;
+
+ /**
+ * The key used for decryption is no longer valid due to license term
+ * expiration.
+ * <p>
+ * The operation can be retried after updating the expired keys.
+ */
+ public static final int ERROR_KEY_EXPIRED = 2;
+
+ /**
+ * A required crypto resource was not able to be allocated while
+ * attempting the requested operation.
+ * <p>
+ * The operation can be retried if the app is able to release resources.
+ */
+ public static final int ERROR_RESOURCE_BUSY = 3;
+
+ /**
+ * The output protection levels supported by the device are not
+ * sufficient to meet the requirements set by the content owner in the
+ * license policy.
+ */
+ public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4;
+
+ /**
+ * Decryption was attempted on a session that is not opened, which could
+ * be due to a failure to open the session, closing the session
+ * prematurely, the session being reclaimed by the resource manager, or
+ * a non-existent session id.
+ */
+ public static final int ERROR_SESSION_NOT_OPENED = 5;
+
+ /**
+ * An operation was attempted that could not be supported by the crypto
+ * system of the device in its current configuration.
+ * <p>
+ * This may occur when the license policy requires device security
+ * features that aren't supported by the device, or due to an internal
+ * error in the crypto system that prevents the specified security
+ * policy from being met.
+ */
+ public static final int ERROR_UNSUPPORTED_OPERATION = 6;
+
+ /**
+ * The security level of the device is not sufficient to meet the
+ * requirements set by the content owner in the license policy.
+ */
+ public static final int ERROR_INSUFFICIENT_SECURITY = 7;
+
+ /**
+ * The video frame being decrypted exceeds the size of the device's
+ * protected output buffers.
+ * <p>
+ * When encountering this error the app should try playing content
+ * of a lower resolution or skipping the problematic frame.
+ */
+ public static final int ERROR_FRAME_TOO_LARGE = 8;
+
+ /**
+ * The session state has been invalidated. This can occur on devices
+ * that are not capable of retaining crypto session state across device
+ * suspend/resume.
+ * <p>
+ * The session must be closed and a new session opened to resume
+ * operation.
+ */
+ public static final int ERROR_LOST_STATE = 9;
+
+ /**
+ * Certificate is malformed or is of the wrong type.
+ * <p>
+ * Ensure the certificate provided by the app or returned from the
+ * license server is valid. Check with the {@link MediaDrm} plugin
+ * vendor for the expected certificate format.
+ */
+ public static final int ERROR_CERTIFICATE_MALFORMED = 10;
+
+ /**
+ * Certificate has not been set.
+ * <p>
+ * Ensure the certificate has been provided by the app. Check with the
+ * {@link MediaDrm} plugin vendor for the expected method to provide
+ * {@link MediaDrm} a certificate.
+ */
+ public static final int ERROR_CERTIFICATE_MISSING = 11;
+
+ /**
+ * An error happened within the crypto library used by the drm plugin.
+ */
+ public static final int ERROR_CRYPTO_LIBRARY = 12;
+
+ /**
+ * Unexpected error reported by the device OEM subsystem.
+ * <p>
+ * Please see the general error handling strategy for unexpected errors
+ * described in {@link ErrorCodes}.
+ */
+ public static final int ERROR_GENERIC_OEM = 13;
+
+ /**
+ * Unexpected internal failure in {@link MediaDrm}/{@link MediaCrypto}.
+ * <p>
+ * Please see the general error handling strategy for unexpected errors
+ * described in {@link ErrorCodes}.
+ */
+ public static final int ERROR_GENERIC_PLUGIN = 14;
+
+ /**
+ * The init data parameter passed to {@link MediaDrm#getKeyRequest} is
+ * empty or invalid.
+ * <p>
+ * Init data is typically obtained from {@link
+ * MediaExtractor#getPsshInfo()} or {@link
+ * MediaExtractor#getDrmInitData()}. Check with the {@link MediaDrm}
+ * plugin vendor for the expected init data format.
+ */
+ public static final int ERROR_INIT_DATA = 15;
+
+ /**
+ * Either the key was not loaded from the license before attempting the
+ * operation, or the key ID parameter provided by the app is incorrect.
+ * <p>
+ * Ensure the proper keys are in the license, and check the key ID
+ * parameter provided by the app is correct. Check with the {@link
+ * MediaDrm} plugin vendor for the expected license format.
+ */
+ public static final int ERROR_KEY_NOT_LOADED = 16;
+
+ /**
+ * The license response was empty, fields are missing or otherwise
+ * unable to be parsed or decrypted.
+ * <p>
+ * Check for mistakes such as empty or overwritten buffers. Otherwise,
+ * check with the {@link MediaDrm} plugin vendor for the expected
+ * license format.
+ */
+ public static final int ERROR_LICENSE_PARSE = 17;
+
+ /**
+ * The operation (e.g. to renew or persist a license) is prohibited by
+ * the license policy.
+ * <p>
+ * Check the license policy configuration on the license server.
+ */
+ public static final int ERROR_LICENSE_POLICY = 18;
+
+ /**
+ * Failed to generate a release request because a field in the offline
+ * license is empty or malformed.
+ * <p>
+ * The license can't be released on the server, but the app may remove
+ * the offline license explicitly using {@link
+ * MediaDrm#removeOfflineLicense}.
+ */
+ public static final int ERROR_LICENSE_RELEASE = 19;
+
+ /**
+ * The license server detected an error in the license request.
+ * <p>
+ * Check for errors on the license server.
+ */
+ public static final int ERROR_LICENSE_REQUEST_REJECTED = 20;
+
+ /**
+ * Failed to restore an offline license because a field in the offline
+ * license is empty or malformed.
+ * <p>
+ * Try requesting the license again if the device is online.
+ */
+ public static final int ERROR_LICENSE_RESTORE = 21;
+
+ /**
+ * Offline license is in an invalid state for the attempted operation.
+ * <p>
+ * Check the sequence of API calls made that can affect offline license
+ * state. For example, this could happen when the app attempts to
+ * restore a license after it has been released.
+ */
+ public static final int ERROR_LICENSE_STATE = 22;
+
+ /**
+ * Failure in the media framework.
+ * <p>
+ * Try releasing media resources (e.g. {@link MediaCodec}, {@link
+ * MediaDrm}), and restarting playback.
+ */
+ public static final int ERROR_MEDIA_FRAMEWORK = 23;
+
+ /**
+ * Error loading the provisioned certificate.
+ * <p>
+ * Re-provisioning may resolve the problem; check with the {@link
+ * MediaDrm} plugin vendor for re-provisioning instructions. Otherwise,
+ * using a different security level may resolve the issue.
+ */
+ public static final int ERROR_PROVISIONING_CERTIFICATE = 24;
+
+ /**
+ * Required steps were not performed before provisioning was attempted.
+ * <p>
+ * Ask the {@link MediaDrm} plugin vendor for situations where this
+ * error may occur.
+ */
+ public static final int ERROR_PROVISIONING_CONFIG = 25;
+
+ /**
+ * The provisioning response was empty, fields are missing or otherwise
+ * unable to be parsed.
+ * <p>
+ * Check for mistakes such as empty or overwritten buffers. Otherwise,
+ * check with the {@link MediaDrm} plugin vendor for the expected
+ * provisioning response format.
+ */
+ public static final int ERROR_PROVISIONING_PARSE = 26;
+
+ /**
+ * Provisioning failed in a way that is likely to succeed on a
+ * subsequent attempt.
+ * <p>
+ * The app should retry the operation.
+ */
+ public static final int ERROR_PROVISIONING_RETRY = 27;
+
+ /**
+ * This indicates that apps using MediaDrm sessions are
+ * temporarily exceeding the capacity of available crypto
+ * resources.
+ * <p>
+ * The app should retry the operation later.
+ */
+ public static final int ERROR_RESOURCE_CONTENTION = 28;
+
+ /**
+ * Failed to generate a secure stop request because a field in the
+ * stored license is empty or malformed.
+ * <p>
+ * The secure stop can't be released on the server, but the app may
+ * remove it explicitly using {@link MediaDrm#removeSecureStop}.
+ */
+ public static final int ERROR_SECURE_STOP_RELEASE = 29;
+
+ /**
+ * The plugin was unable to read data from the filesystem.
+ * <p>
+ * Please see the general error handling strategy for unexpected errors
+ * described in {@link ErrorCodes}.
+ */
+ public static final int ERROR_STORAGE_READ = 30;
+
+ /**
+ * The plugin was unable to write data to the filesystem.
+ * <p>
+ * Please see the general error handling strategy for unexpected errors
+ * described in {@link ErrorCodes}.
+ */
+ public static final int ERROR_STORAGE_WRITE = 31;
+
+ /**
+ * {@link MediaCodec#queueSecureInputBuffer} called with 0 subsamples.
+ * <p>
+ * Check the {@link MediaCodec.CryptoInfo} object passed to {@link
+ * MediaCodec#queueSecureInputBuffer}.
+ */
+ public static final int ERROR_ZERO_SUBSAMPLES = 32;
+
+ }
+
+ /** @hide */
+ @IntDef({
+ ErrorCodes.ERROR_NO_KEY,
+ ErrorCodes.ERROR_KEY_EXPIRED,
+ ErrorCodes.ERROR_RESOURCE_BUSY,
+ ErrorCodes.ERROR_INSUFFICIENT_OUTPUT_PROTECTION,
+ ErrorCodes.ERROR_SESSION_NOT_OPENED,
+ ErrorCodes.ERROR_UNSUPPORTED_OPERATION,
+ ErrorCodes.ERROR_INSUFFICIENT_SECURITY,
+ ErrorCodes.ERROR_FRAME_TOO_LARGE,
+ ErrorCodes.ERROR_LOST_STATE,
+ ErrorCodes.ERROR_CERTIFICATE_MALFORMED,
+ ErrorCodes.ERROR_CERTIFICATE_MISSING,
+ ErrorCodes.ERROR_CRYPTO_LIBRARY,
+ ErrorCodes.ERROR_GENERIC_OEM,
+ ErrorCodes.ERROR_GENERIC_PLUGIN,
+ ErrorCodes.ERROR_INIT_DATA,
+ ErrorCodes.ERROR_KEY_NOT_LOADED,
+ ErrorCodes.ERROR_LICENSE_PARSE,
+ ErrorCodes.ERROR_LICENSE_POLICY,
+ ErrorCodes.ERROR_LICENSE_RELEASE,
+ ErrorCodes.ERROR_LICENSE_REQUEST_REJECTED,
+ ErrorCodes.ERROR_LICENSE_RESTORE,
+ ErrorCodes.ERROR_LICENSE_STATE,
+ ErrorCodes.ERROR_MEDIA_FRAMEWORK,
+ ErrorCodes.ERROR_PROVISIONING_CERTIFICATE,
+ ErrorCodes.ERROR_PROVISIONING_CONFIG,
+ ErrorCodes.ERROR_PROVISIONING_PARSE,
+ ErrorCodes.ERROR_PROVISIONING_RETRY,
+ ErrorCodes.ERROR_SECURE_STOP_RELEASE,
+ ErrorCodes.ERROR_STORAGE_READ,
+ ErrorCodes.ERROR_STORAGE_WRITE,
+ ErrorCodes.ERROR_ZERO_SUBSAMPLES
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaDrmErrorCode {}
+
+ /**
+ * Thrown when a general failure occurs during a MediaDrm operation.
+ * Extends {@link IllegalStateException} with the addition of an error
* code that may be useful in diagnosing the failure.
+ * <p>
+ * Please refer to {@link ErrorCodes} for the general error handling
+ * strategy and details about each possible return value from {@link
+ * MediaDrmStateException#getErrorCode()}.
*/
public static final class MediaDrmStateException extends java.lang.IllegalStateException {
private final int mErrorCode;
@@ -310,15 +674,30 @@ public final class MediaDrm implements AutoCloseable {
}
/**
- * Retrieve the associated error code
+ * Returns error code associated with this {@link
+ * MediaDrmStateException}.
+ * <p>
+ * Please refer to {@link ErrorCodes} for the general error handling
+ * strategy and details about each possible return value.
*
- * @hide
+ * @return an error code defined in {@link MediaDrm.ErrorCodes}.
*/
+ @MediaDrmErrorCode
public int getErrorCode() {
return mErrorCode;
}
/**
+ * Returns true if the {@link MediaDrmStateException} is a transient
+ * issue, perhaps due to resource constraints, and that the operation
+ * (e.g. provisioning) may succeed on a subsequent attempt.
+ */
+ public boolean isTransient() {
+ return mErrorCode == ErrorCodes.ERROR_PROVISIONING_RETRY
+ || mErrorCode == ErrorCodes.ERROR_RESOURCE_CONTENTION;
+ }
+
+ /**
* Retrieve a developer-readable diagnostic information string
* associated with the exception. Do not show this to end-users,
* since this string will not be localized or generally comprehensible
@@ -331,7 +710,13 @@ public final class MediaDrm implements AutoCloseable {
}
/**
- * Thrown when an error occurs in any method that has a session context.
+ * {@link SessionException} is a misnomer because it may occur in methods
+ * <b>without</b> a session context.
+ * <p>
+ * A {@link SessionException} is most likely to be thrown when an operation
+ * failed in a way that is likely to succeed on a subsequent attempt; call
+ * {@link #isTransient()} to determine whether the app should retry the
+ * failing operation.
*/
public static final class SessionException extends RuntimeException {
public SessionException(int errorCode, @Nullable String detailMessage) {
@@ -341,6 +726,7 @@ public final class MediaDrm implements AutoCloseable {
/**
* The SessionException has an unknown error code.
+ * @deprecated Unused.
*/
public static final int ERROR_UNKNOWN = 0;
@@ -348,6 +734,10 @@ public final class MediaDrm implements AutoCloseable {
* This indicates that apps using MediaDrm sessions are
* temporarily exceeding the capacity of available crypto
* resources. The app should retry the operation later.
+ *
+ * @deprecated Please use {@link #isTransient()} instead of comparing
+ * the return value of {@link #getErrorCode()} against
+ * {@link SessionException#ERROR_RESOURCE_CONTENTION}.
*/
public static final int ERROR_RESOURCE_CONTENTION = 1;
@@ -360,12 +750,26 @@ public final class MediaDrm implements AutoCloseable {
/**
* Retrieve the error code associated with the SessionException
+ *
+ * @deprecated Please use {@link #isTransient()} instead of comparing
+ * the return value of {@link #getErrorCode()} against
+ * {@link SessionException#ERROR_RESOURCE_CONTENTION}.
*/
@SessionErrorCode
public int getErrorCode() {
return mErrorCode;
}
+ /**
+ * Returns true if the {@link SessionException} is a transient
+ * issue, perhaps due to resource constraints, and that the operation
+ * (e.g. provisioning, generating requests) may succeed on a subsequent
+ * attempt.
+ */
+ public boolean isTransient() {
+ return mErrorCode == ERROR_RESOURCE_CONTENTION;
+ }
+
private final int mErrorCode;
}
@@ -1143,12 +1547,78 @@ public final class MediaDrm implements AutoCloseable {
* problem with the certifcate
*/
@NonNull
- public native KeyRequest getKeyRequest(
+ public KeyRequest getKeyRequest(
@NonNull byte[] scope, @Nullable byte[] init,
@Nullable String mimeType, @KeyType int keyType,
@Nullable HashMap<String, String> optionalParameters)
- throws NotProvisionedException;
+ throws NotProvisionedException {
+ HashMap<String, String> internalParams;
+ if (optionalParameters == null) {
+ internalParams = new HashMap<>();
+ } else {
+ internalParams = new HashMap<>(optionalParameters);
+ }
+ byte[] rawBytes = getNewestAvailablePackageCertificateRawBytes();
+ byte[] hashBytes = null;
+ if (rawBytes != null) {
+ hashBytes = getDigestBytes(rawBytes, "SHA-256");
+ }
+ if (hashBytes != null) {
+ Base64.Encoder encoderB64 = Base64.getEncoder();
+ String hashBytesB64 = encoderB64.encodeToString(hashBytes);
+ internalParams.put("package_certificate_hash_bytes", hashBytesB64);
+ }
+ return getKeyRequestNative(scope, init, mimeType, keyType, internalParams);
+ }
+ @Nullable
+ private byte[] getNewestAvailablePackageCertificateRawBytes() {
+ Application application = ActivityThread.currentApplication();
+ if (application == null) {
+ Log.w(TAG, "pkg cert: Application is null");
+ return null;
+ }
+ PackageManager pm = application.getPackageManager();
+ if (pm == null) {
+ Log.w(TAG, "pkg cert: PackageManager is null");
+ return null;
+ }
+ PackageInfo packageInfo = null;
+ try {
+ packageInfo = pm.getPackageInfo(mAppPackageName,
+ PackageManager.GET_SIGNING_CERTIFICATES);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, mAppPackageName, e);
+ }
+ if (packageInfo == null || packageInfo.signingInfo == null) {
+ Log.w(TAG, "pkg cert: PackageInfo or SigningInfo is null");
+ return null;
+ }
+ Signature[] signers = packageInfo.signingInfo.getApkContentsSigners();
+ if (signers != null && signers.length == 1) {
+ return signers[0].toByteArray();
+ }
+ Log.w(TAG, "pkg cert: " + signers.length + " signers");
+ return null;
+ }
+
+ @Nullable
+ private static byte[] getDigestBytes(@NonNull byte[] rawBytes, @NonNull String algorithm) {
+ try {
+ MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
+ return messageDigest.digest(rawBytes);
+ } catch (NoSuchAlgorithmException e) {
+ Log.w(TAG, algorithm, e);
+ }
+ return null;
+ }
+
+ @NonNull
+ private native KeyRequest getKeyRequestNative(
+ @NonNull byte[] scope, @Nullable byte[] init,
+ @Nullable String mimeType, @KeyType int keyType,
+ @Nullable HashMap<String, String> optionalParameters)
+ throws NotProvisionedException;
/**
* A key response is received from the license server by the app, then it is
@@ -2493,4 +2963,80 @@ public final class MediaDrm implements AutoCloseable {
return mPlaybackId;
}
}
+
+ /**
+ * Returns recent {@link LogMessage LogMessages} associated with this {@link MediaDrm}
+ * instance.
+ */
+ @NonNull
+ public native List<LogMessage> getLogMessages();
+
+ /**
+ * A {@link LogMessage} records an event in the {@link MediaDrm} framework
+ * or vendor plugin.
+ */
+ public static class LogMessage {
+
+ /**
+ * Timing of the recorded event measured in milliseconds since the Epoch,
+ * 1970-01-01 00:00:00 +0000 (UTC).
+ */
+ public final long timestampMillis;
+
+ /**
+ * Priority of the recorded event.
+ * <p>
+ * Possible priority constants are defined in {@link Log}, e.g.:
+ * <ul>
+ * <li>{@link Log#ASSERT}</li>
+ * <li>{@link Log#ERROR}</li>
+ * <li>{@link Log#WARN}</li>
+ * <li>{@link Log#INFO}</li>
+ * <li>{@link Log#DEBUG}</li>
+ * <li>{@link Log#VERBOSE}</li>
+ * </ul>
+ */
+ @Log.Level
+ public final int priority;
+
+ /**
+ * Description of the recorded event.
+ */
+ @NonNull
+ public final String message;
+
+ private LogMessage(long timestampMillis, int priority, String message) {
+ this.timestampMillis = timestampMillis;
+ if (priority < Log.VERBOSE || priority > Log.ASSERT) {
+ throw new IllegalArgumentException("invalid log priority " + priority);
+ }
+ this.priority = priority;
+ this.message = message;
+ }
+
+ private char logPriorityChar() {
+ switch (priority) {
+ case Log.VERBOSE:
+ return 'V';
+ case Log.DEBUG:
+ return 'D';
+ case Log.INFO:
+ return 'I';
+ case Log.WARN:
+ return 'W';
+ case Log.ERROR:
+ return 'E';
+ case Log.ASSERT:
+ return 'F';
+ default:
+ }
+ return 'U';
+ }
+
+ @Override
+ public String toString() {
+ return String.format("LogMessage{%s %c %s}",
+ Instant.ofEpochMilli(timestampMillis), logPriorityChar(), message);
+ }
+ }
}
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 1a49b85403e4..67f1660fff78 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -20,7 +20,6 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
-import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -1154,6 +1153,22 @@ public final class MediaFormat {
public static final String KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
/**
+ * An optional key describing the opto-electronic transfer function
+ * requested for the output video content.
+ *
+ * The associated value is an integer: 0 if unspecified, or one of the
+ * COLOR_TRANSFER_ values. When unspecified the component will not touch the
+ * video content; otherwise the component will tone-map the raw video frame
+ * to match the requested transfer function.
+ *
+ * After configure, component's input format will contain this key to note
+ * whether the request is supported or not. If the value in the input format
+ * is the same as the requested value, the request is supported. The value
+ * is set to 0 if unsupported.
+ */
+ public static final String KEY_COLOR_TRANSFER_REQUEST = "color-transfer-request";
+
+ /**
* A key describing a unique ID for the content of a media track.
*
* <p>This key is used by {@link MediaExtractor}. Some extractors provide multiple encodings
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index dd0bc61c4225..c51c9dd06c24 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -663,6 +663,10 @@ public class MediaPlayer extends PlayerBase
* result in an exception.</p>
*/
public MediaPlayer() {
+ this(AudioSystem.AUDIO_SESSION_ALLOCATE);
+ }
+
+ private MediaPlayer(int sessionId) {
super(new AudioAttributes.Builder().build(),
AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER);
@@ -684,7 +688,7 @@ public class MediaPlayer extends PlayerBase
native_setup(new WeakReference<MediaPlayer>(this),
getCurrentOpPackageName());
- baseRegisterPlayer();
+ baseRegisterPlayer(sessionId);
}
/*
@@ -913,11 +917,11 @@ public class MediaPlayer extends PlayerBase
AudioAttributes audioAttributes, int audioSessionId) {
try {
- MediaPlayer mp = new MediaPlayer();
+ MediaPlayer mp = new MediaPlayer(audioSessionId);
final AudioAttributes aa = audioAttributes != null ? audioAttributes :
new AudioAttributes.Builder().build();
mp.setAudioAttributes(aa);
- mp.setAudioSessionId(audioSessionId);
+ mp.native_setAudioSessionId(audioSessionId);
mp.setDataSource(context, uri);
if (holder != null) {
mp.setDisplay(holder);
@@ -978,12 +982,12 @@ public class MediaPlayer extends PlayerBase
AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
if (afd == null) return null;
- MediaPlayer mp = new MediaPlayer();
+ MediaPlayer mp = new MediaPlayer(audioSessionId);
final AudioAttributes aa = audioAttributes != null ? audioAttributes :
new AudioAttributes.Builder().build();
mp.setAudioAttributes(aa);
- mp.setAudioSessionId(audioSessionId);
+ mp.native_setAudioSessionId(audioSessionId);
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
afd.close();
@@ -1352,6 +1356,7 @@ public class MediaPlayer extends PlayerBase
}
private void startImpl() {
+ baseStart(0); // unknown device at this point
stayAwake(true);
_start();
}
@@ -1377,6 +1382,7 @@ public class MediaPlayer extends PlayerBase
public void stop() throws IllegalStateException {
stayAwake(false);
_stop();
+ baseStop();
}
private native void _stop() throws IllegalStateException;
@@ -1390,6 +1396,7 @@ public class MediaPlayer extends PlayerBase
public void pause() throws IllegalStateException {
stayAwake(false);
_pause();
+ basePause();
}
private native void _pause() throws IllegalStateException;
@@ -2365,7 +2372,13 @@ public class MediaPlayer extends PlayerBase
* This method must be called before one of the overloaded <code> setDataSource </code> methods.
* @throws IllegalStateException if it is called in an invalid state
*/
- public native void setAudioSessionId(int sessionId) throws IllegalArgumentException, IllegalStateException;
+ public void setAudioSessionId(int sessionId)
+ throws IllegalArgumentException, IllegalStateException {
+ native_setAudioSessionId(sessionId);
+ baseUpdateSessionId(sessionId);
+ }
+
+ private native void native_setAudioSessionId(int sessionId);
/**
* Returns the audio session ID.
@@ -3469,7 +3482,8 @@ public class MediaPlayer extends PlayerBase
case MEDIA_STOPPED:
{
tryToDisableNativeRoutingCallback();
- baseStop();
+ // FIXME see b/179218630
+ //baseStop();
TimeProvider timeProvider = mTimeProvider;
if (timeProvider != null) {
timeProvider.onStopped();
@@ -3479,15 +3493,17 @@ public class MediaPlayer extends PlayerBase
case MEDIA_STARTED:
{
- baseStart(native_getRoutedDeviceId());
+ // FIXME see b/179218630
+ //baseStart(native_getRoutedDeviceId());
tryToEnableNativeRoutingCallback();
}
// fall through
case MEDIA_PAUSED:
{
- if (msg.what == MEDIA_PAUSED) {
- basePause();
- }
+ // FIXME see b/179218630
+ //if (msg.what == MEDIA_PAUSED) {
+ // basePause();
+ //}
TimeProvider timeProvider = mTimeProvider;
if (timeProvider != null) {
timeProvider.onPaused(msg.what == MEDIA_PAUSED);
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 6cf99e288f1b..dc43ad342725 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -384,7 +384,12 @@ public class MediaRouter {
}
public Display[] getAllPresentationDisplays() {
- return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
+ try {
+ return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
+ } catch (RuntimeException ex) {
+ Log.e(TAG, "Unable to get displays.", ex);
+ return null;
+ }
}
private void updatePresentationDisplays(int changedDisplayId) {
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 58ae279d4df1..4407efad6052 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -97,6 +97,7 @@ public abstract class PlayerBase {
* Constructor. Must be given audio attributes, as they are required for AppOps.
* @param attr non-null audio attributes
* @param class non-null class of the implementation of this abstract class
+ * @param sessionId the audio session Id
*/
PlayerBase(@NonNull AudioAttributes attr, int implType) {
if (attr == null) {
@@ -110,7 +111,7 @@ public abstract class PlayerBase {
/**
* Call from derived class when instantiation / initialization is successful
*/
- protected void baseRegisterPlayer() {
+ protected void baseRegisterPlayer(int sessionId) {
if (!USE_AUDIOFLINGER_MUTING_FOR_OP) {
IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
mAppOps = IAppOpsService.Stub.asInterface(b);
@@ -128,7 +129,8 @@ public abstract class PlayerBase {
}
try {
mPlayerIId = getService().trackPlayer(
- new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)));
+ new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this),
+ sessionId));
} catch (RemoteException e) {
Log.e(TAG, "Error talking to audio service, player will not be tracked", e);
}
@@ -145,7 +147,7 @@ public abstract class PlayerBase {
try {
getService().playerAttributes(mPlayerIId, attr);
} catch (RemoteException e) {
- Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e);
+ Log.e(TAG, "Error talking to audio service, audio attributes will not be updated", e);
}
synchronized (mLock) {
boolean attributesChanged = (mAttributes != attr);
@@ -154,6 +156,18 @@ public abstract class PlayerBase {
}
}
+ /**
+ * To be called whenever the session ID of the player changes
+ * @param sessionId, the new session Id
+ */
+ void baseUpdateSessionId(int sessionId) {
+ try {
+ getService().playerSessionId(mPlayerIId, sessionId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to audio service, the session ID will not be updated", e);
+ }
+ }
+
void baseUpdateDeviceId(@Nullable AudioDeviceInfo deviceInfo) {
int deviceId = 0;
if (deviceInfo != null) {
@@ -566,16 +580,19 @@ public abstract class PlayerBase {
public static final int AUDIO_ATTRIBUTES_DEFINED = 1;
public final AudioAttributes mAttributes;
public final IPlayer mIPlayer;
+ public final int mSessionId;
- PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer) {
+ PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer,
+ int sessionId) {
mPlayerType = type;
mAttributes = attr;
mIPlayer = iplayer;
+ mSessionId = sessionId;
}
@Override
public int hashCode() {
- return Objects.hash(mPlayerType);
+ return Objects.hash(mPlayerType, mSessionId);
}
@Override
@@ -588,6 +605,7 @@ public abstract class PlayerBase {
dest.writeInt(mPlayerType);
mAttributes.writeToParcel(dest, 0);
dest.writeStrongBinder(mIPlayer == null ? null : mIPlayer.asBinder());
+ dest.writeInt(mSessionId);
}
public static final @android.annotation.NonNull Parcelable.Creator<PlayerIdCard> CREATOR
@@ -611,6 +629,7 @@ public abstract class PlayerBase {
// IPlayer can be null if unmarshalling a Parcel coming from who knows where
final IBinder b = in.readStrongBinder();
mIPlayer = (b == null ? null : IPlayer.Stub.asInterface(b));
+ mSessionId = in.readInt();
}
@Override
@@ -621,7 +640,8 @@ public abstract class PlayerBase {
PlayerIdCard that = (PlayerIdCard) o;
// FIXME change to the binder player interface once supported as a member
- return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes));
+ return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes)
+ && (mSessionId == that.mSessionId));
}
}
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index bd783ce9f6b2..79d505ebbde8 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -24,6 +24,7 @@ import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources.NotFoundException;
import android.database.Cursor;
+import android.media.audiofx.HapticGenerator;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -77,6 +78,7 @@ public class Ringtone {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private MediaPlayer mLocalPlayer;
private final MyOnCompletionListener mCompletionListener = new MyOnCompletionListener();
+ private HapticGenerator mHapticGenerator;
@UnsupportedAppUsage
private Uri mUri;
@@ -89,6 +91,7 @@ public class Ringtone {
// playback properties, use synchronized with mPlaybackSettingsLock
private boolean mIsLooping = false;
private float mVolume = 1.0f;
+ private boolean mHapticGeneratorEnabled = false;
private final Object mPlaybackSettingsLock = new Object();
/** {@hide} */
@@ -197,15 +200,50 @@ public class Ringtone {
}
/**
+ * Enable or disable the {@link android.media.audiofx.HapticGenerator} effect. The effect can
+ * only be enabled on devices that support the effect.
+ *
+ * @return true if the HapticGenerator effect is successfully enabled. Otherwise, return false.
+ * @see android.media.audiofx.HapticGenerator#isAvailable()
+ */
+ public boolean setHapticGeneratorEnabled(boolean enabled) {
+ if (!HapticGenerator.isAvailable()) {
+ return false;
+ }
+ synchronized (mPlaybackSettingsLock) {
+ mHapticGeneratorEnabled = enabled;
+ applyPlaybackProperties_sync();
+ }
+ return true;
+ }
+
+ /**
+ * Return whether the {@link android.media.audiofx.HapticGenerator} effect is enabled or not.
+ * @return true if the HapticGenerator is enabled.
+ */
+ public boolean isHapticGeneratorEnabled() {
+ synchronized (mPlaybackSettingsLock) {
+ return mHapticGeneratorEnabled;
+ }
+ }
+
+ /**
* Must be called synchronized on mPlaybackSettingsLock
*/
private void applyPlaybackProperties_sync() {
if (mLocalPlayer != null) {
mLocalPlayer.setVolume(mVolume);
mLocalPlayer.setLooping(mIsLooping);
+ if (mHapticGenerator == null && mHapticGeneratorEnabled) {
+ mHapticGenerator = HapticGenerator.create(mLocalPlayer.getAudioSessionId());
+ }
+ if (mHapticGenerator != null) {
+ mHapticGenerator.setEnabled(mHapticGeneratorEnabled);
+ }
} else if (mAllowRemote && (mRemotePlayer != null)) {
try {
- mRemotePlayer.setPlaybackProperties(mRemoteToken, mVolume, mIsLooping);
+ mRemotePlayer.setPlaybackProperties(
+ mRemoteToken, mVolume, mIsLooping, mHapticGeneratorEnabled);
} catch (RemoteException e) {
Log.w(TAG, "Problem setting playback properties: ", e);
}
@@ -413,6 +451,10 @@ public class Ringtone {
private void destroyLocalPlayer() {
if (mLocalPlayer != null) {
+ if (mHapticGenerator != null) {
+ mHapticGenerator.release();
+ mHapticGenerator = null;
+ }
mLocalPlayer.setOnCompletionListener(null);
mLocalPlayer.reset();
mLocalPlayer.release();
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 797caf36203b..32413dc6e841 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -155,7 +155,8 @@ public class SoundPool extends PlayerBase {
}
mAttributes = attributes;
- baseRegisterPlayer();
+ // FIXME: b/174876164 implement session id for soundpool
+ baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);
}
/**
diff --git a/media/java/android/media/metrics/Event.java b/media/java/android/media/metrics/Event.java
new file mode 100644
index 000000000000..5646dcdb6c9c
--- /dev/null
+++ b/media/java/android/media/metrics/Event.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.metrics;
+
+import android.annotation.IntRange;
+
+/**
+ * Abstract class for metrics events.
+ */
+public abstract class Event {
+ private final long mTimeSinceCreatedMillis;
+
+ // hide default constructor
+ /* package */ Event() {
+ mTimeSinceCreatedMillis = MediaMetricsManager.INVALID_TIMESTAMP;
+ }
+
+ protected Event(long timeSinceCreatedMillis) {
+ mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+ }
+
+ /**
+ * Gets time since the corresponding instance is created in millisecond.
+ * @return the timestamp since the instance is created, or -1 if unknown.
+ */
+ @IntRange(from = -1)
+ public long getTimeSinceCreatedMillis() {
+ return mTimeSinceCreatedMillis;
+ }
+}
diff --git a/media/java/android/media/metrics/MediaMetricsManager.java b/media/java/android/media/metrics/MediaMetricsManager.java
index f2eae5fad3c3..de780f672b28 100644
--- a/media/java/android/media/metrics/MediaMetricsManager.java
+++ b/media/java/android/media/metrics/MediaMetricsManager.java
@@ -26,7 +26,8 @@ import android.os.RemoteException;
*/
@SystemService(Context.MEDIA_METRICS_SERVICE)
public class MediaMetricsManager {
- // TODO: unhide APIs.
+ public static final long INVALID_TIMESTAMP = -1;
+
private static final String TAG = "MediaMetricsManager";
private IMediaMetricsManager mService;
diff --git a/media/java/android/media/metrics/NetworkEvent.java b/media/java/android/media/metrics/NetworkEvent.java
index a330bc0b66df..029edeb93374 100644
--- a/media/java/android/media/metrics/NetworkEvent.java
+++ b/media/java/android/media/metrics/NetworkEvent.java
@@ -17,6 +17,7 @@
package android.media.metrics;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
@@ -27,22 +28,30 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
- * Playback network event.
- * @hide
+ * Media network event.
*/
-public final class NetworkEvent implements Parcelable {
+public final class NetworkEvent extends Event implements Parcelable {
+ /** Network type is not specified. Default type. */
public static final int NETWORK_TYPE_NONE = 0;
+ /** Other network type */
public static final int NETWORK_TYPE_OTHER = 1;
+ /** Wi-Fi network */
public static final int NETWORK_TYPE_WIFI = 2;
+ /** Ethernet network */
public static final int NETWORK_TYPE_ETHERNET = 3;
+ /** 2G network */
public static final int NETWORK_TYPE_2G = 4;
+ /** 3G network */
public static final int NETWORK_TYPE_3G = 5;
+ /** 4G network */
public static final int NETWORK_TYPE_4G = 6;
+ /** 5G NSA network */
public static final int NETWORK_TYPE_5G_NSA = 7;
+ /** 5G SA network */
public static final int NETWORK_TYPE_5G_SA = 8;
- private final int mType;
- private final long mTimeSincePlaybackCreatedMillis;
+ private final int mNetworkType;
+ private final long mTimeSinceCreatedMillis;
/** @hide */
@IntDef(prefix = "NETWORK_TYPE_", value = {
@@ -61,6 +70,7 @@ public final class NetworkEvent implements Parcelable {
/**
* Network type to string.
+ * @hide
*/
public static String networkTypeToString(@NetworkType int value) {
switch (value) {
@@ -92,25 +102,34 @@ public final class NetworkEvent implements Parcelable {
*
* @hide
*/
- public NetworkEvent(@NetworkType int type, long timeSincePlaybackCreatedMillis) {
- this.mType = type;
- this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ public NetworkEvent(@NetworkType int type, long timeSinceCreatedMillis) {
+ this.mNetworkType = type;
+ this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
}
+ /**
+ * Gets network type.
+ */
@NetworkType
- public int getType() {
- return mType;
+ public int getNetworkType() {
+ return mNetworkType;
}
- public long getTimeSincePlaybackCreatedMillis() {
- return mTimeSincePlaybackCreatedMillis;
+ /**
+ * Gets timestamp since the creation in milliseconds.
+ * @return the timestamp since the creation in milliseconds, or -1 if unknown.
+ */
+ @Override
+ @IntRange(from = -1)
+ public long getTimeSinceCreatedMillis() {
+ return mTimeSinceCreatedMillis;
}
@Override
public String toString() {
return "NetworkEvent { "
- + "type = " + mType + ", "
- + "timeSincePlaybackCreatedMillis = " + mTimeSincePlaybackCreatedMillis
+ + "networkType = " + mNetworkType + ", "
+ + "timeSinceCreatedMillis = " + mTimeSinceCreatedMillis
+ " }";
}
@@ -119,19 +138,19 @@ public final class NetworkEvent implements Parcelable {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NetworkEvent that = (NetworkEvent) o;
- return mType == that.mType
- && mTimeSincePlaybackCreatedMillis == that.mTimeSincePlaybackCreatedMillis;
+ return mNetworkType == that.mNetworkType
+ && mTimeSinceCreatedMillis == that.mTimeSinceCreatedMillis;
}
@Override
public int hashCode() {
- return Objects.hash(mType, mTimeSincePlaybackCreatedMillis);
+ return Objects.hash(mNetworkType, mTimeSinceCreatedMillis);
}
@Override
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
- dest.writeInt(mType);
- dest.writeLong(mTimeSincePlaybackCreatedMillis);
+ dest.writeInt(mNetworkType);
+ dest.writeLong(mTimeSinceCreatedMillis);
}
@Override
@@ -142,12 +161,15 @@ public final class NetworkEvent implements Parcelable {
/** @hide */
/* package-private */ NetworkEvent(@NonNull android.os.Parcel in) {
int type = in.readInt();
- long timeSincePlaybackCreatedMillis = in.readLong();
+ long timeSinceCreatedMillis = in.readLong();
- this.mType = type;
- this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ this.mNetworkType = type;
+ this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
}
+ /**
+ * Used to read a NetworkEvent from a Parcel.
+ */
public static final @NonNull Parcelable.Creator<NetworkEvent> CREATOR =
new Parcelable.Creator<NetworkEvent>() {
@Override
@@ -165,13 +187,11 @@ public final class NetworkEvent implements Parcelable {
* A builder for {@link NetworkEvent}
*/
public static final class Builder {
- private int mType;
- private long mTimeSincePlaybackCreatedMillis;
+ private int mNetworkType = NETWORK_TYPE_NONE;
+ private long mTimeSinceCreatedMillis = -1;
/**
* Creates a new Builder.
- *
- * @hide
*/
public Builder() {
}
@@ -179,24 +199,24 @@ public final class NetworkEvent implements Parcelable {
/**
* Sets network type.
*/
- public @NonNull Builder setType(@NetworkType int value) {
- mType = value;
+ public @NonNull Builder setNetworkType(@NetworkType int value) {
+ mNetworkType = value;
return this;
}
/**
* Sets timestamp since the creation in milliseconds.
+ * @param value the timestamp since the creation in milliseconds.
+ * -1 indicates the value is unknown.
*/
- public @NonNull Builder setTimeSincePlaybackCreatedMillis(long value) {
- mTimeSincePlaybackCreatedMillis = value;
+ public @NonNull Builder setTimeSinceCreatedMillis(@IntRange(from = -1) long value) {
+ mTimeSinceCreatedMillis = value;
return this;
}
/** Builds the instance. */
public @NonNull NetworkEvent build() {
- NetworkEvent o = new NetworkEvent(
- mType,
- mTimeSincePlaybackCreatedMillis);
+ NetworkEvent o = new NetworkEvent(mNetworkType, mTimeSinceCreatedMillis);
return o;
}
}
diff --git a/media/java/android/media/metrics/PlaybackComponent.java b/media/java/android/media/metrics/PlaybackComponent.java
index 94e55b4843c1..1cadf3be38ee 100644
--- a/media/java/android/media/metrics/PlaybackComponent.java
+++ b/media/java/android/media/metrics/PlaybackComponent.java
@@ -17,13 +17,10 @@
package android.media.metrics;
import android.annotation.NonNull;
-import android.annotation.TestApi;
/**
* Interface for playback related components used by playback metrics.
- * @hide
*/
-@TestApi
public interface PlaybackComponent {
/**
diff --git a/media/java/android/media/metrics/PlaybackErrorEvent.java b/media/java/android/media/metrics/PlaybackErrorEvent.java
index db7000536299..5a0820d16cb9 100644
--- a/media/java/android/media/metrics/PlaybackErrorEvent.java
+++ b/media/java/android/media/metrics/PlaybackErrorEvent.java
@@ -17,8 +17,10 @@
package android.media.metrics;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,17 +29,19 @@ import java.util.Objects;
/**
* Playback error event.
- * @hide
*/
-public final class PlaybackErrorEvent implements Parcelable {
+public final class PlaybackErrorEvent extends Event implements Parcelable {
+ /** Unknown error code. */
public static final int ERROR_CODE_UNKNOWN = 0;
+ /** Error code for other errors */
public static final int ERROR_CODE_OTHER = 1;
+ /** Error code for runtime errors */
public static final int ERROR_CODE_RUNTIME = 2;
private final @Nullable String mExceptionStack;
private final int mErrorCode;
private final int mSubErrorCode;
- private final long mTimeSincePlaybackCreatedMillis;
+ private final long mTimeSinceCreatedMillis;
/** @hide */
@@ -59,11 +63,11 @@ public final class PlaybackErrorEvent implements Parcelable {
@Nullable String exceptionStack,
int errorCode,
int subErrorCode,
- long timeSincePlaybackCreatedMillis) {
+ long timeSinceCreatedMillis) {
this.mExceptionStack = exceptionStack;
this.mErrorCode = errorCode;
this.mSubErrorCode = subErrorCode;
- this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
}
/** @hide */
@@ -72,17 +76,32 @@ public final class PlaybackErrorEvent implements Parcelable {
return mExceptionStack;
}
+
+ /**
+ * Gets error code.
+ */
@ErrorCode
public int getErrorCode() {
return mErrorCode;
}
+
+ /**
+ * Gets sub error code.
+ */
+ @IntRange(from = Integer.MIN_VALUE, to = Integer.MAX_VALUE)
public int getSubErrorCode() {
return mSubErrorCode;
}
- public long getTimeSincePlaybackCreatedMillis() {
- return mTimeSincePlaybackCreatedMillis;
+ /**
+ * Gets the timestamp since creation in milliseconds.
+ * @return the timestamp since the playback is created, or -1 if unknown.
+ */
+ @Override
+ @IntRange(from = -1)
+ public long getTimeSinceCreatedMillis() {
+ return mTimeSinceCreatedMillis;
}
@Override
@@ -91,7 +110,7 @@ public final class PlaybackErrorEvent implements Parcelable {
+ "exceptionStack = " + mExceptionStack + ", "
+ "errorCode = " + mErrorCode + ", "
+ "subErrorCode = " + mSubErrorCode + ", "
- + "timeSincePlaybackCreatedMillis = " + mTimeSincePlaybackCreatedMillis
+ + "timeSinceCreatedMillis = " + mTimeSinceCreatedMillis
+ " }";
}
@@ -103,13 +122,13 @@ public final class PlaybackErrorEvent implements Parcelable {
return Objects.equals(mExceptionStack, that.mExceptionStack)
&& mErrorCode == that.mErrorCode
&& mSubErrorCode == that.mSubErrorCode
- && mTimeSincePlaybackCreatedMillis == that.mTimeSincePlaybackCreatedMillis;
+ && mTimeSinceCreatedMillis == that.mTimeSinceCreatedMillis;
}
@Override
public int hashCode() {
return Objects.hash(mExceptionStack, mErrorCode, mSubErrorCode,
- mTimeSincePlaybackCreatedMillis);
+ mTimeSinceCreatedMillis);
}
@Override
@@ -120,7 +139,7 @@ public final class PlaybackErrorEvent implements Parcelable {
if (mExceptionStack != null) dest.writeString(mExceptionStack);
dest.writeInt(mErrorCode);
dest.writeInt(mSubErrorCode);
- dest.writeLong(mTimeSincePlaybackCreatedMillis);
+ dest.writeLong(mTimeSinceCreatedMillis);
}
@Override
@@ -134,14 +153,15 @@ public final class PlaybackErrorEvent implements Parcelable {
String exceptionStack = (flg & 0x1) == 0 ? null : in.readString();
int errorCode = in.readInt();
int subErrorCode = in.readInt();
- long timeSincePlaybackCreatedMillis = in.readLong();
+ long timeSinceCreatedMillis = in.readLong();
this.mExceptionStack = exceptionStack;
this.mErrorCode = errorCode;
this.mSubErrorCode = subErrorCode;
- this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
}
+
public static final @NonNull Parcelable.Creator<PlaybackErrorEvent> CREATOR =
new Parcelable.Creator<PlaybackErrorEvent>() {
@Override
@@ -162,27 +182,18 @@ public final class PlaybackErrorEvent implements Parcelable {
private @Nullable Exception mException;
private int mErrorCode;
private int mSubErrorCode;
- private long mTimeSincePlaybackCreatedMillis;
+ private long mTimeSinceCreatedMillis = -1;
/**
* Creates a new Builder.
- *
- * @hide
*/
- public Builder(
- @Nullable Exception exception,
- int errorCode,
- int subErrorCode,
- long timeSincePlaybackCreatedMillis) {
- mException = exception;
- mErrorCode = errorCode;
- mSubErrorCode = subErrorCode;
- mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ public Builder() {
}
/**
* Sets the {@link Exception} object.
*/
+ @SuppressLint("MissingGetterMatchingBuilder") // Exception is not parcelable.
public @NonNull Builder setException(@NonNull Exception value) {
mException = value;
return this;
@@ -199,16 +210,19 @@ public final class PlaybackErrorEvent implements Parcelable {
/**
* Sets sub error code.
*/
- public @NonNull Builder setSubErrorCode(int value) {
+ public @NonNull Builder setSubErrorCode(
+ @IntRange(from = Integer.MIN_VALUE, to = Integer.MAX_VALUE) int value) {
mSubErrorCode = value;
return this;
}
/**
- * Set the timestamp in milliseconds.
+ * Set the timestamp since creation in milliseconds.
+ * @param value the timestamp since the creation in milliseconds.
+ * -1 indicates the value is unknown.
*/
- public @NonNull Builder setTimeSincePlaybackCreatedMillis(long value) {
- mTimeSincePlaybackCreatedMillis = value;
+ public @NonNull Builder setTimeSinceCreatedMillis(@IntRange(from = -1) long value) {
+ mTimeSinceCreatedMillis = value;
return this;
}
@@ -227,7 +241,7 @@ public final class PlaybackErrorEvent implements Parcelable {
stack,
mErrorCode,
mSubErrorCode,
- mTimeSincePlaybackCreatedMillis);
+ mTimeSinceCreatedMillis);
return o;
}
}
diff --git a/media/java/android/media/metrics/PlaybackMetrics.java b/media/java/android/media/metrics/PlaybackMetrics.java
index 070b4e4aa14b..4aa61662ba52 100644
--- a/media/java/android/media/metrics/PlaybackMetrics.java
+++ b/media/java/android/media/metrics/PlaybackMetrics.java
@@ -17,6 +17,7 @@
package android.media.metrics;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
@@ -33,35 +34,57 @@ import java.util.Objects;
/**
* This class is used to store playback data.
- * @hide
*/
public final class PlaybackMetrics implements Parcelable {
- // TODO(b/177209128): JavaDoc for the constants.
+ /** Unknown stream source. */
public static final int STREAM_SOURCE_UNKNOWN = 0;
+ /** Stream from network. */
public static final int STREAM_SOURCE_NETWORK = 1;
+ /** Stream from device. */
public static final int STREAM_SOURCE_DEVICE = 2;
+ /** Stream from more than one sources. */
public static final int STREAM_SOURCE_MIXED = 3;
+ /** Unknown stream type. */
public static final int STREAM_TYPE_UNKNOWN = 0;
+ /** Other stream type. */
public static final int STREAM_TYPE_OTHER = 1;
+ /** Progressive stream type. */
public static final int STREAM_TYPE_PROGRESSIVE = 2;
+ /** DASH (Dynamic Adaptive Streaming over HTTP) stream type. */
public static final int STREAM_TYPE_DASH = 3;
+ /** HLS (HTTP Live Streaming) stream type. */
public static final int STREAM_TYPE_HLS = 4;
+ /** SS (HTTP Smooth Streaming) stream type. */
public static final int STREAM_TYPE_SS = 5;
+ /** VOD (Video on Demand) playback type. */
public static final int PLAYBACK_TYPE_VOD = 0;
+ /** Live playback type. */
public static final int PLAYBACK_TYPE_LIVE = 1;
+ /** Other playback type. */
public static final int PLAYBACK_TYPE_OTHER = 2;
+ /** DRM is not used. */
public static final int DRM_TYPE_NONE = 0;
+ /** Other DRM type. */
public static final int DRM_TYPE_OTHER = 1;
+ /** Play ready DRM type. */
public static final int DRM_TYPE_PLAY_READY = 2;
+ /** Widevine L1 DRM type. */
public static final int DRM_TYPE_WIDEVINE_L1 = 3;
+ /** Widevine L3 DRM type. */
public static final int DRM_TYPE_WIDEVINE_L3 = 4;
- // TODO: add DRM_TYPE_CLEARKEY
+ /** Widevine L3 fallback DRM type. */
+ public static final int DRM_TYPE_WV_L3_FALLBACK = 5;
+ /** Clear key DRM type. */
+ public static final int DRM_TYPE_CLEARKEY = 6;
+ /** Main contents. */
public static final int CONTENT_TYPE_MAIN = 0;
+ /** Advertisement contents. */
public static final int CONTENT_TYPE_AD = 1;
+ /** Other contents. */
public static final int CONTENT_TYPE_OTHER = 2;
@@ -102,7 +125,9 @@ public final class PlaybackMetrics implements Parcelable {
DRM_TYPE_OTHER,
DRM_TYPE_PLAY_READY,
DRM_TYPE_WIDEVINE_L1,
- DRM_TYPE_WIDEVINE_L3
+ DRM_TYPE_WIDEVINE_L3,
+ DRM_TYPE_WV_L3_FALLBACK,
+ DRM_TYPE_CLEARKEY
})
@Retention(RetentionPolicy.SOURCE)
public @interface DrmType {}
@@ -173,6 +198,11 @@ public final class PlaybackMetrics implements Parcelable {
this.mNetworkTransferDurationMillis = networkTransferDurationMillis;
}
+ /**
+ * Gets the media duration in milliseconds.
+ * @return the media duration in milliseconds, or -1 if unknown.
+ */
+ @IntRange(from = -1)
public long getMediaDurationMillis() {
return mMediaDurationMillis;
}
@@ -241,28 +271,36 @@ public final class PlaybackMetrics implements Parcelable {
/**
* Gets video frames played.
+ * @return the video frames played, or -1 if unknown.
*/
+ @IntRange(from = -1, to = Integer.MAX_VALUE)
public int getVideoFramesPlayed() {
return mVideoFramesPlayed;
}
/**
* Gets video frames dropped.
+ * @return the video frames dropped, or -1 if unknown.
*/
+ @IntRange(from = -1, to = Integer.MAX_VALUE)
public int getVideoFramesDropped() {
return mVideoFramesDropped;
}
/**
* Gets audio underrun count.
+ * @return the audio underrun count, or -1 if unknown.
*/
+ @IntRange(from = -1, to = Integer.MAX_VALUE)
public int getAudioUnderrunCount() {
return mAudioUnderrunCount;
}
/**
* Gets number of network bytes read.
+ * @return the number of network bytes read, or -1 if unknown.
*/
+ @IntRange(from = -1)
public long getNetworkBytesRead() {
return mNetworkBytesRead;
}
@@ -270,6 +308,7 @@ public final class PlaybackMetrics implements Parcelable {
/**
* Gets number of local bytes read.
*/
+ @IntRange(from = -1)
public long getLocalBytesRead() {
return mLocalBytesRead;
}
@@ -277,6 +316,7 @@ public final class PlaybackMetrics implements Parcelable {
/**
* Gets network transfer duration in milliseconds.
*/
+ @IntRange(from = -1)
public long getNetworkTransferDurationMillis() {
return mNetworkTransferDurationMillis;
}
@@ -415,34 +455,33 @@ public final class PlaybackMetrics implements Parcelable {
*/
public static final class Builder {
- private long mMediaDurationMillis;
- private int mStreamSource;
- private int mStreamType;
- private int mPlaybackType;
- private int mDrmType;
- private int mContentType;
+ private long mMediaDurationMillis = -1;
+ private int mStreamSource = STREAM_SOURCE_UNKNOWN;
+ private int mStreamType = STREAM_TYPE_UNKNOWN;
+ private int mPlaybackType = PLAYBACK_TYPE_OTHER;
+ private int mDrmType = DRM_TYPE_NONE;
+ private int mContentType = CONTENT_TYPE_OTHER;
private @Nullable String mPlayerName;
private @Nullable String mPlayerVersion;
private @NonNull List<Long> mExperimentIds = new ArrayList<>();
- private int mVideoFramesPlayed;
- private int mVideoFramesDropped;
- private int mAudioUnderrunCount;
- private long mNetworkBytesRead;
- private long mLocalBytesRead;
- private long mNetworkTransferDurationMillis;
+ private int mVideoFramesPlayed = -1;
+ private int mVideoFramesDropped = -1;
+ private int mAudioUnderrunCount = -1;
+ private long mNetworkBytesRead = -1;
+ private long mLocalBytesRead = -1;
+ private long mNetworkTransferDurationMillis = -1;
/**
* Creates a new Builder.
- *
- * @hide
*/
public Builder() {
}
/**
* Sets the media duration in milliseconds.
+ * @param value the media duration in milliseconds. -1 indicates the value is unknown.
*/
- public @NonNull Builder setMediaDurationMillis(long value) {
+ public @NonNull Builder setMediaDurationMillis(@IntRange(from = -1) long value) {
mMediaDurationMillis = value;
return this;
}
@@ -474,7 +513,7 @@ public final class PlaybackMetrics implements Parcelable {
/**
* Sets the DRM type.
*/
- public @NonNull Builder setDrmType(@StreamType int value) {
+ public @NonNull Builder setDrmType(@DrmType int value) {
mDrmType = value;
return this;
}
@@ -513,48 +552,58 @@ public final class PlaybackMetrics implements Parcelable {
/**
* Sets the video frames played.
+ * @param value the video frames played. -1 indicates the value is unknown.
*/
- public @NonNull Builder setVideoFramesPlayed(int value) {
+ public @NonNull Builder setVideoFramesPlayed(
+ @IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
mVideoFramesPlayed = value;
return this;
}
/**
* Sets the video frames dropped.
+ * @param value the video frames dropped. -1 indicates the value is unknown.
*/
- public @NonNull Builder setVideoFramesDropped(int value) {
+ public @NonNull Builder setVideoFramesDropped(
+ @IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
mVideoFramesDropped = value;
return this;
}
/**
* Sets the audio underrun count.
+ * @param value the audio underrun count. -1 indicates the value is unknown.
*/
- public @NonNull Builder setAudioUnderrunCount(int value) {
+ public @NonNull Builder setAudioUnderrunCount(
+ @IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
mAudioUnderrunCount = value;
return this;
}
/**
* Sets the number of network bytes read.
+ * @param value the number of network bytes read. -1 indicates the value is unknown.
*/
- public @NonNull Builder setNetworkBytesRead(long value) {
+ public @NonNull Builder setNetworkBytesRead(@IntRange(from = -1) long value) {
mNetworkBytesRead = value;
return this;
}
/**
* Sets the number of local bytes read.
+ * @param value the number of local bytes read. -1 indicates the value is unknown.
*/
- public @NonNull Builder setLocalBytesRead(long value) {
+ public @NonNull Builder setLocalBytesRead(@IntRange(from = -1) long value) {
mLocalBytesRead = value;
return this;
}
/**
* Sets the network transfer duration in milliseconds.
+ * @param value the network transfer duration in milliseconds.
+ * -1 indicates the value is unknown.
*/
- public @NonNull Builder setNetworkTransferDurationMillis(long value) {
+ public @NonNull Builder setNetworkTransferDurationMillis(@IntRange(from = -1) long value) {
mNetworkTransferDurationMillis = value;
return this;
}
diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java
index 3056e9820f9d..4ee8a45fe196 100644
--- a/media/java/android/media/metrics/PlaybackSession.java
+++ b/media/java/android/media/metrics/PlaybackSession.java
@@ -45,7 +45,6 @@ public final class PlaybackSession implements AutoCloseable {
/**
* Reports playback metrics.
- * @hide
*/
public void reportPlaybackMetrics(@NonNull PlaybackMetrics metrics) {
mManager.reportPlaybackMetrics(mId, metrics);
@@ -53,33 +52,29 @@ public final class PlaybackSession implements AutoCloseable {
/**
* Reports error event.
- * @hide
*/
- public void reportPlaybackErrorEvent(PlaybackErrorEvent event) {
+ public void reportPlaybackErrorEvent(@NonNull PlaybackErrorEvent event) {
mManager.reportPlaybackErrorEvent(mId, event);
}
/**
* Reports network event.
- * @hide
*/
- public void reportNetworkEvent(NetworkEvent event) {
+ public void reportNetworkEvent(@NonNull NetworkEvent event) {
mManager.reportNetworkEvent(mId, event);
}
/**
* Reports playback state event.
- * @hide
*/
- public void reportPlaybackStateEvent(PlaybackStateEvent event) {
+ public void reportPlaybackStateEvent(@NonNull PlaybackStateEvent event) {
mManager.reportPlaybackStateEvent(mId, event);
}
/**
* Reports track change event.
- * @hide
*/
- public void reportTrackChangeEvent(TrackChangeEvent event) {
+ public void reportTrackChangeEvent(@NonNull TrackChangeEvent event) {
mManager.reportTrackChangeEvent(mId, event);
}
diff --git a/media/java/android/media/metrics/PlaybackStateEvent.java b/media/java/android/media/metrics/PlaybackStateEvent.java
index 6ce5bf0f0f33..8ca5b75dec98 100644
--- a/media/java/android/media/metrics/PlaybackStateEvent.java
+++ b/media/java/android/media/metrics/PlaybackStateEvent.java
@@ -17,6 +17,7 @@
package android.media.metrics;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
@@ -27,10 +28,8 @@ import java.util.Objects;
/**
* Playback state event.
- * @hide
*/
-public final class PlaybackStateEvent implements Parcelable {
- // TODO: more states
+public final class PlaybackStateEvent extends Event implements Parcelable {
/** Playback has not started (initial state) */
public static final int STATE_NOT_STARTED = 0;
/** Playback is buffering in the background for initial playback start */
@@ -41,23 +40,57 @@ public final class PlaybackStateEvent implements Parcelable {
public static final int STATE_PLAYING = 3;
/** Playback is paused but ready to play */
public static final int STATE_PAUSED = 4;
-
- private int mState;
- private long mTimeSincePlaybackCreatedMillis;
+ /** Playback is handling a seek. */
+ public static final int STATE_SEEKING = 5;
+ /** Playback is buffering to resume active playback. */
+ public static final int STATE_BUFFERING = 6;
+ /** Playback is buffering while paused. */
+ public static final int STATE_PAUSED_BUFFERING = 7;
+ /** Playback is suppressed (e.g. due to audio focus loss). */
+ public static final int STATE_SUPPRESSED = 9;
+ /**
+ * Playback is suppressed (e.g. due to audio focus loss) while buffering to resume a playback.
+ */
+ public static final int STATE_SUPPRESSED_BUFFERING = 10;
+ /** Playback has reached the end of the media. */
+ public static final int STATE_ENDED = 11;
+ /** Playback is stopped and can be restarted. */
+ public static final int STATE_STOPPED = 12;
+ /** Playback is stopped due a fatal error and can be retried. */
+ public static final int STATE_FAILED = 13;
+ /** Playback is interrupted by an ad. */
+ public static final int STATE_INTERRUPTED_BY_AD = 14;
+ /** Playback is abandoned before reaching the end of the media. */
+ public static final int STATE_ABANDONED = 15;
+
+ private final int mState;
+ private final long mTimeSinceCreatedMillis;
// These track ExoPlayer states. See the ExoPlayer documentation for the state transitions.
+ /** @hide */
@IntDef(prefix = "STATE_", value = {
STATE_NOT_STARTED,
STATE_JOINING_BACKGROUND,
STATE_JOINING_FOREGROUND,
STATE_PLAYING,
- STATE_PAUSED
+ STATE_PAUSED,
+ STATE_SEEKING,
+ STATE_BUFFERING,
+ STATE_PAUSED_BUFFERING,
+ STATE_SUPPRESSED,
+ STATE_SUPPRESSED_BUFFERING,
+ STATE_ENDED,
+ STATE_STOPPED,
+ STATE_FAILED,
+ STATE_INTERRUPTED_BY_AD,
+ STATE_ABANDONED,
})
@Retention(java.lang.annotation.RetentionPolicy.SOURCE)
public @interface State {}
/**
* Converts playback state to string.
+ * @hide
*/
public static String stateToString(@State int value) {
switch (value) {
@@ -71,6 +104,26 @@ public final class PlaybackStateEvent implements Parcelable {
return "STATE_PLAYING";
case STATE_PAUSED:
return "STATE_PAUSED";
+ case STATE_SEEKING:
+ return "STATE_SEEKING";
+ case STATE_BUFFERING:
+ return "STATE_BUFFERING";
+ case STATE_PAUSED_BUFFERING:
+ return "STATE_PAUSED_BUFFERING";
+ case STATE_SUPPRESSED:
+ return "STATE_SUPPRESSED";
+ case STATE_SUPPRESSED_BUFFERING:
+ return "STATE_SUPPRESSED_BUFFERING";
+ case STATE_ENDED:
+ return "STATE_ENDED";
+ case STATE_STOPPED:
+ return "STATE_STOPPED";
+ case STATE_FAILED:
+ return "STATE_FAILED";
+ case STATE_INTERRUPTED_BY_AD:
+ return "STATE_INTERRUPTED_BY_AD";
+ case STATE_ABANDONED:
+ return "STATE_ABANDONED";
default:
return Integer.toHexString(value);
}
@@ -83,14 +136,13 @@ public final class PlaybackStateEvent implements Parcelable {
*/
public PlaybackStateEvent(
int state,
- long timeSincePlaybackCreatedMillis) {
+ long timeSinceCreatedMillis) {
+ this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
this.mState = state;
- this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
}
/**
* Gets playback state.
- * @return
*/
public int getState() {
return mState;
@@ -98,9 +150,12 @@ public final class PlaybackStateEvent implements Parcelable {
/**
* Gets time since the corresponding playback is created in millisecond.
+ * @return the timestamp since the playback is created, or -1 if unknown.
*/
- public long getTimeSincePlaybackCreatedMillis() {
- return mTimeSincePlaybackCreatedMillis;
+ @Override
+ @IntRange(from = -1)
+ public long getTimeSinceCreatedMillis() {
+ return mTimeSinceCreatedMillis;
}
@Override
@@ -109,18 +164,18 @@ public final class PlaybackStateEvent implements Parcelable {
if (o == null || getClass() != o.getClass()) return false;
PlaybackStateEvent that = (PlaybackStateEvent) o;
return mState == that.mState
- && mTimeSincePlaybackCreatedMillis == that.mTimeSincePlaybackCreatedMillis;
+ && mTimeSinceCreatedMillis == that.mTimeSinceCreatedMillis;
}
@Override
public int hashCode() {
- return Objects.hash(mState, mTimeSincePlaybackCreatedMillis);
+ return Objects.hash(mState, mTimeSinceCreatedMillis);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mState);
- dest.writeLong(mTimeSincePlaybackCreatedMillis);
+ dest.writeLong(mTimeSinceCreatedMillis);
}
@Override
@@ -131,10 +186,10 @@ public final class PlaybackStateEvent implements Parcelable {
/** @hide */
/* package-private */ PlaybackStateEvent(@NonNull Parcel in) {
int state = in.readInt();
- long timeSincePlaybackCreatedMillis = in.readLong();
+ long timeSinceCreatedMillis = in.readLong();
this.mState = state;
- this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
}
public static final @NonNull Parcelable.Creator<PlaybackStateEvent> CREATOR =
@@ -150,4 +205,43 @@ public final class PlaybackStateEvent implements Parcelable {
}
};
+ /**
+ * A builder for {@link PlaybackStateEvent}
+ */
+ public static final class Builder {
+ private int mState = STATE_NOT_STARTED;
+ private long mTimeSinceCreatedMillis = -1;
+
+ /**
+ * Creates a new Builder.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Sets playback state.
+ */
+ public @NonNull Builder setState(@State int value) {
+ mState = value;
+ return this;
+ }
+
+ /**
+ * Sets timestamp since the creation in milliseconds.
+ * @param value the timestamp since the creation in milliseconds.
+ * -1 indicates the value is unknown.
+ */
+ public @NonNull Builder setTimeSinceCreatedMillis(@IntRange(from = -1) long value) {
+ mTimeSinceCreatedMillis = value;
+ return this;
+ }
+
+ /** Builds the instance. */
+ public @NonNull PlaybackStateEvent build() {
+ PlaybackStateEvent o = new PlaybackStateEvent(
+ mState,
+ mTimeSinceCreatedMillis);
+ return o;
+ }
+ }
}
diff --git a/media/java/android/media/metrics/TrackChangeEvent.java b/media/java/android/media/metrics/TrackChangeEvent.java
index fff0e36c062a..ef25357457c5 100644
--- a/media/java/android/media/metrics/TrackChangeEvent.java
+++ b/media/java/android/media/metrics/TrackChangeEvent.java
@@ -17,6 +17,7 @@
package android.media.metrics;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
@@ -28,20 +29,29 @@ import java.util.Objects;
/**
* Playback track change event.
- * @hide
*/
-public final class TrackChangeEvent implements Parcelable {
+public final class TrackChangeEvent extends Event implements Parcelable {
+ /** The track is off. */
public static final int TRACK_STATE_OFF = 0;
+ /** The track is on. */
public static final int TRACK_STATE_ON = 1;
+ /** Unknown track change reason. */
public static final int TRACK_CHANGE_REASON_UNKNOWN = 0;
+ /** Other track change reason. */
public static final int TRACK_CHANGE_REASON_OTHER = 1;
+ /** Track change reason for initial state. */
public static final int TRACK_CHANGE_REASON_INITIAL = 2;
+ /** Track change reason for manual changes. */
public static final int TRACK_CHANGE_REASON_MANUAL = 3;
+ /** Track change reason for adaptive changes. */
public static final int TRACK_CHANGE_REASON_ADAPTIVE = 4;
+ /** Audio track. */
public static final int TRACK_TYPE_AUDIO = 0;
+ /** Video track. */
public static final int TRACK_TYPE_VIDEO = 1;
+ /** Text track. */
public static final int TRACK_TYPE_TEXT = 2;
private final int mState;
@@ -50,7 +60,7 @@ public final class TrackChangeEvent implements Parcelable {
private final @Nullable String mSampleMimeType;
private final @Nullable String mCodecName;
private final int mBitrate;
- private final long mTimeSincePlaybackCreatedMillis;
+ private final long mTimeSinceCreatedMillis;
private final int mType;
private final @Nullable String mLanguage;
private final @Nullable String mLanguageRegion;
@@ -96,7 +106,7 @@ public final class TrackChangeEvent implements Parcelable {
@Nullable String sampleMimeType,
@Nullable String codecName,
int bitrate,
- long timeSincePlaybackCreatedMillis,
+ long timeSinceCreatedMillis,
int type,
@Nullable String language,
@Nullable String languageRegion,
@@ -110,7 +120,7 @@ public final class TrackChangeEvent implements Parcelable {
this.mSampleMimeType = sampleMimeType;
this.mCodecName = codecName;
this.mBitrate = bitrate;
- this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
this.mType = type;
this.mLanguage = language;
this.mLanguageRegion = languageRegion;
@@ -120,34 +130,60 @@ public final class TrackChangeEvent implements Parcelable {
this.mHeight = height;
}
+ /**
+ * Gets track state.
+ */
@TrackState
public int getTrackState() {
return mState;
}
+ /**
+ * Gets track change reason.
+ */
@TrackChangeReason
public int getTrackChangeReason() {
return mReason;
}
+ /**
+ * Gets container MIME type.
+ */
public @Nullable String getContainerMimeType() {
return mContainerMimeType;
}
+ /**
+ * Gets the MIME type of the video/audio/text samples.
+ */
public @Nullable String getSampleMimeType() {
return mSampleMimeType;
}
+ /**
+ * Gets codec name.
+ */
public @Nullable String getCodecName() {
return mCodecName;
}
+ /**
+ * Gets bitrate.
+ * @return the bitrate, or -1 if unknown.
+ */
+ @IntRange(from = -1, to = Integer.MAX_VALUE)
public int getBitrate() {
return mBitrate;
}
- public long getTimeSincePlaybackCreatedMillis() {
- return mTimeSincePlaybackCreatedMillis;
+ /**
+ * Gets timestamp since the creation in milliseconds.
+ * @return the timestamp since the creation in milliseconds, or -1 if unknown.
+ */
+ @Override
+ @IntRange(from = -1)
+ public long getTimeSinceCreatedMillis() {
+ return mTimeSinceCreatedMillis;
}
@TrackType
@@ -155,26 +191,55 @@ public final class TrackChangeEvent implements Parcelable {
return mType;
}
+ /**
+ * Gets language code.
+ * @return a two-letter ISO 639-1 language code.
+ */
public @Nullable String getLanguage() {
return mLanguage;
}
+
+ /**
+ * Gets language region code.
+ * @return an IETF BCP 47 optional language region subtag based on a two-letter country code.
+ */
public @Nullable String getLanguageRegion() {
return mLanguageRegion;
}
+ /**
+ * Gets channel count.
+ * @return the channel count, or -1 if unknown.
+ */
+ @IntRange(from = -1, to = Integer.MAX_VALUE)
public int getChannelCount() {
return mChannelCount;
}
+ /**
+ * Gets sample rate.
+ * @return the sample rate, or -1 if unknown.
+ */
+ @IntRange(from = -1, to = Integer.MAX_VALUE)
public int getSampleRate() {
return mSampleRate;
}
+ /**
+ * Gets video width.
+ * @return the video width, or -1 if unknown.
+ */
+ @IntRange(from = -1, to = Integer.MAX_VALUE)
public int getWidth() {
return mWidth;
}
+ /**
+ * Gets video height.
+ * @return the video height, or -1 if unknown.
+ */
+ @IntRange(from = -1, to = Integer.MAX_VALUE)
public int getHeight() {
return mHeight;
}
@@ -194,7 +259,7 @@ public final class TrackChangeEvent implements Parcelable {
if (mSampleMimeType != null) dest.writeString(mSampleMimeType);
if (mCodecName != null) dest.writeString(mCodecName);
dest.writeInt(mBitrate);
- dest.writeLong(mTimeSincePlaybackCreatedMillis);
+ dest.writeLong(mTimeSinceCreatedMillis);
dest.writeInt(mType);
if (mLanguage != null) dest.writeString(mLanguage);
if (mLanguageRegion != null) dest.writeString(mLanguageRegion);
@@ -218,7 +283,7 @@ public final class TrackChangeEvent implements Parcelable {
String sampleMimeType = (flg & 0x8) == 0 ? null : in.readString();
String codecName = (flg & 0x10) == 0 ? null : in.readString();
int bitrate = in.readInt();
- long timeSincePlaybackCreatedMillis = in.readLong();
+ long timeSinceCreatedMillis = in.readLong();
int type = in.readInt();
String language = (flg & 0x100) == 0 ? null : in.readString();
String languageRegion = (flg & 0x200) == 0 ? null : in.readString();
@@ -233,7 +298,7 @@ public final class TrackChangeEvent implements Parcelable {
this.mSampleMimeType = sampleMimeType;
this.mCodecName = codecName;
this.mBitrate = bitrate;
- this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
this.mType = type;
this.mLanguage = language;
this.mLanguageRegion = languageRegion;
@@ -256,38 +321,24 @@ public final class TrackChangeEvent implements Parcelable {
}
};
-
-
- // Code below generated by codegen v1.0.22.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/media/java/android/media/metrics/TrackChangeEvent.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
@Override
public String toString() {
- return "TrackChangeEvent { " +
- "state = " + mState + ", " +
- "reason = " + mReason + ", " +
- "containerMimeType = " + mContainerMimeType + ", " +
- "sampleMimeType = " + mSampleMimeType + ", " +
- "codecName = " + mCodecName + ", " +
- "bitrate = " + mBitrate + ", " +
- "timeSincePlaybackCreatedMillis = " + mTimeSincePlaybackCreatedMillis + ", " +
- "type = " + mType + ", " +
- "language = " + mLanguage + ", " +
- "languageRegion = " + mLanguageRegion + ", " +
- "channelCount = " + mChannelCount + ", " +
- "sampleRate = " + mSampleRate + ", " +
- "width = " + mWidth + ", " +
- "height = " + mHeight +
- " }";
+ return "TrackChangeEvent { "
+ + "state = " + mState + ", "
+ + "reason = " + mReason + ", "
+ + "containerMimeType = " + mContainerMimeType + ", "
+ + "sampleMimeType = " + mSampleMimeType + ", "
+ + "codecName = " + mCodecName + ", "
+ + "bitrate = " + mBitrate + ", "
+ + "timeSinceCreatedMillis = " + mTimeSinceCreatedMillis + ", "
+ + "type = " + mType + ", "
+ + "language = " + mLanguage + ", "
+ + "languageRegion = " + mLanguageRegion + ", "
+ + "channelCount = " + mChannelCount + ", "
+ + "sampleRate = " + mSampleRate + ", "
+ + "width = " + mWidth + ", "
+ + "height = " + mHeight
+ + " }";
}
@Override
@@ -301,7 +352,7 @@ public final class TrackChangeEvent implements Parcelable {
&& Objects.equals(mSampleMimeType, that.mSampleMimeType)
&& Objects.equals(mCodecName, that.mCodecName)
&& mBitrate == that.mBitrate
- && mTimeSincePlaybackCreatedMillis == that.mTimeSincePlaybackCreatedMillis
+ && mTimeSinceCreatedMillis == that.mTimeSinceCreatedMillis
&& mType == that.mType
&& Objects.equals(mLanguage, that.mLanguage)
&& Objects.equals(mLanguageRegion, that.mLanguageRegion)
@@ -314,7 +365,7 @@ public final class TrackChangeEvent implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(mState, mReason, mContainerMimeType, mSampleMimeType, mCodecName,
- mBitrate, mTimeSincePlaybackCreatedMillis, mType, mLanguage, mLanguageRegion,
+ mBitrate, mTimeSinceCreatedMillis, mType, mLanguage, mLanguageRegion,
mChannelCount, mSampleRate, mWidth, mHeight);
}
@@ -323,32 +374,33 @@ public final class TrackChangeEvent implements Parcelable {
*/
public static final class Builder {
// TODO: check track type for the setters.
- private int mState;
- private int mReason;
+ private int mState = TRACK_STATE_OFF;
+ private int mReason = TRACK_CHANGE_REASON_UNKNOWN;
private @Nullable String mContainerMimeType;
private @Nullable String mSampleMimeType;
private @Nullable String mCodecName;
- private int mBitrate;
- private long mTimeSincePlaybackCreatedMillis;
- private int mType;
+ private int mBitrate = -1;
+ private long mTimeSinceCreatedMillis = -1;
+ private final int mType;
private @Nullable String mLanguage;
private @Nullable String mLanguageRegion;
- private int mChannelCount;
- private int mSampleRate;
- private int mWidth;
- private int mHeight;
+ private int mChannelCount = -1;
+ private int mSampleRate = -1;
+ private int mWidth = -1;
+ private int mHeight = -1;
private long mBuilderFieldsSet = 0L;
/**
* Creates a new Builder.
- *
- * @hide
*/
public Builder(int type) {
mType = type;
}
+ /**
+ * Sets track state.
+ */
public @NonNull Builder setTrackState(@TrackState int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x1;
@@ -356,6 +408,9 @@ public final class TrackChangeEvent implements Parcelable {
return this;
}
+ /**
+ * Sets track change reason.
+ */
public @NonNull Builder setTrackChangeReason(@TrackChangeReason int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x2;
@@ -363,6 +418,9 @@ public final class TrackChangeEvent implements Parcelable {
return this;
}
+ /**
+ * Sets container MIME type.
+ */
public @NonNull Builder setContainerMimeType(@NonNull String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x4;
@@ -370,6 +428,9 @@ public final class TrackChangeEvent implements Parcelable {
return this;
}
+ /**
+ * Sets the MIME type of the video/audio/text samples.
+ */
public @NonNull Builder setSampleMimeType(@NonNull String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x8;
@@ -377,6 +438,9 @@ public final class TrackChangeEvent implements Parcelable {
return this;
}
+ /**
+ * Sets codec name.
+ */
public @NonNull Builder setCodecName(@NonNull String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x10;
@@ -384,27 +448,33 @@ public final class TrackChangeEvent implements Parcelable {
return this;
}
- public @NonNull Builder setBitrate(int value) {
+ /**
+ * Sets bitrate in bits per second.
+ * @param value the bitrate in bits per second. -1 indicates the value is unknown.
+ */
+ public @NonNull Builder setBitrate(@IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x20;
mBitrate = value;
return this;
}
- public @NonNull Builder setTimeSincePlaybackCreatedMillis(long value) {
+ /**
+ * Sets timestamp since the creation in milliseconds.
+ * @param value the timestamp since the creation in milliseconds.
+ * -1 indicates the value is unknown.
+ */
+ public @NonNull Builder setTimeSinceCreatedMillis(@IntRange(from = -1) long value) {
checkNotUsed();
mBuilderFieldsSet |= 0x40;
- mTimeSincePlaybackCreatedMillis = value;
- return this;
- }
-
- public @NonNull Builder setTrackType(@TrackType int value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x80;
- mType = value;
+ mTimeSinceCreatedMillis = value;
return this;
}
+ /**
+ * Sets language code.
+ * @param value a two-letter ISO 639-1 language code.
+ */
public @NonNull Builder setLanguage(@NonNull String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x100;
@@ -412,6 +482,11 @@ public final class TrackChangeEvent implements Parcelable {
return this;
}
+ /**
+ * Sets language region code.
+ * @param value an IETF BCP 47 optional language region subtag based on a two-letter country
+ * code.
+ */
public @NonNull Builder setLanguageRegion(@NonNull String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x200;
@@ -419,28 +494,46 @@ public final class TrackChangeEvent implements Parcelable {
return this;
}
- public @NonNull Builder setChannelCount(int value) {
+ /**
+ * Sets channel count.
+ * @param value the channel count. -1 indicates the value is unknown.
+ */
+ public @NonNull Builder setChannelCount(
+ @IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x400;
mChannelCount = value;
return this;
}
- public @NonNull Builder setSampleRate(int value) {
+ /**
+ * Sets sample rate.
+ * @param value the sample rate. -1 indicates the value is unknown.
+ */
+ public @NonNull Builder setSampleRate(
+ @IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x800;
mSampleRate = value;
return this;
}
- public @NonNull Builder setWidth(int value) {
+ /**
+ * Sets video width.
+ * @param value the video width. -1 indicates the value is unknown.
+ */
+ public @NonNull Builder setWidth(@IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x1000;
mWidth = value;
return this;
}
- public @NonNull Builder setHeight(int value) {
+ /**
+ * Sets video height.
+ * @param value the video height. -1 indicates the value is unknown.
+ */
+ public @NonNull Builder setHeight(@IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x2000;
mHeight = value;
@@ -459,7 +552,7 @@ public final class TrackChangeEvent implements Parcelable {
mSampleMimeType,
mCodecName,
mBitrate,
- mTimeSincePlaybackCreatedMillis,
+ mTimeSinceCreatedMillis,
mType,
mLanguage,
mLanguageRegion,
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 31fb8d03c4a0..9bf126b7a875 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -35,7 +35,7 @@ interface ISession {
ISessionController getController();
void setFlags(int flags);
void setActive(boolean active);
- void setMediaButtonReceiver(in PendingIntent mbr);
+ void setMediaButtonReceiver(in PendingIntent mbr, String sessionPackageName);
void setMediaButtonBroadcastReceiver(in ComponentName broadcastReceiver);
void setLaunchPendingIntent(in PendingIntent pi);
void destroySession();
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 66d579408db0..dc476b873786 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -73,8 +73,10 @@ interface ISessionManager {
void setOnMediaKeyListener(in IOnMediaKeyListener listener);
boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid);
- void setCustomMediaKeyDispatcherForTesting(String name);
- void setCustomSessionPolicyProviderForTesting(String name);
+ void setCustomMediaKeyDispatcher(String name);
+ void setCustomMediaSessionPolicyProvider(String name);
+ boolean hasCustomMediaKeyDispatcher(String componentName);
+ boolean hasCustomMediaSessionPolicyProvider(String componentName);
int getSessionPolicies(in MediaSession.Token token);
void setSessionPolicies(in MediaSession.Token token, int policies);
}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 24118b086c24..20fa53d3ec32 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -286,7 +286,7 @@ public final class MediaSession {
@Deprecated
public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
try {
- mBinder.setMediaButtonReceiver(mbr);
+ mBinder.setMediaButtonReceiver(mbr, mContext.getPackageName());
} catch (RemoteException e) {
Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index f580ea5d57de..8de5e42e93b2 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -958,9 +958,9 @@ public final class MediaSessionManager {
* @hide
*/
@VisibleForTesting
- public void setCustomMediaKeyDispatcherForTesting(@Nullable String name) {
+ public void setCustomMediaKeyDispatcher(@Nullable String name) {
try {
- mService.setCustomMediaKeyDispatcherForTesting(name);
+ mService.setCustomMediaKeyDispatcher(name);
} catch (RemoteException e) {
Log.e(TAG, "Failed to set custom media key dispatcher name", e);
}
@@ -968,22 +968,58 @@ public final class MediaSessionManager {
/**
* Set the component name for the custom
- * {@link com.android.server.media.SessionPolicyProvider} class. Set to null to restore to the
- * custom {@link com.android.server.media.SessionPolicyProvider} class name retrieved from the
- * config value.
+ * {@link com.android.server.media.MediaSessionPolicyProvider} class. Set to null to restore to
+ * the custom {@link com.android.server.media.MediaSessionPolicyProvider} class name retrieved
+ * from the config value.
*
* @hide
*/
@VisibleForTesting
- public void setCustomSessionPolicyProviderForTesting(@Nullable String name) {
+ public void setCustomMediaSessionPolicyProvider(@Nullable String name) {
try {
- mService.setCustomSessionPolicyProviderForTesting(name);
+ mService.setCustomMediaSessionPolicyProvider(name);
} catch (RemoteException e) {
Log.e(TAG, "Failed to set custom session policy provider name", e);
}
}
/**
+ * Get the component name for the custom {@link com.android.server.media.MediaKeyDispatcher}
+ * class.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public boolean hasCustomMediaKeyDispatcher(@NonNull String componentName) {
+ Objects.requireNonNull(componentName, "componentName shouldn't be null");
+ try {
+ return mService.hasCustomMediaKeyDispatcher(componentName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to check if custom media key dispatcher with given component"
+ + " name exists", e);
+ }
+ return false;
+ }
+
+ /**
+ * Get the component name for the custom
+ * {@link com.android.server.media.MediaSessionPolicyProvider} class.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public boolean hasCustomMediaSessionPolicyProvider(@NonNull String componentName) {
+ Objects.requireNonNull(componentName, "componentName shouldn't be null");
+ try {
+ return mService.hasCustomMediaSessionPolicyProvider(componentName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to check if custom media session policy provider with given"
+ + " component name exists", e);
+ }
+ return false;
+ }
+
+ /**
* Get session policies of the specified {@link MediaSession.Token}.
*
* @hide
@@ -1127,9 +1163,10 @@ public final class MediaSessionManager {
* toast showing the volume should be shown.
*
* @param sessionToken the remote media session token
- * @param flags extra information about how to handle the volume change
+ * @param flags flags containing extra action or information regarding the volume change
*/
- void onVolumeChanged(@NonNull MediaSession.Token sessionToken, int flags);
+ void onVolumeChanged(@NonNull MediaSession.Token sessionToken,
+ @AudioManager.Flags int flags);
/**
* Called when the default remote session is changed where the default remote session
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 98b9ad8ec2b3..c0185dcc4539 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -2525,7 +2525,10 @@ public final class TvInputManager {
/**
* Pauses TV program recording in the current recording session.
*
- * @param params A set of extra parameters which might be handled with this event.
+ * @param params Domain-specific data for this request. Keys <em>must</em> be a scoped
+ * name, i.e. prefixed with a package name you own, so that different developers
+ * will not create conflicting keys.
+ * {@link TvRecordingClient#pauseRecording(Bundle)}.
*/
void pauseRecording(@NonNull Bundle params) {
if (mToken == null) {
@@ -2542,7 +2545,10 @@ public final class TvInputManager {
/**
* Resumes TV program recording in the current recording session.
*
- * @param params A set of extra parameters which might be handled with this event.
+ * @param params Domain-specific data for this request. Keys <em>must</em> be a scoped
+ * name, i.e. prefixed with a package name you own, so that different developers
+ * will not create conflicting keys.
+ * {@link TvRecordingClient#resumeRecording(Bundle)}.
*/
void resumeRecording(@NonNull Bundle params) {
if (mToken == null) {
diff --git a/media/java/android/media/tv/tunerresourcemanager/Android.bp b/media/java/android/media/tv/tunerresourcemanager/Android.bp
index c38d9194df44..c904ca2be00c 100644
--- a/media/java/android/media/tv/tunerresourcemanager/Android.bp
+++ b/media/java/android/media/tv/tunerresourcemanager/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "framework-media-tv-tunerresourcemanager-sources-aidl",
srcs: [
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 65b64d7e8df3..ce4550492740 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -1,3 +1,20 @@
+package {
+ default_applicable_licenses: ["frameworks_base_media_jni_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_media_jni_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
cc_library_shared {
name: "libmedia_jni",
@@ -64,6 +81,7 @@ cc_library_shared {
"android.hardware.cas@1.0",
"android.hardware.cas.native@1.0",
"android.hardware.drm@1.3",
+ "android.hardware.drm@1.4",
"android.hidl.memory@1.0",
"android.hidl.token@1.0-utils",
],
@@ -180,6 +198,10 @@ cc_library_shared {
"libstagefright_foundation_headers",
],
+ // TunerService is a system service required for Tuner feature.
+ // TunerJNI is a client of TunerService so we build the dependency here.
+ required: ["mediatuner"],
+
export_include_dirs: ["."],
cflags: [
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 0e8719eeb79c..73e1f7df89a1 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -37,6 +37,7 @@
#include <mediadrm/DrmUtils.h>
#include <mediadrm/IDrmMetricsConsumer.h>
#include <mediadrm/IDrm.h>
+#include <utils/Vector.h>
using ::android::os::PersistableBundle;
namespace drm = ::android::hardware::drm;
@@ -187,6 +188,11 @@ struct KeyStatusFields {
jclass classId;
};
+struct LogMessageFields {
+ jmethodID init;
+ jclass classId;
+};
+
struct fields_t {
jfieldID context;
jmethodID post_event;
@@ -208,6 +214,7 @@ struct fields_t {
jmethodID createFromParcelId;
jclass parcelCreatorClassId;
KeyStatusFields keyStatus;
+ LogMessageFields logMessage;
};
static fields_t gFields;
@@ -224,6 +231,19 @@ jbyteArray hidlVectorToJByteArray(const hardware::hidl_vec<uint8_t> &vector) {
return result;
}
+jobject hidlLogMessagesToJavaList(JNIEnv *env, const Vector<drm::V1_4::LogMessage> &logs) {
+ jclass clazz = gFields.arraylistClassId;
+ jobject arrayList = env->NewObject(clazz, gFields.arraylist.init);
+ clazz = gFields.logMessage.classId;
+ for (auto log: logs) {
+ jobject jLog = env->NewObject(clazz, gFields.logMessage.init,
+ static_cast<jlong>(log.timeMs),
+ static_cast<jint>(log.priority),
+ env->NewStringUTF(log.message.c_str()));
+ env->CallBooleanMethod(arrayList, gFields.arraylist.add, jLog);
+ }
+ return arrayList;
+}
} // namespace anonymous
// ----------------------------------------------------------------------------
@@ -323,11 +343,56 @@ void JNIDrmListener::notify(DrmPlugin::EventType eventType, int extra,
}
}
+jint MediaErrorToJavaError(status_t err) {
+#define STATUS_CASE(status) \
+ case status: \
+ return J##status
+
+ switch (err) {
+ STATUS_CASE(ERROR_DRM_UNKNOWN);
+ STATUS_CASE(ERROR_DRM_NO_LICENSE);
+ STATUS_CASE(ERROR_DRM_LICENSE_EXPIRED);
+ STATUS_CASE(ERROR_DRM_RESOURCE_BUSY);
+ STATUS_CASE(ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION);
+ STATUS_CASE(ERROR_DRM_SESSION_NOT_OPENED);
+ STATUS_CASE(ERROR_DRM_CANNOT_HANDLE);
+ STATUS_CASE(ERROR_DRM_INSUFFICIENT_SECURITY);
+ STATUS_CASE(ERROR_DRM_FRAME_TOO_LARGE);
+ STATUS_CASE(ERROR_DRM_SESSION_LOST_STATE);
+ STATUS_CASE(ERROR_DRM_CERTIFICATE_MALFORMED);
+ STATUS_CASE(ERROR_DRM_CERTIFICATE_MISSING);
+ STATUS_CASE(ERROR_DRM_CRYPTO_LIBRARY);
+ STATUS_CASE(ERROR_DRM_GENERIC_OEM);
+ STATUS_CASE(ERROR_DRM_GENERIC_PLUGIN);
+ STATUS_CASE(ERROR_DRM_INIT_DATA);
+ STATUS_CASE(ERROR_DRM_KEY_NOT_LOADED);
+ STATUS_CASE(ERROR_DRM_LICENSE_PARSE);
+ STATUS_CASE(ERROR_DRM_LICENSE_POLICY);
+ STATUS_CASE(ERROR_DRM_LICENSE_RELEASE);
+ STATUS_CASE(ERROR_DRM_LICENSE_REQUEST_REJECTED);
+ STATUS_CASE(ERROR_DRM_LICENSE_RESTORE);
+ STATUS_CASE(ERROR_DRM_LICENSE_STATE);
+ STATUS_CASE(ERROR_DRM_MEDIA_FRAMEWORK);
+ STATUS_CASE(ERROR_DRM_PROVISIONING_CERTIFICATE);
+ STATUS_CASE(ERROR_DRM_PROVISIONING_CONFIG);
+ STATUS_CASE(ERROR_DRM_PROVISIONING_PARSE);
+ STATUS_CASE(ERROR_DRM_PROVISIONING_RETRY);
+ STATUS_CASE(ERROR_DRM_RESOURCE_CONTENTION);
+ STATUS_CASE(ERROR_DRM_SECURE_STOP_RELEASE);
+ STATUS_CASE(ERROR_DRM_STORAGE_READ);
+ STATUS_CASE(ERROR_DRM_STORAGE_WRITE);
+ STATUS_CASE(ERROR_DRM_ZERO_SUBSAMPLES);
+#undef STATUS_CASE
+ }
+ return static_cast<jint>(err);
+}
+
static void throwStateException(JNIEnv *env, const char *msg, status_t err) {
ALOGE("Illegal state exception: %s (%d)", msg, err);
+ jint jerr = MediaErrorToJavaError(err);
jobject exception = env->NewObject(gFields.stateException.classId,
- gFields.stateException.init, static_cast<int>(err),
+ gFields.stateException.init, static_cast<int>(jerr),
env->NewStringUTF(msg));
env->Throw(static_cast<jthrowable>(exception));
}
@@ -357,43 +422,11 @@ static bool isSessionException(status_t err) {
}
static bool throwExceptionAsNecessary(
- JNIEnv *env, status_t err, const char *msg = NULL) {
-
- const char *drmMessage = NULL;
-
- switch (err) {
- case ERROR_DRM_UNKNOWN:
- drmMessage = "General DRM error";
- break;
- case ERROR_DRM_NO_LICENSE:
- drmMessage = "No license";
- break;
- case ERROR_DRM_LICENSE_EXPIRED:
- drmMessage = "License expired";
- break;
- case ERROR_DRM_SESSION_NOT_OPENED:
- drmMessage = "Session not opened";
- break;
- case ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED:
- drmMessage = "Not initialized";
- break;
- case ERROR_DRM_DECRYPT:
- drmMessage = "Decrypt error";
- break;
- case ERROR_DRM_CANNOT_HANDLE:
- drmMessage = "Invalid parameter or data format";
- break;
- case ERROR_DRM_INVALID_STATE:
- drmMessage = "Invalid state";
- break;
- default:
- break;
- }
-
- String8 vendorMessage;
- if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
- vendorMessage = String8::format("DRM vendor-defined error: %d", err);
- drmMessage = vendorMessage.string();
+ JNIEnv *env, const sp<IDrm> &drm, status_t err, const char *msg = NULL) {
+ std::string msgStr;
+ if (drm != NULL) {
+ msgStr = DrmUtils::GetExceptionMessage(err, msg, drm);
+ msg = msgStr.c_str();
}
if (err == BAD_VALUE || err == ERROR_DRM_CANNOT_HANDLE) {
@@ -419,15 +452,6 @@ static bool throwExceptionAsNecessary(
throwSessionException(env, msg, err);
return true;
} else if (err != OK) {
- String8 errbuf;
- if (drmMessage != NULL) {
- if (msg == NULL) {
- msg = drmMessage;
- } else {
- errbuf = String8::format("%s: %s", msg, drmMessage);
- msg = errbuf.string();
- }
- }
throwStateException(env, msg, err);
return true;
}
@@ -907,6 +931,10 @@ static void android_media_MediaDrm_native_init(JNIEnv *env) {
FIND_CLASS(clazz, "android/media/MediaDrm$KeyStatus");
gFields.keyStatus.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
GET_METHOD_ID(gFields.keyStatus.init, clazz, "<init>", "([BI)V");
+
+ FIND_CLASS(clazz, "android/media/MediaDrm$LogMessage");
+ gFields.logMessage.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
+ GET_METHOD_ID(gFields.logMessage.init, clazz, "<init>", "(JILjava/lang/String;)V");
}
static void android_media_MediaDrm_native_setup(
@@ -1021,7 +1049,7 @@ static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative(
status_t err = JDrm::IsCryptoSchemeSupported(uuid.array(), mimeType,
securityLevel, &isSupported);
- if (throwExceptionAsNecessary(env, err, "Failed to query crypto scheme support")) {
+ if (throwExceptionAsNecessary(env, NULL, err, "Failed to query crypto scheme support")) {
return false;
}
return isSupported;
@@ -1044,7 +1072,7 @@ static jbyteArray android_media_MediaDrm_openSession(
status_t err = drm->openSession(level, sessionId);
- if (throwExceptionAsNecessary(env, err, "Failed to open session")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to open session")) {
return NULL;
}
@@ -1063,7 +1091,7 @@ static void android_media_MediaDrm_closeSession(
status_t err = drm->closeSession(sessionId);
- throwExceptionAsNecessary(env, err, "Failed to close session");
+ throwExceptionAsNecessary(env, drm, err, "Failed to close session");
}
static jobject android_media_MediaDrm_getKeyRequest(
@@ -1116,7 +1144,7 @@ static jobject android_media_MediaDrm_getKeyRequest(
status_t err = drm->getKeyRequest(sessionId, initData, mimeType,
keyType, optParams, request, defaultUrl, &keyRequestType);
- if (throwExceptionAsNecessary(env, err, "Failed to get key request")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to get key request")) {
return NULL;
}
@@ -1186,7 +1214,7 @@ static jbyteArray android_media_MediaDrm_provideKeyResponse(
status_t err = drm->provideKeyResponse(sessionId, response, keySetId);
- if (throwExceptionAsNecessary(env, err, "Failed to handle key response")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to handle key response")) {
return NULL;
}
return VectorToJByteArray(env, keySetId);
@@ -1210,7 +1238,7 @@ static void android_media_MediaDrm_removeKeys(
status_t err = drm->removeKeys(keySetId);
- throwExceptionAsNecessary(env, err, "Failed to remove keys");
+ throwExceptionAsNecessary(env, drm, err, "Failed to remove keys");
}
static void android_media_MediaDrm_restoreKeys(
@@ -1233,7 +1261,7 @@ static void android_media_MediaDrm_restoreKeys(
status_t err = drm->restoreKeys(sessionId, keySetId);
- throwExceptionAsNecessary(env, err, "Failed to restore keys");
+ throwExceptionAsNecessary(env, drm, err, "Failed to restore keys");
}
static jobject android_media_MediaDrm_queryKeyStatus(
@@ -1249,7 +1277,7 @@ static jobject android_media_MediaDrm_queryKeyStatus(
status_t err = drm->queryKeyStatus(sessionId, infoMap);
- if (throwExceptionAsNecessary(env, err, "Failed to query key status")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to query key status")) {
return NULL;
}
@@ -1279,7 +1307,7 @@ static jobject android_media_MediaDrm_getProvisionRequestNative(
String8 certAuthority = JStringToString8(env, jcertAuthority);
status_t err = drm->getProvisionRequest(certType, certAuthority, request, defaultUrl);
- if (throwExceptionAsNecessary(env, err, "Failed to get provision request")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to get provision request")) {
return NULL;
}
@@ -1334,7 +1362,7 @@ static jobject android_media_MediaDrm_provideProvisionResponseNative(
env->SetObjectField(certificateObj, gFields.certificate.wrappedPrivateKey, jwrappedKey);
}
- throwExceptionAsNecessary(env, err, "Failed to handle provision response");
+ throwExceptionAsNecessary(env, drm, err, "Failed to handle provision response");
return certificateObj;
}
@@ -1350,7 +1378,7 @@ static jobject android_media_MediaDrm_getSecureStops(
status_t err = drm->getSecureStops(secureStops);
- if (throwExceptionAsNecessary(env, err, "Failed to get secure stops")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to get secure stops")) {
return NULL;
}
@@ -1369,7 +1397,7 @@ static jobject android_media_MediaDrm_getSecureStopIds(
status_t err = drm->getSecureStopIds(secureStopIds);
- if (throwExceptionAsNecessary(env, err, "Failed to get secure stop Ids")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to get secure stop Ids")) {
return NULL;
}
@@ -1388,7 +1416,7 @@ static jbyteArray android_media_MediaDrm_getSecureStop(
status_t err = drm->getSecureStop(JByteArrayToVector(env, ssid), secureStop);
- if (throwExceptionAsNecessary(env, err, "Failed to get secure stop")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to get secure stop")) {
return NULL;
}
@@ -1407,7 +1435,7 @@ static void android_media_MediaDrm_releaseSecureStops(
status_t err = drm->releaseSecureStops(ssRelease);
- throwExceptionAsNecessary(env, err, "Failed to release secure stops");
+ throwExceptionAsNecessary(env, drm, err, "Failed to release secure stops");
}
static void android_media_MediaDrm_removeSecureStop(
@@ -1420,7 +1448,7 @@ static void android_media_MediaDrm_removeSecureStop(
status_t err = drm->removeSecureStop(JByteArrayToVector(env, ssid));
- throwExceptionAsNecessary(env, err, "Failed to remove secure stop");
+ throwExceptionAsNecessary(env, drm, err, "Failed to remove secure stop");
}
static void android_media_MediaDrm_removeAllSecureStops(
@@ -1433,7 +1461,7 @@ static void android_media_MediaDrm_removeAllSecureStops(
status_t err = drm->removeAllSecureStops();
- throwExceptionAsNecessary(env, err, "Failed to remove all secure stops");
+ throwExceptionAsNecessary(env, drm, err, "Failed to remove all secure stops");
}
@@ -1472,7 +1500,7 @@ static jint android_media_MediaDrm_getConnectedHdcpLevel(JNIEnv *env,
status_t err = drm->getHdcpLevels(&connected, &max);
- if (throwExceptionAsNecessary(env, err, "Failed to get HDCP levels")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to get HDCP levels")) {
return gHdcpLevels.kHdcpLevelUnknown;
}
return HdcpLevelTojint(connected);
@@ -1491,7 +1519,7 @@ static jint android_media_MediaDrm_getMaxHdcpLevel(JNIEnv *env,
status_t err = drm->getHdcpLevels(&connected, &max);
- if (throwExceptionAsNecessary(env, err, "Failed to get HDCP levels")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to get HDCP levels")) {
return gHdcpLevels.kHdcpLevelUnknown;
}
return HdcpLevelTojint(max);
@@ -1508,7 +1536,7 @@ static jint android_media_MediaDrm_getOpenSessionCount(JNIEnv *env,
uint32_t open = 0, max = 0;
status_t err = drm->getNumberOfSessions(&open, &max);
- if (throwExceptionAsNecessary(env, err, "Failed to get number of sessions")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to get number of sessions")) {
return 0;
}
return open;
@@ -1525,7 +1553,7 @@ static jint android_media_MediaDrm_getMaxSessionCount(JNIEnv *env,
uint32_t open = 0, max = 0;
status_t err = drm->getNumberOfSessions(&open, &max);
- if (throwExceptionAsNecessary(env, err, "Failed to get number of sessions")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to get number of sessions")) {
return 0;
}
return max;
@@ -1545,7 +1573,7 @@ static jint android_media_MediaDrm_getSecurityLevel(JNIEnv *env,
status_t err = drm->getSecurityLevel(sessionId, &level);
- if (throwExceptionAsNecessary(env, err, "Failed to get security level")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to get security level")) {
return gSecurityLevels.kSecurityLevelUnknown;
}
@@ -1577,7 +1605,7 @@ static jobject android_media_MediaDrm_getOfflineLicenseKeySetIds(
status_t err = drm->getOfflineLicenseKeySetIds(keySetIds);
- if (throwExceptionAsNecessary(env, err, "Failed to get offline key set Ids")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to get offline key set Ids")) {
return NULL;
}
@@ -1594,7 +1622,7 @@ static void android_media_MediaDrm_removeOfflineLicense(
status_t err = drm->removeOfflineLicense(JByteArrayToVector(env, keySetId));
- throwExceptionAsNecessary(env, err, "Failed to remove offline license");
+ throwExceptionAsNecessary(env, drm, err, "Failed to remove offline license");
}
static jint android_media_MediaDrm_getOfflineLicenseState(JNIEnv *env,
@@ -1611,7 +1639,7 @@ static jint android_media_MediaDrm_getOfflineLicenseState(JNIEnv *env,
status_t err = drm->getOfflineLicenseState(keySetId, &state);
- if (throwExceptionAsNecessary(env, err, "Failed to get offline license state")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to get offline license state")) {
return gOfflineLicenseStates.kOfflineLicenseStateUnknown;
}
@@ -1644,7 +1672,7 @@ static jstring android_media_MediaDrm_getPropertyString(
status_t err = drm->getPropertyString(name, value);
- if (throwExceptionAsNecessary(env, err, "Failed to get property")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to get property")) {
return NULL;
}
@@ -1670,7 +1698,7 @@ static jbyteArray android_media_MediaDrm_getPropertyByteArray(
status_t err = drm->getPropertyByteArray(name, value);
- if (throwExceptionAsNecessary(env, err, "Failed to get property")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to get property")) {
return NULL;
}
@@ -1702,7 +1730,7 @@ static void android_media_MediaDrm_setPropertyString(
status_t err = drm->setPropertyString(name, value);
- throwExceptionAsNecessary(env, err, "Failed to set property");
+ throwExceptionAsNecessary(env, drm, err, "Failed to set property");
}
static void android_media_MediaDrm_setPropertyByteArray(
@@ -1730,7 +1758,7 @@ static void android_media_MediaDrm_setPropertyByteArray(
status_t err = drm->setPropertyByteArray(name, value);
- throwExceptionAsNecessary(env, err, "Failed to set property");
+ throwExceptionAsNecessary(env, drm, err, "Failed to set property");
}
static void android_media_MediaDrm_setCipherAlgorithmNative(
@@ -1754,7 +1782,7 @@ static void android_media_MediaDrm_setCipherAlgorithmNative(
status_t err = drm->setCipherAlgorithm(sessionId, algorithm);
- throwExceptionAsNecessary(env, err, "Failed to set cipher algorithm");
+ throwExceptionAsNecessary(env, drm, err, "Failed to set cipher algorithm");
}
static void android_media_MediaDrm_setMacAlgorithmNative(
@@ -1778,7 +1806,7 @@ static void android_media_MediaDrm_setMacAlgorithmNative(
status_t err = drm->setMacAlgorithm(sessionId, algorithm);
- throwExceptionAsNecessary(env, err, "Failed to set mac algorithm");
+ throwExceptionAsNecessary(env, drm, err, "Failed to set mac algorithm");
}
@@ -1806,7 +1834,7 @@ static jbyteArray android_media_MediaDrm_encryptNative(
status_t err = drm->encrypt(sessionId, keyId, input, iv, output);
- if (throwExceptionAsNecessary(env, err, "Failed to encrypt")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to encrypt")) {
return NULL;
}
@@ -1836,7 +1864,7 @@ static jbyteArray android_media_MediaDrm_decryptNative(
Vector<uint8_t> output;
status_t err = drm->decrypt(sessionId, keyId, input, iv, output);
- if (throwExceptionAsNecessary(env, err, "Failed to decrypt")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to decrypt")) {
return NULL;
}
@@ -1866,7 +1894,7 @@ static jbyteArray android_media_MediaDrm_signNative(
status_t err = drm->sign(sessionId, keyId, message, signature);
- if (throwExceptionAsNecessary(env, err, "Failed to sign")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to sign")) {
return NULL;
}
@@ -1897,7 +1925,7 @@ static jboolean android_media_MediaDrm_verifyNative(
status_t err = drm->verify(sessionId, keyId, message, signature, match);
- throwExceptionAsNecessary(env, err, "Failed to verify");
+ throwExceptionAsNecessary(env, drm, err, "Failed to verify");
return match;
}
@@ -1946,7 +1974,7 @@ static jbyteArray android_media_MediaDrm_signRSANative(
status_t err = drm->signRSA(sessionId, algorithm, message, wrappedKey, signature);
- if (throwExceptionAsNecessary(env, err, "Failed to sign")) {
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to sign")) {
return NULL;
}
@@ -1993,7 +2021,23 @@ static void android_media_MediaDrm_setPlaybackId(
playbackId = JStringToString8(env, jplaybackId);
}
status_t err = drm->setPlaybackId(sessionId, playbackId.c_str());
- throwExceptionAsNecessary(env, err, "Failed to set playbackId");
+ throwExceptionAsNecessary(env, drm, err, "Failed to set playbackId");
+}
+
+static jobject android_media_MediaDrm_getLogMessages(
+ JNIEnv *env, jobject thiz) {
+ sp<IDrm> drm = GetDrm(env, thiz);
+ if (!CheckDrm(env, drm)) {
+ return NULL;
+ }
+
+ Vector<drm::V1_4::LogMessage> logs;
+ status_t err = drm->getLogMessages(logs);
+ ALOGI("drm->getLogMessages %zu logs", logs.size());
+ if (throwExceptionAsNecessary(env, drm, err, "Failed to get log messages")) {
+ return NULL;
+ }
+ return hidlLogMessagesToJavaList(env, logs);
}
static const JNINativeMethod gMethods[] = {
@@ -2016,7 +2060,7 @@ static const JNINativeMethod gMethods[] = {
{ "closeSessionNative", "([B)V",
(void *)android_media_MediaDrm_closeSession },
- { "getKeyRequest", "([B[BLjava/lang/String;ILjava/util/HashMap;)"
+ { "getKeyRequestNative", "([B[BLjava/lang/String;ILjava/util/HashMap;)"
"Landroid/media/MediaDrm$KeyRequest;",
(void *)android_media_MediaDrm_getKeyRequest },
@@ -2123,6 +2167,9 @@ static const JNINativeMethod gMethods[] = {
{ "setPlaybackId", "([BLjava/lang/String;)V",
(void *)android_media_MediaDrm_setPlaybackId },
+
+ { "getLogMessages", "()Ljava/util/List;",
+ (void *)android_media_MediaDrm_getLogMessages },
};
int register_android_media_Drm(JNIEnv *env) {
diff --git a/media/jni/android_media_MediaDrm.h b/media/jni/android_media_MediaDrm.h
index b1f544cb2dbe..dc0793af2d17 100644
--- a/media/jni/android_media_MediaDrm.h
+++ b/media/jni/android_media_MediaDrm.h
@@ -28,6 +28,44 @@
namespace {
+enum {
+ // TODO(b/180483929): use reverse jni e.g. android_media_MediaDrm_native_init
+ // KEEP IN SYNC with MediaDrm$ErrorCodes in MediaDrm.java!
+ JERROR_DRM_UNKNOWN = 0,
+ JERROR_DRM_NO_LICENSE = 1,
+ JERROR_DRM_LICENSE_EXPIRED = 2,
+ JERROR_DRM_RESOURCE_BUSY = 3,
+ JERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION = 4,
+ JERROR_DRM_SESSION_NOT_OPENED = 5,
+ JERROR_DRM_CANNOT_HANDLE = 6,
+ JERROR_DRM_INSUFFICIENT_SECURITY = 7,
+ JERROR_DRM_FRAME_TOO_LARGE = 8,
+ JERROR_DRM_SESSION_LOST_STATE = 9,
+ JERROR_DRM_CERTIFICATE_MALFORMED = 10,
+ JERROR_DRM_CERTIFICATE_MISSING = 11,
+ JERROR_DRM_CRYPTO_LIBRARY = 12,
+ JERROR_DRM_GENERIC_OEM = 13,
+ JERROR_DRM_GENERIC_PLUGIN = 14,
+ JERROR_DRM_INIT_DATA = 15,
+ JERROR_DRM_KEY_NOT_LOADED = 16,
+ JERROR_DRM_LICENSE_PARSE = 17,
+ JERROR_DRM_LICENSE_POLICY = 18,
+ JERROR_DRM_LICENSE_RELEASE = 19,
+ JERROR_DRM_LICENSE_REQUEST_REJECTED = 20,
+ JERROR_DRM_LICENSE_RESTORE = 21,
+ JERROR_DRM_LICENSE_STATE = 22,
+ JERROR_DRM_MEDIA_FRAMEWORK = 23,
+ JERROR_DRM_PROVISIONING_CERTIFICATE = 24,
+ JERROR_DRM_PROVISIONING_CONFIG = 25,
+ JERROR_DRM_PROVISIONING_PARSE = 26,
+ JERROR_DRM_PROVISIONING_RETRY = 27,
+ JERROR_DRM_RESOURCE_CONTENTION = 28,
+ JERROR_DRM_SECURE_STOP_RELEASE = 29,
+ JERROR_DRM_STORAGE_READ = 30,
+ JERROR_DRM_STORAGE_WRITE = 31,
+ JERROR_DRM_ZERO_SUBSAMPLES = 32,
+};
+
struct ListenerArgs {
jbyteArray jSessionId;
jbyteArray jData;
@@ -98,6 +136,8 @@ private:
DISALLOW_EVIL_CONSTRUCTORS(JDrm);
};
+jint MediaErrorToJavaError(status_t err);
+
} // namespace android
#endif // _ANDROID_MEDIA_DRM_H_
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index bd8d2e9f77a4..98ac5b983098 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1409,7 +1409,7 @@ static const JNINativeMethod gMethods[] = {
{"native_setup", "(Ljava/lang/Object;Ljava/lang/String;)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},
- {"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
+ {"native_setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
{"_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
{"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect},
{"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData},
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index 40e4c54c2921..c2fc91d5cfea 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_media_jni_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_media_jni_license"],
+}
+
cc_library_shared {
name: "libaudioeffect_jni",
diff --git a/media/jni/soundpool/Android.bp b/media/jni/soundpool/Android.bp
index 6141308a8fdb..7b498e027d1c 100644
--- a/media/jni/soundpool/Android.bp
+++ b/media/jni/soundpool/Android.bp
@@ -1,3 +1,22 @@
+package {
+ default_applicable_licenses: [
+ "frameworks_base_media_jni_soundpool_license",
+ ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_media_jni_soundpool_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
tidy_errors = [
// https://clang.llvm.org/extra/clang-tidy/checks/list.html
// For many categories, the checks are too many to specify individually.
diff --git a/media/jni/soundpool/tests/Android.bp b/media/jni/soundpool/tests/Android.bp
index 52f59ed69503..7d31c106f9c4 100644
--- a/media/jni/soundpool/tests/Android.bp
+++ b/media/jni/soundpool/tests/Android.bp
@@ -1,3 +1,14 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_media_jni_soundpool_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: [
+ "frameworks_base_media_jni_soundpool_license",
+ ],
+}
+
cc_binary {
name: "soundpool_stress",
host_supported: false,
diff --git a/media/jni/tuner/ClientHelper.h b/media/jni/tuner/ClientHelper.h
index 185b2f651d29..508dccf36841 100644
--- a/media/jni/tuner/ClientHelper.h
+++ b/media/jni/tuner/ClientHelper.h
@@ -19,6 +19,7 @@
#include <android/binder_parcel_utils.h>
#include <android/hardware/tv/tuner/1.1/types.h>
+#include <utils/Log.h>
using Status = ::ndk::ScopedAStatus;
@@ -37,6 +38,7 @@ public:
} else if (s.isOk()) {
return Result::SUCCESS;
}
+ ALOGE("Aidl exception code %s", s.getDescription().c_str());
return Result::UNKNOWN_ERROR;
}
};
diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp
index 748d45808932..359ef364083c 100644
--- a/media/jni/tuner/DemuxClient.cpp
+++ b/media/jni/tuner/DemuxClient.cpp
@@ -216,14 +216,13 @@ Result DemuxClient::disconnectCiCam() {
Result DemuxClient::close() {
if (mTunerDemux != NULL) {
Status s = mTunerDemux->close();
+ mDemux = NULL;
return ClientHelper::getServiceSpecificErrorCode(s);
}
if (mDemux != NULL) {
Result res = mDemux->close();
- if (res == Result::SUCCESS) {
- mDemux = NULL;
- }
+ mDemux = NULL;
return res;
}
diff --git a/media/jni/tuner/DemuxClient.h b/media/jni/tuner/DemuxClient.h
index 31eb35a1a56d..c38a8fa34690 100644
--- a/media/jni/tuner/DemuxClient.h
+++ b/media/jni/tuner/DemuxClient.h
@@ -100,6 +100,11 @@ public:
*/
Result close();
+ /**
+ * Get the Aidl demux to set as source.
+ */
+ shared_ptr<ITunerDemux> getAidlDemux() { return mTunerDemux; }
+
void setId(int id) { mId = id; }
int getId() { return mId; }
diff --git a/media/jni/tuner/DescramblerClient.cpp b/media/jni/tuner/DescramblerClient.cpp
index 979beeac6b3a..07be5cf33764 100644
--- a/media/jni/tuner/DescramblerClient.cpp
+++ b/media/jni/tuner/DescramblerClient.cpp
@@ -27,13 +27,12 @@ namespace android {
/////////////// DescramblerClient ///////////////////////
-// TODO: pending aidl interface
-DescramblerClient::DescramblerClient() {
- //mTunerDescrambler = tunerDescrambler;
+DescramblerClient::DescramblerClient(shared_ptr<ITunerDescrambler> tunerDescrambler) {
+ mTunerDescrambler = tunerDescrambler;
}
DescramblerClient::~DescramblerClient() {
- //mTunerDescrambler = NULL;
+ mTunerDescrambler = NULL;
mDescrambler = NULL;
}
@@ -47,7 +46,10 @@ Result DescramblerClient::setDemuxSource(sp<DemuxClient> demuxClient) {
return Result::INVALID_ARGUMENT;
}
- // TODO: pending aidl interface
+ if (mTunerDescrambler != NULL) {
+ Status s = mTunerDescrambler->setDemuxSource(demuxClient->getAidlDemux());
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDescrambler != NULL) {
return mDescrambler->setDemuxSource(demuxClient->getId());
@@ -57,7 +59,10 @@ Result DescramblerClient::setDemuxSource(sp<DemuxClient> demuxClient) {
}
Result DescramblerClient::setKeyToken(vector<uint8_t> keyToken) {
- // TODO: pending aidl interface
+ if (mTunerDescrambler != NULL) {
+ Status s = mTunerDescrambler->setKeyToken(keyToken);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDescrambler != NULL) {
return mDescrambler->setKeyToken(keyToken);
@@ -67,7 +72,11 @@ Result DescramblerClient::setKeyToken(vector<uint8_t> keyToken) {
}
Result DescramblerClient::addPid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) {
- // TODO: pending aidl interface
+ if (mTunerDescrambler != NULL) {
+ Status s = mTunerDescrambler->addPid(
+ getAidlDemuxPid(pid), optionalSourceFilter->getAidlFilter());
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDescrambler != NULL) {
return mDescrambler->addPid(pid, optionalSourceFilter->getHalFilter());
@@ -76,23 +85,47 @@ Result DescramblerClient::addPid(DemuxPid pid, sp<FilterClient> optionalSourceFi
return Result::INVALID_STATE;}
Result DescramblerClient::removePid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) {
- // TODO: pending aidl interface
+ if (mTunerDescrambler != NULL) {
+ Status s = mTunerDescrambler->removePid(
+ getAidlDemuxPid(pid), optionalSourceFilter->getAidlFilter());
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDescrambler != NULL) {
- return mDescrambler->addPid(pid, optionalSourceFilter->getHalFilter());
+ return mDescrambler->removePid(pid, optionalSourceFilter->getHalFilter());
}
- return Result::INVALID_STATE;}
+ return Result::INVALID_STATE;
+}
Result DescramblerClient::close() {
- // TODO: pending aidl interface
+ if (mTunerDescrambler != NULL) {
+ Status s = mTunerDescrambler->close();
+ mTunerDescrambler = NULL;
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDescrambler != NULL) {
- return mDescrambler->close();
+ Result res = mDescrambler->close();
+ mDescrambler = NULL;
+ return res;
}
- return Result::INVALID_STATE;}
+ return Result::INVALID_STATE;
+}
/////////////// DescramblerClient Helper Methods ///////////////////////
+TunerDemuxPid DescramblerClient::getAidlDemuxPid(DemuxPid& pid) {
+ TunerDemuxPid aidlPid;
+ switch (pid.getDiscriminator()) {
+ case DemuxPid::hidl_discriminator::tPid:
+ aidlPid.set<TunerDemuxPid::tPid>((int)pid.tPid());
+ break;
+ case DemuxPid::hidl_discriminator::mmtpPid:
+ aidlPid.set<TunerDemuxPid::mmtpPid>((int)pid.mmtpPid());
+ break;
+ }
+ return aidlPid;
+}
} // namespace android
diff --git a/media/jni/tuner/DescramblerClient.h b/media/jni/tuner/DescramblerClient.h
index 8af688314db1..a8fa1e2e0497 100644
--- a/media/jni/tuner/DescramblerClient.h
+++ b/media/jni/tuner/DescramblerClient.h
@@ -17,14 +17,15 @@
#ifndef _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_
#define _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_
-//#include <aidl/android/media/tv/tuner/ITunerDescrambler.h>
+#include <aidl/android/media/tv/tuner/ITunerDescrambler.h>
#include <android/hardware/tv/tuner/1.0/IDescrambler.h>
#include <android/hardware/tv/tuner/1.1/types.h>
#include "DemuxClient.h"
#include "FilterClient.h"
-//using ::aidl::android::media::tv::tuner::ITunerDescrambler;
+using ::aidl::android::media::tv::tuner::ITunerDescrambler;
+using ::aidl::android::media::tv::tuner::TunerDemuxPid;
using ::android::hardware::tv::tuner::V1_0::IDescrambler;
using ::android::hardware::tv::tuner::V1_0::Result;
@@ -37,8 +38,7 @@ namespace android {
struct DescramblerClient : public RefBase {
public:
- // TODO: pending hidl interface
- DescramblerClient();
+ DescramblerClient(shared_ptr<ITunerDescrambler> tunerDescrambler);
~DescramblerClient();
// TODO: remove after migration to Tuner Service is done.
@@ -70,12 +70,13 @@ public:
Result close();
private:
+ TunerDemuxPid getAidlDemuxPid(DemuxPid& pid);
+
/**
* An AIDL Tuner Descrambler Singleton assigned at the first time the Tuner Client
* opens a descrambler. Default null when descrambler is not opened.
*/
- // TODO: pending on aidl interface
- //shared_ptr<ITunerDescrambler> mTunerDescrambler;
+ shared_ptr<ITunerDescrambler> mTunerDescrambler;
/**
* A Descrambler HAL interface that is ready before migrating to the TunerDescrambler.
diff --git a/media/jni/tuner/DvrClient.cpp b/media/jni/tuner/DvrClient.cpp
index 04004858aaee..779318008a9b 100644
--- a/media/jni/tuner/DvrClient.cpp
+++ b/media/jni/tuner/DvrClient.cpp
@@ -316,14 +316,13 @@ Result DvrClient::flush() {
Result DvrClient::close() {
if (mTunerDvr != NULL) {
Status s = mTunerDvr->close();
+ mTunerDvr = NULL;
return ClientHelper::getServiceSpecificErrorCode(s);
}
if (mDvr != NULL) {
Result res = mDvr->close();
- if (res == Result::SUCCESS) {
- mDvr = NULL;
- }
+ mDvr = NULL;
return res;
}
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index 8b4ca371056e..f31d4651350a 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -43,6 +43,7 @@ using ::android::hardware::tv::tuner::V1_0::DemuxStreamId;
using ::android::hardware::tv::tuner::V1_0::DemuxTpid;
using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using ::android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEvent;
using ::android::hardware::tv::tuner::V1_1::ScramblingStatus;
namespace android {
@@ -261,14 +262,14 @@ Result FilterClient::close() {
if (mTunerFilter != NULL) {
Status s = mTunerFilter->close();
closeAvSharedMemory();
+ mTunerFilter = NULL;
return ClientHelper::getServiceSpecificErrorCode(s);
}
if (mFilter != NULL) {
Result res = mFilter->close();
- if (res == Result::SUCCESS) {
- mFilter = NULL;
- }
+ mFilter = NULL;
+ mFilter_1_1 = NULL;
closeAvSharedMemory();
return res;
}
@@ -480,7 +481,7 @@ void FilterClient::getAidlIpAddress(DemuxIpAddress ipAddr,
case DemuxIpAddress::SrcIpAddress::hidl_discriminator::v4: {
int size = ipAddr.srcIpAddress.v4().size();
srcIpAddress.isIpV6 = false;
- srcIpAddress.addr.resize(ipAddr.srcIpAddress.v4().size());
+ srcIpAddress.addr.resize(size);
copy(&ipAddr.srcIpAddress.v4()[0], &ipAddr.srcIpAddress.v4()[size],
srcIpAddress.addr.begin());
break;
@@ -493,8 +494,6 @@ void FilterClient::getAidlIpAddress(DemuxIpAddress ipAddr,
srcIpAddress.addr.begin());
break;
}
- default:
- break;
}
switch (ipAddr.dstIpAddress.getDiscriminator()) {
case DemuxIpAddress::DstIpAddress::hidl_discriminator::v4: {
@@ -513,8 +512,6 @@ void FilterClient::getAidlIpAddress(DemuxIpAddress ipAddr,
dstIpAddress.addr.begin());
break;
}
- default:
- break;
}
}
@@ -696,8 +693,6 @@ void TunerFilterCallback::getHidlFilterEvent(const vector<TunerFilterEvent>& fil
getHidlRestartEvent(filterEvents, eventExt);
break;
}
- default:
- break;
}
}
@@ -883,19 +878,18 @@ void TunerFilterCallback::getHidlMonitorEvent(const vector<TunerFilterEvent>& fi
DemuxFilterEventExt& eventExt) {
auto monitor = filterEvents[0].get<TunerFilterEvent::monitor>();
eventExt.events.resize(1);
+ DemuxFilterMonitorEvent monitorEvent;
switch (monitor.getTag()) {
case TunerFilterMonitorEvent::scramblingStatus: {
- eventExt.events[0].monitorEvent().scramblingStatus(
- static_cast<ScramblingStatus>(monitor.scramblingStatus));
+ monitorEvent.scramblingStatus(static_cast<ScramblingStatus>(monitor.scramblingStatus));
+ eventExt.events[0].monitorEvent(monitorEvent);
break;
}
case TunerFilterMonitorEvent::cid: {
- eventExt.events[0].monitorEvent().cid(static_cast<uint32_t>(monitor.cid));
+ monitorEvent.cid(static_cast<uint32_t>(monitor.cid));
+ eventExt.events[0].monitorEvent(monitorEvent);
break;
}
- default:
- eventExt.events[0].noinit();
- break;
}
}
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index f454907851a4..9e3664275dac 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -49,9 +49,12 @@ using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtBandwidth;
using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtGuardInterval;
using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtMode;
using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendModulationStatus;
using ::android::hardware::tv::tuner::V1_0::FrontendScanAtsc3PlpInfo;
+using ::android::hardware::tv::tuner::V1_0::FrontendStatusAtsc3PlpInfo;
using ::android::hardware::tv::tuner::V1_0::LnbVoltage;
using ::android::hardware::tv::tuner::V1_1::Constant;
+using ::android::hardware::tv::tuner::V1_1::FrontendBandwidth;
using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode;
using ::android::hardware::tv::tuner::V1_1::FrontendDtmbBandwidth;
using ::android::hardware::tv::tuner::V1_1::FrontendDtmbGuardInterval;
@@ -61,19 +64,22 @@ using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTransmissionMode;
using ::android::hardware::tv::tuner::V1_1::FrontendDvbcBandwidth;
using ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation;
using ::android::hardware::tv::tuner::V1_1::FrontendDvbtTransmissionMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendGuardInterval;
+using ::android::hardware::tv::tuner::V1_1::FrontendInterleaveMode;
using ::android::hardware::tv::tuner::V1_1::FrontendModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendRollOff;
using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion;
+using ::android::hardware::tv::tuner::V1_1::FrontendTransmissionMode;
using ::android::hardware::tv::tuner::V1_1::FrontendType;
namespace android {
/////////////// FrontendClient ///////////////////////
-FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int id, int type) {
+FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int type) {
mTunerFrontend = tunerFrontend;
mAidlCallback = NULL;
mHidlCallback = NULL;
- mId = id;
mType = type;
}
@@ -91,8 +97,8 @@ Result FrontendClient::setCallback(sp<FrontendClientCallback> frontendClientCall
if (mTunerFrontend != NULL) {
mAidlCallback = ::ndk::SharedRefBase::make<TunerFrontendCallback>(frontendClientCallback);
mAidlCallback->setFrontendType(mType);
- mTunerFrontend->setCallback(mAidlCallback);
- return Result::SUCCESS;
+ Status s = mTunerFrontend->setCallback(mAidlCallback);
+ return ClientHelper::getServiceSpecificErrorCode(s);
}
mHidlCallback = new HidlFrontendCallback(frontendClientCallback);
@@ -104,6 +110,11 @@ void FrontendClient::setHidlFrontend(sp<IFrontend> frontend) {
mFrontend_1_1 = ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFrontend);
}
+// TODO: move after migration is done
+void FrontendClient::setId(int id) {
+ mId = id;
+}
+
Result FrontendClient::tune(const FrontendSettings& settings,
const FrontendSettingsExt1_1& settingsExt1_1) {
if (mTunerFrontend != NULL) {
@@ -243,9 +254,8 @@ vector<FrontendStatusExt1_1> FrontendClient::getStatusExtended_1_1(
Result FrontendClient::setLnb(sp<LnbClient> lnbClient) {
if (mTunerFrontend != NULL) {
- // TODO: handle error message.
- /*mTunerFrontend->setLnb(lnbClient->getAidlLnb());
- return Result::SUCCESS;*/
+ Status s = mTunerFrontend->setLnb(lnbClient->getAidlLnb());
+ return ClientHelper::getServiceSpecificErrorCode(s);
}
if (mFrontend != NULL) {
@@ -258,9 +268,8 @@ Result FrontendClient::setLnb(sp<LnbClient> lnbClient) {
Result FrontendClient::setLna(bool bEnable) {
if (mTunerFrontend != NULL) {
- // TODO: handle error message.
- /*mTunerFrontend->setLna(bEnable);
- return Result::SUCCESS;*/
+ Status s = mTunerFrontend->setLna(bEnable);
+ return ClientHelper::getServiceSpecificErrorCode(s);
}
if (mFrontend != NULL) {
@@ -275,9 +284,11 @@ int FrontendClient::linkCiCamToFrontend(int ciCamId) {
int ltsId = (int)Constant::INVALID_LTS_ID;
if (mTunerFrontend != NULL) {
- // TODO: handle error message.
- /*mTunerFrontend->linkCiCamToFrontend(ciCamId, ltsId);
- return ltsId;*/
+ Status s = mTunerFrontend->linkCiCamToFrontend(ciCamId, &ltsId);
+ if (ClientHelper::getServiceSpecificErrorCode(s) == Result::SUCCESS) {
+ return ltsId;
+ }
+ return (int)Constant::INVALID_LTS_ID;
}
if (mFrontend_1_1 != NULL) {
@@ -297,9 +308,8 @@ int FrontendClient::linkCiCamToFrontend(int ciCamId) {
Result FrontendClient::unlinkCiCamToFrontend(int ciCamId) {
if (mTunerFrontend != NULL) {
- // TODO: handle error message.
- /*mTunerFrontend->unlinkCiCamToFrontend(ciCamId);
- return Result::SUCCESS;*/
+ Status s = mTunerFrontend->unlinkCiCamToFrontend(ciCamId);
+ return ClientHelper::getServiceSpecificErrorCode(s);
}
if (mFrontend_1_1 != NULL) {
@@ -312,15 +322,14 @@ Result FrontendClient::unlinkCiCamToFrontend(int ciCamId) {
Result FrontendClient::close() {
if (mTunerFrontend != NULL) {
Status s = mTunerFrontend->close();
+ mTunerFrontend = NULL;
return ClientHelper::getServiceSpecificErrorCode(s);
}
if (mFrontend != NULL) {
Result result = mFrontend->close();
- if (result == Result::SUCCESS) {
- mFrontend = NULL;
- mFrontend_1_1 = NULL;
- }
+ mFrontend = NULL;
+ mFrontend_1_1 = NULL;
return result;
}
@@ -334,13 +343,26 @@ shared_ptr<ITunerFrontend> FrontendClient::getAidlFrontend() {
}
int FrontendClient::getId() {
- return mId;
+ if (mTunerFrontend != NULL) {
+ Status s = mTunerFrontend->getFrontendId(&mId);
+ if (ClientHelper::getServiceSpecificErrorCode(s) == Result::SUCCESS) {
+ return mId;
+ }
+ ALOGE("Failed to getFrontendId from Tuner Frontend");
+ return -1;
+ }
+
+ if (mFrontend != NULL) {
+ return mId;
+ }
+
+ return -1;
}
vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus>& aidlStatus) {
vector<FrontendStatus> hidlStatus;
for (TunerFrontendStatus s : aidlStatus) {
- FrontendStatus status;
+ FrontendStatus status = FrontendStatus();
switch (s.getTag()) {
case TunerFrontendStatus::isDemodLocked: {
status.isDemodLocked(s.get<TunerFrontendStatus::isDemodLocked>());
@@ -390,25 +412,31 @@ vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus>
}
case TunerFrontendStatus::modulation: {
auto aidlMod = s.get<TunerFrontendStatus::modulation>();
+ FrontendModulationStatus modulation;
switch (mType) {
case (int)FrontendType::DVBC:
- status.modulation().dvbc(static_cast<FrontendDvbcModulation>(aidlMod));
+ modulation.dvbc(static_cast<FrontendDvbcModulation>(aidlMod));
+ status.modulation(modulation);
hidlStatus.push_back(status);
break;
case (int)FrontendType::DVBS:
- status.modulation().dvbs(static_cast<FrontendDvbsModulation>(aidlMod));
+ modulation.dvbs(static_cast<FrontendDvbsModulation>(aidlMod));
+ status.modulation(modulation);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBS:
- status.modulation().isdbs(static_cast<FrontendIsdbsModulation>(aidlMod));
+ modulation.isdbs(static_cast<FrontendIsdbsModulation>(aidlMod));
+ status.modulation(modulation);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBS3:
- status.modulation().isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod));
+ modulation.isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod));
+ status.modulation(modulation);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBT:
- status.modulation().isdbt(static_cast<FrontendIsdbtModulation>(aidlMod));
+ modulation.isdbt(static_cast<FrontendIsdbtModulation>(aidlMod));
+ status.modulation(modulation);
hidlStatus.push_back(status);
break;
default:
@@ -467,7 +495,7 @@ vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus>
}
case TunerFrontendStatus::hierarchy: {
status.hierarchy(static_cast<FrontendDvbtHierarchy>(
- s.get<TunerFrontendStatus::freqOffset>()));
+ s.get<TunerFrontendStatus::hierarchy>()));
hidlStatus.push_back(status);
break;
}
@@ -478,15 +506,16 @@ vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus>
}
case TunerFrontendStatus::plpInfo: {
int size = s.get<TunerFrontendStatus::plpInfo>().size();
- status.plpInfo().resize(size);
+ hidl_vec<FrontendStatusAtsc3PlpInfo> info(size);
for (int i = 0; i < size; i++) {
auto aidlInfo = s.get<TunerFrontendStatus::plpInfo>()[i];
- status.plpInfo()[i] = {
+ info[i] = {
.plpId = (uint8_t)aidlInfo.plpId,
.isLocked = aidlInfo.isLocked,
.uec = (uint32_t)aidlInfo.uec,
};
}
+ status.plpInfo(info);
hidlStatus.push_back(status);
break;
}
@@ -504,52 +533,54 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
FrontendStatusExt1_1 status;
switch (s.getTag()) {
case TunerFrontendStatus::modulations: {
+ vector<FrontendModulation> ms;
for (auto aidlMod : s.get<TunerFrontendStatus::modulations>()) {
- int size = status.modulations().size();
- status.modulations().resize(size + 1);
+ FrontendModulation m;
switch (mType) {
case (int)FrontendType::DVBC:
- status.modulations()[size].dvbc(
- static_cast<FrontendDvbcModulation>(aidlMod));
+ m.dvbc(static_cast<FrontendDvbcModulation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::DVBS:
- status.modulations()[size].dvbs(
- static_cast<FrontendDvbsModulation>(aidlMod));
+ m.dvbs(static_cast<FrontendDvbsModulation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::DVBT:
- status.modulations()[size].dvbt(
- static_cast<FrontendDvbtConstellation>(aidlMod));
+ m.dvbt(static_cast<FrontendDvbtConstellation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::ISDBS:
- status.modulations()[size].isdbs(
- static_cast<FrontendIsdbsModulation>(aidlMod));
+ m.isdbs(static_cast<FrontendIsdbsModulation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::ISDBS3:
- status.modulations()[size].isdbs3(
- static_cast<FrontendIsdbs3Modulation>(aidlMod));
+ m.isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::ISDBT:
- status.modulations()[size].isdbt(
- static_cast<FrontendIsdbtModulation>(aidlMod));
+ m.isdbt(static_cast<FrontendIsdbtModulation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::ATSC:
- status.modulations()[size].atsc(
- static_cast<FrontendAtscModulation>(aidlMod));
+ m.atsc(static_cast<FrontendAtscModulation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::ATSC3:
- status.modulations()[size].atsc3(
- static_cast<FrontendAtsc3Modulation>(aidlMod));
+ m.atsc3(static_cast<FrontendAtsc3Modulation>(aidlMod));
+ ms.push_back(m);
break;
case (int)FrontendType::DTMB:
- status.modulations()[size].dtmb(
- static_cast<FrontendDtmbModulation>(aidlMod));
+ m.dtmb(static_cast<FrontendDtmbModulation>(aidlMod));
+ ms.push_back(m);
break;
default:
- status.modulations().resize(size);
break;
}
}
- hidlStatus.push_back(status);
+ if (ms.size() > 0) {
+ status.modulations(ms);
+ hidlStatus.push_back(status);
+ }
break;
}
case TunerFrontendStatus::bers: {
@@ -572,25 +603,31 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
}
case TunerFrontendStatus::bandwidth: {
auto aidlBand = s.get<TunerFrontendStatus::bandwidth>();
+ FrontendBandwidth band;
switch (mType) {
case (int)FrontendType::ATSC3:
- status.bandwidth().atsc3(static_cast<FrontendAtsc3Bandwidth>(aidlBand));
+ band.atsc3(static_cast<FrontendAtsc3Bandwidth>(aidlBand));
+ status.bandwidth(band);
hidlStatus.push_back(status);
break;
case (int)FrontendType::DVBC:
- status.bandwidth().dvbc(static_cast<FrontendDvbcBandwidth>(aidlBand));
+ band.dvbc(static_cast<FrontendDvbcBandwidth>(aidlBand));
+ status.bandwidth(band);
hidlStatus.push_back(status);
break;
case (int)FrontendType::DVBT:
- status.bandwidth().dvbt(static_cast<FrontendDvbtBandwidth>(aidlBand));
+ band.dvbt(static_cast<FrontendDvbtBandwidth>(aidlBand));
+ status.bandwidth(band);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBT:
- status.bandwidth().isdbt(static_cast<FrontendIsdbtBandwidth>(aidlBand));
+ band.isdbt(static_cast<FrontendIsdbtBandwidth>(aidlBand));
+ status.bandwidth(band);
hidlStatus.push_back(status);
break;
case (int)FrontendType::DTMB:
- status.bandwidth().dtmb(static_cast<FrontendDtmbBandwidth>(aidlBand));
+ band.dtmb(static_cast<FrontendDtmbBandwidth>(aidlBand));
+ status.bandwidth(band);
hidlStatus.push_back(status);
break;
default:
@@ -600,17 +637,21 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
}
case TunerFrontendStatus::interval: {
auto aidlInter = s.get<TunerFrontendStatus::interval>();
+ FrontendGuardInterval inter;
switch (mType) {
case (int)FrontendType::DVBT:
- status.interval().dvbt(static_cast<FrontendDvbtGuardInterval>(aidlInter));
+ inter.dvbt(static_cast<FrontendDvbtGuardInterval>(aidlInter));
+ status.interval(inter);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBT:
- status.interval().isdbt(static_cast<FrontendIsdbtGuardInterval>(aidlInter));
+ inter.isdbt(static_cast<FrontendIsdbtGuardInterval>(aidlInter));
+ status.interval(inter);
hidlStatus.push_back(status);
break;
case (int)FrontendType::DTMB:
- status.interval().dtmb(static_cast<FrontendDtmbGuardInterval>(aidlInter));
+ inter.dtmb(static_cast<FrontendDtmbGuardInterval>(aidlInter));
+ status.interval(inter);
hidlStatus.push_back(status);
break;
default:
@@ -620,19 +661,21 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
}
case TunerFrontendStatus::transmissionMode: {
auto aidlTran = s.get<TunerFrontendStatus::transmissionMode>();
+ FrontendTransmissionMode trans;
switch (mType) {
case (int)FrontendType::DVBT:
- status.transmissionMode().dvbt(
- static_cast<FrontendDvbtTransmissionMode>(aidlTran));
+ trans.dvbt(static_cast<FrontendDvbtTransmissionMode>(aidlTran));
+ status.transmissionMode(trans);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBT:
- status.transmissionMode().isdbt(static_cast<FrontendIsdbtMode>(aidlTran));
+ trans.isdbt(static_cast<FrontendIsdbtMode>(aidlTran));
+ status.transmissionMode(trans);
hidlStatus.push_back(status);
break;
case (int)FrontendType::DTMB:
- status.transmissionMode().dtmb(
- static_cast<FrontendDtmbTransmissionMode>(aidlTran));
+ trans.dtmb(static_cast<FrontendDtmbTransmissionMode>(aidlTran));
+ status.transmissionMode(trans);
hidlStatus.push_back(status);
break;
default:
@@ -651,28 +694,30 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
break;
}
case TunerFrontendStatus::interleaving: {
+ vector<FrontendInterleaveMode> modes;
for (auto aidlInter : s.get<TunerFrontendStatus::interleaving>()) {
- int size = status.interleaving().size();
- status.interleaving().resize(size + 1);
+ FrontendInterleaveMode mode;
switch (mType) {
case (int)FrontendType::DVBC:
- status.interleaving()[size].dvbc(
- static_cast<FrontendCableTimeInterleaveMode>(aidlInter));
+ mode.dvbc(static_cast<FrontendCableTimeInterleaveMode>(aidlInter));
+ modes.push_back(mode);
break;
case (int)FrontendType::ATSC3:
- status.interleaving()[size].atsc3(
- static_cast<FrontendAtsc3TimeInterleaveMode>(aidlInter));
+ mode.atsc3(static_cast<FrontendAtsc3TimeInterleaveMode>(aidlInter));
+ modes.push_back(mode);
break;
case (int)FrontendType::DTMB:
- status.interleaving()[size].dtmb(
- static_cast<FrontendDtmbTimeInterleaveMode>(aidlInter));
+ mode.dtmb(static_cast<FrontendDtmbTimeInterleaveMode>(aidlInter));
+ modes.push_back(mode);
break;
default:
- status.interleaving().resize(size);
break;
}
}
- hidlStatus.push_back(status);
+ if (modes.size() > 0) {
+ status.interleaving(modes);
+ hidlStatus.push_back(status);
+ }
break;
}
case TunerFrontendStatus::isdbtSegment: {
@@ -691,17 +736,21 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
}
case TunerFrontendStatus::rollOff: {
auto aidlRoll = s.get<TunerFrontendStatus::rollOff>();
+ FrontendRollOff roll;
switch (mType) {
case (int)FrontendType::DVBS:
- status.rollOff().dvbs(static_cast<FrontendDvbsRolloff>(aidlRoll));
+ roll.dvbs(static_cast<FrontendDvbsRolloff>(aidlRoll));
+ status.rollOff(roll);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBS:
- status.rollOff().isdbs(static_cast<FrontendIsdbsRolloff>(aidlRoll));
+ roll.isdbs(static_cast<FrontendIsdbsRolloff>(aidlRoll));
+ status.rollOff(roll);
hidlStatus.push_back(status);
break;
case (int)FrontendType::ISDBS3:
- status.rollOff().isdbs3(static_cast<FrontendIsdbs3Rolloff>(aidlRoll));
+ roll.isdbs3(static_cast<FrontendIsdbs3Rolloff>(aidlRoll));
+ status.rollOff(roll);
hidlStatus.push_back(status);
break;
default:
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index 298b3974aeb9..f71616cb32b7 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -108,7 +108,7 @@ private:
struct FrontendClient : public RefBase {
public:
- FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int id, int type);
+ FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int type);
~FrontendClient();
/**
@@ -180,6 +180,7 @@ public:
shared_ptr<ITunerFrontend> getAidlFrontend();
+ void setId(int id);
int getId();
private:
diff --git a/media/jni/tuner/LnbClient.cpp b/media/jni/tuner/LnbClient.cpp
index 8d08c2586b48..5b6e46eba418 100644
--- a/media/jni/tuner/LnbClient.cpp
+++ b/media/jni/tuner/LnbClient.cpp
@@ -113,11 +113,14 @@ Result LnbClient::sendDiseqcMessage(vector<uint8_t> diseqcMessage) {
Result LnbClient::close() {
if (mTunerLnb != NULL) {
Status s = mTunerLnb->close();
+ mTunerLnb = NULL;
return ClientHelper::getServiceSpecificErrorCode(s);
}
if (mLnb != NULL) {
- return mLnb->close();
+ Result res = mLnb->close();
+ mLnb = NULL;
+ return res;
}
return Result::INVALID_STATE;
diff --git a/media/jni/tuner/LnbClient.h b/media/jni/tuner/LnbClient.h
index e7869e890e8f..465dc2331ecf 100644
--- a/media/jni/tuner/LnbClient.h
+++ b/media/jni/tuner/LnbClient.h
@@ -108,7 +108,7 @@ public:
*/
Result close();
- //shared_ptr<ITunerLnb> getAidlLnb() { return mTunerLnb; }
+ shared_ptr<ITunerLnb> getAidlLnb() { return mTunerLnb; }
void setId(LnbId id) { mId = id; }
LnbId getId() { return mId; }
diff --git a/media/jni/tuner/TimeFilterClient.cpp b/media/jni/tuner/TimeFilterClient.cpp
index 432238d261e5..e123c9f57ce7 100644
--- a/media/jni/tuner/TimeFilterClient.cpp
+++ b/media/jni/tuner/TimeFilterClient.cpp
@@ -126,11 +126,14 @@ long TimeFilterClient::getSourceTime() {
Result TimeFilterClient::close() {
if (mTunerTimeFilter != NULL) {
Status s = mTunerTimeFilter->close();
+ mTunerTimeFilter = NULL;
return ClientHelper::getServiceSpecificErrorCode(s);
}
if (mTimeFilter != NULL) {
- return mTimeFilter->close();
+ Result res = mTimeFilter->close();
+ mTimeFilter = NULL;
+ return res;
}
return Result::INVALID_STATE;
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index a604490daf58..cf17ed6f4383 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -22,7 +22,10 @@
#include "TunerClient.h"
+using ::aidl::android::media::tv::tuner::TunerFrontendCapabilities;
+using ::aidl::android::media::tv::tuner::TunerFrontendDtmbCapabilities;
using ::android::hardware::tv::tuner::V1_0::FrontendId;
+using ::android::hardware::tv::tuner::V1_0::FrontendStatusType;
using ::android::hardware::tv::tuner::V1_0::FrontendType;
namespace android {
@@ -43,13 +46,12 @@ TunerClient::TunerClient() {
// Connect with Tuner Service.
::ndk::SpAIBinder binder(AServiceManager_getService("media.tuner"));
mTunerService = ITunerService::fromBinder(binder);
- // TODO: Remove after JNI migration is done.
- mTunerService = NULL;
if (mTunerService == NULL) {
ALOGE("Failed to get tuner service");
} else {
// TODO: b/178124017 update TRM in TunerService independently.
mTunerService->updateTunerResources();
+ mTunerService->getTunerHalVersion(&mTunerVersion);
}
}
@@ -112,7 +114,7 @@ sp<FrontendClient> TunerClient::openFrontend(int frontendHandle) {
if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
return NULL;
}
- return new FrontendClient(tunerFrontend, frontendHandle, aidlFrontendInfo.type);
+ return new FrontendClient(tunerFrontend, aidlFrontendInfo.type);
}
if (mTuner != NULL) {
@@ -124,8 +126,10 @@ sp<FrontendClient> TunerClient::openFrontend(int frontendHandle) {
if (res != Result::SUCCESS) {
return NULL;
}
- sp<FrontendClient> frontendClient = new FrontendClient(NULL, id, (int)hidlInfo.type);
+ sp<FrontendClient> frontendClient = new FrontendClient(
+ NULL, (int)hidlInfo.type);
frontendClient->setHidlFrontend(hidlFrontend);
+ frontendClient->setId(id);
return frontendClient;
}
}
@@ -136,12 +140,11 @@ sp<FrontendClient> TunerClient::openFrontend(int frontendHandle) {
shared_ptr<FrontendInfo> TunerClient::getFrontendInfo(int id) {
if (mTunerService != NULL) {
TunerFrontendInfo aidlFrontendInfo;
- // TODO: handle error code
Status s = mTunerService->getFrontendInfo(id, &aidlFrontendInfo);
if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
return NULL;
}
- return make_shared<FrontendInfo>(FrontendInfoAidlToHidl(aidlFrontendInfo));
+ return make_shared<FrontendInfo>(frontendInfoAidlToHidl(aidlFrontendInfo));
}
if (mTuner != NULL) {
@@ -157,7 +160,22 @@ shared_ptr<FrontendInfo> TunerClient::getFrontendInfo(int id) {
}
shared_ptr<FrontendDtmbCapabilities> TunerClient::getFrontendDtmbCapabilities(int id) {
- // pending aidl interface
+ if (mTunerService != NULL) {
+ TunerFrontendDtmbCapabilities dtmbCaps;
+ Status s = mTunerService->getFrontendDtmbCapabilities(id, &dtmbCaps);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return NULL;
+ }
+ FrontendDtmbCapabilities hidlCaps{
+ .transmissionModeCap = static_cast<uint32_t>(dtmbCaps.transmissionModeCap),
+ .bandwidthCap = static_cast<uint32_t>(dtmbCaps.bandwidthCap),
+ .modulationCap = static_cast<uint32_t>(dtmbCaps.modulationCap),
+ .codeRateCap = static_cast<uint32_t>(dtmbCaps.codeRateCap),
+ .guardIntervalCap = static_cast<uint32_t>(dtmbCaps.guardIntervalCap),
+ .interleaveModeCap = static_cast<uint32_t>(dtmbCaps.interleaveModeCap),
+ };
+ return make_shared<FrontendDtmbCapabilities>(hidlCaps);
+ }
if (mTuner_1_1 != NULL) {
Result result;
@@ -224,17 +242,18 @@ shared_ptr<DemuxCapabilities> TunerClient::getDemuxCaps() {
return NULL;
}
-sp<DescramblerClient> TunerClient::openDescrambler(int /*descramblerHandle*/) {
+sp<DescramblerClient> TunerClient::openDescrambler(int descramblerHandle) {
if (mTunerService != NULL) {
- // TODO: handle error code
- /*shared_ptr<ITunerDescrambler> tunerDescrambler;
- mTunerService->openDescrambler(demuxHandle, &tunerDescrambler);
- return new DescramblerClient(tunerDescrambler);*/
+ shared_ptr<ITunerDescrambler> tunerDescrambler;
+ Status s = mTunerService->openDescrambler(descramblerHandle, &tunerDescrambler);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return NULL;
+ }
+ return new DescramblerClient(tunerDescrambler);
}
if (mTuner != NULL) {
- // TODO: pending aidl interface
- sp<DescramblerClient> descramblerClient = new DescramblerClient();
+ sp<DescramblerClient> descramblerClient = new DescramblerClient(NULL);
sp<IDescrambler> hidlDescrambler = openHidlDescrambler();
if (hidlDescrambler != NULL) {
descramblerClient->setHidlDescrambler(hidlDescrambler);
@@ -340,7 +359,7 @@ void TunerClient::updateLnbResources() {
sp<ITuner> TunerClient::getHidlTuner() {
if (mTuner == NULL) {
- mTunerVersion = 0;
+ mTunerVersion = TUNER_HAL_VERSION_UNKNOWN;
mTuner_1_1 = ::android::hardware::tv::tuner::V1_1::ITuner::getService();
if (mTuner_1_1 == NULL) {
@@ -349,11 +368,11 @@ sp<ITuner> TunerClient::getHidlTuner() {
if (mTuner == NULL) {
ALOGW("Failed to get tuner 1.0 service.");
} else {
- mTunerVersion = 1 << 16;
+ mTunerVersion = TUNER_HAL_VERSION_1_0;
}
} else {
mTuner = static_cast<sp<ITuner>>(mTuner_1_1);
- mTunerVersion = ((1 << 16) | 1);
+ mTunerVersion = TUNER_HAL_VERSION_1_1;
}
}
return mTuner;
@@ -486,7 +505,7 @@ DemuxCapabilities TunerClient::getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps
return caps;
}
-FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) {
+FrontendInfo TunerClient::frontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) {
FrontendInfo hidlFrontendInfo {
.type = static_cast<FrontendType>(aidlFrontendInfo.type),
.minFrequency = static_cast<uint32_t>(aidlFrontendInfo.minFrequency),
@@ -496,8 +515,102 @@ FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendI
.acquireRange = static_cast<uint32_t>(aidlFrontendInfo.acquireRange),
.exclusiveGroupId = static_cast<uint32_t>(aidlFrontendInfo.exclusiveGroupId),
};
- // TODO: handle Frontend caps
+ int size = aidlFrontendInfo.statusCaps.size();
+ hidlFrontendInfo.statusCaps.resize(size);
+ for (int i = 0; i < size; i++) {
+ hidlFrontendInfo.statusCaps[i] =
+ static_cast<FrontendStatusType>(aidlFrontendInfo.statusCaps[i]);
+ }
+
+ switch (aidlFrontendInfo.caps.getTag()) {
+ case TunerFrontendCapabilities::analogCaps: {
+ auto analog = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::analogCaps>();
+ hidlFrontendInfo.frontendCaps.analogCaps({
+ .typeCap = static_cast<uint32_t>(analog.typeCap),
+ .sifStandardCap = static_cast<uint32_t>(analog.sifStandardCap),
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::atscCaps: {
+ auto atsc = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::atscCaps>();
+ hidlFrontendInfo.frontendCaps.atscCaps({
+ .modulationCap = static_cast<uint32_t>(atsc.modulationCap),
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::atsc3Caps: {
+ auto atsc3 = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::atsc3Caps>();
+ hidlFrontendInfo.frontendCaps.atsc3Caps({
+ .bandwidthCap = static_cast<uint32_t>(atsc3.bandwidthCap),
+ .modulationCap = static_cast<uint32_t>(atsc3.modulationCap),
+ .timeInterleaveModeCap = static_cast<uint32_t>(atsc3.timeInterleaveModeCap),
+ .codeRateCap = static_cast<uint32_t>(atsc3.codeRateCap),
+ .fecCap = static_cast<uint32_t>(atsc3.fecCap),
+ .demodOutputFormatCap = static_cast<uint8_t>(atsc3.demodOutputFormatCap),
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::cableCaps: {
+ auto cable = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::cableCaps>();
+ hidlFrontendInfo.frontendCaps.dvbcCaps({
+ .modulationCap = static_cast<uint32_t>(cable.modulationCap),
+ .fecCap = static_cast<uint64_t>(cable.codeRateCap),
+ .annexCap = static_cast<uint8_t>(cable.annexCap),
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::dvbsCaps: {
+ auto dvbs = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::dvbsCaps>();
+ hidlFrontendInfo.frontendCaps.dvbsCaps({
+ .modulationCap = static_cast<int32_t>(dvbs.modulationCap),
+ .innerfecCap = static_cast<uint64_t>(dvbs.codeRateCap),
+ .standard = static_cast<uint8_t>(dvbs.standard),
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::dvbtCaps: {
+ auto dvbt = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::dvbtCaps>();
+ hidlFrontendInfo.frontendCaps.dvbtCaps({
+ .transmissionModeCap = static_cast<uint32_t>(dvbt.transmissionModeCap),
+ .bandwidthCap = static_cast<uint32_t>(dvbt.bandwidthCap),
+ .constellationCap = static_cast<uint32_t>(dvbt.constellationCap),
+ .coderateCap = static_cast<uint32_t>(dvbt.codeRateCap),
+ .hierarchyCap = static_cast<uint32_t>(dvbt.hierarchyCap),
+ .guardIntervalCap = static_cast<uint32_t>(dvbt.guardIntervalCap),
+ .isT2Supported = dvbt.isT2Supported,
+ .isMisoSupported = dvbt.isMisoSupported,
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::isdbsCaps: {
+ auto isdbs = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::isdbsCaps>();
+ hidlFrontendInfo.frontendCaps.isdbsCaps({
+ .modulationCap = static_cast<uint32_t>(isdbs.modulationCap),
+ .coderateCap = static_cast<uint32_t>(isdbs.codeRateCap),
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::isdbs3Caps: {
+ auto isdbs3 = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::isdbs3Caps>();
+ hidlFrontendInfo.frontendCaps.isdbs3Caps({
+ .modulationCap = static_cast<uint32_t>(isdbs3.modulationCap),
+ .coderateCap = static_cast<uint32_t>(isdbs3.codeRateCap),
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::isdbtCaps: {
+ auto isdbt = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::isdbtCaps>();
+ hidlFrontendInfo.frontendCaps.isdbtCaps({
+ .modeCap = static_cast<uint32_t>(isdbt.modeCap),
+ .bandwidthCap = static_cast<uint32_t>(isdbt.bandwidthCap),
+ .modulationCap = static_cast<uint32_t>(isdbt.modulationCap),
+ .coderateCap = static_cast<uint32_t>(isdbt.codeRateCap),
+ .guardIntervalCap = static_cast<uint32_t>(isdbt.guardIntervalCap),
+ });
+ break;
+ }
+ }
return hidlFrontendInfo;
}
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index acd018eb1e44..9671cf787e3e 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -48,6 +48,10 @@ using namespace std;
namespace android {
+const static int TUNER_HAL_VERSION_UNKNOWN = 0;
+const static int TUNER_HAL_VERSION_1_0 = 1 << 16;
+const static int TUNER_HAL_VERSION_1_1 = (1 << 16) | 1;
+
typedef enum {
FRONTEND,
LNB,
@@ -147,7 +151,7 @@ private:
sp<IDescrambler> openHidlDescrambler();
vector<int> getLnbHandles();
DemuxCapabilities getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps);
- FrontendInfo FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo);
+ FrontendInfo frontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo);
void updateTunerResources();
void updateFrontendResources();
void updateLnbResources();
diff --git a/media/lib/remotedisplay/Android.bp b/media/lib/remotedisplay/Android.bp
index 5f4b930f350e..bfb0cb854be3 100644
--- a/media/lib/remotedisplay/Android.bp
+++ b/media/lib/remotedisplay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_sdk_library {
name: "com.android.media.remotedisplay",
srcs: ["java/**/*.java"],
diff --git a/media/lib/signer/Android.bp b/media/lib/signer/Android.bp
index 3b2578754087..6504176eb6f1 100644
--- a/media/lib/signer/Android.bp
+++ b/media/lib/signer/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_sdk_library {
name: "com.android.mediadrm.signer",
srcs: ["java/**/*.java"],
diff --git a/media/lib/tvremote/Android.bp b/media/lib/tvremote/Android.bp
index c5d14191c6c8..a58f677c3c30 100644
--- a/media/lib/tvremote/Android.bp
+++ b/media/lib/tvremote/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_sdk_library {
name: "com.android.media.tv.remoteprovider",
srcs: ["java/**/*.java"],
diff --git a/media/lib/tvremote/tests/Android.bp b/media/lib/tvremote/tests/Android.bp
index f00eed070798..f02cfc393c81 100644
--- a/media/lib/tvremote/tests/Android.bp
+++ b/media/lib/tvremote/tests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "TvRemoteTests",
srcs: ["src/**/*.java"],
diff --git a/media/mca/filterfw/Android.bp b/media/mca/filterfw/Android.bp
index 0e0ecf331afc..ef3583f1a77f 100644
--- a/media/mca/filterfw/Android.bp
+++ b/media/mca/filterfw/Android.bp
@@ -13,6 +13,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_shared {
name: "libfilterfw",
diff --git a/media/mca/filterfw/native/Android.bp b/media/mca/filterfw/native/Android.bp
index 7a8a6a18664e..7e4a34e8d934 100644
--- a/media/mca/filterfw/native/Android.bp
+++ b/media/mca/filterfw/native/Android.bp
@@ -16,6 +16,15 @@
//####################
// Build module libfilterfw_static
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_static {
name: "libfilterfw_native",
diff --git a/media/mca/filterpacks/Android.bp b/media/mca/filterpacks/Android.bp
index 34fb27dd1663..b50df6eb572c 100644
--- a/media/mca/filterpacks/Android.bp
+++ b/media/mca/filterpacks/Android.bp
@@ -13,6 +13,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_static {
name: "libfilterpack_base",
srcs: [
diff --git a/media/mca/samples/CameraEffectsRecordingSample/Android.bp b/media/mca/samples/CameraEffectsRecordingSample/Android.bp
index 96e81ab9b9cc..541660c1e4a2 100644
--- a/media/mca/samples/CameraEffectsRecordingSample/Android.bp
+++ b/media/mca/samples/CameraEffectsRecordingSample/Android.bp
@@ -15,6 +15,15 @@
// Build activity
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "CameraEffectsRecordingSample",
srcs: ["**/*.java"],
@@ -23,4 +32,3 @@ android_test {
enabled: false,
},
}
-
diff --git a/media/mca/tests/Android.bp b/media/mca/tests/Android.bp
index 6b11dd9db7ba..f02b4c0a4bfb 100644
--- a/media/mca/tests/Android.bp
+++ b/media/mca/tests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "CameraEffectsTests",
libs: [
diff --git a/media/native/midi/Android.bp b/media/native/midi/Android.bp
index 2da45b6a78be..7acb8c744ba7 100644
--- a/media/native/midi/Android.bp
+++ b/media/native/midi/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_shared {
name: "libamidi",
diff --git a/media/packages/BluetoothMidiService/Android.bp b/media/packages/BluetoothMidiService/Android.bp
index 25c34c3631dc..94a7a17638a5 100644
--- a/media/packages/BluetoothMidiService/Android.bp
+++ b/media/packages/BluetoothMidiService/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "BluetoothMidiLib",
srcs: [
diff --git a/media/packages/BluetoothMidiService/tests/unit/Android.bp b/media/packages/BluetoothMidiService/tests/unit/Android.bp
index fa4612bc22e7..67c7e4230f30 100644
--- a/media/packages/BluetoothMidiService/tests/unit/Android.bp
+++ b/media/packages/BluetoothMidiService/tests/unit/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "BluetoothMidiTests",
srcs: ["src/**/*.java"],
diff --git a/media/tests/AudioPolicyTest/Android.bp b/media/tests/AudioPolicyTest/Android.bp
index ed3383752695..95d1c6ccb67c 100644
--- a/media/tests/AudioPolicyTest/Android.bp
+++ b/media/tests/AudioPolicyTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "audiopolicytest",
srcs: ["**/*.java"],
diff --git a/media/tests/CameraBrowser/Android.bp b/media/tests/CameraBrowser/Android.bp
index 8e3ca1943b17..1408640a6b0d 100644
--- a/media/tests/CameraBrowser/Android.bp
+++ b/media/tests/CameraBrowser/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "CameraBrowser",
srcs: ["**/*.java"],
diff --git a/media/tests/EffectsTest/Android.bp b/media/tests/EffectsTest/Android.bp
index 214e8c02f742..644e453b8956 100644
--- a/media/tests/EffectsTest/Android.bp
+++ b/media/tests/EffectsTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "EffectsTest",
srcs: ["**/*.java"],
diff --git a/media/tests/MediaDump/Android.bp b/media/tests/MediaDump/Android.bp
index 0eba8b24f6cd..f54b97a06558 100644
--- a/media/tests/MediaDump/Android.bp
+++ b/media/tests/MediaDump/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "MediaDump",
// Only compile source java files in this apk.
diff --git a/media/tests/MediaFrameworkTest/Android.bp b/media/tests/MediaFrameworkTest/Android.bp
index 3f1954a0874a..48d56d898138 100644
--- a/media/tests/MediaFrameworkTest/Android.bp
+++ b/media/tests/MediaFrameworkTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "mediaframeworktest",
srcs: ["**/*.java"],
diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp
index a439b79fa2f0..d41bc02906ab 100644
--- a/media/tests/MediaRouter/Android.bp
+++ b/media/tests/MediaRouter/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "mediaroutertest",
@@ -17,4 +26,4 @@ android_test {
platform_apis: true,
certificate: "platform",
-} \ No newline at end of file
+}
diff --git a/media/tests/MtpTests/Android.bp b/media/tests/MtpTests/Android.bp
index 7d2c7c693fa5..3016873e1de8 100644
--- a/media/tests/MtpTests/Android.bp
+++ b/media/tests/MtpTests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "MtpTests",
srcs: ["**/*.java"],
diff --git a/media/tests/ScoAudioTest/Android.bp b/media/tests/ScoAudioTest/Android.bp
index ad2b91716145..f8a893bf7f2c 100644
--- a/media/tests/ScoAudioTest/Android.bp
+++ b/media/tests/ScoAudioTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "scoaudiotest",
platform_apis: true,
diff --git a/media/tests/SoundPoolTest/Android.bp b/media/tests/SoundPoolTest/Android.bp
index 473f531f675b..0a50106d52c8 100644
--- a/media/tests/SoundPoolTest/Android.bp
+++ b/media/tests/SoundPoolTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "SoundPoolTest",
srcs: ["**/*.java"],
diff --git a/media/tests/TunerTest/Android.bp b/media/tests/TunerTest/Android.bp
index 5c3e9ab9812f..8e8816cee876 100644
--- a/media/tests/TunerTest/Android.bp
+++ b/media/tests/TunerTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "mediatunertest",
diff --git a/media/tests/audiotests/Android.bp b/media/tests/audiotests/Android.bp
index 5db0ab0cde6d..c52c0336979c 100644
--- a/media/tests/audiotests/Android.bp
+++ b/media/tests/audiotests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_test {
name: "shared_mem_test",
gtest: false,
diff --git a/media/tests/players/Android.bp b/media/tests/players/Android.bp
index 23c5f04c93a6..3b6df707badb 100644
--- a/media/tests/players/Android.bp
+++ b/media/tests/players/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_test_library {
name: "invoke_mock_media_player",
srcs: ["invoke_mock_media_player.cpp"],
diff --git a/mime/Android.bp b/mime/Android.bp
index 23a8fbf5059c..a3ea65cb2efe 100644
--- a/mime/Android.bp
+++ b/mime/Android.bp
@@ -13,6 +13,15 @@
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_defaults {
name: "mimemap-defaults",
srcs: [
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 3daaf05fa8d5..9566b9242fc7 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -13,6 +13,15 @@
// limitations under the License.
// The headers module is in frameworks/native/Android.bp.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
ndk_library {
name: "libandroid",
symbol_file: "libandroid.map.txt",
@@ -88,7 +97,7 @@ cc_library_shared {
"libarect",
],
- header_libs: [ "libhwui_internal_headers",],
+ header_libs: [ "libhwui_internal_headers", "libandroid_headers_private"],
whole_static_libs: ["libnativewindow"],
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 314bf2910155..b01878b3070b 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -225,6 +225,7 @@ LIBANDROID {
AStorageManager_unmountObb;
ASurfaceControl_create; # introduced=29
ASurfaceControl_createFromWindow; # introduced=29
+ ASurfaceControl_acquire; # introduced=31
ASurfaceControl_release; # introduced=29
ASurfaceTexture_acquireANativeWindow; # introduced=28
ASurfaceTexture_attachToGLContext; # introduced=28
@@ -299,3 +300,13 @@ LIBANDROID {
local:
*;
};
+
+LIBANDROID_PLATFORM {
+ global:
+ extern "C++" {
+ ASurfaceControl_registerSurfaceStatsListener*;
+ ASurfaceControl_unregisterSurfaceStatsListener*;
+ ASurfaceControlStats_getAcquireTime*;
+ ASurfaceControlStats_getFrameNumber*;
+ };
+} LIBANDROID; \ No newline at end of file
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 189be800e018..e51add276647 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -17,6 +17,7 @@
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/native_window.h>
#include <android/surface_control.h>
+#include <surface_control_private.h>
#include <configstore/Utils.h>
@@ -185,10 +186,58 @@ ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* deb
return reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
}
+void ASurfaceControl_acquire(ASurfaceControl* aSurfaceControl) {
+ SurfaceControl* surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+
+ SurfaceControl_acquire(surfaceControl);
+}
+
void ASurfaceControl_release(ASurfaceControl* aSurfaceControl) {
- sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+ SurfaceControl* surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+
+ SurfaceControl_release(surfaceControl);
+}
+
+struct ASurfaceControlStats {
+ int64_t acquireTime;
+ sp<Fence> previousReleaseFence;
+ uint64_t frameNumber;
+};
+
+void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, void* context,
+ ASurfaceControl_SurfaceStatsListener func) {
+ SurfaceStatsCallback callback = [func](void* callback_context,
+ nsecs_t,
+ const sp<Fence>&,
+ const SurfaceStats& surfaceStats) {
+
+ ASurfaceControlStats aSurfaceControlStats;
- SurfaceControl_release(surfaceControl.get());
+ ASurfaceControl* aSurfaceControl =
+ reinterpret_cast<ASurfaceControl*>(surfaceStats.surfaceControl.get());
+ aSurfaceControlStats.acquireTime = surfaceStats.acquireTime;
+ aSurfaceControlStats.previousReleaseFence = surfaceStats.previousReleaseFence;
+ aSurfaceControlStats.frameNumber = surfaceStats.eventStats.frameNumber;
+
+ (*func)(callback_context, aSurfaceControl, &aSurfaceControlStats);
+ };
+ TransactionCompletedListener::getInstance()->addSurfaceStatsListener(context,
+ reinterpret_cast<void*>(func), ASurfaceControl_to_SurfaceControl(control), callback);
+}
+
+
+void ASurfaceControl_unregisterSurfaceStatsListener(void* context,
+ ASurfaceControl_SurfaceStatsListener func) {
+ TransactionCompletedListener::getInstance()->removeSurfaceStatsListener(context,
+ reinterpret_cast<void*>(func));
+}
+
+int64_t ASurfaceControlStats_getAcquireTime(ASurfaceControlStats* stats) {
+ return stats->acquireTime;
+}
+
+uint64_t ASurfaceControlStats_getFrameNumber(ASurfaceControlStats* stats) {
+ return stats->frameNumber;
}
ASurfaceTransaction* ASurfaceTransaction_create() {
@@ -209,11 +258,6 @@ void ASurfaceTransaction_apply(ASurfaceTransaction* aSurfaceTransaction) {
transaction->apply();
}
-typedef struct ASurfaceControlStats {
- int64_t acquireTime;
- sp<Fence> previousReleaseFence;
-} ASurfaceControlStats;
-
struct ASurfaceTransactionStats {
std::unordered_map<ASurfaceControl*, ASurfaceControlStats> aSurfaceControlStats;
int64_t latchTime;
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 3d633ea7089d..1709dfd973d6 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_shared {
name: "libjnigraphics",
diff --git a/native/webview/loader/Android.bp b/native/webview/loader/Android.bp
index dfa5bdde0785..bb9b8903273e 100644
--- a/native/webview/loader/Android.bp
+++ b/native/webview/loader/Android.bp
@@ -17,6 +17,15 @@
// Loader library which handles address space reservation and relro sharing.
// Does NOT link any native chromium code.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_shared {
name: "libwebviewchromium_loader",
diff --git a/native/webview/plat_support/Android.bp b/native/webview/plat_support/Android.bp
index 1a3b36d046e1..2e94e8452fdd 100644
--- a/native/webview/plat_support/Android.bp
+++ b/native/webview/plat_support/Android.bp
@@ -18,6 +18,38 @@
// Native support library (libwebviewchromium_plat_support.so) - does NOT link
// any native chromium code.
+package {
+ default_applicable_licenses: [
+ "frameworks_base_native_webview_plat_support_license",
+ ],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_native_webview_plat_support_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ "SPDX-license-identifier-BSD",
+ ],
+ license_text: [
+ "LICENSE",
+ ],
+}
+
cc_library_shared {
name: "libwebviewchromium_plat_support",
diff --git a/nfc-extras/Android.bp b/nfc-extras/Android.bp
index cbacd48b080e..43b2830f544b 100644
--- a/nfc-extras/Android.bp
+++ b/nfc-extras/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_sdk_library {
name: "com.android.nfc_extras",
srcs: ["java/**/*.java"],
diff --git a/nfc-extras/tests/Android.bp b/nfc-extras/tests/Android.bp
index fc52006d14d2..4c1f2fb77e72 100644
--- a/nfc-extras/tests/Android.bp
+++ b/nfc-extras/tests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "NfcExtrasTests",
diff --git a/obex/Android.bp b/obex/Android.bp
index 6558eb36d001..95eac81e80be 100644
--- a/obex/Android.bp
+++ b/obex/Android.bp
@@ -14,6 +14,36 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["frameworks_base_obex_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_obex_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ "SPDX-license-identifier-BSD",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
java_sdk_library {
name: "javax.obex",
srcs: ["javax/**/*.java"],
diff --git a/packages/AppPredictionLib/Android.bp b/packages/AppPredictionLib/Android.bp
index e0f4dedc9368..5a68fdc9ae75 100644
--- a/packages/AppPredictionLib/Android.bp
+++ b/packages/AppPredictionLib/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "app_prediction",
diff --git a/packages/BackupEncryption/Android.bp b/packages/BackupEncryption/Android.bp
index 3a078d24e535..0244f289b40c 100644
--- a/packages/BackupEncryption/Android.bp
+++ b/packages/BackupEncryption/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "BackupEncryption",
defaults: ["platform_app_defaults"],
diff --git a/packages/BackupEncryption/test/robolectric-integration/Android.bp b/packages/BackupEncryption/test/robolectric-integration/Android.bp
index 67365df4b28f..c842e425fd6f 100644
--- a/packages/BackupEncryption/test/robolectric-integration/Android.bp
+++ b/packages/BackupEncryption/test/robolectric-integration/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_robolectric_test {
name: "BackupEncryptionRoboIntegTests",
srcs: [
diff --git a/packages/BackupEncryption/test/robolectric/Android.bp b/packages/BackupEncryption/test/robolectric/Android.bp
index 2a36dcf0baba..7665d8f4e4c2 100644
--- a/packages/BackupEncryption/test/robolectric/Android.bp
+++ b/packages/BackupEncryption/test/robolectric/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_robolectric_test {
name: "BackupEncryptionRoboTests",
srcs: [
diff --git a/packages/BackupEncryption/test/unittest/Android.bp b/packages/BackupEncryption/test/unittest/Android.bp
index d7c510b57518..f005170884c8 100644
--- a/packages/BackupEncryption/test/unittest/Android.bp
+++ b/packages/BackupEncryption/test/unittest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "BackupEncryptionUnitTests",
srcs: ["src/**/*.java"],
@@ -19,4 +28,4 @@ android_test {
test_suites: ["device-tests"],
instrumentation_for: "BackupEncryption",
certificate: "platform",
-} \ No newline at end of file
+}
diff --git a/packages/BackupRestoreConfirmation/Android.bp b/packages/BackupRestoreConfirmation/Android.bp
index 6fe039d48357..ad3f4c12dfb1 100644
--- a/packages/BackupRestoreConfirmation/Android.bp
+++ b/packages/BackupRestoreConfirmation/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "BackupRestoreConfirmation",
defaults: ["platform_app_defaults"],
diff --git a/packages/CarrierDefaultApp/Android.bp b/packages/CarrierDefaultApp/Android.bp
index c1b0b2da2cb5..fc753da19394 100644
--- a/packages/CarrierDefaultApp/Android.bp
+++ b/packages/CarrierDefaultApp/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "CarrierDefaultApp",
srcs: ["src/**/*.java"],
diff --git a/packages/CarrierDefaultApp/tests/unit/Android.bp b/packages/CarrierDefaultApp/tests/unit/Android.bp
index 5655abb046b2..54c9016ee53a 100644
--- a/packages/CarrierDefaultApp/tests/unit/Android.bp
+++ b/packages/CarrierDefaultApp/tests/unit/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "CarrierDefaultAppUnitTests",
certificate: "platform",
diff --git a/packages/CompanionDeviceManager/Android.bp b/packages/CompanionDeviceManager/Android.bp
index 354d2c7a6228..4a526501cf96 100644
--- a/packages/CompanionDeviceManager/Android.bp
+++ b/packages/CompanionDeviceManager/Android.bp
@@ -12,6 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: [
+ "frameworks_base_packages_CompanionDeviceManager_license",
+ ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_packages_CompanionDeviceManager_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
android_app {
name: "CompanionDeviceManager",
defaults: ["platform_app_defaults"],
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 72589e39d555..c30d4bf322cf 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -165,6 +165,7 @@ public class DeviceChooserActivity extends Activity {
protected void onStop() {
super.onStop();
if (!isFinishing() && !isChangingConfigurations()) {
+ Log.i(LOG_TAG, "onStop() - cancelling");
cancel();
}
}
@@ -195,7 +196,6 @@ public class DeviceChooserActivity extends Activity {
titleView.setText(title);
}
- //TODO put in resources xmls
private ProgressBar getProgressBar() {
final ProgressBar progressBar = new ProgressBar(this);
progressBar.setForegroundGravity(Gravity.CENTER_HORIZONTAL);
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index fd71670ee404..67d4b41f164c 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -309,7 +309,7 @@ public class DeviceDiscoveryService extends Service {
}
private void onDeviceLost(@Nullable DeviceFilterPair device) {
- if (DEBUG) Log.i(LOG_TAG, "Lost device " + device.getDisplayName());
+ Log.i(LOG_TAG, "Lost device " + device.getDisplayName());
Handler.getMain().sendMessage(obtainMessage(
DeviceDiscoveryService::onDeviceLostMainThread, this, device));
}
@@ -322,7 +322,8 @@ public class DeviceDiscoveryService extends Service {
void onDeviceSelected(String callingPackage, String deviceAddress) {
mServiceCallback.complete(new Association(
- getUserId(), deviceAddress, callingPackage, mRequest.getDeviceProfile(), false));
+ getUserId(), deviceAddress, callingPackage, mRequest.getDeviceProfile(), false,
+ System.currentTimeMillis()));
}
void onCancel() {
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
index 8db8d7699a1e..c7e261c93689 100644
--- a/packages/Connectivity/framework/Android.bp
+++ b/packages/Connectivity/framework/Android.bp
@@ -14,16 +14,47 @@
// limitations under the License.
//
-// TODO: use a java_library in the bootclasspath instead
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
- name: "framework-connectivity-sources",
+ name: "framework-connectivity-internal-sources",
srcs: [
"src/**/*.java",
"src/**/*.aidl",
],
path: "src",
visibility: [
+ "//visibility:private",
+ ],
+}
+
+filegroup {
+ name: "framework-connectivity-aidl-export-sources",
+ srcs: [
+ "aidl-export/**/*.aidl",
+ ],
+ path: "aidl-export",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+// TODO: use a java_library in the bootclasspath instead
+filegroup {
+ name: "framework-connectivity-sources",
+ srcs: [
+ ":framework-connectivity-internal-sources",
+ ":framework-connectivity-aidl-export-sources",
+ ],
+ visibility: [
"//frameworks/base",
"//packages/modules/Connectivity:__subpackages__",
],
-} \ No newline at end of file
+}
diff --git a/packages/Connectivity/framework/src/android/net/CaptivePortalData.aidl b/packages/Connectivity/framework/aidl-export/android/net/CaptivePortalData.aidl
index 1d57ee759136..1d57ee759136 100644
--- a/packages/Connectivity/framework/src/android/net/CaptivePortalData.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/CaptivePortalData.aidl
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.aidl b/packages/Connectivity/framework/aidl-export/android/net/ConnectivityDiagnosticsManager.aidl
index 82ba0ca113c5..82ba0ca113c5 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/ConnectivityDiagnosticsManager.aidl
diff --git a/packages/Connectivity/framework/src/android/net/DhcpInfo.aidl b/packages/Connectivity/framework/aidl-export/android/net/DhcpInfo.aidl
index 29cd21fe7652..29cd21fe7652 100644
--- a/packages/Connectivity/framework/src/android/net/DhcpInfo.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/DhcpInfo.aidl
diff --git a/packages/Connectivity/framework/src/android/net/IpConfiguration.aidl b/packages/Connectivity/framework/aidl-export/android/net/IpConfiguration.aidl
index 7a30f0e79cad..7a30f0e79cad 100644
--- a/packages/Connectivity/framework/src/android/net/IpConfiguration.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/IpConfiguration.aidl
diff --git a/packages/Connectivity/framework/src/android/net/IpPrefix.aidl b/packages/Connectivity/framework/aidl-export/android/net/IpPrefix.aidl
index 0d70f2a1ed2c..0d70f2a1ed2c 100644
--- a/packages/Connectivity/framework/src/android/net/IpPrefix.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/IpPrefix.aidl
diff --git a/packages/Connectivity/framework/src/android/net/KeepalivePacketData.aidl b/packages/Connectivity/framework/aidl-export/android/net/KeepalivePacketData.aidl
index d456b53fd188..d456b53fd188 100644
--- a/packages/Connectivity/framework/src/android/net/KeepalivePacketData.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/KeepalivePacketData.aidl
diff --git a/packages/Connectivity/framework/src/android/net/LinkAddress.aidl b/packages/Connectivity/framework/aidl-export/android/net/LinkAddress.aidl
index 9c804db08d61..9c804db08d61 100644
--- a/packages/Connectivity/framework/src/android/net/LinkAddress.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/LinkAddress.aidl
diff --git a/packages/Connectivity/framework/src/android/net/LinkProperties.aidl b/packages/Connectivity/framework/aidl-export/android/net/LinkProperties.aidl
index a8b3c7b0392f..a8b3c7b0392f 100644
--- a/packages/Connectivity/framework/src/android/net/LinkProperties.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/LinkProperties.aidl
diff --git a/packages/Connectivity/framework/src/android/net/MacAddress.aidl b/packages/Connectivity/framework/aidl-export/android/net/MacAddress.aidl
index 48a18a7ac821..48a18a7ac821 100644
--- a/packages/Connectivity/framework/src/android/net/MacAddress.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/MacAddress.aidl
diff --git a/packages/Connectivity/framework/src/android/net/Network.aidl b/packages/Connectivity/framework/aidl-export/android/net/Network.aidl
index 05622025bf33..05622025bf33 100644
--- a/packages/Connectivity/framework/src/android/net/Network.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/Network.aidl
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.aidl b/packages/Connectivity/framework/aidl-export/android/net/NetworkAgentConfig.aidl
index cb70bdd31260..cb70bdd31260 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/NetworkAgentConfig.aidl
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.aidl b/packages/Connectivity/framework/aidl-export/android/net/NetworkCapabilities.aidl
index 01d328605de4..01d328605de4 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/NetworkCapabilities.aidl
diff --git a/packages/Connectivity/framework/src/android/net/NetworkInfo.aidl b/packages/Connectivity/framework/aidl-export/android/net/NetworkInfo.aidl
index f50187302966..f50187302966 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkInfo.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/NetworkInfo.aidl
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.aidl b/packages/Connectivity/framework/aidl-export/android/net/NetworkRequest.aidl
index 508defc6b497..508defc6b497 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/NetworkRequest.aidl
diff --git a/packages/Connectivity/framework/src/android/net/ProxyInfo.aidl b/packages/Connectivity/framework/aidl-export/android/net/ProxyInfo.aidl
index a5d0c120e747..a5d0c120e747 100644
--- a/packages/Connectivity/framework/src/android/net/ProxyInfo.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/ProxyInfo.aidl
diff --git a/packages/Connectivity/framework/src/android/net/RouteInfo.aidl b/packages/Connectivity/framework/aidl-export/android/net/RouteInfo.aidl
index 7af9fdaef342..7af9fdaef342 100644
--- a/packages/Connectivity/framework/src/android/net/RouteInfo.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/RouteInfo.aidl
diff --git a/packages/Connectivity/framework/src/android/net/StaticIpConfiguration.aidl b/packages/Connectivity/framework/aidl-export/android/net/StaticIpConfiguration.aidl
index 8aac701fe7e1..8aac701fe7e1 100644
--- a/packages/Connectivity/framework/src/android/net/StaticIpConfiguration.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/StaticIpConfiguration.aidl
diff --git a/packages/Connectivity/framework/src/android/net/TestNetworkInterface.aidl b/packages/Connectivity/framework/aidl-export/android/net/TestNetworkInterface.aidl
index e1f4f9f794eb..e1f4f9f794eb 100644
--- a/packages/Connectivity/framework/src/android/net/TestNetworkInterface.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/TestNetworkInterface.aidl
diff --git a/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.aidl b/packages/Connectivity/framework/aidl-export/android/net/apf/ApfCapabilities.aidl
index 7c4d4c2da4bc..7c4d4c2da4bc 100644
--- a/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/apf/ApfCapabilities.aidl
diff --git a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
index 9b56b23cc879..f4b46e9f11ed 100644
--- a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
+++ b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
@@ -16,12 +16,15 @@
package android.net;
+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;
/**
@@ -40,10 +43,29 @@ public final class CaptivePortalData implements Parcelable {
private final long mExpiryTimeMillis;
private final boolean mCaptive;
private final String mVenueFriendlyName;
+ private final int mVenueInfoUrlSource;
+ private final int mTermsAndConditionsSource;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"CAPTIVE_PORTAL_DATA_SOURCE_"}, value = {
+ CAPTIVE_PORTAL_DATA_SOURCE_OTHER,
+ CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT})
+ public @interface CaptivePortalDataSource {}
+
+ /**
+ * Source of information: Other (default)
+ */
+ public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0;
+
+ /**
+ * Source of information: Wi-Fi Passpoint
+ */
+ public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1;
private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl,
boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive,
- String venueFriendlyName) {
+ String venueFriendlyName, int venueInfoUrlSource, int termsAndConditionsSource) {
mRefreshTimeMillis = refreshTimeMillis;
mUserPortalUrl = userPortalUrl;
mVenueInfoUrl = venueInfoUrl;
@@ -52,11 +74,14 @@ public final class CaptivePortalData implements Parcelable {
mExpiryTimeMillis = expiryTimeMillis;
mCaptive = captive;
mVenueFriendlyName = venueFriendlyName;
+ mVenueInfoUrlSource = venueInfoUrlSource;
+ mTermsAndConditionsSource = termsAndConditionsSource;
}
private CaptivePortalData(Parcel p) {
this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(),
- p.readLong(), p.readLong(), p.readBoolean(), p.readString());
+ p.readLong(), p.readLong(), p.readBoolean(), p.readString(), p.readInt(),
+ p.readInt());
}
@Override
@@ -74,6 +99,8 @@ public final class CaptivePortalData implements Parcelable {
dest.writeLong(mExpiryTimeMillis);
dest.writeBoolean(mCaptive);
dest.writeString(mVenueFriendlyName);
+ dest.writeInt(mVenueInfoUrlSource);
+ dest.writeInt(mTermsAndConditionsSource);
}
/**
@@ -88,6 +115,9 @@ public final class CaptivePortalData implements Parcelable {
private long mExpiryTime = -1;
private boolean mCaptive;
private String mVenueFriendlyName;
+ private @CaptivePortalDataSource int mVenueInfoUrlSource = CAPTIVE_PORTAL_DATA_SOURCE_OTHER;
+ private @CaptivePortalDataSource int mUserPortalUrlSource =
+ CAPTIVE_PORTAL_DATA_SOURCE_OTHER;
/**
* Create an empty builder.
@@ -100,8 +130,8 @@ public final class CaptivePortalData implements Parcelable {
public Builder(@Nullable CaptivePortalData data) {
if (data == null) return;
setRefreshTime(data.mRefreshTimeMillis)
- .setUserPortalUrl(data.mUserPortalUrl)
- .setVenueInfoUrl(data.mVenueInfoUrl)
+ .setUserPortalUrl(data.mUserPortalUrl, data.mTermsAndConditionsSource)
+ .setVenueInfoUrl(data.mVenueInfoUrl, data.mVenueInfoUrlSource)
.setSessionExtendable(data.mIsSessionExtendable)
.setBytesRemaining(data.mByteLimit)
.setExpiryTime(data.mExpiryTimeMillis)
@@ -123,7 +153,18 @@ public final class CaptivePortalData implements Parcelable {
*/
@NonNull
public Builder setUserPortalUrl(@Nullable Uri userPortalUrl) {
+ return setUserPortalUrl(userPortalUrl, CAPTIVE_PORTAL_DATA_SOURCE_OTHER);
+ }
+
+ /**
+ * Set the URL to be used for users to login to the portal, if captive, and the source of
+ * the data, see {@link CaptivePortalDataSource}
+ */
+ @NonNull
+ public Builder setUserPortalUrl(@Nullable Uri userPortalUrl,
+ @CaptivePortalDataSource int source) {
mUserPortalUrl = userPortalUrl;
+ mUserPortalUrlSource = source;
return this;
}
@@ -132,7 +173,18 @@ public final class CaptivePortalData implements Parcelable {
*/
@NonNull
public Builder setVenueInfoUrl(@Nullable Uri venueInfoUrl) {
+ return setVenueInfoUrl(venueInfoUrl, CAPTIVE_PORTAL_DATA_SOURCE_OTHER);
+ }
+
+ /**
+ * Set the URL that can be used by users to view information about the network venue, and
+ * the source of the data, see {@link CaptivePortalDataSource}
+ */
+ @NonNull
+ public Builder setVenueInfoUrl(@Nullable Uri venueInfoUrl,
+ @CaptivePortalDataSource int source) {
mVenueInfoUrl = venueInfoUrl;
+ mVenueInfoUrlSource = source;
return this;
}
@@ -188,7 +240,8 @@ public final class CaptivePortalData implements Parcelable {
public CaptivePortalData build() {
return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl,
mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive,
- mVenueFriendlyName);
+ mVenueFriendlyName, mVenueInfoUrlSource,
+ mUserPortalUrlSource);
}
}
@@ -249,6 +302,22 @@ public final class CaptivePortalData implements Parcelable {
}
/**
+ * Get the information source of the Venue URL
+ * @return The source that the Venue URL was obtained from
+ */
+ public @CaptivePortalDataSource int getVenueInfoUrlSource() {
+ return mVenueInfoUrlSource;
+ }
+
+ /**
+ * Get the information source of the user portal URL
+ * @return The source that the user portal URL was obtained from
+ */
+ public @CaptivePortalDataSource int getUserPortalUrlSource() {
+ return mTermsAndConditionsSource;
+ }
+
+ /**
* Get the venue friendly name
*/
@Nullable
@@ -272,7 +341,8 @@ public final class CaptivePortalData implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl,
- mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName);
+ mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName,
+ mVenueInfoUrlSource, mTermsAndConditionsSource);
}
@Override
@@ -286,7 +356,9 @@ public final class CaptivePortalData implements Parcelable {
&& mByteLimit == other.mByteLimit
&& mExpiryTimeMillis == other.mExpiryTimeMillis
&& mCaptive == other.mCaptive
- && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName);
+ && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName)
+ && mVenueInfoUrlSource == other.mVenueInfoUrlSource
+ && mTermsAndConditionsSource == other.mTermsAndConditionsSource;
}
@Override
@@ -300,6 +372,8 @@ public final class CaptivePortalData implements Parcelable {
+ ", expiryTime: " + mExpiryTimeMillis
+ ", captive: " + mCaptive
+ ", venueFriendlyName: " + mVenueFriendlyName
+ + ", venueInfoUrlSource: " + mVenueInfoUrlSource
+ + ", termsAndConditionsSource: " + mTermsAndConditionsSource
+ "}";
}
}
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java b/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java
index 9afa5d1311c5..92a792b78410 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java
@@ -49,17 +49,6 @@ public final class ConnectivityFrameworkInitializer {
}
);
- // TODO: move outside of the connectivity JAR
- SystemServiceRegistry.registerContextAwareService(
- Context.VPN_MANAGEMENT_SERVICE,
- VpnManager.class,
- (context) -> {
- final ConnectivityManager cm = context.getSystemService(
- ConnectivityManager.class);
- return cm.createVpnManager();
- }
- );
-
SystemServiceRegistry.registerContextAwareService(
Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
ConnectivityDiagnosticsManager.class,
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 3b054e942178..4ddae533bb80 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -21,6 +21,7 @@ import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
import static android.net.NetworkRequest.Type.LISTEN;
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 android.annotation.CallbackExecutor;
@@ -48,8 +49,6 @@ import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.INetworkActivityListener;
-import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
@@ -455,7 +454,7 @@ public class ConnectivityManager {
* @hide
*/
@SystemApi
- public static final int TETHERING_WIFI = TetheringManager.TETHERING_WIFI;
+ public static final int TETHERING_WIFI = 0;
/**
* USB tethering type.
@@ -463,7 +462,7 @@ public class ConnectivityManager {
* @hide
*/
@SystemApi
- public static final int TETHERING_USB = TetheringManager.TETHERING_USB;
+ public static final int TETHERING_USB = 1;
/**
* Bluetooth tethering type.
@@ -471,7 +470,7 @@ public class ConnectivityManager {
* @hide
*/
@SystemApi
- public static final int TETHERING_BLUETOOTH = TetheringManager.TETHERING_BLUETOOTH;
+ public static final int TETHERING_BLUETOOTH = 2;
/**
* Wifi P2p tethering type.
@@ -823,6 +822,7 @@ public class ConnectivityManager {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
private final IConnectivityManager mService;
+
/**
* A kludge to facilitate static access where a Context pointer isn't available, like in the
* case of the static set/getProcessDefaultNetwork methods and from the Network class.
@@ -833,7 +833,6 @@ public class ConnectivityManager {
private final Context mContext;
- private INetworkManagementService mNMService;
private INetworkPolicyManager mNPManager;
private final TetheringManager mTetheringManager;
@@ -1068,106 +1067,55 @@ public class ConnectivityManager {
}
/**
- * Checks if a VPN app supports always-on mode.
- *
- * In order to support the always-on feature, an app has to
- * <ul>
- * <li>target {@link VERSION_CODES#N API 24} or above, and
- * <li>not opt out through the {@link VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}
- * meta-data field.
- * </ul>
- *
- * @param userId The identifier of the user for whom the VPN app is installed.
- * @param vpnPackage The canonical package name of the VPN app.
- * @return {@code true} if and only if the VPN app exists and supports always-on mode.
+ * Calls VpnManager#isAlwaysOnVpnPackageSupportedForUser.
+ * @deprecated TODO: remove when callers have migrated to VpnManager.
* @hide
*/
+ @Deprecated
public boolean isAlwaysOnVpnPackageSupportedForUser(int userId, @Nullable String vpnPackage) {
- try {
- return mService.isAlwaysOnVpnPackageSupported(userId, vpnPackage);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getVpnManager().isAlwaysOnVpnPackageSupportedForUser(userId, vpnPackage);
}
/**
- * Configures an always-on VPN connection through a specific application.
- * This connection is automatically granted and persisted after a reboot.
- *
- * <p>The designated package should declare a {@link VpnService} in its
- * manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
- * otherwise the call will fail.
- *
- * @param userId The identifier of the user to set an always-on VPN for.
- * @param vpnPackage The package name for an installed VPN app on the device, or {@code null}
- * to remove an existing always-on VPN configuration.
- * @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
- * {@code false} otherwise.
- * @param lockdownAllowlist The list of packages that are allowed to access network directly
- * when VPN is in lockdown mode but is not running. Non-existent packages are ignored so
- * this method must be called when a package that should be allowed is installed or
- * uninstalled.
- * @return {@code true} if the package is set as always-on VPN controller;
- * {@code false} otherwise.
+ * Calls VpnManager#setAlwaysOnVpnPackageForUser.
+ * @deprecated TODO: remove when callers have migrated to VpnManager.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
+ @Deprecated
public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage,
boolean lockdownEnabled, @Nullable List<String> lockdownAllowlist) {
- try {
- return mService.setAlwaysOnVpnPackage(
- userId, vpnPackage, lockdownEnabled, lockdownAllowlist);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getVpnManager().setAlwaysOnVpnPackageForUser(userId, vpnPackage, lockdownEnabled,
+ lockdownAllowlist);
}
- /**
- * Returns the package name of the currently set always-on VPN application.
- * If there is no always-on VPN set, or the VPN is provided by the system instead
- * of by an app, {@code null} will be returned.
- *
- * @return Package name of VPN controller responsible for always-on VPN,
- * or {@code null} if none is set.
+ /**
+ * Calls VpnManager#getAlwaysOnVpnPackageForUser.
+ * @deprecated TODO: remove when callers have migrated to VpnManager.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
+ @Deprecated
public String getAlwaysOnVpnPackageForUser(int userId) {
- try {
- return mService.getAlwaysOnVpnPackage(userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getVpnManager().getAlwaysOnVpnPackageForUser(userId);
}
/**
- * @return whether always-on VPN is in lockdown mode.
- *
+ * Calls VpnManager#isVpnLockdownEnabled.
+ * @deprecated TODO: remove when callers have migrated to VpnManager.
* @hide
- **/
- @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
+ */
+ @Deprecated
public boolean isVpnLockdownEnabled(int userId) {
- try {
- return mService.isVpnLockdownEnabled(userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
+ return getVpnManager().isVpnLockdownEnabled(userId);
}
/**
- * @return the list of packages that are allowed to access network when always-on VPN is in
- * lockdown mode but not connected. Returns {@code null} when VPN lockdown is not active.
- *
+ * Calls VpnManager#getVpnLockdownAllowlist.
+ * @deprecated TODO: remove when callers have migrated to VpnManager.
* @hide
- **/
- @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
- public List<String> getVpnLockdownWhitelist(int userId) {
- try {
- return mService.getVpnLockdownWhitelist(userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ */
+ @Deprecated
+ public List<String> getVpnLockdownAllowlist(int userId) {
+ return getVpnManager().getVpnLockdownAllowlist(userId);
}
/**
@@ -1220,6 +1168,45 @@ public class ConnectivityManager {
}
/**
+ * Informs ConnectivityService of whether the legacy lockdown VPN, as implemented by
+ * LockdownVpnTracker, is in use. This is deprecated for new devices starting from Android 12
+ * but is still supported for backwards compatibility.
+ * <p>
+ * This type of VPN is assumed always to use the system default network, and must always declare
+ * exactly one underlying network, which is the network that was the default when the VPN
+ * connected.
+ * <p>
+ * Calling this method with {@code true} enables legacy behaviour, specifically:
+ * <ul>
+ * <li>Any VPN that applies to userId 0 behaves specially with respect to deprecated
+ * {@link #CONNECTIVITY_ACTION} broadcasts. Any such broadcasts will have the state in the
+ * {@link #EXTRA_NETWORK_INFO} replaced by state of the VPN network. Also, any time the VPN
+ * connects, a {@link #CONNECTIVITY_ACTION} broadcast will be sent for the network
+ * underlying the VPN.</li>
+ * <li>Deprecated APIs that return {@link NetworkInfo} objects will have their state
+ * similarly replaced by the VPN network state.</li>
+ * <li>Information on current network interfaces passed to NetworkStatsService will not
+ * include any VPN interfaces.</li>
+ * </ul>
+ *
+ * @param enabled whether legacy lockdown VPN is enabled or disabled
+ *
+ * TODO: @SystemApi(client = MODULE_LIBRARIES)
+ *
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_SETTINGS})
+ public void setLegacyLockdownVpnEnabled(boolean enabled) {
+ try {
+ mService.setLegacyLockdownVpnEnabled(enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns details about the currently active default data network
* for a given uid. This is for internal use only to avoid spying
* other apps.
@@ -1368,7 +1355,7 @@ public class ConnectivityManager {
public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
try {
return mService.getDefaultNetworkCapabilitiesForUser(
- userId, mContext.getOpPackageName());
+ userId, mContext.getOpPackageName(), getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1450,7 +1437,8 @@ public class ConnectivityManager {
@Nullable
public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
try {
- return mService.getNetworkCapabilities(network, mContext.getOpPackageName());
+ return mService.getNetworkCapabilities(
+ network, mContext.getOpPackageName(), getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2220,17 +2208,6 @@ public class ConnectivityManager {
void onNetworkActive();
}
- private INetworkManagementService getNetworkManagementService() {
- synchronized (this) {
- if (mNMService != null) {
- return mNMService;
- }
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- mNMService = INetworkManagementService.Stub.asInterface(b);
- return mNMService;
- }
- }
-
private final ArrayMap<OnNetworkActiveListener, INetworkActivityListener>
mNetworkActivityListeners = new ArrayMap<>();
@@ -2255,7 +2232,7 @@ public class ConnectivityManager {
};
try {
- getNetworkManagementService().registerNetworkActivityListener(rl);
+ mService.registerNetworkActivityListener(rl);
mNetworkActivityListeners.put(l, rl);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -2272,7 +2249,7 @@ public class ConnectivityManager {
INetworkActivityListener rl = mNetworkActivityListeners.get(l);
Preconditions.checkArgument(rl != null, "Listener was not registered.");
try {
- getNetworkManagementService().unregisterNetworkActivityListener(rl);
+ mService.registerNetworkActivityListener(rl);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2288,7 +2265,7 @@ public class ConnectivityManager {
*/
public boolean isDefaultNetworkActive() {
try {
- return getNetworkManagementService().isNetworkActive();
+ return mService.isDefaultNetworkActive();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2808,7 +2785,7 @@ public class ConnectivityManager {
*/
@SystemApi
@Deprecated
- public static final int TETHER_ERROR_NO_ERROR = TetheringManager.TETHER_ERROR_NO_ERROR;
+ public static final int TETHER_ERROR_NO_ERROR = 0;
/**
* @deprecated Use {@link TetheringManager#TETHER_ERROR_UNKNOWN_IFACE}.
* {@hide}
@@ -2884,8 +2861,7 @@ public class ConnectivityManager {
*/
@SystemApi
@Deprecated
- public static final int TETHER_ERROR_PROVISION_FAILED =
- TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
+ public static final int TETHER_ERROR_PROVISION_FAILED = 11;
/**
* @deprecated Use {@link TetheringManager#TETHER_ERROR_DHCPSERVER_ERROR}.
* {@hide}
@@ -2899,8 +2875,7 @@ public class ConnectivityManager {
*/
@SystemApi
@Deprecated
- public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN =
- TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
+ public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13;
/**
* Get a more detailed error code after a Tethering or Untethering
@@ -3178,20 +3153,13 @@ public class ConnectivityManager {
}
/**
- * If the LockdownVpn mechanism is enabled, updates the vpn
- * with a reload of its profile.
- *
- * @return a boolean with {@code} indicating success
- *
- * <p>This method can only be called by the system UID
- * {@hide}
+ * Calls VpnManager#updateLockdownVpn.
+ * @deprecated TODO: remove when callers have migrated to VpnManager.
+ * @hide
*/
+ @Deprecated
public boolean updateLockdownVpn() {
- try {
- return mService.updateLockdownVpn();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getVpnManager().updateLockdownVpn();
}
/**
@@ -3720,7 +3688,8 @@ public class ConnectivityManager {
printStackTrace();
checkCallbackNotNull(callback);
Preconditions.checkArgument(
- reqType == TRACK_DEFAULT || need != null, "null NetworkCapabilities");
+ reqType == TRACK_DEFAULT || reqType == TRACK_SYSTEM_DEFAULT || need != null,
+ "null NetworkCapabilities");
final NetworkRequest request;
final String callingPackageName = mContext.getOpPackageName();
try {
@@ -3735,7 +3704,8 @@ public class ConnectivityManager {
Binder binder = new Binder();
if (reqType == LISTEN) {
request = mService.listenForNetwork(
- need, messenger, binder, callingPackageName);
+ need, messenger, binder, callingPackageName,
+ getAttributionTag());
} else {
request = mService.requestNetwork(
need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType,
@@ -4180,7 +4150,8 @@ public class ConnectivityManager {
checkPendingIntentNotNull(operation);
try {
mService.pendingListenForNetwork(
- request.networkCapabilities, operation, mContext.getOpPackageName());
+ request.networkCapabilities, operation, mContext.getOpPackageName(),
+ getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
@@ -4189,8 +4160,9 @@ public class ConnectivityManager {
}
/**
- * Registers to receive notifications about changes in the system default network. The callbacks
- * will continue to be called until either the application exits or
+ * Registers to receive notifications about changes in the application's default network. This
+ * may be a physical network or a virtual network, such as a VPN that applies to the
+ * application. 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
@@ -4203,7 +4175,7 @@ public class ConnectivityManager {
* {@link #unregisterNetworkCallback(NetworkCallback)}.
*
* @param networkCallback The {@link NetworkCallback} that the system will call as the
- * system default network changes.
+ * application's default network changes.
* The callback is invoked on the default internal Handler.
* @throws RuntimeException if the app already has too many callbacks registered.
*/
@@ -4213,8 +4185,9 @@ public class ConnectivityManager {
}
/**
- * Registers to receive notifications about changes in the system default network. The callbacks
- * will continue to be called until either the application exits or
+ * Registers to receive notifications about changes in the application's default network. This
+ * may be a physical network or a virtual network, such as a VPN that applies to the
+ * application. 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
@@ -4227,26 +4200,60 @@ public class ConnectivityManager {
* {@link #unregisterNetworkCallback(NetworkCallback)}.
*
* @param networkCallback The {@link NetworkCallback} that the system will call as the
- * system default network changes.
+ * application's default network changes.
* @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.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback,
@NonNull Handler handler) {
- // This works because if the NetworkCapabilities are null,
- // ConnectivityService takes them from the default request.
- //
- // Since the capabilities are exactly the same as the default request's
- // capabilities, this request is guaranteed, at all times, to be
- // satisfied by the same network, if any, that satisfies the default
- // request, i.e., the system default network.
CallbackHandler cbHandler = new CallbackHandler(handler);
sendRequestForNetwork(null /* NetworkCapabilities need */, networkCallback, 0,
TRACK_DEFAULT, TYPE_NONE, cbHandler);
}
/**
+ * Registers to receive notifications about changes in the system default network. The callbacks
+ * will continue to be called until either the application exits or
+ * {@link #unregisterNetworkCallback(NetworkCallback)} is called.
+ *
+ * This method should not be used to determine networking state seen by applications, because in
+ * many cases, most or even all application traffic may not use the default network directly,
+ * and traffic from different applications may go on different networks by default. As an
+ * example, if a VPN is connected, traffic from all applications might be sent through the VPN
+ * and not onto the system default network. Applications or system components desiring to do
+ * determine network state as seen by applications should use other methods such as
+ * {@link #registerDefaultNetworkCallback(NetworkCallback, Handler)}.
+ *
+ * <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
+ * all variants of this method, of {@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 networkCallback The {@link NetworkCallback} that the system will call as the
+ * system default network changes.
+ * @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.
+ *
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @SuppressLint({"ExecutorRegistration", "PairedRegistration"})
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_SETTINGS})
+ public void registerSystemDefaultNetworkCallback(@NonNull NetworkCallback networkCallback,
+ @NonNull Handler handler) {
+ CallbackHandler cbHandler = new CallbackHandler(handler);
+ sendRequestForNetwork(null /* NetworkCapabilities need */, networkCallback, 0,
+ TRACK_SYSTEM_DEFAULT, 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
@@ -4516,6 +4523,8 @@ public class ConnectivityManager {
try {
mService.factoryReset();
mTetheringManager.stopAllTethering();
+ // TODO: Migrate callers to VpnManager#factoryReset.
+ getVpnManager().factoryReset();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -4809,9 +4818,13 @@ public class ConnectivityManager {
return new TestNetworkManager(ITestNetworkManager.Stub.asInterface(tnBinder));
}
- /** @hide */
- public VpnManager createVpnManager() {
- return new VpnManager(mContext, mService);
+ /**
+ * Temporary hack to shim calls from ConnectivityManager to VpnManager. We cannot store a
+ * private final mVpnManager because ConnectivityManager is initialized before VpnManager.
+ * @hide TODO: remove.
+ */
+ public VpnManager getVpnManager() {
+ return mContext.getSystemService(VpnManager.class);
}
/** @hide */
@@ -4845,15 +4858,6 @@ public class ConnectivityManager {
}
}
- private void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) {
- try {
- mService.setOemNetworkPreference(preference);
- } catch (RemoteException e) {
- Log.e(TAG, "setOemNetworkPreference() failed for preference: " + preference.toString());
- throw e.rethrowFromSystemServer();
- }
- }
-
@NonNull
private final List<QosCallbackConnection> mQosCallbackConnections = new ArrayList<>();
@@ -5055,4 +5059,60 @@ public class ConnectivityManager {
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();
+ }
+
+ /**
+ * Used by automotive devices to set the network preferences used to direct traffic at an
+ * application level as per the given OemNetworkPreferences. An example use-case would be an
+ * automotive OEM wanting to provide connectivity for applications critical to the usage of a
+ * vehicle via a particular network.
+ *
+ * Calling this will overwrite the existing preference.
+ *
+ * @param preference {@link OemNetworkPreferences} The application network preference to be set.
+ * @param executor the executor on which listener will be invoked.
+ * @param listener {@link OnSetOemNetworkPreferenceListener} optional listener used to
+ * communicate completion of setOemNetworkPreference(). This will only be
+ * called once upon successful completion of setOemNetworkPreference().
+ * @throws IllegalArgumentException if {@code preference} contains invalid preference values.
+ * @throws SecurityException if missing the appropriate permissions.
+ * @throws UnsupportedOperationException if called on a non-automotive device.
+ * @hide
+ */
+ @SystemApi
+ @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) {
+ 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() {
+ @Override
+ public void onComplete() {
+ executor.execute(listener::onComplete);
+ }
+ };
+
+ try {
+ mService.setOemNetworkPreference(preference, listenerInternal);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setOemNetworkPreference() failed for preference: " + preference.toString());
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index e2672c480c10..160338d396af 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -20,6 +20,8 @@ import android.app.PendingIntent;
import android.net.ConnectionInfo;
import android.net.ConnectivityDiagnosticsManager;
import android.net.IConnectivityDiagnosticsCallback;
+import android.net.IOnSetOemNetworkPreferenceListener;
+import android.net.INetworkActivityListener;
import android.net.IQosCallback;
import android.net.ISocketKeepaliveCallback;
import android.net.LinkProperties;
@@ -35,16 +37,12 @@ import android.net.UidRange;
import android.net.QosSocketInfo;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.INetworkActivityListener;
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
import com.android.connectivity.aidl.INetworkAgent;
-import com.android.internal.net.LegacyVpnInfo;
-import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnProfile;
/**
* Interface that answers queries about, and allows changing, the
@@ -66,7 +64,7 @@ interface IConnectivityManager
Network getNetworkForType(int networkType);
Network[] getAllNetworks();
NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
- int userId, String callingPackageName);
+ int userId, String callingPackageName, String callingAttributionTag);
boolean isNetworkSupported(int networkType);
@@ -75,7 +73,8 @@ interface IConnectivityManager
LinkProperties getLinkPropertiesForType(int networkType);
LinkProperties getLinkProperties(in Network network);
- NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName);
+ NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName,
+ String callingAttributionTag);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
NetworkState[] getAllNetworkState();
@@ -121,35 +120,8 @@ interface IConnectivityManager
ProxyInfo getProxyForNetwork(in Network nework);
- boolean prepareVpn(String oldPackage, String newPackage, int userId);
-
- void setVpnPackageAuthorization(String packageName, int userId, int vpnType);
-
- ParcelFileDescriptor establishVpn(in VpnConfig config);
-
- boolean provisionVpnProfile(in VpnProfile profile, String packageName);
-
- void deleteVpnProfile(String packageName);
-
- void startVpnProfile(String packageName);
-
- void stopVpnProfile(String packageName);
-
- VpnConfig getVpnConfig(int userId);
-
- @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- void startLegacyVpn(in VpnProfile profile);
-
- LegacyVpnInfo getLegacyVpnInfo(int userId);
-
- boolean updateLockdownVpn();
- boolean isAlwaysOnVpnPackageSupported(int userId, String packageName);
- boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown,
- in List<String> lockdownWhitelist);
- String getAlwaysOnVpnPackage(int userId);
- boolean isVpnLockdownEnabled(int userId);
- List<String> getVpnLockdownWhitelist(int userId);
void setRequireVpnForUids(boolean requireVpn, in UidRange[] ranges);
+ void setLegacyLockdownVpnEnabled(boolean enabled);
void setProvisioningNotificationVisible(boolean visible, int networkType, in String action);
@@ -176,10 +148,12 @@ 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, String callingPackageName,
+ String callingAttributionTag);
void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
- in PendingIntent operation, String callingPackageName);
+ in PendingIntent operation, String callingPackageName,
+ String callingAttributionTag);
void releaseNetworkRequest(in NetworkRequest networkRequest);
@@ -196,10 +170,6 @@ interface IConnectivityManager
int getRestoreDefaultNetworkDelay(int networkType);
- boolean addVpnAddress(String address, int prefixLength);
- boolean removeVpnAddress(String address, int prefixLength);
- boolean setUnderlyingNetworksForVpn(in Network[] networks);
-
void factoryReset();
void startNattKeepalive(in Network network, int intervalSeconds,
@@ -219,8 +189,6 @@ interface IConnectivityManager
byte[] getNetworkWatchlistConfigHash();
int getConnectionOwnerUid(in ConnectionInfo connectionInfo);
- boolean isCallerCurrentAlwaysOnVpnApp();
- boolean isCallerCurrentAlwaysOnVpnLockdownApp();
void registerConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback,
in NetworkRequest request, String callingPackageName);
@@ -242,5 +210,6 @@ interface IConnectivityManager
void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback);
void unregisterQosCallback(in IQosCallback callback);
- void setOemNetworkPreference(in OemNetworkPreferences preference);
+ void setOemNetworkPreference(in OemNetworkPreferences preference,
+ in IOnSetOemNetworkPreferenceListener listener);
}
diff --git a/core/java/android/os/INetworkActivityListener.aidl b/packages/Connectivity/framework/src/android/net/INetworkActivityListener.aidl
index 24e6e55f1e57..79687dd3bf06 100644
--- a/core/java/android/os/INetworkActivityListener.aidl
+++ b/packages/Connectivity/framework/src/android/net/INetworkActivityListener.aidl
@@ -13,7 +13,7 @@
** limitations under the License.
*/
-package android.os;
+package android.net;
/**
* @hide
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 55b2c3c9e11f..26d14cbfaa95 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -762,12 +762,14 @@ public final class NetworkCapabilities implements Parcelable {
final int originalSignalStrength = mSignalStrength;
final int originalOwnerUid = getOwnerUid();
final int[] originalAdministratorUids = getAdministratorUids();
+ final TransportInfo originalTransportInfo = getTransportInfo();
clearAll();
mTransportTypes = (originalTransportTypes & TEST_NETWORKS_ALLOWED_TRANSPORTS)
| (1 << TRANSPORT_TEST);
mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
mNetworkSpecifier = originalSpecifier;
mSignalStrength = originalSignalStrength;
+ mTransportInfo = originalTransportInfo;
// Only retain the owner and administrator UIDs if they match the app registering the remote
// caller that registered the network.
@@ -2083,9 +2085,10 @@ public final class NetworkCapabilities implements Parcelable {
/**
* Check if private dns is broken.
*
- * @return {@code true} if {@code mPrivateDnsBroken} is set when private DNS is broken.
+ * @return {@code true} if private DNS is broken on this network.
* @hide
*/
+ @SystemApi
public boolean isPrivateDnsBroken() {
return mPrivateDnsBroken;
}
@@ -2328,6 +2331,17 @@ public final class NetworkCapabilities implements Parcelable {
}
/**
+ * Completely clears the contents of this object, removing even the capabilities that are
+ * set by default when the object is constructed.
+ * @return this builder
+ */
+ @NonNull
+ public Builder clearAll() {
+ mCaps.clearAll();
+ return this;
+ }
+
+ /**
* Sets the owner UID.
*
* The default value is {@link Process#INVALID_UID}. Pass this value to reset.
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index b9ef4c21ef6f..4e3085f4704d 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -86,17 +86,14 @@ public class NetworkRequest implements Parcelable {
* callbacks about the single, highest scoring current network
* (if any) that matches the specified NetworkCapabilities, or
*
- * - TRACK_DEFAULT, a hybrid of the two designed such that the
- * framework will issue callbacks for the single, highest scoring
- * current network (if any) that matches the capabilities of the
- * default Internet request (mDefaultRequest), but which cannot cause
- * the framework to either create or retain the existence of any
- * specific network. Note that from the point of view of the request
- * matching code, TRACK_DEFAULT is identical to REQUEST: its special
- * behaviour is not due to different semantics, but to the fact that
- * the system will only ever create a TRACK_DEFAULT with capabilities
- * that are identical to the default request's capabilities, thus
- * causing it to share fate in every way with the default request.
+ * - TRACK_DEFAULT, which causes the framework to issue callbacks for
+ * the single, highest scoring current network (if any) that will
+ * be chosen for an app, but which cannot cause the framework to
+ * either create or retain the existence of any specific network.
+ *
+ * - TRACK_SYSTEM_DEFAULT, which causes the framework to send callbacks
+ * for the network (if any) that satisfies the default Internet
+ * request.
*
* - BACKGROUND_REQUEST, like REQUEST but does not cause any networks
* to retain the NET_CAPABILITY_FOREGROUND capability. A network with
@@ -119,6 +116,7 @@ public class NetworkRequest implements Parcelable {
TRACK_DEFAULT,
REQUEST,
BACKGROUND_REQUEST,
+ TRACK_SYSTEM_DEFAULT,
};
/**
@@ -532,6 +530,8 @@ public class NetworkRequest implements Parcelable {
return NetworkRequestProto.TYPE_REQUEST;
case BACKGROUND_REQUEST:
return NetworkRequestProto.TYPE_BACKGROUND_REQUEST;
+ case TRACK_SYSTEM_DEFAULT:
+ return NetworkRequestProto.TYPE_TRACK_SYSTEM_DEFAULT;
default:
return NetworkRequestProto.TYPE_UNKNOWN;
}
diff --git a/packages/Connectivity/framework/src/android/net/Proxy.java b/packages/Connectivity/framework/src/android/net/Proxy.java
index 03cfbbb4a22d..77c8a4f4579b 100644
--- a/packages/Connectivity/framework/src/android/net/Proxy.java
+++ b/packages/Connectivity/framework/src/android/net/Proxy.java
@@ -32,8 +32,6 @@ import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* A convenience class for accessing the user and default proxy
@@ -66,40 +64,9 @@ public final class Proxy {
@Deprecated
public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
- /** @hide */
- public static final int PROXY_VALID = 0;
- /** @hide */
- public static final int PROXY_HOSTNAME_EMPTY = 1;
- /** @hide */
- public static final int PROXY_HOSTNAME_INVALID = 2;
- /** @hide */
- public static final int PROXY_PORT_EMPTY = 3;
- /** @hide */
- public static final int PROXY_PORT_INVALID = 4;
- /** @hide */
- public static final int PROXY_EXCLLIST_INVALID = 5;
-
private static ConnectivityManager sConnectivityManager = null;
- // Hostname / IP REGEX validation
- // Matches blank input, ips, and domain names
- private static final String NAME_IP_REGEX =
- "[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*";
-
- private static final String HOSTNAME_REGEXP = "^$|^" + NAME_IP_REGEX + "$";
-
- private static final Pattern HOSTNAME_PATTERN;
-
- private static final String EXCL_REGEX =
- "[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*(\\.[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*)*";
-
- private static final String EXCLLIST_REGEXP = "^$|^" + EXCL_REGEX + "(," + EXCL_REGEX + ")*$";
-
- private static final Pattern EXCLLIST_PATTERN;
-
static {
- HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
- EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
sDefaultProxySelector = ProxySelector.getDefault();
}
@@ -218,33 +185,6 @@ public final class Proxy {
return false;
}
- /**
- * Validate syntax of hostname, port and exclusion list entries
- * {@hide}
- */
- public static int validate(String hostname, String port, String exclList) {
- Matcher match = HOSTNAME_PATTERN.matcher(hostname);
- Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList);
-
- if (!match.matches()) return PROXY_HOSTNAME_INVALID;
-
- if (!listMatch.matches()) return PROXY_EXCLLIST_INVALID;
-
- if (hostname.length() > 0 && port.length() == 0) return PROXY_PORT_EMPTY;
-
- if (port.length() > 0) {
- if (hostname.length() == 0) return PROXY_HOSTNAME_EMPTY;
- int portVal = -1;
- try {
- portVal = Integer.parseInt(port);
- } catch (NumberFormatException ex) {
- return PROXY_PORT_INVALID;
- }
- if (portVal <= 0 || portVal > 0xFFFF) return PROXY_PORT_INVALID;
- }
- return PROXY_VALID;
- }
-
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Deprecated
diff --git a/packages/Connectivity/framework/src/android/net/ProxyInfo.java b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
index 9c9fed102828..229db0d717cd 100644
--- a/packages/Connectivity/framework/src/android/net/ProxyInfo.java
+++ b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
@@ -23,6 +23,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.net.module.util.ProxyUtils;
+
import java.net.InetSocketAddress;
import java.net.URLConnection;
import java.util.List;
@@ -233,7 +235,7 @@ public class ProxyInfo implements Parcelable {
*/
public boolean isValid() {
if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
- return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
+ return ProxyUtils.PROXY_VALID == ProxyUtils.validate(mHost == null ? "" : mHost,
mPort == 0 ? "" : Integer.toString(mPort),
mExclusionList == null ? "" : mExclusionList);
}
diff --git a/packages/Connectivity/framework/src/android/net/TestNetworkManager.java b/packages/Connectivity/framework/src/android/net/TestNetworkManager.java
index 4e894143bf91..a174a7be85d1 100644
--- a/packages/Connectivity/framework/src/android/net/TestNetworkManager.java
+++ b/packages/Connectivity/framework/src/android/net/TestNetworkManager.java
@@ -41,7 +41,6 @@ public class TestNetworkManager {
/**
* Prefix for tap interfaces created by this class.
- * @hide
*/
public static final String TEST_TAP_PREFIX = "testtap";
diff --git a/packages/Connectivity/framework/src/android/net/VpnManager.java b/packages/Connectivity/framework/src/android/net/VpnManager.java
deleted file mode 100644
index c87b8279c4d6..000000000000
--- a/packages/Connectivity/framework/src/android/net/VpnManager.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.os.RemoteException;
-
-import com.android.internal.net.VpnProfile;
-
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.security.GeneralSecurityException;
-
-/**
- * This class provides an interface for apps to manage platform VPN profiles
- *
- * <p>Apps can use this API to provide profiles with which the platform can set up a VPN without
- * further app intermediation. When a VPN profile is present and the app is selected as an always-on
- * VPN, the platform will directly trigger the negotiation of the VPN without starting or waking the
- * app (unlike VpnService).
- *
- * <p>VPN apps using supported protocols should preferentially use this API over the {@link
- * VpnService} API for ease-of-development and reduced maintainance burden. This also give the user
- * the guarantee that VPN network traffic is not subjected to on-device packet interception.
- *
- * @see Ikev2VpnProfile
- */
-public class VpnManager {
- /** Type representing a lack of VPN @hide */
- public static final int TYPE_VPN_NONE = -1;
- /** VPN service type code @hide */
- public static final int TYPE_VPN_SERVICE = 1;
- /** Platform VPN type code @hide */
- public static final int TYPE_VPN_PLATFORM = 2;
-
- /** @hide */
- @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM})
- @Retention(RetentionPolicy.SOURCE)
- public @interface VpnType {}
-
- @NonNull private final Context mContext;
- @NonNull private final IConnectivityManager mService;
-
- private static Intent getIntentForConfirmation() {
- final Intent intent = new Intent();
- final ComponentName componentName = ComponentName.unflattenFromString(
- Resources.getSystem().getString(
- com.android.internal.R.string.config_platformVpnConfirmDialogComponent));
- intent.setComponent(componentName);
- return intent;
- }
-
- /**
- * Create an instance of the VpnManager with the given context.
- *
- * <p>Internal only. Applications are expected to obtain an instance of the VpnManager via the
- * {@link Context.getSystemService()} method call.
- *
- * @hide
- */
- public VpnManager(@NonNull Context ctx, @NonNull IConnectivityManager service) {
- mContext = checkNotNull(ctx, "missing Context");
- mService = checkNotNull(service, "missing IConnectivityManager");
- }
-
- /**
- * Install a VpnProfile configuration keyed on the calling app's package name.
- *
- * <p>This method returns {@code null} if user consent has already been granted, or an {@link
- * Intent} to a system activity. If an intent is returned, the application should launch the
- * activity using {@link Activity#startActivityForResult} to request user consent. The activity
- * may pop up a dialog to require user action, and the result will come back via its {@link
- * Activity#onActivityResult}. If the result is {@link Activity#RESULT_OK}, the user has
- * consented, and the VPN profile can be started.
- *
- * @param profile the VpnProfile provided by this package. Will override any previous VpnProfile
- * stored for this package.
- * @return an Intent requesting user consent to start the VPN, or null if consent is not
- * required based on privileges or previous user consent.
- */
- @Nullable
- public Intent provisionVpnProfile(@NonNull PlatformVpnProfile profile) {
- final VpnProfile internalProfile;
-
- try {
- internalProfile = profile.toVpnProfile();
- } catch (GeneralSecurityException | IOException e) {
- // Conversion to VpnProfile failed; this is an invalid profile. Both of these exceptions
- // indicate a failure to convert a PrivateKey or X509Certificate to a Base64 encoded
- // string as required by the VpnProfile.
- throw new IllegalArgumentException("Failed to serialize PlatformVpnProfile", e);
- }
-
- try {
- // Profile can never be null; it either gets set, or an exception is thrown.
- if (mService.provisionVpnProfile(internalProfile, mContext.getOpPackageName())) {
- return null;
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- return getIntentForConfirmation();
- }
-
- /**
- * Delete the VPN profile configuration that was provisioned by the calling app
- *
- * @throws SecurityException if this would violate user settings
- */
- public void deleteProvisionedVpnProfile() {
- try {
- mService.deleteVpnProfile(mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Request the startup of a previously provisioned VPN.
- *
- * @throws SecurityException exception if user or device settings prevent this VPN from being
- * setup, or if user consent has not been granted
- */
- public void startProvisionedVpnProfile() {
- try {
- mService.startVpnProfile(mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /** Tear down the VPN provided by the calling app (if any) */
- public void stopProvisionedVpnProfile() {
- try {
- mService.stopVpnProfile(mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-}
diff --git a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
new file mode 100644
index 000000000000..0242ba08742c
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+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;
+
+/**
+ * Container for VPN-specific transport information.
+ *
+ * @see android.net.TransportInfo
+ * @see NetworkCapabilities#getTransportInfo()
+ *
+ * @hide
+ */
+@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 VpnTransportInfo(@VpnManager.VpnType int type) {
+ this.type = type;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof VpnTransportInfo)) return false;
+
+ VpnTransportInfo that = (VpnTransportInfo) o;
+ return this.type == that.type;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type);
+ }
+
+ @Override
+ public String toString() {
+ final String typeString = sTypeToString.get(type, "VPN_TYPE_???");
+ return String.format("VpnTransportInfo{%s}", typeString);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(type);
+ }
+
+ public static final @NonNull Creator<VpnTransportInfo> CREATOR =
+ new Creator<VpnTransportInfo>() {
+ public VpnTransportInfo createFromParcel(Parcel in) {
+ return new VpnTransportInfo(in.readInt());
+ }
+ public VpnTransportInfo[] newArray(int size) {
+ return new VpnTransportInfo[size];
+ }
+ };
+}
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index 8fc318180778..f20b89fb842c 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_shared {
name: "libservice-connectivity",
// TODO: build against the NDK (sdk_version: "30" for example)
@@ -25,7 +34,6 @@ cc_library_shared {
],
srcs: [
"jni/com_android_server_TestNetworkService.cpp",
- "jni/com_android_server_connectivity_Vpn.cpp",
"jni/onload.cpp",
],
shared_libs: [
diff --git a/packages/Connectivity/service/jni/onload.cpp b/packages/Connectivity/service/jni/onload.cpp
index 3afcb0e8f688..00128794bcd0 100644
--- a/packages/Connectivity/service/jni/onload.cpp
+++ b/packages/Connectivity/service/jni/onload.cpp
@@ -19,7 +19,6 @@
namespace android {
-int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_TestNetworkService(JNIEnv* env);
extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
@@ -29,12 +28,11 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
return JNI_ERR;
}
- if (register_android_server_connectivity_Vpn(env) < 0
- || register_android_server_TestNetworkService(env) < 0) {
+ if (register_android_server_TestNetworkService(env) < 0) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
-}; \ No newline at end of file
+};
diff --git a/packages/CtsShim/Android.bp b/packages/CtsShim/Android.bp
index 49608b3856e0..31cd76079131 100644
--- a/packages/CtsShim/Android.bp
+++ b/packages/CtsShim/Android.bp
@@ -17,6 +17,15 @@
//##########################################################
// Variant: Privileged app
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app_import {
name: "CtsShimPrivPrebuilt",
diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp
index 14a3376380df..0b3f9bb62a9b 100644
--- a/packages/CtsShim/build/Android.bp
+++ b/packages/CtsShim/build/Android.bp
@@ -17,6 +17,15 @@
//##########################################################
// Variant: Privileged app upgrade
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "CtsShimPrivUpgrade",
// this needs to be a privileged application
diff --git a/packages/CtsShim/build/jni/Android.bp b/packages/CtsShim/build/jni/Android.bp
index 4a1973cc0b46..ba586dbb2d88 100644
--- a/packages/CtsShim/build/jni/Android.bp
+++ b/packages/CtsShim/build/jni/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_shared {
name: "libshim_jni",
srcs: ["Shim.c"],
diff --git a/packages/DynamicSystemInstallationService/Android.bp b/packages/DynamicSystemInstallationService/Android.bp
index a8cf5d605f17..ad86f4667f67 100644
--- a/packages/DynamicSystemInstallationService/Android.bp
+++ b/packages/DynamicSystemInstallationService/Android.bp
@@ -1,3 +1,22 @@
+package {
+ default_applicable_licenses: [
+ "frameworks_base_packages_DynamicSystemInstallationService_license",
+ ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_packages_DynamicSystemInstallationService_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
android_app {
name: "DynamicSystemInstallationService",
defaults: ["platform_app_defaults"],
diff --git a/packages/DynamicSystemInstallationService/tests/Android.bp b/packages/DynamicSystemInstallationService/tests/Android.bp
index 3bdf82966889..50b65b4067fd 100644
--- a/packages/DynamicSystemInstallationService/tests/Android.bp
+++ b/packages/DynamicSystemInstallationService/tests/Android.bp
@@ -1,3 +1,14 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_packages_DynamicSystemInstallationService_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: [
+ "frameworks_base_packages_DynamicSystemInstallationService_license",
+ ],
+}
+
android_test {
name: "DynamicSystemInstallationServiceTests",
diff --git a/packages/EasterEgg/Android.bp b/packages/EasterEgg/Android.bp
index b858ab01ffd9..f8785f2b8e2c 100644
--- a/packages/EasterEgg/Android.bp
+++ b/packages/EasterEgg/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
// the build system in pi-dev can't quite handle R.java in kt
// so we will have a mix of java and kotlin files
diff --git a/packages/EncryptedLocalTransport/Android.bp b/packages/EncryptedLocalTransport/Android.bp
index 00e9c71c7654..09e563076c95 100644
--- a/packages/EncryptedLocalTransport/Android.bp
+++ b/packages/EncryptedLocalTransport/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "EncryptedLocalTransport",
defaults: ["platform_app_defaults"],
diff --git a/packages/ExtShared/Android.bp b/packages/ExtShared/Android.bp
index 279ac9de9035..b1fd7f64292d 100644
--- a/packages/ExtShared/Android.bp
+++ b/packages/ExtShared/Android.bp
@@ -12,6 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["frameworks_base_packages_ExtShared_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_packages_ExtShared_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
android_app {
name: "ExtShared",
defaults: ["platform_app_defaults"],
diff --git a/packages/ExternalStorageProvider/Android.bp b/packages/ExternalStorageProvider/Android.bp
index f1e629961e11..0bbdf7aae9a5 100644
--- a/packages/ExternalStorageProvider/Android.bp
+++ b/packages/ExternalStorageProvider/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "ExternalStorageProvider",
defaults: ["platform_app_defaults"],
diff --git a/packages/ExternalStorageProvider/tests/Android.bp b/packages/ExternalStorageProvider/tests/Android.bp
index 04cf01af073a..633f18610921 100644
--- a/packages/ExternalStorageProvider/tests/Android.bp
+++ b/packages/ExternalStorageProvider/tests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ExternalStorageProviderTests",
diff --git a/packages/FakeOemFeatures/Android.bp b/packages/FakeOemFeatures/Android.bp
index b63e3a136700..25e3a862f6c4 100644
--- a/packages/FakeOemFeatures/Android.bp
+++ b/packages/FakeOemFeatures/Android.bp
@@ -1,3 +1,22 @@
+package {
+ default_applicable_licenses: [
+ "frameworks_base_packages_FakeOemFeatures_license",
+ ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_packages_FakeOemFeatures_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
android_app {
name: "FakeOemFeatures",
defaults: ["platform_app_defaults"],
diff --git a/packages/FusedLocation/Android.bp b/packages/FusedLocation/Android.bp
index ada463ac6b59..64b4c54e74ad 100644
--- a/packages/FusedLocation/Android.bp
+++ b/packages/FusedLocation/Android.bp
@@ -12,6 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: [
+ "frameworks_base_packages_FusedLocation_license",
+ ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_packages_FusedLocation_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
android_app {
name: "FusedLocation",
defaults: ["platform_app_defaults"],
diff --git a/packages/InputDevices/Android.bp b/packages/InputDevices/Android.bp
index 5afbe729d960..477acc5e71a8 100644
--- a/packages/InputDevices/Android.bp
+++ b/packages/InputDevices/Android.bp
@@ -12,6 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: [
+ "frameworks_base_packages_InputDevices_license",
+ ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_packages_InputDevices_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
android_app {
name: "InputDevices",
defaults: ["platform_app_defaults"],
diff --git a/packages/LocalTransport/Android.bp b/packages/LocalTransport/Android.bp
index 9a98a86002b8..d4fa1915a140 100644
--- a/packages/LocalTransport/Android.bp
+++ b/packages/LocalTransport/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "LocalTransport",
defaults: ["platform_app_defaults"],
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 4d9c675de3ae..4106ac5e10b1 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -12,6 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: [
+ "frameworks_base_packages_PackageInstaller_license",
+ ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_packages_PackageInstaller_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
android_app {
name: "PackageInstaller",
defaults: ["platform_app_defaults"],
diff --git a/packages/PackageInstaller/OWNERS b/packages/PackageInstaller/OWNERS
index 252670a6fb13..8e1774b0baa2 100644
--- a/packages/PackageInstaller/OWNERS
+++ b/packages/PackageInstaller/OWNERS
@@ -1,5 +1,4 @@
svetoslavganov@google.com
-moltmann@google.com
toddke@google.com
suprabh@google.com
diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml
index d411831eef3f..65e75cdba405 100644
--- a/packages/PackageInstaller/res/values-eu/strings.xml
+++ b/packages/PackageInstaller/res/values-eu/strings.xml
@@ -83,9 +83,9 @@
<string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak tableta honetan."</string>
<string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak telebista honetan."</string>
<string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak telefono honetan."</string>
- <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonoak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik telefonoak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
- <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik tabletak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
- <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik telebistak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
+ <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonoak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik telefonoak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
+ <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik tabletak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
+ <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik telebistak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
<string name="anonymous_source_continue" msgid="4375745439457209366">"Egin aurrera"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Ezarpenak"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Wear aplikazioak instalatzea/desinstalatzea"</string>
diff --git a/packages/PrintRecommendationService/Android.bp b/packages/PrintRecommendationService/Android.bp
index d368f3cb18f9..084dd99833c0 100644
--- a/packages/PrintRecommendationService/Android.bp
+++ b/packages/PrintRecommendationService/Android.bp
@@ -12,6 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: [
+ "frameworks_base_packages_PrintRecommendationService_license",
+ ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_packages_PrintRecommendationService_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
android_app {
name: "PrintRecommendationService",
defaults: ["platform_app_defaults"],
diff --git a/packages/PrintSpooler/Android.bp b/packages/PrintSpooler/Android.bp
index d38fd020bb5f..772c69fe079b 100644
--- a/packages/PrintSpooler/Android.bp
+++ b/packages/PrintSpooler/Android.bp
@@ -12,6 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: [
+ "frameworks_base_packages_PrintSpooler_license",
+ ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_packages_PrintSpooler_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
android_app {
name: "PrintSpooler",
defaults: ["platform_app_defaults"],
diff --git a/packages/PrintSpooler/jni/Android.bp b/packages/PrintSpooler/jni/Android.bp
index 789312e58863..44df70e89d54 100644
--- a/packages/PrintSpooler/jni/Android.bp
+++ b/packages/PrintSpooler/jni/Android.bp
@@ -1,3 +1,14 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_packages_PrintSpooler_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: [
+ "frameworks_base_packages_PrintSpooler_license",
+ ],
+}
+
cc_library_shared {
name: "libprintspooler_jni",
diff --git a/packages/PrintSpooler/res/values-fa/strings.xml b/packages/PrintSpooler/res/values-fa/strings.xml
index 719fc9219450..e69ca7664b53 100644
--- a/packages/PrintSpooler/res/values-fa/strings.xml
+++ b/packages/PrintSpooler/res/values-fa/strings.xml
@@ -50,8 +50,8 @@
<string name="search" msgid="5421724265322228497">"جستجو"</string>
<string name="all_printers_label" msgid="3178848870161526399">"همه چاپگرها"</string>
<string name="add_print_service_label" msgid="5356702546188981940">"افزودن سرویس"</string>
- <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"کادر جستجو نمایان شد"</string>
- <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"کادر جستجو پنهان شد"</string>
+ <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"چارگوش جستجو نمایان شد"</string>
+ <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"چارگوش جستجو پنهان شد"</string>
<string name="print_add_printer" msgid="1088656468360653455">"افزودن چاپگر"</string>
<string name="print_select_printer" msgid="7388760939873368698">"انتخاب چاپگر"</string>
<string name="print_forget_printer" msgid="5035287497291910766">"فراموش کردن چاپگر"</string>
diff --git a/packages/PrintSpooler/tests/outofprocess/Android.bp b/packages/PrintSpooler/tests/outofprocess/Android.bp
index 0e028b04aaaf..69a1d7fa59e4 100644
--- a/packages/PrintSpooler/tests/outofprocess/Android.bp
+++ b/packages/PrintSpooler/tests/outofprocess/Android.bp
@@ -12,6 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_packages_PrintSpooler_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: [
+ "frameworks_base_packages_PrintSpooler_license",
+ ],
+}
+
android_test {
name: "PrintSpoolerOutOfProcessTests",
diff --git a/packages/SettingsLib/ActionBarShadow/Android.bp b/packages/SettingsLib/ActionBarShadow/Android.bp
index d2848564abea..800ab671cedb 100644
--- a/packages/SettingsLib/ActionBarShadow/Android.bp
+++ b/packages/SettingsLib/ActionBarShadow/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibActionBarShadow",
diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp
index cd3fb0cc2143..51c9c39cdf03 100644
--- a/packages/SettingsLib/ActionButtonsPreference/Android.bp
+++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibActionButtonsPreference",
diff --git a/packages/SettingsLib/AdaptiveIcon/Android.bp b/packages/SettingsLib/AdaptiveIcon/Android.bp
index 7f4442deecd6..934aacfddfb9 100644
--- a/packages/SettingsLib/AdaptiveIcon/Android.bp
+++ b/packages/SettingsLib/AdaptiveIcon/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibAdaptiveIcon",
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 315cfabb559d..63158fff2fbc 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLib",
diff --git a/packages/SettingsLib/AppPreference/Android.bp b/packages/SettingsLib/AppPreference/Android.bp
index b07176a064f9..1817a77981a9 100644
--- a/packages/SettingsLib/AppPreference/Android.bp
+++ b/packages/SettingsLib/AppPreference/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibAppPreference",
diff --git a/packages/SettingsLib/BarChartPreference/Android.bp b/packages/SettingsLib/BarChartPreference/Android.bp
index 477e8979b03b..ae2606661697 100644
--- a/packages/SettingsLib/BarChartPreference/Android.bp
+++ b/packages/SettingsLib/BarChartPreference/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibBarChartPreference",
diff --git a/packages/SettingsLib/DisplayDensityUtils/Android.bp b/packages/SettingsLib/DisplayDensityUtils/Android.bp
index 27d0cb5ad48c..b7bfb5fd8af5 100644
--- a/packages/SettingsLib/DisplayDensityUtils/Android.bp
+++ b/packages/SettingsLib/DisplayDensityUtils/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibDisplayDensityUtils",
diff --git a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
index 4d454941af7c..b0a9b95492a6 100644
--- a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
+++ b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
@@ -45,13 +45,22 @@ public class EmergencyNumberUtils {
public static final Uri EMERGENCY_NUMBER_OVERRIDE_AUTHORITY = new Uri.Builder().scheme(
ContentResolver.SCHEME_CONTENT)
- .authority("com.android.emergency.numbers")
+ .authority("com.android.emergency.gesture")
.build();
public static final String METHOD_NAME_GET_EMERGENCY_NUMBER_OVERRIDE =
"GET_EMERGENCY_NUMBER_OVERRIDE";
public static final String METHOD_NAME_SET_EMERGENCY_NUMBER_OVERRIDE =
"SET_EMERGENCY_NUMBER_OVERRIDE";
+ public static final String METHOD_NAME_SET_EMERGENCY_GESTURE = "SET_EMERGENCY_GESTURE";
+ public static final String METHOD_NAME_SET_EMERGENCY_SOUND = "SET_EMERGENCY_SOUND";
+ public static final String METHOD_NAME_GET_EMERGENCY_GESTURE_ENABLED = "GET_EMERGENCY_GESTURE";
+ public static final String METHOD_NAME_GET_EMERGENCY_GESTURE_SOUND_ENABLED =
+ "GET_EMERGENCY_SOUND";
public static final String EMERGENCY_GESTURE_CALL_NUMBER = "emergency_gesture_call_number";
+ public static final String EMERGENCY_SETTING_VALUE = "emergency_setting_value";
+ public static final int EMERGENCY_SETTING_ON = 1;
+ public static final int EMERGENCY_SETTING_OFF = 0;
+
@VisibleForTesting
static final String FALL_BACK_NUMBER = "112";
@@ -103,6 +112,51 @@ public class EmergencyNumberUtils {
METHOD_NAME_SET_EMERGENCY_NUMBER_OVERRIDE, null /* args */, bundle);
}
+ /**
+ * Enable/disable the emergency gesture setting
+ */
+ public void setEmergencyGestureEnabled(boolean enabled) {
+ final Bundle bundle = new Bundle();
+ bundle.putInt(EMERGENCY_SETTING_VALUE,
+ enabled ? EMERGENCY_SETTING_ON : EMERGENCY_SETTING_OFF);
+ mContext.getContentResolver().call(EMERGENCY_NUMBER_OVERRIDE_AUTHORITY,
+ METHOD_NAME_SET_EMERGENCY_GESTURE, null /* args */, bundle);
+ }
+
+ /**
+ * Enable/disable the emergency gesture sound setting
+ */
+ public void setEmergencySoundEnabled(boolean enabled) {
+ final Bundle bundle = new Bundle();
+ bundle.putInt(EMERGENCY_SETTING_VALUE,
+ enabled ? EMERGENCY_SETTING_ON : EMERGENCY_SETTING_OFF);
+ mContext.getContentResolver().call(EMERGENCY_NUMBER_OVERRIDE_AUTHORITY,
+ METHOD_NAME_SET_EMERGENCY_SOUND, null /* args */, bundle);
+ }
+
+ /**
+ * Whether or not emergency gesture is enabled.
+ */
+ public boolean getEmergencyGestureEnabled() {
+ final Bundle bundle = mContext.getContentResolver().call(
+ EMERGENCY_NUMBER_OVERRIDE_AUTHORITY,
+ METHOD_NAME_GET_EMERGENCY_GESTURE_ENABLED, null /* args */, null /* bundle */);
+ return bundle == null ? true : bundle.getInt(EMERGENCY_SETTING_VALUE, EMERGENCY_SETTING_ON)
+ == EMERGENCY_SETTING_ON;
+ }
+
+ /**
+ * Whether or not emergency gesture sound is enabled.
+ */
+ public boolean getEmergencyGestureSoundEnabled() {
+ final Bundle bundle = mContext.getContentResolver().call(
+ EMERGENCY_NUMBER_OVERRIDE_AUTHORITY,
+ METHOD_NAME_GET_EMERGENCY_GESTURE_SOUND_ENABLED, null /* args */,
+ null /* bundle */);
+ return bundle == null ? true : bundle.getInt(EMERGENCY_SETTING_VALUE, EMERGENCY_SETTING_OFF)
+ == EMERGENCY_SETTING_ON;
+ }
+
private String getEmergencyNumberOverride() {
final Bundle bundle = mContext.getContentResolver().call(
EMERGENCY_NUMBER_OVERRIDE_AUTHORITY,
diff --git a/packages/SettingsLib/EntityHeaderWidgets/Android.bp b/packages/SettingsLib/EntityHeaderWidgets/Android.bp
index 280848a0d7c4..bd83cdce0078 100644
--- a/packages/SettingsLib/EntityHeaderWidgets/Android.bp
+++ b/packages/SettingsLib/EntityHeaderWidgets/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibEntityHeaderWidgets",
diff --git a/packages/SettingsLib/HelpUtils/Android.bp b/packages/SettingsLib/HelpUtils/Android.bp
index 285131d19725..5826047b9f52 100644
--- a/packages/SettingsLib/HelpUtils/Android.bp
+++ b/packages/SettingsLib/HelpUtils/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibHelpUtils",
diff --git a/packages/SettingsLib/LayoutPreference/Android.bp b/packages/SettingsLib/LayoutPreference/Android.bp
index a1f9a768c76c..8a4e53d80a7c 100644
--- a/packages/SettingsLib/LayoutPreference/Android.bp
+++ b/packages/SettingsLib/LayoutPreference/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibLayoutPreference",
diff --git a/packages/SettingsLib/ProgressBar/Android.bp b/packages/SettingsLib/ProgressBar/Android.bp
index eae21d8858dc..b5bc8f77045b 100644
--- a/packages/SettingsLib/ProgressBar/Android.bp
+++ b/packages/SettingsLib/ProgressBar/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibProgressBar",
@@ -6,4 +15,4 @@ android_library {
sdk_version: "system_current",
min_sdk_version: "21",
-} \ No newline at end of file
+}
diff --git a/packages/SettingsLib/RadioButtonPreference/Android.bp b/packages/SettingsLib/RadioButtonPreference/Android.bp
index 136d6daad99a..b309c01e0b7b 100644
--- a/packages/SettingsLib/RadioButtonPreference/Android.bp
+++ b/packages/SettingsLib/RadioButtonPreference/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibRadioButtonPreference",
diff --git a/packages/SettingsLib/RestrictedLockUtils/Android.bp b/packages/SettingsLib/RestrictedLockUtils/Android.bp
index b2f088257bb9..c0623edabefe 100644
--- a/packages/SettingsLib/RestrictedLockUtils/Android.bp
+++ b/packages/SettingsLib/RestrictedLockUtils/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibRestrictedLockUtils",
@@ -10,4 +19,4 @@ android_library {
sdk_version: "system_current",
min_sdk_version: "21",
-} \ No newline at end of file
+}
diff --git a/packages/SettingsLib/SchedulesProvider/Android.bp b/packages/SettingsLib/SchedulesProvider/Android.bp
index ef592527ba92..c4373bb2dc1f 100644
--- a/packages/SettingsLib/SchedulesProvider/Android.bp
+++ b/packages/SettingsLib/SchedulesProvider/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibSchedulesProvider",
diff --git a/packages/SettingsLib/SearchProvider/Android.bp b/packages/SettingsLib/SearchProvider/Android.bp
index 5254dde71553..f96011ad5d09 100644
--- a/packages/SettingsLib/SearchProvider/Android.bp
+++ b/packages/SettingsLib/SearchProvider/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibSearchProvider",
diff --git a/packages/SettingsLib/SearchWidget/Android.bp b/packages/SettingsLib/SearchWidget/Android.bp
index 7541ca456138..b7367b4a10a7 100644
--- a/packages/SettingsLib/SearchWidget/Android.bp
+++ b/packages/SettingsLib/SearchWidget/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibSearchWidget",
diff --git a/packages/SettingsLib/SettingsSpinner/Android.bp b/packages/SettingsLib/SettingsSpinner/Android.bp
index ca23616ca588..c5b2fe66c315 100644
--- a/packages/SettingsLib/SettingsSpinner/Android.bp
+++ b/packages/SettingsLib/SettingsSpinner/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibSettingsSpinner",
diff --git a/packages/SettingsLib/SettingsTheme/Android.bp b/packages/SettingsLib/SettingsTheme/Android.bp
index 6579fd9b4238..bda863a71453 100644
--- a/packages/SettingsLib/SettingsTheme/Android.bp
+++ b/packages/SettingsLib/SettingsTheme/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibSettingsTheme",
diff --git a/packages/SettingsLib/Tile/Android.bp b/packages/SettingsLib/Tile/Android.bp
index bf16ef317fd8..cc570ccbb506 100644
--- a/packages/SettingsLib/Tile/Android.bp
+++ b/packages/SettingsLib/Tile/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibTile",
diff --git a/packages/SettingsLib/Utils/Android.bp b/packages/SettingsLib/Utils/Android.bp
index c5f0ee6c939c..1cf42ff1c566 100644
--- a/packages/SettingsLib/Utils/Android.bp
+++ b/packages/SettingsLib/Utils/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLibUtils",
diff --git a/packages/SettingsLib/res/drawable/ic_mobile_call_strength_1.xml b/packages/SettingsLib/res/drawable/ic_mobile_call_strength_1.xml
new file mode 100644
index 000000000000..46e2d4554d46
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_mobile_call_strength_1.xml
@@ -0,0 +1,35 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.17,14.84l-3.26,-0.65c-0.33,-0.07 -0.67,0.04 -0.9,0.27l-2.62,2.62c-2.75,-1.49 -5.01,-3.75 -6.5,-6.5l2.62,-2.62c0.24,-0.24 0.34,-0.58 0.27,-0.9L9.13,3.8C9.04,3.34 8.63,3 8.15,3H4C3.44,3 2.97,3.47 3,4.03c0.17,2.91 1.04,5.63 2.43,8.01c1.57,2.69 3.81,4.93 6.5,6.5c2.38,1.39 5.1,2.26 8.01,2.43c0.56,0.03 1.03,-0.44 1.03,-1v-4.15C20.97,15.34 20.64,14.93 20.17,14.84z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,8h2v3h-2z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.5,5h2v6h-2z"
+ android:fillAlpha="0.3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,3h2v8h-2z"
+ android:fillAlpha="0.3"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_mobile_call_strength_2.xml b/packages/SettingsLib/res/drawable/ic_mobile_call_strength_2.xml
new file mode 100644
index 000000000000..d9cd590e650a
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_mobile_call_strength_2.xml
@@ -0,0 +1,34 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.17,14.84l-3.26,-0.65c-0.33,-0.07 -0.67,0.04 -0.9,0.27l-2.62,2.62c-2.75,-1.49 -5.01,-3.75 -6.5,-6.5l2.62,-2.62c0.24,-0.24 0.34,-0.58 0.27,-0.9L9.13,3.8C9.04,3.34 8.63,3 8.15,3H4C3.44,3 2.97,3.47 3,4.03c0.17,2.91 1.04,5.63 2.43,8.01c1.57,2.69 3.81,4.93 6.5,6.5c2.38,1.39 5.1,2.26 8.01,2.43c0.56,0.03 1.03,-0.44 1.03,-1v-4.15C20.97,15.34 20.64,14.93 20.17,14.84z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,8h2v3h-2z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.5,5h2v6h-2z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,3h2v8h-2z"
+ android:fillAlpha="0.3"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_mobile_call_strength_3.xml b/packages/SettingsLib/res/drawable/ic_mobile_call_strength_3.xml
new file mode 100644
index 000000000000..e80fd08a8c0a
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_mobile_call_strength_3.xml
@@ -0,0 +1,33 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.17,14.84l-3.26,-0.65c-0.33,-0.07 -0.67,0.04 -0.9,0.27l-2.62,2.62c-2.75,-1.49 -5.01,-3.75 -6.5,-6.5l2.62,-2.62c0.24,-0.24 0.34,-0.58 0.27,-0.9L9.13,3.8C9.04,3.34 8.63,3 8.15,3H4C3.44,3 2.97,3.47 3,4.03c0.17,2.91 1.04,5.63 2.43,8.01c1.57,2.69 3.81,4.93 6.5,6.5c2.38,1.39 5.1,2.26 8.01,2.43c0.56,0.03 1.03,-0.44 1.03,-1v-4.15C20.97,15.34 20.64,14.93 20.17,14.84z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,8h2v3h-2z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.5,5h2v6h-2z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,3h2v8h-2z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_wifi_call_strength_1.xml b/packages/SettingsLib/res/drawable/ic_wifi_call_strength_1.xml
new file mode 100644
index 000000000000..493912b0ddcd
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_wifi_call_strength_1.xml
@@ -0,0 +1,35 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.17,14.84l-3.26,-0.65c-0.33,-0.07 -0.67,0.04 -0.9,0.27l-2.62,2.62c-2.75,-1.49 -5.01,-3.75 -6.5,-6.5l2.62,-2.62c0.24,-0.24 0.34,-0.58 0.27,-0.9L9.13,3.8C9.04,3.34 8.63,3 8.15,3H4C3.44,3 2.97,3.47 3,4.03c0.17,2.91 1.04,5.63 2.43,8.01c1.57,2.69 3.81,4.93 6.5,6.5c2.38,1.39 5.1,2.26 8.01,2.43c0.56,0.03 1.03,-0.44 1.03,-1v-4.15C20.97,15.34 20.64,14.93 20.17,14.84z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.05,9.59C17.69,9.22 17.19,9 16.64,9c-0.55,0 -1.05,0.22 -1.41,0.59L16.64,11L18.05,9.59z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.64,7.5c0.96,0 1.84,0.39 2.47,1.03l1.42,-1.42c-1,-1 -2.37,-1.61 -3.89,-1.61c-1.52,0 -2.89,0.62 -3.89,1.61l1.42,1.42C14.8,7.89 15.67,7.5 16.64,7.5z"
+ android:fillAlpha="0.3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.64,4c1.93,0 3.68,0.79 4.95,2.05L23,4.64C21.37,3.01 19.12,2 16.64,2c-2.49,0 -4.74,1.01 -6.36,2.64l1.42,1.42C12.96,4.79 14.71,4 16.64,4z"
+ android:fillAlpha="0.3"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_wifi_call_strength_2.xml b/packages/SettingsLib/res/drawable/ic_wifi_call_strength_2.xml
new file mode 100644
index 000000000000..af677fb604ae
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_wifi_call_strength_2.xml
@@ -0,0 +1,34 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.17,14.84l-3.26,-0.65c-0.33,-0.07 -0.67,0.04 -0.9,0.27l-2.62,2.62c-2.75,-1.49 -5.01,-3.75 -6.5,-6.5l2.62,-2.62c0.24,-0.24 0.34,-0.58 0.27,-0.9L9.13,3.8C9.04,3.34 8.63,3 8.15,3H4C3.44,3 2.97,3.47 3,4.03c0.17,2.91 1.04,5.63 2.43,8.01c1.57,2.69 3.81,4.93 6.5,6.5c2.38,1.39 5.1,2.26 8.01,2.43c0.56,0.03 1.03,-0.44 1.03,-1v-4.15C20.97,15.34 20.64,14.93 20.17,14.84z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.05,9.59C17.69,9.22 17.19,9 16.64,9c-0.55,0 -1.05,0.22 -1.41,0.59L16.64,11L18.05,9.59z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.64,7.5c0.96,0 1.84,0.39 2.47,1.03l1.42,-1.42c-1,-1 -2.37,-1.61 -3.89,-1.61c-1.52,0 -2.89,0.62 -3.89,1.61l1.42,1.42C14.8,7.89 15.67,7.5 16.64,7.5z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.64,4c1.93,0 3.68,0.79 4.95,2.05L23,4.64C21.37,3.01 19.12,2 16.64,2c-2.49,0 -4.74,1.01 -6.36,2.64l1.42,1.42C12.96,4.79 14.71,4 16.64,4z"
+ android:fillAlpha="0.3"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_wifi_call_strength_3.xml b/packages/SettingsLib/res/drawable/ic_wifi_call_strength_3.xml
new file mode 100644
index 000000000000..68b39da82ae5
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_wifi_call_strength_3.xml
@@ -0,0 +1,33 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.17,14.84l-3.26,-0.65c-0.33,-0.07 -0.67,0.04 -0.9,0.27l-2.62,2.62c-2.75,-1.49 -5.01,-3.75 -6.5,-6.5l2.62,-2.62c0.24,-0.24 0.34,-0.58 0.27,-0.9L9.13,3.8C9.04,3.34 8.63,3 8.15,3H4C3.44,3 2.97,3.47 3,4.03c0.17,2.91 1.04,5.63 2.43,8.01c1.57,2.69 3.81,4.93 6.5,6.5c2.38,1.39 5.1,2.26 8.01,2.43c0.56,0.03 1.03,-0.44 1.03,-1v-4.15C20.97,15.34 20.64,14.93 20.17,14.84z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.05,9.59C17.69,9.22 17.19,9 16.64,9c-0.55,0 -1.05,0.22 -1.41,0.59L16.64,11L18.05,9.59z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.64,7.5c0.96,0 1.84,0.39 2.47,1.03l1.42,-1.42c-1,-1 -2.37,-1.61 -3.89,-1.61c-1.52,0 -2.89,0.62 -3.89,1.61l1.42,1.42C14.8,7.89 15.67,7.5 16.64,7.5z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.64,4c1.93,0 3.68,0.79 4.95,2.05L23,4.64C21.37,3.01 19.12,2 16.64,2c-2.49,0 -4.74,1.01 -6.36,2.64l1.42,1.42C12.96,4.79 14.71,4 16.64,4z"/>
+</vector>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 889980a05bfa..4b253c459b83 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Skep tans nuwe gebruiker …"</string>
<string name="user_nickname" msgid="262624187455825083">"Bynaam"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Voeg gas by"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Beëindig gastesessie"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Verwyder gas"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gas"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Neem \'n foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Kies \'n prent"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Data, drie stawe."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Datasein vol."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet is ontkoppel."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet gekoppel."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Geen oproepe nie."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index c41e4d5d5869..4982f820ff37 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -554,7 +554,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="4754204715192830850">"የእንግዳ ክፍለ-ጊዜ ጨርስ"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"የውሂብ ሦስት አሞሌዎች።"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"የውሂብ አመልካች ሙሉ ነው።"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ኤተርኔት ተነቅሏል።"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"ኤተርኔት ተገናኝቷል።"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ኢተርኔት።"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"መደወል የለም።"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index f8d1d576c759..5eaefd02483b 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -558,7 +558,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="4754204715192830850">"إنهاء جلسة الضيف"</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>
@@ -595,5 +595,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"إشارة البيانات تتكون من ثلاثة أشرطة."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"إشارة البيانات كاملة."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"‏تم قطع اتصال Ethernet."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"‏تم إنشاء اتصال Ethernet."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"إيثرنت"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"لا يتم الاتصال."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index c6078f898c3e..571f7d00ef89 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -554,7 +554,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="4754204715192830850">"অতিথিৰ ছেশ্বন সমাপ্ত কৰক"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"ডেটা ছিংগনেলত তিনিডাল দণ্ড আছে।"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"ডেটা ছিগনেল পূৰা আছে।"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ইথাৰনেট সংযোগ বিচ্ছিন্ন হৈছে।"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"ইথাৰনেট সংযোগ হৈছে।"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ইথাৰনেট।"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"কল কৰা নহয়"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index d3e0a25593e4..36a13f50fe11 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yeni istifadəçi yaradılır…"</string>
<string name="user_nickname" msgid="262624187455825083">"Ləqəb"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Qonaq əlavə edin"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Qonaq sessiyasını bitirin"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Qonağı silin"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Qonaq"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Foto çəkin"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Şəkil seçin"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Data üç xətdir."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Data siqnalı tamdır."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet bağlantısı kəsilib."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet qoşuludur."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Zəng yoxdur."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 85412224e995..d7cc909fe15f 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -555,7 +555,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Pravi se novi korisnik…"</string>
<string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Završi sesiju gosta"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Slikaj"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string>
@@ -592,5 +592,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Signal za podatke od tri crte."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Signal za podatke je najjači."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Veza sa eternetom je prekinuta."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Eternet je povezan."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Eternet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Bez pozivanja."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index aab600f3330e..01201b34f6a8 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/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="4754204715192830850">"Завяршыць гасцявы сеанс"</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>
@@ -593,5 +593,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"3 планкі дадзеных."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Поўны сігнал перадачы дадзеных."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet адлучаны."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet падлучаны."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Ніякіх выклікаў."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 92f293599f97..00f5e710b727 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -554,7 +554,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="4754204715192830850">"Прекратяване на сесията като гост"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Данните са с три чертички."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Сигналът за данни е пълен."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Връзката с Ethernet е прекратена."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Установена е връзка с Ethernet."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Без обаждания."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index b40e24e3a672..43bb7581026c 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -554,7 +554,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="4754204715192830850">"গেস্ট সেশন শেষ করুন"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"তিন দন্ড ডেটার সংকেত৷"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"পূর্ণ ডেটার সংকেত রয়েছে৷"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ইথারনেটের সংযোগ বিচ্ছিন্ন হয়েছে৷"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"ইথারনেট সংযুক্ত হয়েছে৷"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ইথারনেট।"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"কল করবেন না।"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index cec2e4533878..dacacfbb142d 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -555,7 +555,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Kreiranje novog korisnika…"</string>
<string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Završi sesiju gosta"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Snimite fotografiju"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberite sliku"</string>
@@ -592,5 +592,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Prijenos podataka na tri crtice."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Signal za prijenos podataka pun."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Veza sa Ethernetom je prekinuta."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet je spojen."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Nema pozivanja."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 6134da873a0e..17482ed18963 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"S\'està creant l\'usuari…"</string>
<string name="user_nickname" msgid="262624187455825083">"Àlies"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Afegeix un convidat"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Finalitza la sessió de convidat"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Suprimeix el convidat"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidat"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Fes una foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Tria una imatge"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Senyal de dades: tres barres."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Senyal de dades: complet."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"S\'ha desconnectat l\'Ethernet."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet connectada"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Sense trucades."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index f29a3dd54ff8..ddd5c523c8f9 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -556,7 +556,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Vytváření nového uživatele…"</string>
<string name="user_nickname" msgid="262624187455825083">"Přezdívka"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Přidat hosta"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Ukončení relace hosta"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranit hosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Host"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Pořídit fotku"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrat obrázek"</string>
@@ -593,5 +593,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Tři čárky signálu datové sítě."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Plný signál datové sítě."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Síť ethernet je odpojena."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Síť ethernet je připojena."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Bez volání."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index d92d41d536bc..421ed742bd23 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -554,7 +554,7 @@
<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_exit_guest" msgid="4754204715192830850">"Afslut gæstesessionen"</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>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Vælg et billede"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Data tre bjælker."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Datasignal fuldt."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet er ikke tilsluttet."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet er tilsluttet."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Opkald er deaktiveret."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index ac05b620539c..7cb994cb06de 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Neuer Nutzer wird erstellt…"</string>
<string name="user_nickname" msgid="262624187455825083">"Alias"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Gast hinzufügen"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Gastsitzung beenden"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Gast entfernen"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Foto machen"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Bild auswählen"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Datensignal - drei Balken"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Volle Datensignalstärke"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet nicht verbunden"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet verbunden"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Keine Anrufe."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 0b11d69132c5..7a73ccdf2c63 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -554,7 +554,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="4754204715192830850">"Λήξη περιόδου σύνδεσης επισκέπτη"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Τρεις γραμμές δεδομένων."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Πλήρες σήμα δεδομένων."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Το Ethernet αποσυνδέθηκε."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Το Ethernet συνδέθηκε."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Χωρίς κλήσεις."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index b98c4b8968e9..62419538309b 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"End Guest session"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Data three bars."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Data signal full."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet disconnected."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet connected."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"No calling."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index aa0d3f1ddf18..198fee4b58af 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"End Guest session"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Data three bars."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Data signal full."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet disconnected."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet connected."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"No calling."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index b98c4b8968e9..62419538309b 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"End Guest session"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Data three bars."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Data signal full."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet disconnected."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet connected."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"No calling."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index b98c4b8968e9..62419538309b 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"End Guest session"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Data three bars."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Data signal full."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet disconnected."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet connected."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"No calling."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index c01f3a05645e..27cd8b34889e 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‎‏‏‏‎Creating new user…‎‏‎‎‏‎"</string>
<string name="user_nickname" msgid="262624187455825083">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‏‎‏‎‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‎‏‏‏‎‏‏‎Nickname‎‏‎‎‏‎"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‎Add guest‎‏‎‎‏‎"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‏‏‎‎‏‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎End guest session‎‏‎‎‏‎"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‎‎‎‎Remove guest‎‏‎‎‏‎"</string>
<string name="guest_nickname" msgid="6332276931583337261">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‎‏‏‎‏‎Guest‎‏‎‎‏‎"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎‎Take a photo‎‏‎‎‏‎"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎Choose an image‎‏‎‎‏‎"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‏‎‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‏‏‎‏‎‎‏‏‎‏‎‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎Data three bars.‎‏‎‎‏‎"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‏‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‏‎‎‏‏‎‎‎‏‎‎‎‏‏‎‎‎‎‎‎‎‎‏‎Data signal full.‎‏‎‎‏‎"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‎‎‏‎‎‎‏‎‎‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‏‏‏‎‎‏‏‏‎‏‎‎‎‎‏‎‏‎‏‎‎‎‏‎Ethernet disconnected.‎‏‎‎‏‎"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‎Ethernet connected.‎‏‎‎‏‎"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‎‎‏‎‏‏‎‎‎‏‏‏‎‏‏‎Ethernet.‎‏‎‎‏‎"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‎‎‎‏‏‏‎‎‎‏‎‏‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‏‎‏‎‎‎‏‎‎‎‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‎‎No calling.‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 1da8e37186c6..5e5b39a1bd0a 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario nuevo…"</string>
<string name="user_nickname" msgid="262624187455825083">"Sobrenombre"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Agregar invitado"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Finalizar sesión de invitado"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Tomar una foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Elegir una imagen"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Tres barras de datos"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Señal de datos completa"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet desconectada"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet conectada"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Sin llamadas."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 54226ce0fe5c..8d791771bf05 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario…"</string>
<string name="user_nickname" msgid="262624187455825083">"Apodo"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Finalizar sesión de invitado"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Hacer foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Seleccionar una imagen"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Tres barras de datos"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Señal de datos al máximo"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Conexión Ethernet desconectada."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Conexión Ethernet conectada."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Sin llamadas."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 4c747e36ef52..18ee62371ef6 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Uue kasutaja loomine …"</string>
<string name="user_nickname" msgid="262624187455825083">"Hüüdnimi"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Lisa külaline"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Lõpeta külastajaseanss"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Eemalda külaline"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Külaline"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Pildistage"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Valige pilt"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Andmeside: kolm pulka."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Andmesignaal on tugev."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Etherneti-ühendus on katkestatud."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Etherneti-ühendus on loodud."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Helistamine pole võimalik."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 6016cd20ff37..71be593a055b 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Beste erabiltzaile bat sortzen…"</string>
<string name="user_nickname" msgid="262624187455825083">"Goitizena"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Gehitu gonbidatua"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Amaitu gonbidatuentzako saioa"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Kendu gonbidatua"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gonbidatua"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Atera argazki bat"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Aukeratu irudi bat"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Datu-seinaleak hiru barra ditu."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Datu-seinale osoa."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet bidezko konexioa eten da."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet bidez konektatu da."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Deirik ez."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index e85557000a61..81a2cf888d1f 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -554,7 +554,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="4754204715192830850">"پایان دادن به جلسه مهمان"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"سه نوار برای داده."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"قدرت سیگنال داده کامل است."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"اترنت قطع شد."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"اترنت متصل شد."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"اترنت."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"تماس گرفته نشود."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 724e52a95927..0703c450a2ed 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Luodaan uutta käyttäjää…"</string>
<string name="user_nickname" msgid="262624187455825083">"Lempinimi"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Lopeta Vierailija-käyttökerta"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Vieras"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Ota kuva"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Valitse kuva"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Datasignaali - kolme palkkia"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Vahva kuuluvuus."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet on irrotettu."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet on yhdistetty."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Ei puheluita."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 74c3005b0228..5b8c93880446 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Créer un utilisateur…"</string>
<string name="user_nickname" msgid="262624187455825083">"Pseudo"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Mettre fin à la session d\'invité"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Sélectionner une image"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Signal bon"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Signal excellent"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet déconnecté."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet connecté."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Aucun appel."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index c9651ce570c5..b025357a4642 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Création d\'un nouvel utilisateur…"</string>
<string name="user_nickname" msgid="262624187455825083">"Pseudo"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Fermer la session Invité"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choisir une image"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Signal bon"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Signal excellent"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet déconnecté"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet connecté"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Pas d\'appels."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index f499f587a850..d68595e98e04 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario novo…"</string>
<string name="user_nickname" msgid="262624187455825083">"Alcume"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Engadir convidado"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Finalizar sesión de invitado"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar convidado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Tirar foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Escoller imaxe"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Tres barras de sinal de datos"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Sinal de datos: completo"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Desconectouse a Ethernet."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Conectouse a Ethernet."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Sen chamadas."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 23ee1de46484..e151ddb428d8 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -554,7 +554,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="4754204715192830850">"અતિથિ સત્ર સમાપ્ત કરો"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"ડેટા ત્રણ બાર."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"ડેટા સિગ્નલ પૂર્ણ."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ઇથરનેટ ડિસ્કનેક્ટ થયું."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"ઇથરનેટ કનેક્ટ થયું."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ઇથરનેટ."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"કોઈ કૉલિંગ નહીં."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index a2fc43c250cc..beab7bdcc08e 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -554,7 +554,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="4754204715192830850">"मेहमान के तौर पर ब्राउज़ करने का सेशन खत्म करें"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"डेटा तीन बार."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"डेटा सि‍ग्‍नल पूरा."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ईथरनेट डिस्‍कनेक्‍ट किया गया."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"ईथरनेट कनेक्‍ट किया गया."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ईथरनेट."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"वॉइस कॉल की सुविधा उपलब्ध नहीं है."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 396051d51a09..8989cc9c69db 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -555,7 +555,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Izrada novog korisnika…"</string>
<string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodavanje gosta"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Završi gostujuću sesiju"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Uklanjanje gosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Fotografiraj"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string>
@@ -592,5 +592,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Podatkovni signal tri stupca."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Podatkovni signal pun."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Prekinuta je veza s ethernetom."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Uspostavljena je veza s ethernetom."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Bez poziva."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 0c9bc54c0861..7c5a1a004568 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Új felhasználó létrehozása…"</string>
<string name="user_nickname" msgid="262624187455825083">"Becenév"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Vendég hozzáadása"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"A vendég munkamenet befejezése"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Vendég munkamenet eltávolítása"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Vendég"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Fotó készítése"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Kép kiválasztása"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Adat három sáv."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Adatjel teljes."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet leválasztva."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet csatlakoztatva."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Nem kezdeményezhet hanghívást."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 56c93b635e20..6684d3507c77 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -554,7 +554,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="4754204715192830850">"Ավարտել հյուրի աշխատաշրջանը"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Տվյալների երեք գիծ:"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Տվյալների ազդանշանը լրիվ է:"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet-ը անջատված է:"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet-ը կապակցված է:"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet։"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Զանգել հնարավոր չէ։"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 0440f4796143..075444cd04f8 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Membuat pengguna baru …"</string>
<string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Tambahkan tamu"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Akhiri sesi tamu"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Hapus tamu"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Tamu"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih gambar"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Data tiga batang."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Sinyal data penuh."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet terputus."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet tersambung."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Tidak ada panggilan."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 0bb294f5869b..b079f4d9b75e 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Stofnar nýjan notanda…"</string>
<string name="user_nickname" msgid="262624187455825083">"Gælunafn"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Bæta gesti við"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Ljúka gestalotu"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Fjarlægja gest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gestur"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Taka mynd"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Velja mynd"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Sendistyrkur gagnatengingar er þrjú strik."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Fullur sendistyrkur gagnatengingar."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet aftengt."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet tengt."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Engin símtöl."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 54049d79a76b..bf525c535583 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creazione nuovo utente…"</string>
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Aggiungi ospite"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Termina sessione Ospite"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Rimuovi ospite"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Ospite"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Scatta una foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Scegli un\'immagine"</string>
@@ -577,7 +577,7 @@
<string name="data_connection_4g_plus" msgid="5194902328408751020">"4G+"</string>
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
- <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"Wi-Fi operatore"</string>
+ <string name="data_connection_carrier_wifi" msgid="2250268321065848954">"CWF"</string>
<string name="cell_data_off_content_description" msgid="2280700839891636498">"Dati mobili disattivati"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Non impostato per l\'utilizzo dei dati"</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Nessun telefono."</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Dati: tre barre."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Massimo segnale dati."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Connessione Ethernet annullata."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Connessione Ethernet stabilita."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Chiamate non disponibili."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 6cc4347946f4..9055e77b08dd 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/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="4754204715192830850">"הפסקת הגלישה כאורח"</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>
@@ -593,5 +593,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"שלושה פסים של נתונים."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"אות הנתונים מלא."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"אתרנט מנותק."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"אתרנט מחובר."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"אתרנט."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"אין שיחות."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index ec674adb75d0..7fa0df93e2ce 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -554,7 +554,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="4754204715192830850">"ゲスト セッションを終了する"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"データ信号:レベル3"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"データ信号:フル"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"イーサネット接続を解除しました。"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"イーサネットに接続しました。"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"イーサネット。"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"通話なし。"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 30ab3b49afdd..7dd1eee09f83 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -554,7 +554,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="4754204715192830850">"სტუმრის სესიის დასრულება"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"მონაცემების გადაცემა: სამი ზოლი"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"მონაცემთა გადაცემის საიმედო სიგნალი."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet კავშირი შეწყვეტილია."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet დაკავშირებულია."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"ზარების გარეშე."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index ef7852710af4..59b1d63af04f 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -554,7 +554,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="4754204715192830850">"Қонақ сеансын аяқтау"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Дерекқор үш баған."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Дерекқор сигналы толы."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet ажыратылған."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet қосылған."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Қоңырау шалу мүмкін емес."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index ed59c747f8fd..b01839d2280f 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -554,7 +554,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="4754204715192830850">"បញ្ចប់វគ្គភ្ញៀវ"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"ទិន្នន័យ​បី​កាំ។"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"សញ្ញា​ទិន្នន័យ​ពេញ។"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"បានផ្តាច់អ៊ីសឺរណិត។"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"បានភ្ជាប់អ៊ីសឺរណិត។"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"អ៊ីសឺរណិត។"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"គ្មាន​ការហៅ​ទូរសព្ទទេ​។"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 995dc8632e5c..217c917f1d2b 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -554,7 +554,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="4754204715192830850">"ಅತಿಥಿ ಸೆಷನ್ ಅಂತ್ಯಗೊಳಿಸಿ"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"ಡೇಟಾ ಮೂರು ಪಟ್ಟಿಗಳು."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"ಡೇಟಾ ಸಂಕೇತ ತುಂಬಿದೆ."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ಇಥರ್ನೆಟ್ ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"ಇಥರ್ನೆಟ್ ಸಂಪರ್ಕಗೊಳಿಸಲಾಗಿದೆ."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ಇಥರ್ನೆಟ್."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"ಕರೆ ಮಾಡಲಾಗುವುದಿಲ್ಲ."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 7863d481295e..0b8c71d0d9cd 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -554,7 +554,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="4754204715192830850">"게스트 세션 종료"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"데이터 신호 막대가 세 개입니다."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"데이터 신호가 강합니다."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"이더넷에서 연결 해제되었습니다."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"이더넷에 연결되었습니다."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"이더넷에 연결되었습니다."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"통화 모드가 없습니다."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ky/arrays.xml b/packages/SettingsLib/res/values-ky/arrays.xml
index f9a4d98b9319..993d81e570b5 100644
--- a/packages/SettingsLib/res/values-ky/arrays.xml
+++ b/packages/SettingsLib/res/values-ky/arrays.xml
@@ -86,7 +86,7 @@
<item msgid="8147982633566548515">"карта14"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Тутум тандаганды колдонуу (демейки)"</item>
+ <item msgid="2494959071796102843">"Система тандаганды колдонуу (демейки)"</item>
<item msgid="4055460186095649420">"SBC"</item>
<item msgid="720249083677397051">"AAC"</item>
<item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
@@ -96,7 +96,7 @@
<item msgid="6486050771049225">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_TWSP">aptX™ TWS+</xliff:g> аудио"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Тутум тандаганды колдонуу (демейки)"</item>
+ <item msgid="8868109554557331312">"Система тандаганды колдонуу (демейки)"</item>
<item msgid="9024885861221697796">"SBC"</item>
<item msgid="4688890470703790013">"AAC"</item>
<item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
@@ -106,38 +106,38 @@
<item msgid="6486050771049481">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_TWSP">aptX™ TWS+</xliff:g> аудио"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
- <item msgid="926809261293414607">"Тутум тандаганды колдонуу (демейки)"</item>
+ <item msgid="926809261293414607">"Система тандаганды колдонуу (демейки)"</item>
<item msgid="8003118270854840095">"44,1 кГц"</item>
<item msgid="3208896645474529394">"48,0 кГц"</item>
<item msgid="8420261949134022577">"88,2 кГц"</item>
<item msgid="8887519571067543785">"96,0 кГц"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
- <item msgid="2284090879080331090">"Тутум тандаганды колдонуу (демейки)"</item>
+ <item msgid="2284090879080331090">"Система тандаганды колдонуу (демейки)"</item>
<item msgid="1872276250541651186">"44,1 кГц"</item>
<item msgid="8736780630001704004">"48,0 кГц"</item>
<item msgid="7698585706868856888">"88,2 кГц"</item>
<item msgid="8946330945963372966">"96,0 кГц"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
- <item msgid="2574107108483219051">"Тутум тандаганды колдонуу (демейки)"</item>
+ <item msgid="2574107108483219051">"Система тандаганды колдонуу (демейки)"</item>
<item msgid="4671992321419011165">"16 бит/үлгү"</item>
<item msgid="1933898806184763940">"24 бит/үлгү"</item>
<item msgid="1212577207279552119">"32 бит/үлгү"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
- <item msgid="9196208128729063711">"Тутум тандаганды колдонуу (демейки)"</item>
+ <item msgid="9196208128729063711">"Система тандаганды колдонуу (демейки)"</item>
<item msgid="1084497364516370912">"16 бит/үлгү"</item>
<item msgid="2077889391457961734">"24 бит/үлгү"</item>
<item msgid="3836844909491316925">"32 бит/үлгү"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_channel_mode_titles">
- <item msgid="3014194562841654656">"Тутум тандаганды колдонуу (демейки)"</item>
+ <item msgid="3014194562841654656">"Система тандаганды колдонуу (демейки)"</item>
<item msgid="5982952342181788248">"Моно"</item>
<item msgid="927546067692441494">"Стерео"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
- <item msgid="1997302811102880485">"Тутум тандаганды колдонуу (демейки)"</item>
+ <item msgid="1997302811102880485">"Система тандаганды колдонуу (демейки)"</item>
<item msgid="8005696114958453588">"Моно"</item>
<item msgid="1333279807604675720">"Стерео"</item>
</string-array>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 45d05c6372ce..cad00a38e89d 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -143,7 +143,7 @@
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
<string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Алынып салынган колдонмолор"</string>
<string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Өчүрүлгөн колдонмолор жана колдонуучулар"</string>
- <string name="data_usage_ota" msgid="7984667793701597001">"Тутум жаңыртуулары"</string>
+ <string name="data_usage_ota" msgid="7984667793701597001">"Системанын жаңыртуулары"</string>
<string name="tether_settings_title_usb" msgid="3728686573430917722">"USB модем"</string>
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"Wi-Fi байланыш түйүнү"</string>
<string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Bluetooth модем"</string>
@@ -162,7 +162,7 @@
<string name="tts_default_pitch_title" msgid="6988592215554485479">"Негизги тон"</string>
<string name="tts_default_pitch_summary" msgid="9132719475281551884">"Синтезделген кептин интонациясына таасирин тийгизет"</string>
<string name="tts_default_lang_title" msgid="4698933575028098940">"Тил"</string>
- <string name="tts_lang_use_system" msgid="6312945299804012406">"Тутум тилин колдонуу"</string>
+ <string name="tts_lang_use_system" msgid="6312945299804012406">"Системанын тилин колдонуу"</string>
<string name="tts_lang_not_selected" msgid="7927823081096056147">"Тил тандалган жок"</string>
<string name="tts_default_lang_summary" msgid="9042620014800063470">"Текстти окуй турган тилди тандоо"</string>
<string name="tts_play_example_title" msgid="1599468547216481684">"Үлгүнү угуу"</string>
@@ -483,7 +483,7 @@
<string name="retail_demo_reset_next" msgid="3688129033843885362">"Кийинки"</string>
<string name="retail_demo_reset_title" msgid="1866911701095959800">"Сырсөз талап кылынат"</string>
<string name="active_input_method_subtypes" msgid="4232680535471633046">"Жигердүү киргизүү ыкмалары"</string>
- <string name="use_system_language_to_select_input_method_subtypes" msgid="4865195835541387040">"Тутум тилдерин колдонуу"</string>
+ <string name="use_system_language_to_select_input_method_subtypes" msgid="4865195835541387040">"Системанын тилдерин колдонуу"</string>
<string name="failed_to_open_app_settings_toast" msgid="764897252657692092">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> тууралоолору ачылган жок"</string>
<string name="ime_security_warning" msgid="6547562217880551450">"Бул киргизүү ыкмасы сиз терген бардык тексттер, сырсөздөр жана кредиттик карталар сыяктуу жеке маалыматтарды кошо чогултушу мүмкүн. Бул <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> колдонмосу менен байланыштуу. Ушул киргизүү ыкма колдонулсунбу?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"Эскертүү: Өчүрүп-күйгүзгөндөн кийин, бул колдонмо телефондун кулпусу ачылмайынча иштебейт"</string>
@@ -554,7 +554,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="4754204715192830850">"Конок сеансын бүтүрүү"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Мобилдик интернеттин сигналы үч таякча."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Мобилдик интернеттин сигналы толук."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet ажырады."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet туташты."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Чалуу жок."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index ca6e06c13892..5bd297e310f5 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -554,7 +554,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="4754204715192830850">"ສິ້ນສຸດເຊດຊັນແຂກ"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"ຂໍ້ມູນສາມຂີດ."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"ສັນ​ຍານຂໍ້ມູນ​ເຕັມ."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ອີ​ເທີ​ເນັດ​ຕັດ​ເຊື່ອມ​ຕໍ່​ແລ້ວ."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"ອີ​ເທີ​ເນັດ​ເຊື່ອມ​ຕໍ່​ແລ້ວ."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ອີເທີເນັດ."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"ບໍ່ສາມາດໂທສຽງໄດ້."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 173b57eaba57..8c7485f29af7 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -556,7 +556,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Kuriamas naujas naudotojas…"</string>
<string name="user_nickname" msgid="262624187455825083">"Slapyvardis"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Pridėti svečią"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Baigti svečio sesiją"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Pašalinti svečią"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Svečias"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Fotografuoti"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Pasirinkti vaizdą"</string>
@@ -593,5 +593,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Trys duomenų juostos."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Stiprus duomenų signalas."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Atsijungta nuo eterneto."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Prijungta prie eterneto."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Eternetas."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Nekviečiama."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index a8bd2cc34a51..d8020c7c1d05 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -555,7 +555,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Notiek jauna lietotāja izveide…"</string>
<string name="user_nickname" msgid="262624187455825083">"Segvārds"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Pievienot viesi"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Beigt viesa sesiju"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Noņemt viesi"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Viesis"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Uzņemt fotoattēlu"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Izvēlēties attēlu"</string>
@@ -592,5 +592,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Dati: trīs joslas."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Pilna piekļuve datu signālam."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Pārtraukts savienojums ar tīklu Ethernet."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Izveidots savienojums ar tīklu Ethernet."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Tīkls Ethernet"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Zvanīšana nav pieejama."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 0ef3f0eee6b5..e4c8425495b7 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -554,7 +554,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="4754204715192830850">"Заврши ја гостинската сесија"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Податоци три цртички."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Сигналот за податоци е исполнет."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Етернетот е исклучен."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Етернетот е поврзан."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Етернет."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Без повици."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index e6289ae3591e..28422c930683 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -554,7 +554,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="4754204715192830850">"അതിഥി സെഷൻ അവസാനിപ്പിക്കുക"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"ഡാറ്റ മൂന്ന് ബാർ."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"ഡാറ്റ സിഗ്‌നൽ പൂർണ്ണമാണ്."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ഇതർനെറ്റ് വിച്ഛേദിച്ചു."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"ഇതർനെറ്റ് കണക്റ്റുചെയ്‌തു."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ഇതർനെറ്റ്."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"വോയ്‌സ് കോൾ ലഭ്യമല്ല."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index ead712c30b89..0d78ea9ac2e3 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -554,7 +554,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="4754204715192830850">"Зочны сургалтыг дуусгах"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Дата гурван баганатай."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Дата дохио дүүрэн."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet саллаа."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet холбогдсон."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Этернэт."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Дуудлага байхгүй."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index edcc71b17e8a..d27f7051d1f1 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -554,7 +554,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="4754204715192830850">"अतिथी सत्र संपवा"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"डेटा तीन बार."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"डेटा सिग्नल पूर्ण."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"इथरनेट डिस्कनेक्ट केले."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"इथरनेट कनेक्ट केले."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"इथरनेट."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"कॉलिंग उपलब्ध नाही."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 8a14437e3647..c5a2c6276318 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Mencipta pengguna baharu…"</string>
<string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Tambah tetamu"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Tamatkan sesi tetamu"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Alih keluar tetamu"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Tetamu"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih imej"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Data tiga bar."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Isyarat data penuh."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet diputuskan sambungan."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet disambungkan."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Tiada panggilan."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 377c8b241144..ed34f7d3dd9e 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -554,7 +554,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="4754204715192830850">"ဧည့်သည်ဆက်ရှင်ကို အဆုံးသတ်ရန်"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"ဒေတာသုံးဘား။"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"ဒေတာထုတ်လွှင့်မှုအပြည့်ဖမ်းမိခြင်း"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet နှင့်ချိတ်ဆက်မှုပြတ်တောက်"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet ချိတ်ဆက်ထား။"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"အီသာနက်။"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"ခေါ်ဆိုမှု မရှိပါ။"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index f0e773b1833d..5366e7e13843 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Oppretter en ny bruker …"</string>
<string name="user_nickname" msgid="262624187455825083">"Kallenavn"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Legg til en gjest"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Avslutt gjesteøkten"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gjesten"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gjest"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Ta et bilde"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Velg et bilde"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Data – tre stolper."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Datasignal er fullt."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet er frakoblet."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet er tilkoblet."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Ingen ringing."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 0bde70fe209d..cde7b8033509 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -554,7 +554,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="4754204715192830850">"अतिथिको सत्र अन्त्य गर्नुहोस्"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"डेटा तिन बाधाहरू।"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"डेटा संकेत पूर्ण।"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"इथरनेट विच्छेद भयो।"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"इथरनेट जोडियो।"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"इथरनेट।"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"कल गर्ने सुविधा उपलब्ध छैन।"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 2bc1e8c0f7f0..bb1402a93227 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Nieuwe gebruiker maken…"</string>
<string name="user_nickname" msgid="262624187455825083">"Bijnaam"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Gast toevoegen"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Gastsessie beëindigen"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Gast verwijderen"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Foto maken"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Afbeelding kiezen"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Gegevens: drie streepjes."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Gegevenssignaal is op volle sterkte."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernetverbinding verbroken."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet verbonden."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Geen gesprekken."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 787c87182cd7..90fc8bce9339 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -554,7 +554,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="4754204715192830850">"ଅତିଥି ସେସନ୍ ଶେଷ କରନ୍ତୁ"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"ଡାଟାର ତିନୋଟି ବାର୍‍ ଅଛି।"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"ଡାଟା ସିଗ୍ନାଲ୍ ପୂର୍ଣ୍ଣ ଅଛି।"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ଇଥରନେଟ୍‍ ବିଚ୍ଛିନ୍ନ ହୋଇଛି।"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"ଇଥରନେଟ୍‍ ସଂଯୁକ୍ତ ହୋଇଛି।"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ଇଥରନେଟ୍।"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"କୌଣସି କଲିଂ ନାହିଁ।"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 9483e060081c..4c20e196b1bb 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -554,7 +554,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="4754204715192830850">"ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਸਮਾਪਤ ਕਰੋ"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">" ਡਾਟਾ ਤਿੰਨ ਬਾਰ।"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">" ਡਾਟਾ ਸਿਗਨਲ ਪੂਰਾ।"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ਈਥਰਨੈੱਟ ਡਿਸਕਨੈਕਟ ਹੋ ਗਿਆ।"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"ਈਥਰਨੈੱਟ ਕਨੈਕਟ ਹੋ ਗਿਆ।"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ਈਥਰਨੈੱਟ।"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"ਕਾਲਿੰਗ ਸੇਵਾ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index c7f1595c86d0..6d037d8e0aaf 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -556,7 +556,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Tworzę nowego użytkownika…"</string>
<string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gościa"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Kończenie sesji gościa"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Usuń gościa"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gość"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Zrób zdjęcie"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Wybierz obraz"</string>
@@ -593,5 +593,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Dane: trzy paski."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Dane: pełna moc sygnału."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Rozłączono z siecią Ethernet."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Połączono z siecią Ethernet."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Brak połączenia."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 881bccb90062..3feed99762ef 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Criando novo usuário…"</string>
<string name="user_nickname" msgid="262624187455825083">"Apelido"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Encerrar sessão de visitante"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Três barras do sinal de dados."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Sinal de dados cheio."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet desconectada."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet conectada."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Sem chamadas."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 94cad918cab8..a886aa9d4cb4 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"A criar novo utilizador…"</string>
<string name="user_nickname" msgid="262624187455825083">"Pseudónimo"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Terminar a sessão de convidado"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Três barras de dados."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Sinal de dados completo."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet desligada."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet ligada."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Sem chamadas."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 881bccb90062..3feed99762ef 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Criando novo usuário…"</string>
<string name="user_nickname" msgid="262624187455825083">"Apelido"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Encerrar sessão de visitante"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Três barras do sinal de dados."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Sinal de dados cheio."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet desconectada."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet conectada."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Sem chamadas."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 63d8b8f53f7f..7fbe6225bc9c 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -555,7 +555,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Se creează un utilizator nou…"</string>
<string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Încheiați sesiunea pentru invitați"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invitat"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Faceți o fotografie"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Alegeți o imagine"</string>
@@ -592,5 +592,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Semnal pentru date: trei bare."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Semnal pentru date: complet."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet deconectat."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet conectat."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Apelarea nu este disponibilă."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index ac43e493d73b..1669df6136f0 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/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="4754204715192830850">"Завершить гостевой сеанс"</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>
@@ -593,5 +593,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Сигнал передачи данных: три деления."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Надежный сигнал передачи данных."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Устройство отключено от Ethernet."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Устройство подключено к Ethernet."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Совершение вызовов невозможно."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 08fd9a03d7c4..1fdd968c9b7e 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -554,7 +554,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="4754204715192830850">"ආරාධිත සැසිය අවසන් කරන්න"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"දත්ත තීරු 3."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"දත්ත සංඥාව පිරී ඇත."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ඊතර්නෙට් විසන්ධි කරන ලදී."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"ඊතර්නෙට් සම්බන්ධ කරන ලදී."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ඊතර්නෙට්."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"ඇමතුම් නැත."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 8f05684e6de9..03b454ea6a14 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -556,7 +556,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Vytvára sa nový používateľ…"</string>
<string name="user_nickname" msgid="262624187455825083">"Prezývka"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Pridať hosťa"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Ukončiť reláciu hosťa"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Odobrať hosťa"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Hosť"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Odfotiť"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrať obrázok"</string>
@@ -593,5 +593,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Tri čiarky signálu dátovej siete."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Plný signál dátovej siete."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Sieť ethernet je odpojená"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Sieť ethernet je pripojená"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Žiadne volanie."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 3dccf3b53f9c..73b48187747c 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -556,7 +556,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ustvarjanje novega uporabnika …"</string>
<string name="user_nickname" msgid="262624187455825083">"Vzdevek"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodajanje gosta"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Končaj sejo gosta"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranitev gosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Fotografiranje"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Izberi sliko"</string>
@@ -593,5 +593,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Podatki s tremi črticami."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Podatkovni signal poln."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernetna povezava je prekinjena."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernetna povezava je vzpostavljena."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Klicanje ni mogoče."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index c09ad4c42fa4..a07bb2bc1cfd 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Po krijohet një përdorues i ri…"</string>
<string name="user_nickname" msgid="262624187455825083">"Pseudonimi"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Shto të ftuar"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Jepi fund sesionit të vizitorit"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Hiq të ftuarin"</string>
<string name="guest_nickname" msgid="6332276931583337261">"I ftuar"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Bëj një fotografi"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Zgjidh një imazh"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Sinjali është me tre vija."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Sinjali i të dhënave është i plotë."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Lidhja e eternetit u shkëput."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Lidhja e eternetit u lidh."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Eternet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Telefonatat nuk ofrohen"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index b5a91bfed1d0..0faaf83e65c9 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -555,7 +555,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="4754204715192830850">"Заврши сесију госта"</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>
@@ -592,5 +592,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Сигнал за податке од три црте."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Сигнал за податке је најјачи."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Веза са етернетом је прекинута."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Етернет је повезан."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Етернет."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Без позивања."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 16a1be606707..87606d0f7ed5 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Skapar ny användare …"</string>
<string name="user_nickname" msgid="262624187455825083">"Smeknamn"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Lägg till gäst"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Avsluta gästsession"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Ta bort gäst"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gäst"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Ta ett foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Välj en bild"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Data: tre staplar."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Datasignalen är full."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet har kopplats från."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet har anslutits."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Inga anrop."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 58896c87baaa..d479b0b174b8 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -554,7 +554,7 @@
<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_exit_guest" msgid="4754204715192830850">"Maliza kipindi cha 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>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Chagua picha"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Fito tatu za habari."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Ishara ya data imejaa."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethaneti imeondolewa."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethaneti imeunganishwa."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethaneti."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Huwezi kupiga wala kupokea simu."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 71cf7d41fcc4..247889bb4e83 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -554,7 +554,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="4754204715192830850">"விருந்தினர் அமர்வை நிறைவுசெய்"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"தரவு சிக்னல் மூன்று கோட்டில் உள்ளது."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"தரவு சிக்னல் முழுமையாக உள்ளது."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ஈத்தர்நெட் துண்டிக்கப்பட்டது."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"ஈத்தர்நெட் இணைக்கப்பட்டது."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ஈதர்நெட்."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"அழைப்பை மேற்கொள்ள முடியவில்லை."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index c3a65e702a28..b5d584f23d7e 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -554,7 +554,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="4754204715192830850">"గెస్ట్ సెషన్‌ను ముగించు"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"డేటా మూడు బార్లు."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"డేటా సిగ్నల్ సంపూర్ణంగా ఉంది."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ఈథర్‌నెట్ డిస్‌కనెక్ట్ చేయబడింది."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"ఈథర్‌నెట్ కనెక్ట్ చేయబడింది."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ఈథర్‌నెట్."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"కాలింగ్ మోడ్ ఆఫ్‌లో ఉంది."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 903accbdc7ff..dc1514f2777b 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -554,7 +554,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="4754204715192830850">"จบเซสชันผู้เยี่ยมชม"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"สัญญาณข้อมูลสามขีด"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"สัญญาณข้อมูลเต็ม"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ยกเลิกการเชื่อมต่ออีเทอร์เน็ตแล้ว"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"เชื่อมต่ออีเทอร์เน็ตแล้ว"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"อีเทอร์เน็ต"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"ไม่มีการโทร"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index b259201fe029..7c4ceec08dd1 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Gumagawa ng bagong user…"</string>
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Magdagdag ng bisita"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Tapusin ang session ng bisita"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Alisin ang bisita"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Bisita"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Kumuha ng larawan"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Pumili ng larawan"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Data na tatlong bar."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Puno ang signal ng data."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Nadiskonekta ang Ethernet."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Nakakonekta ang Ethernet."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Hindi makakatawag."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index a0bc3626d086..2b5dff15d101 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yeni kullanıcı oluşturuluyor…"</string>
<string name="user_nickname" msgid="262624187455825083">"Takma ad"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Misafir ekle"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Misafir oturumunu sonlandır"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Misafir oturumunu kaldır"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Misafir"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Fotoğraf çek"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Resim seç"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Veri sinyali üç çubuk."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Veri sinyali tam."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet bağlantısı kesildi."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet bağlandı."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Çağrı yok."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 509c8a602794..6fff40dab7f2 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/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="4754204715192830850">"Завершити сеанс у режимі \"Гість\""</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>
@@ -593,5 +593,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Три смужки сигналу даних."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Максимальний сигнал даних."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet відключено."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Ethernet підключено."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Виклики недоступні."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index e097ab2f5789..055d9b86e3e0 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -554,7 +554,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="4754204715192830850">"مہمان سیشن ختم کریں"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"ڈیٹا کے تین بارز۔"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"ڈیٹا سگنل بھرا ہوا ہے۔"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ایتھرنیٹ منقطع ہے۔"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"ایتھرنیٹ منسلک ہے۔"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ایتھرنیٹ۔"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"کوئی کالنگ نہیں ہے۔"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 8c2a95d5c316..d897ba697580 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -554,7 +554,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="4754204715192830850">"Mehmon seansini yakunlash"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmon rejimini 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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Ma’lumotlar uchta panelda."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Internet signali butun."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Qurilma Ethernet tarmog‘idan uzildi."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Qurilma Ethernet tarmog‘iga ulandi."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Chaqiruv imkonsiz."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 7a4d89bd4f22..c6b7915b927d 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Đang tạo người dùng mới…"</string>
<string name="user_nickname" msgid="262624187455825083">"Biệt hiệu"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Kết thúc phiên khách"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Xóa phiên khách"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Khách"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Chụp ảnh"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Chọn một hình ảnh"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Tín hiệu dữ liệu ba vạch."</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Tín hiệu dữ liệu đầy đủ."</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Đã ngắt kết nối Ethernet."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"Đã kết nối Ethernet."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Không thể gọi điện."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 7cd61fc5491c..05b27d14c4f9 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -554,7 +554,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="4754204715192830850">"结束访客会话"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"数据信号强度为三格。"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"数据信号满格。"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"以太网已断开连接。"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"以太网已连接。"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"以太网。"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"不启用通话。"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 458071cd8cb2..13398d74588c 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -554,7 +554,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="4754204715192830850">"結束訪客工作階段"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"數據網絡訊號強度為三格。"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"數據網絡訊號滿格。"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"以太網連接中斷。"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"已連接以太網。"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"以太網絡。"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"不啟用通話。"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 867ed8b61c17..fead5e50f7e4 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -554,7 +554,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="4754204715192830850">"結束訪客工作階段"</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>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"數據網路訊號強度三格。"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"數據網路訊號滿格。"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"未連上乙太網路。"</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"已連上乙太網路。"</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"乙太網路。"</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"不顯示在螢幕上。"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index e64dbd30dc82..79b8946f1e98 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -554,7 +554,7 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Idala umsebenzisi omusha…"</string>
<string name="user_nickname" msgid="262624187455825083">"Isiteketiso"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Engeza isivakashi"</string>
- <string name="guest_exit_guest" msgid="4754204715192830850">"Misa isikhathi sesihambeli"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Susa isihambeli"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Isihambeli"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Thatha isithombe"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Khetha isithombe"</string>
@@ -591,5 +591,6 @@
<string name="accessibility_data_three_bars" msgid="2813876214466722413">"Amabha amathathu edatha"</string>
<string name="accessibility_data_signal_full" msgid="1808301899314382337">"Igcwele i-signal yedatha"</string>
<string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"I-Ethernet inqanyuliwe."</string>
- <string name="accessibility_ethernet_connected" msgid="2093872142317190618">"I-Ethernet ixhunyiwe."</string>
+ <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"I-Ethernet."</string>
+ <string name="accessibility_no_calling" msgid="3540827068323895748">"Akukho ukwenza ikholi"</string>
</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 1079c0875b92..a8d187994232 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1403,7 +1403,7 @@
<!-- Label for adding a new guest in the user switcher [CHAR LIMIT=35] -->
<string name="guest_new_guest">Add guest</string>
<!-- Label for exiting and removing the guest session in the user switcher [CHAR LIMIT=35] -->
- <string name="guest_exit_guest">End guest session</string>
+ <string name="guest_exit_guest">Remove guest</string>
<!-- Name for the guest user [CHAR LIMIT=35] -->
<string name="guest_nickname">Guest</string>
@@ -1508,4 +1508,7 @@
<string name="accessibility_ethernet_disconnected">Ethernet disconnected.</string>
<!-- Content description of the Ethernet connection when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_ethernet_connected">Ethernet.</string>
+
+ <!-- Content description of the no calling for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_no_calling">No calling.</string>
</resources>
diff --git a/packages/SettingsLib/search/Android.bp b/packages/SettingsLib/search/Android.bp
index d398aa5c44ac..45746d9848df 100644
--- a/packages/SettingsLib/search/Android.bp
+++ b/packages/SettingsLib/search/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "SettingsLib-search",
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
index 45028ff8f4f9..eff9e74e0e70 100644
--- a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
+++ b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
@@ -48,6 +48,8 @@ public class AccessibilityContentDescriptions {
public static final int WIFI_NO_CONNECTION = R.string.accessibility_no_wifi;
+ public static final int NO_CALLING = R.string.accessibility_no_calling;
+
public static final int[] ETHERNET_CONNECTION_VALUES = {
R.string.accessibility_ethernet_disconnected,
R.string.accessibility_ethernet_connected,
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 927da0ae8495..2b357c57b306 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -396,6 +396,28 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
}
/**
+ * Check if USB data signaling (except from charging functions) is disabled by the admin.
+ * Only a device owner or a profile owner on an organization-owned managed profile can disable
+ * USB data signaling.
+ *
+ * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
+ * or {@code null} if USB data signaling is not disabled.
+ */
+ public static EnforcedAdmin checkIfUsbDataSignalingIsDisabled(Context context, int userId) {
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ if (dpm == null || dpm.isUsbDataSignalingEnabledForUser(userId)) {
+ return null;
+ } else {
+ EnforcedAdmin admin = getProfileOrDeviceOwner(context, getUserHandleOf(userId));
+ int managedProfileId = getManagedProfileId(context, userId);
+ if (admin == null && managedProfileId != UserHandle.USER_NULL) {
+ admin = getProfileOrDeviceOwner(context, getUserHandleOf(managedProfileId));
+ }
+ return admin;
+ }
+ }
+
+ /**
* Check if {@param packageName} is restricted by the profile or device owner from using
* metered data.
*
diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
index 64cb0f1b7885..43717aba3abd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
@@ -196,7 +196,7 @@ public class ConnectivitySubsystemsRecoveryManager {
}
private void stopTrackingWifiRestart() {
- mWifiManager.unregisterWifiSubsystemRestartTrackingCallback(
+ mWifiManager.unregisterSubsystemRestartTrackingCallback(
mWifiSubsystemRestartTrackingCallback);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
index caabf9af35f6..1474f184775d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
@@ -16,10 +16,13 @@
package com.android.settingslib.development;
+import static com.android.settingslib.RestrictedLockUtilsInternal.checkIfUsbDataSignalingIsDisabled;
+
import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
@@ -28,9 +31,9 @@ import androidx.annotation.VisibleForTesting;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import androidx.preference.SwitchPreference;
import androidx.preference.TwoStatePreference;
+import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.core.ConfirmationDialogController;
public abstract class AbstractEnableAdbPreferenceController extends
@@ -44,7 +47,7 @@ public abstract class AbstractEnableAdbPreferenceController extends
public static final int ADB_SETTING_OFF = 0;
- protected SwitchPreference mPreference;
+ protected RestrictedSwitchPreference mPreference;
public AbstractEnableAdbPreferenceController(Context context) {
super(context);
@@ -54,7 +57,7 @@ public abstract class AbstractEnableAdbPreferenceController extends
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
if (isAvailable()) {
- mPreference = (SwitchPreference) screen.findPreference(KEY_ENABLE_ADB);
+ mPreference = (RestrictedSwitchPreference) screen.findPreference(KEY_ENABLE_ADB);
}
}
@@ -77,6 +80,10 @@ public abstract class AbstractEnableAdbPreferenceController extends
@Override
public void updateState(Preference preference) {
((TwoStatePreference) preference).setChecked(isAdbEnabled());
+ if (isAvailable()) {
+ ((RestrictedSwitchPreference) preference).setDisabledByAdmin(
+ checkIfUsbDataSignalingIsDisabled(mContext, UserHandle.myUserId()));
+ }
}
public void enablePreference(boolean enabled) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
index 81ca9eaf8e36..228de039fc1b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
@@ -163,8 +163,12 @@ public class RecentLocationAccesses {
long locationAccessFinishTime = 0L;
// Earliest time for a location access to end and still be shown in list.
long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
+ // Compute the most recent access time from all op entries.
for (AppOpsManager.OpEntry entry : entries) {
- locationAccessFinishTime = entry.getLastAccessTime(TRUSTED_STATE_FLAGS);
+ long lastAccessTime = entry.getLastAccessTime(TRUSTED_STATE_FLAGS);
+ if (lastAccessTime > locationAccessFinishTime) {
+ locationAccessFinishTime = lastAccessTime;
+ }
}
// Bail out if the entry is out of date.
if (locationAccessFinishTime < recentLocationCutoffTime) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java
index 9c18be54f7ff..844f639b1473 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java
@@ -66,7 +66,7 @@ public class MobileMappings {
return toIconKey(TelephonyManager.NETWORK_TYPE_LTE) + "_CA_Plus";
case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA:
return toIconKey(TelephonyManager.NETWORK_TYPE_NR);
- case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE:
+ case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED:
return toIconKey(TelephonyManager.NETWORK_TYPE_NR) + "_Plus";
default:
return "unsupported";
@@ -181,7 +181,7 @@ public class MobileMappings {
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA),
TelephonyIcons.NR_5G);
networkToIconLookup.put(toDisplayIconKey(
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE),
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED),
TelephonyIcons.NR_5G_PLUS);
networkToIconLookup.put(toIconKey(
TelephonyManager.NETWORK_TYPE_NR),
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index 2409dbc78f34..26d9c57b2d27 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -272,9 +272,14 @@ public class MobileStatusTracker {
.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode).append(',')
.append("dataState=").append(dataState).append(',')
.append("serviceState=").append(serviceState == null ? ""
- : serviceState.toString()).append(',')
+ : "mVoiceRegState=" + serviceState.getState() + "("
+ + ServiceState.rilServiceStateToString(serviceState.getState())
+ + ")" + ", mDataRegState=" + serviceState.getDataRegState() + "("
+ + ServiceState.rilServiceStateToString(
+ serviceState.getDataRegState()) + ")")
+ .append(',')
.append("signalStrength=").append(signalStrength == null ? ""
- : signalStrength.toString()).append(',')
+ : signalStrength.getLevel()).append(',')
.append("telephonyDisplayInfo=").append(telephonyDisplayInfo == null ? ""
: telephonyDisplayInfo.toString()).append(']').toString();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
index 53731ac7c75a..d6e74f22258a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
@@ -401,5 +401,21 @@ public class TelephonyIcons {
ICON_NAME_TO_ICON.put("datadisable", DATA_DISABLED);
ICON_NAME_TO_ICON.put("notdefaultdata", NOT_DEFAULT_DATA);
}
+
+ public static final int[] WIFI_CALL_STRENGTH_ICONS = {
+ R.drawable.ic_wifi_call_strength_1,
+ R.drawable.ic_wifi_call_strength_1,
+ R.drawable.ic_wifi_call_strength_2,
+ R.drawable.ic_wifi_call_strength_3,
+ R.drawable.ic_wifi_call_strength_3
+ };
+
+ public static final int[] MOBILE_CALL_STRENGTH_ICONS = {
+ R.drawable.ic_mobile_call_strength_1,
+ R.drawable.ic_mobile_call_strength_1,
+ R.drawable.ic_mobile_call_strength_2,
+ R.drawable.ic_mobile_call_strength_3,
+ R.drawable.ic_mobile_call_strength_3
+ };
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index effc4d32fc3d..8158497046b9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -39,6 +39,8 @@ import android.util.FeatureFlagUtils;
import com.android.settingslib.R;
import com.android.settingslib.Utils;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -47,6 +49,8 @@ import java.util.Set;
* Track status of Wi-Fi for the Sys UI.
*/
public class WifiStatusTracker {
+ private static final int HISTORY_SIZE = 32;
+ private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
private final Context mContext;
private final WifiNetworkScoreCache mWifiNetworkScoreCache;
private final WifiManager mWifiManager;
@@ -54,6 +58,10 @@ public class WifiStatusTracker {
private final ConnectivityManager mConnectivityManager;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Set<Integer> mNetworks = new HashSet<>();
+ // Save the previous HISTORY_SIZE states for logging.
+ private final String[] mHistory = new String[HISTORY_SIZE];
+ // Where to copy the next state into.
+ private int mHistoryIndex;
private final WifiNetworkScoreCache.CacheListener mCacheListener =
new WifiNetworkScoreCache.CacheListener(mHandler) {
@Override
@@ -93,6 +101,13 @@ public class WifiStatusTracker {
} else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
}
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("onCapabilitiesChanged: ")
+ .append("network=").append(network).append(",")
+ .append("networkCapabilities=").append(networkCapabilities)
+ .toString();
+ recordLastWifiNetwork(log);
if (wifiInfo != null) {
updateWifiInfo(wifiInfo);
updateStatusLabel();
@@ -102,6 +117,12 @@ public class WifiStatusTracker {
@Override
public void onLost(Network network) {
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("onLost: ")
+ .append("network=").append(network)
+ .toString();
+ recordLastWifiNetwork(log);
if (mNetworks.contains(network.getNetId())) {
mNetworks.remove(network.getNetId());
updateWifiInfo(null);
@@ -344,4 +365,25 @@ public class WifiStatusTracker {
}
return null;
}
+
+ private void recordLastWifiNetwork(String log) {
+ mHistory[mHistoryIndex] = log;
+ mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
+ }
+
+ /** Dump function. */
+ public void dump(PrintWriter pw) {
+ pw.println(" - WiFi Network History ------");
+ int size = 0;
+ for (int i = 0; i < HISTORY_SIZE; i++) {
+ if (mHistory[i] != null) size++;
+ }
+ // Print out the previous states in ordered number.
+ for (int i = mHistoryIndex + HISTORY_SIZE - 1;
+ i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
+ pw.println(" Previous WiFiNetwork("
+ + (mHistoryIndex + HISTORY_SIZE - i) + "): "
+ + mHistory[i & (HISTORY_SIZE - 1)]);
+ }
+ }
}
diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp
index 92e32d96cdf3..64563be78fbb 100644
--- a/packages/SettingsLib/tests/integ/Android.bp
+++ b/packages/SettingsLib/tests/integ/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "SettingsLibTests",
defaults: [
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index 3756c3bf7704..dc661c234116 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -16,6 +16,15 @@
// SettingsLib Shell app just for Robolectric test target. #
//###########################################################
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "SettingsLibShell",
defaults: ["SettingsLibDefaults"],
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
index e84a25c0ba4e..5f53a92c131e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
@@ -19,18 +19,24 @@ package com.android.settingslib.development;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import androidx.preference.SwitchPreference;
+
+import com.android.settingslib.RestrictedSwitchPreference;
import org.junit.Before;
import org.junit.Test;
@@ -43,26 +49,34 @@ import org.robolectric.shadows.ShadowApplication;
@RunWith(RobolectricTestRunner.class)
public class EnableAdbPreferenceControllerTest {
+
+ private static final ComponentName TEST_COMPONENT_NAME = new ComponentName("test", "test");
+
@Mock(answer = RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen;
@Mock
private UserManager mUserManager;
@Mock
private PackageManager mPackageManager;
+ @Mock
+ private DevicePolicyManager mDevicePolicyManager;
private Context mContext;
- private SwitchPreference mPreference;
+ private RestrictedSwitchPreference mPreference;
private ConcreteEnableAdbPreferenceController mController;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
ShadowApplication shadowContext = ShadowApplication.getInstance();
shadowContext.setSystemService(Context.USER_SERVICE, mUserManager);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mContext.getSystemService(DevicePolicyManager.class)).thenReturn(mDevicePolicyManager);
+ doReturn(mContext).when(mContext).createPackageContextAsUser(
+ any(String.class), anyInt(), any(UserHandle.class));
mController = new ConcreteEnableAdbPreferenceController(mContext);
- mPreference = new SwitchPreference(mContext);
+ mPreference = new RestrictedSwitchPreference(mContext);
mPreference.setKey(mController.getPreferenceKey());
when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
}
@@ -125,6 +139,9 @@ public class EnableAdbPreferenceControllerTest {
@Test
public void updateState_settingsOn_shouldCheck() {
when(mUserManager.isAdminUser()).thenReturn(true);
+ when(mDevicePolicyManager.getProfileOwner()).thenReturn(TEST_COMPONENT_NAME);
+ when(mDevicePolicyManager.isUsbDataSignalingEnabledForUser(
+ UserHandle.myUserId())).thenReturn(true);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ADB_ENABLED, 1);
mPreference.setChecked(false);
@@ -138,6 +155,9 @@ public class EnableAdbPreferenceControllerTest {
@Test
public void updateState_settingsOff_shouldUncheck() {
when(mUserManager.isAdminUser()).thenReturn(true);
+ when(mDevicePolicyManager.getProfileOwner()).thenReturn(TEST_COMPONENT_NAME);
+ when(mDevicePolicyManager.isUsbDataSignalingEnabledForUser(
+ UserHandle.myUserId())).thenReturn(true);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ADB_ENABLED, 0);
mPreference.setChecked(true);
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index 2e5347869554..f490c87aa4ec 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -1,3 +1,22 @@
+package {
+ default_applicable_licenses: [
+ "frameworks_base_packages_SettingsProvider_license",
+ ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_packages_SettingsProvider_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
android_app {
name: "SettingsProvider",
defaults: ["platform_app_defaults"],
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index a0f582facc4c..78ca5e28ffcc 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -256,7 +256,7 @@
<integer name="def_accessibility_magnification_capabilities">3</integer>
<!-- Default for Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW -->
- <bool name="def_enable_non_resizable_multi_window">false</bool>
+ <bool name="def_enable_non_resizable_multi_window">true</bool>
<!-- Default for Settings.System.TIME_12_24 -->
<string name="def_time_format" translatable="false">12</string>
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index ad6a5312f156..66165b6d1ff2 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -147,10 +147,5 @@ public class GlobalSettingsValidators {
VALIDATORS.put(Global.DEVELOPMENT_SETTINGS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.NOTIFICATION_FEEDBACK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.RESTRICTED_NETWORKING_MODE, BOOLEAN_VALIDATOR);
- VALIDATORS.put(
- Global.ONE_HANDED_KEYGUARD_SIDE,
- new InclusiveIntegerRangeValidator(
- /* first= */Global.ONE_HANDED_KEYGUARD_SIDE_LEFT,
- /* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT));
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index a6e2af9b1674..9cd7083a2a11 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -24,6 +24,7 @@ import android.app.backup.FullBackupDataOutput;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
@@ -212,7 +213,6 @@ public class SettingsBackupAgent extends BackupAgentHelper {
@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) throws IOException {
-
byte[] systemSettingsData = getSystemSettings();
byte[] secureSettingsData = getSecureSettings();
byte[] globalSettingsData = getGlobalSettings();
@@ -1204,17 +1204,25 @@ public class SettingsBackupAgent extends BackupAgentHelper {
}
private byte[] getSimSpecificSettingsData() {
- SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
- byte[] simSpecificData = subManager.getAllSimSpecificSettingsForBackup();
- Log.i(TAG, "sim specific data of length + " + simSpecificData.length
+ byte[] simSpecificData = new byte[0];
+ PackageManager packageManager = getBaseContext().getPackageManager();
+ if (packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+ simSpecificData = subManager.getAllSimSpecificSettingsForBackup();
+ Log.i(TAG, "sim specific data of length + " + simSpecificData.length
+ " successfully retrieved");
+ }
return simSpecificData;
}
private void restoreSimSpecificSettings(byte[] data) {
- SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
- subManager.restoreAllSimSpecificSettingsFromBackup(data);
+ PackageManager packageManager = getBaseContext().getPackageManager();
+ boolean hasTelephony = packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
+ if (hasTelephony) {
+ SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+ subManager.restoreAllSimSpecificSettingsFromBackup(data);
+ }
}
private void updateWindowManagerIfNeeded(Integer previousDensity) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index f6bd020c9eec..befd1d448d39 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -297,6 +297,24 @@ public class SettingsProvider extends ContentProvider {
Settings.System.getCloneFromParentOnValueSettings(sSystemCloneFromParentOnDependency);
}
+ private static final Set<String> sAllSecureSettings = new ArraySet<>();
+ private static final Set<String> sReadableSecureSettings = new ArraySet<>();
+ static {
+ Settings.Secure.getPublicSettings(sAllSecureSettings, sReadableSecureSettings);
+ }
+
+ private static final Set<String> sAllSystemSettings = new ArraySet<>();
+ private static final Set<String> sReadableSystemSettings = new ArraySet<>();
+ static {
+ Settings.System.getPublicSettings(sAllSystemSettings, sReadableSystemSettings);
+ }
+
+ private static final Set<String> sAllGlobalSettings = new ArraySet<>();
+ private static final Set<String> sReadableGlobalSettings = new ArraySet<>();
+ static {
+ Settings.Global.getPublicSettings(sAllGlobalSettings, sReadableGlobalSettings);
+ }
+
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -1919,6 +1937,7 @@ public class SettingsProvider extends ContentProvider {
if (UserHandle.getAppId(Binder.getCallingUid()) < Process.FIRST_APPLICATION_UID) {
return;
}
+ checkReadableAnnotation(settingsType, settingName);
ApplicationInfo ai = getCallingApplicationInfoOrThrow();
if (!ai.isInstantApp()) {
return;
@@ -1932,6 +1951,41 @@ public class SettingsProvider extends ContentProvider {
}
}
+ /**
+ * Check if the target settings key is readable. Reject if the caller app is trying to access a
+ * settings key defined in the Settings.Secure, Settings.System or Settings.Global and is not
+ * annotated as @Readable.
+ * Notice that a key string that is not defined in any of the Settings.* classes will still be
+ * regarded as readable.
+ */
+ private void checkReadableAnnotation(int settingsType, String settingName) {
+ final Set<String> allFields;
+ final Set<String> readableFields;
+ switch (settingsType) {
+ case SETTINGS_TYPE_GLOBAL:
+ allFields = sAllGlobalSettings;
+ readableFields = sReadableGlobalSettings;
+ break;
+ case SETTINGS_TYPE_SYSTEM:
+ allFields = sAllSystemSettings;
+ readableFields = sReadableSystemSettings;
+ break;
+ case SETTINGS_TYPE_SECURE:
+ allFields = sAllSecureSettings;
+ readableFields = sReadableSecureSettings;
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid settings type: " + settingsType);
+ }
+
+ if (allFields.contains(settingName) && !readableFields.contains(settingName)) {
+ throw new SecurityException(
+ "Settings key: <" + settingName + "> is not readable. From S+, new public "
+ + "settings keys need to be annotated with @Readable unless they are "
+ + "annotated with @hide.");
+ }
+ }
+
private ApplicationInfo getCallingApplicationInfoOrThrow() {
// We always use the callingUid for this lookup. This means that if hypothetically an
// app was installed in user A with cross user and in user B as an Instant App
@@ -4736,17 +4790,23 @@ public class SettingsProvider extends ContentProvider {
}
if (currentVersion == 192) {
- // Version 192: set the default value for magnification capabilities. If
- // magnification is enabled by the user, set it to full-screen, and set a value
- // to show a prompt when using the magnification first time after upgrading.
+ // Version 192: set the default value for magnification capabilities.
+ // If the device supports magnification area and magnification is enabled
+ // by the user, set it to full-screen, and set a value to show a prompt
+ // when using the magnification first time after upgrading.
final SettingsState secureSettings = getSecureSettingsLocked(userId);
final Setting magnificationCapabilities = secureSettings.getSettingLocked(
Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY);
+ final boolean supportMagnificationArea = getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_magnification_area);
+ final int capability = supportMagnificationArea
+ ? R.integer.def_accessibility_magnification_capabilities
+ : Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ final String supportShowPrompt = supportMagnificationArea ? "1" : "0";
if (magnificationCapabilities.isNull()) {
secureSettings.insertSettingLocked(
Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
- String.valueOf(getContext().getResources().getInteger(
- R.integer.def_accessibility_magnification_capabilities)),
+ String.valueOf(getContext().getResources().getInteger(capability)),
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
if (isMagnificationSettingsOn(secureSettings)) {
@@ -4756,7 +4816,8 @@ public class SettingsProvider extends ContentProvider {
null, false /* makeDefault */,
SettingsState.SYSTEM_PACKAGE_NAME);
secureSettings.insertSettingLocked(
- Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, "1",
+ Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT,
+ supportShowPrompt,
null, false /* makeDefault */,
SettingsState.SYSTEM_PACKAGE_NAME);
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c11877a8a963..6719f179ef86 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -283,7 +283,6 @@ public class SettingsBackupTest {
Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS,
Settings.Global.EUICC_SWITCH_SLOT_TIMEOUT_MILLIS,
Settings.Global.FANCY_IME_ANIMATIONS,
- Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
Settings.Global.FORCED_APP_STANDBY_ENABLED,
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
@@ -324,6 +323,7 @@ public class SettingsBackupTest {
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+ Settings.Global.LOCATION_ENABLE_STATIONARY_THROTTLE,
Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST,
Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
Settings.Global.LOCK_SOUND,
diff --git a/packages/SharedStorageBackup/Android.bp b/packages/SharedStorageBackup/Android.bp
index d02f4800fc83..21516fade1ab 100644
--- a/packages/SharedStorageBackup/Android.bp
+++ b/packages/SharedStorageBackup/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "SharedStorageBackup",
defaults: ["platform_app_defaults"],
diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp
index 546642db881a..c87916fa3b95 100644
--- a/packages/Shell/Android.bp
+++ b/packages/Shell/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
// used both for the android_app and android_library
shell_srcs = ["src/**/*.java",":dumpstate_aidl"]
shell_static_libs = ["androidx.legacy_legacy-support-v4"]
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index eab0990bb868..a15ceb6d8811 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -87,6 +87,7 @@
<!-- TODO(b/152310230): remove once APIs are confirmed to be sufficient -->
<uses-permission android:name="com.android.permission.USE_INSTALLER_V2" />
<uses-permission android:name="android.permission.MOVE_PACKAGE" />
+ <uses-permission android:name="android.permission.KEEP_UNINSTALLED_PACKAGES" />
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
<uses-permission android:name="android.permission.ACCESS_INSTANT_APPS" />
@@ -99,6 +100,7 @@
<uses-permission android:name="android.permission.REBOOT" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
<uses-permission android:name="android.permission.POWER_SAVER" />
+ <uses-permission android:name="android.permission.BATTERY_PREDICTION" />
<uses-permission android:name="android.permission.INSTALL_LOCATION_PROVIDER" />
<uses-permission android:name="android.permission.BACKUP" />
<uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
@@ -343,6 +345,12 @@
<!-- Permissions required for CTS test - AdbManagerTest -->
<uses-permission android:name="android.permission.MANAGE_DEBUGGING" />
+ <!-- Permission required for CTS test - CtsTelephonyTestCases -->
+ <uses-permission android:name="android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE" />
+
+ <!-- Permission required for CTS test - CtsTelephonyTestCases -->
+ <uses-permission android:name="android.permission.PERFORM_IMS_SINGLE_REGISTRATION" />
+
<!-- Permission needed for CTS test - DisplayTest -->
<uses-permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS" />
@@ -399,6 +407,13 @@
<uses-permission android:name="android.permission.BIND_VOICE_INTERACTION" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
+ <uses-permission android:name="android.permission.BIND_RESUME_ON_REBOOT_SERVICE" />
+
+ <!-- Permission required for CTS test - CtsRebootReadinessTestCases -->
+ <uses-permission android:name="android.permission.SIGNAL_REBOOT_READINESS" />
+
+ <!-- Permission required for CTS test - PeopleManagerTest -->
+ <uses-permission android:name="android.permission.READ_PEOPLE_DATA" />
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS
index 6ba1fcb058b1..34901f5830c4 100644
--- a/packages/Shell/OWNERS
+++ b/packages/Shell/OWNERS
@@ -6,7 +6,6 @@ nandana@google.com
svetoslavganov@google.com
hackbod@google.com
yamasani@google.com
-moltmann@google.com
toddke@google.com
cbrubaker@google.com
omakoto@google.com
diff --git a/packages/Shell/res/values-ky/strings.xml b/packages/Shell/res/values-ky/strings.xml
index 3567ac276e63..d73ee2fdb4e9 100644
--- a/packages/Shell/res/values-ky/strings.xml
+++ b/packages/Shell/res/values-ky/strings.xml
@@ -25,7 +25,7 @@
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Мүчүлүштүктөр жөнүндө кабар жакында телефонго чыгат"</string>
<string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Мүчүлүштүк тууралуу кабарды жөнөтүү үчүн таптап коюңуз"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Мүчүлүштүк тууралуу билдирүүңүздү бөлүшүү үчүн таптап коюңуз"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Мүчүлүштүк тууралуу кабарды скриншотсуз жөнөтүү үчүн солго серпиңиз же скриншот даяр болгуча күтүңүз"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Мүчүлүштүк тууралуу кабарды скриншотсуз жөнөтүү үчүн солго сүрүңүз же скриншот даяр болгуча күтүңүз"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Мүчүлүштүк тууралуу билдирүүңүздү скриншотсуз бөлүшүү үчүн таптап коюңуз же скриншот даяр болгуча күтө туруңуз"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Мүчүлүштүк тууралуу билдирүүңүздү скриншотсуз бөлүшүү үчүн таптап коюңуз же скриншот даяр болгуча күтө туруңуз"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"Мүчүлүштүктөр тууралуу билдирүүлөрдө тутумдун ар кандай таржымалдарынан алынган дайындар, ошондой эле купуя маалымат камтылышы мүмкүн (мисалы, жайгашкан жер сыяктуу). Мындай билдирүүлөрдү бир гана ишеничтүү адамдар жана колдонмолор менен бөлүшүңүз."</string>
diff --git a/packages/Shell/tests/Android.bp b/packages/Shell/tests/Android.bp
index 8536c4fbb5a3..70e8c10a961d 100644
--- a/packages/Shell/tests/Android.bp
+++ b/packages/Shell/tests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ShellTests",
srcs: ["src/**/*.java"],
diff --git a/packages/SimAppDialog/Android.bp b/packages/SimAppDialog/Android.bp
index 9c0d78c41e5e..a9fe3ad1a596 100644
--- a/packages/SimAppDialog/Android.bp
+++ b/packages/SimAppDialog/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "SimAppDialog",
defaults: ["platform_app_defaults"],
diff --git a/packages/SoundPicker/Android.bp b/packages/SoundPicker/Android.bp
index 56e7cd162cdf..2c89d6dbce56 100644
--- a/packages/SoundPicker/Android.bp
+++ b/packages/SoundPicker/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "SoundPicker",
defaults: ["platform_app_defaults"],
diff --git a/packages/StatementService/Android.bp b/packages/StatementService/Android.bp
index 2664a0316f5c..32defc822733 100644
--- a/packages/StatementService/Android.bp
+++ b/packages/StatementService/Android.bp
@@ -11,6 +11,15 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "StatementService",
defaults: ["platform_app_defaults"],
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 77e2b97352a3..3bb00e1901ea 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -14,6 +14,23 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_packages_SystemUI_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
java_library {
name: "SystemUI-proto",
@@ -76,8 +93,8 @@ android_library {
"androidx.dynamicanimation_dynamicanimation",
"androidx-constraintlayout_constraintlayout",
"androidx.exifinterface_exifinterface",
- "kotlinx-coroutines-android",
- "kotlinx-coroutines-core",
+ "kotlinx_coroutines_android",
+ "kotlinx_coroutines",
"iconloader_base",
"SystemUI-tags",
"SystemUI-proto",
@@ -215,6 +232,5 @@ android_app {
dxflags: ["--multi-dex"],
required: [
"privapp_whitelist_com.android.systemui",
- "checked-wm_shell_protolog.json",
],
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a881d2516450..23b6564ba476 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -603,6 +603,14 @@
android:permission="android.permission.BIND_REMOTEVIEWS"
android:exported="false" />
+ <!-- ContentProvider that returns a People Tile preview for a given shortcut -->
+ <provider
+ android:name="com.android.systemui.people.PeopleProvider"
+ android:authorities="com.android.systemui.people.PeopleProvider"
+ android:exported="true"
+ android:permission="android.permission.GET_PEOPLE_TILE_PREVIEW">
+ </provider>
+
<!-- a gallery of delicious treats -->
<service
android:name=".DessertCaseDream"
diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md
index ee8d02301d5d..60994d892b6e 100644
--- a/packages/SystemUI/README.md
+++ b/packages/SystemUI/README.md
@@ -144,6 +144,10 @@ Biometric UI.
Delegates SysUI events to WM Shell controllers.
+### [com.android.systemui.people.widget.PeopleSpaceWidgetEnabler](/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java)
+
+Enables People Space widgets.
+
---
* [Plugins](/packages/SystemUI/docs/plugins.md)
diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp
index ab4f800d2586..9e67e4becc8e 100644
--- a/packages/SystemUI/plugin/Android.bp
+++ b/packages/SystemUI/plugin/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
java_library {
name: "SystemUIPluginLib",
diff --git a/packages/SystemUI/plugin/ExamplePlugin/Android.bp b/packages/SystemUI/plugin/ExamplePlugin/Android.bp
index c6c80f3780a3..3f0fdedb57da 100644
--- a/packages/SystemUI/plugin/ExamplePlugin/Android.bp
+++ b/packages/SystemUI/plugin/ExamplePlugin/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
android_app {
name: "ExamplePlugin",
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java
index 0831e0ef7795..da079cf04403 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java
@@ -103,5 +103,10 @@ public interface ToastPlugin extends Plugin {
default Animator getOutAnimation() {
return null;
}
+
+ /**
+ * Called on orientation changes.
+ */
+ default void onOrientationChange(int orientation) { }
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
index d43aaf07c6be..beee03b52579 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
@@ -46,6 +46,21 @@ public interface DetailAdapter {
return true;
}
+ /**
+ * @return if detail panel should animate when shown or closed
+ */
+ default boolean shouldAnimate() {
+ return true;
+ }
+
+ /**
+ * @return true if the callback handled the event and wants to keep the detail panel open, false
+ * otherwise. Returning false will close the panel.
+ */
+ default boolean onDoneButtonClicked() {
+ return false;
+ }
+
default UiEventLogger.UiEventEnum openDetailEvent() {
return INVALID;
}
diff --git a/packages/SystemUI/plugin_core/Android.bp b/packages/SystemUI/plugin_core/Android.bp
index 581fef721acc..34d31d9955fc 100644
--- a/packages/SystemUI/plugin_core/Android.bp
+++ b/packages/SystemUI/plugin_core/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
java_library {
sdk_version: "current",
name: "PluginCoreLib",
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_backspace_black_24dp.xml b/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml
index 1f6b24bb85bf..dd35dd91f5d6 100644
--- a/packages/SystemUI/res-keyguard/drawable/ic_backspace_black_24dp.xml
+++ b/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml
@@ -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.
@@ -20,6 +20,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:fillColor="#FF000000"
+ android:fillColor="?android:attr/colorBackground"
android:pathData="M9,15.59L12.59,12L9,8.41L10.41,7L14,10.59L17.59,7L19,8.41L15.41,12L19,15.59L17.59,17L14,13.41L10.41,17L9,15.59zM21,6H8l-4.5,6L8,18h13V6M21,4c1.1,0 2,0.9 2,2v12c0,1.1 -0.9,2 -2,2H8c-0.63,0 -1.22,-0.3 -1.6,-0.8L1,12l5.4,-7.2C6.78,4.3 7.37,4 8,4H21L21,4z"/>
</vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/num_pad_key_background.xml b/packages/SystemUI/res-keyguard/drawable/num_pad_key_background.xml
index b7a9fafd0c44..604ab723da90 100644
--- a/packages/SystemUI/res-keyguard/drawable/num_pad_key_background.xml
+++ b/packages/SystemUI/res-keyguard/drawable/num_pad_key_background.xml
@@ -16,8 +16,23 @@
* limitations under the License.
*/
-->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="?android:attr/colorBackground" />
- <corners android:radius="10dp" />
-</shape>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/background">
+ <shape>
+ <solid android:color="?android:attr/colorControlNormal" />
+ <corners android:radius="10dp" />
+ </shape>
+ </item>
+ <item android:id="@+id/ripple">
+ <ripple
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="?android:attr/colorControlNormal" />
+ <corners android:radius="10dp" />
+ </shape>
+ </item>
+ </ripple>
+ </item>
+</layer-list>
+
diff --git a/packages/SystemUI/res-keyguard/drawable/ripple_drawable_pin.xml b/packages/SystemUI/res-keyguard/drawable/ripple_drawable_pin.xml
new file mode 100644
index 000000000000..51c442abf2fd
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ripple_drawable_pin.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight"
+ android:radius="40dp"/>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
index b5f55af50bff..79868093fb12 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
@@ -24,7 +24,7 @@
<include
style="@style/BouncerSecurityContainer"
layout="@layout/keyguard_host_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
</FrameLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 35a2195accab..f3ec39d2b1fb 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -74,9 +74,7 @@
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:textSize="100dp"
- android:includeFontPadding="false"
android:fontFamily="@font/clock"
- android:lineSpacingMultiplier=".7"
android:typeface="monospace"
android:elegantTextHeight="false"
dozeWeight="200"
@@ -96,8 +94,6 @@
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:textSize="@dimen/large_clock_text_size"
- android:includeFontPadding="false"
- android:lineSpacingMultiplier=".7"
android:fontFamily="@font/clock"
android:typeface="monospace"
android:elegantTextHeight="false"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
index c75ee51517d1..04e645bd0a32 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
@@ -41,14 +41,13 @@
android:layout_gravity="center">
<com.android.keyguard.KeyguardSecurityViewFlipper
android:id="@+id/view_flipper"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingTop="@dimen/keyguard_security_view_top_margin"
android:paddingStart="@dimen/keyguard_security_view_lateral_margin"
android:paddingEnd="@dimen/keyguard_security_view_lateral_margin"
- android:layout_gravity="center"
android:gravity="center">
</com.android.keyguard.KeyguardSecurityViewFlipper>
</com.android.keyguard.KeyguardSecurityContainer>
diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml
index 6176f7c1dd0a..8d9d6ee68c67 100644
--- a/packages/SystemUI/res-keyguard/values/config.xml
+++ b/packages/SystemUI/res-keyguard/values/config.xml
@@ -22,5 +22,4 @@
<!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
- <bool name="can_use_one_handed_bouncer">false</bool>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index a928b759e1b8..2a5784a1e6e9 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -40,7 +40,7 @@
<dimen name="keyguard_security_view_top_margin">8dp</dimen>
<dimen name="keyguard_security_view_lateral_margin">36dp</dimen>
- <dimen name="keyguard_eca_top_margin">24dp</dimen>
+ <dimen name="keyguard_eca_top_margin">18dp</dimen>
<!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
Should be 0 on devices with plenty of room (e.g. tablets) -->
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index cd82b805be9c..2391803957e5 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -52,9 +52,8 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="NumPadKey.Delete">
- <item name="android:src">@drawable/ic_backspace_black_24dp</item>
- <item name="android:tint">?android:attr/textColorSecondary</item>
- <item name="android:tintMode">src_in</item>
+ <item name="android:colorControlNormal">?android:attr/textColorSecondary</item>
+ <item name="android:src">@drawable/ic_backspace_24dp</item>
</style>
<style name="NumPadKey.Enter">
<item name="android:colorControlNormal">?android:attr/textColorSecondary</item>
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_inner.xml b/packages/SystemUI/res/color/kg_user_avatar_frame.xml
index 4c1042e70c1a..174981e2a660 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_inner.xml
+++ b/packages/SystemUI/res/color/kg_user_avatar_frame.xml
@@ -14,14 +14,10 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.keyguard.AlphaOptimizedLinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_user_switcher_inner"
- android:orientation="vertical"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
- android:layout_gravity="end"
- android:gravity="end"
- android:paddingTop="4dp">
-</com.android.keyguard.AlphaOptimizedLinearLayout>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_activated="true"
+ android:color="@color/kg_user_switcher_avatar_background" />
+ <item android:color="@color/kg_user_switcher_avatar_background" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/circle_green_10dp.xml b/packages/SystemUI/res/drawable/circle_green_10dp.xml
new file mode 100644
index 000000000000..571ec621da9f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/circle_green_10dp.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <size android:height="10dp"
+ android:width="10dp" />
+ <solid android:color="#34A853" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/end_guest_button_background.xml b/packages/SystemUI/res/drawable/end_guest_button_background.xml
new file mode 100644
index 000000000000..5644b657a609
--- /dev/null
+++ b/packages/SystemUI/res/drawable/end_guest_button_background.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
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <stroke
+ android:width="@dimen/end_guest_button_border_size"
+ android:color="?android:attr/colorControlHighlight" />
+ <corners android:radius="@dimen/end_guest_button_corner_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/kg_bg_avatar.xml b/packages/SystemUI/res/drawable/kg_bg_avatar.xml
new file mode 100644
index 000000000000..addb3f7508f5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/kg_bg_avatar.xml
@@ -0,0 +1,28 @@
+<?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="48dp"
+ android:height="48dp"
+ android:viewportWidth="100"
+ android:viewportHeight="100">
+
+ <path
+ android:fillColor="@color/kg_user_switcher_avatar_background"
+ android:pathData="M50,50m-50,0a50,50 0,1 1,100 0a50,50 0,1 1,-100 0"/>
+
+</vector>
diff --git a/packages/SystemUI/res/drawable/people_space_content_background.xml b/packages/SystemUI/res/drawable/people_space_content_background.xml
index 5310840939b8..32314d29277e 100644
--- a/packages/SystemUI/res/drawable/people_space_content_background.xml
+++ b/packages/SystemUI/res/drawable/people_space_content_background.xml
@@ -16,5 +16,5 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="?android:attr/colorControlHighlight" />
- <corners android:radius="@dimen/people_space_widget_radius" />
+ <corners android:radius="@dimen/people_space_image_radius" />
</shape>
diff --git a/packages/SystemUI/res/drawable/people_space_round_tile_view_card.xml b/packages/SystemUI/res/drawable/people_space_new_story_outline.xml
index 59af7750dada..a1737f92fa22 100644
--- a/packages/SystemUI/res/drawable/people_space_round_tile_view_card.xml
+++ b/packages/SystemUI/res/drawable/people_space_new_story_outline.xml
@@ -13,7 +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">
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
<solid android:color="?android:attr/colorBackground" />
- <corners android:radius="@dimen/people_space_widget_round_radius" />
+ <stroke android:width="2dp" android:color="?android:attr/colorAccent" />
</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
index 827cf4a9d3b6..109442d7ed79 100644
--- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml
+++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
@@ -16,8 +16,6 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="#242424" /> <!-- 14% of white -->
- <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding"
- android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" />
+ <solid android:color="#FFFFFF" />
<corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/toast_background.xml b/packages/SystemUI/res/drawable/toast_background.xml
new file mode 100644
index 000000000000..5c45e8346e3c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/toast_background.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"
+ android:shape="rectangle">
+ <solid android:color="#FFFFFFFF" />
+ <corners android:radius="@dimen/toast_bg_radius" />
+</shape>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 8f202e277932..d20f991dda90 100755
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -43,14 +43,17 @@
android:accessibilityLiveRegion="polite"/>
<com.android.systemui.statusbar.phone.KeyguardIndicationTextView
- android:id="@+id/keyguard_indication_enterprise_disclosure"
- android:layout_width="match_parent"
+ android:id="@+id/keyguard_indication_text_bottom"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
+ android:minHeight="48dp"
+ android:layout_gravity="center_horizontal"
+ android:layout_centerHorizontal="true"
android:paddingStart="@dimen/keyguard_indication_text_padding"
android:paddingEnd="@dimen/keyguard_indication_text_padding"
android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
- android:alpha=".54"
+ android:alpha=".8"
android:visibility="gone"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml b/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml
new file mode 100644
index 000000000000..3938b73d08ff
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyguard_qs_user_switch.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
+ -->
+<!-- This is a view that shows a user switcher in Keyguard. -->
+<com.android.systemui.statusbar.phone.UserAvatarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_qs_user_switch_view"
+ android:layout_width="@dimen/kg_framed_avatar_size"
+ android:layout_height="@dimen/kg_framed_avatar_size"
+ android:layout_centerHorizontal="true"
+ android:layout_gravity="center_horizontal|bottom"
+ systemui:avatarPadding="0dp"
+ systemui:badgeDiameter="18dp"
+ systemui:badgeMargin="1dp"
+ systemui:frameColor="@color/kg_user_avatar_frame"
+ systemui:framePadding="0dp"
+ systemui:frameWidth="0dp">
+</com.android.systemui.statusbar.phone.UserAvatarView>
diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index 416ee8147e33..2789ed125b09 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -43,17 +43,12 @@
<include layout="@layout/system_icons" />
</FrameLayout>
- <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
- android:layout_width="@dimen/multi_user_switch_width_keyguard"
- android:layout_height="match_parent"
- android:background="@drawable/ripple_drawable"
- android:layout_marginEnd="@dimen/multi_user_switch_keyguard_margin">
- <ImageView android:id="@+id/multi_user_avatar"
- android:layout_width="@dimen/multi_user_avatar_keyguard_size"
- android:layout_height="@dimen/multi_user_avatar_keyguard_size"
- android:layout_gravity="center"
- android:scaleType="centerInside"/>
- </com.android.systemui.statusbar.phone.MultiUserSwitch>
+
+ <ImageView android:id="@+id/multi_user_avatar"
+ android:layout_width="@dimen/multi_user_avatar_keyguard_size"
+ android:layout_height="@dimen/multi_user_avatar_keyguard_size"
+ android:layout_gravity="center"
+ android:scaleType="centerInside"/>
</LinearLayout>
<Space
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher.xml b/packages/SystemUI/res/layout/keyguard_user_switcher.xml
index 983ba6d5e240..253c03e9effb 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher.xml
@@ -14,10 +14,50 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<view xmlns:android="http://schemas.android.com/apk/res/android"
- class="com.android.systemui.statusbar.policy.KeyguardUserSwitcher$Container"
- android:visibility="gone"
- android:layout_height="match_parent"
- android:layout_width="match_parent">
- <!-- KeyguardUserSwitcher loads keyguard_user_switcher_inner.xml here -->
-</view> \ No newline at end of file
+<!-- This is a view that shows a user switcher in Keyguard. -->
+<com.android.systemui.statusbar.policy.KeyguardUserSwitcherView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_user_switcher_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="end">
+
+ <com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView
+ android:id="@+id/keyguard_user_switcher_list"
+ android:orientation="vertical"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="top|end"
+ android:gravity="end" />
+
+ <LinearLayout
+ android:id="@+id/end_guest_button"
+ android:layout_height="@dimen/end_guest_button_layout_height"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="@dimen/end_guest_button_margin_bottom"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:paddingLeft="@dimen/end_guest_button_padding_horizontal"
+ android:paddingRight="@dimen/end_guest_button_padding_horizontal"
+ android:background="@drawable/end_guest_button_background"
+ android:visibility="gone">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:src="@drawable/ic_exit_to_app"
+ android:background="@android:color/transparent"
+ android:color="?attr/wallpaperTextColor" />
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:gravity="center"
+ android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
+ android:textColor="?attr/wallpaperTextColor"
+ android:textSize="13sp"
+ android:text="@string/guest_exit_button" />
+ </LinearLayout>
+
+</com.android.systemui.statusbar.policy.KeyguardUserSwitcherView>
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
index 1cd1a04ab462..aaa372a5be6e 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
@@ -19,29 +19,30 @@
<!-- LinearLayout -->
<com.android.systemui.statusbar.policy.KeyguardUserDetailItemView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:sysui="http://schemas.android.com/apk/res-auto"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:layout_marginEnd="8dp"
- android:gravity="center_vertical"
+ android:gravity="end|center_vertical"
android:clickable="true"
- android:background="@drawable/ripple_drawable"
- sysui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
- sysui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.Activated">
- <TextView android:id="@+id/user_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="13dp"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
- />
- <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture"
- android:layout_width="@dimen/kg_framed_avatar_size"
- android:layout_height="@dimen/kg_framed_avatar_size"
- android:contentDescription="@null"
- sysui:frameWidth="@dimen/keyguard_user_switcher_border_thickness"
- sysui:framePadding="2.5dp"
- sysui:badgeDiameter="18dp"
- sysui:badgeMargin="1dp"
- sysui:frameColor="@color/kg_user_switcher_rounded_background_color" />
+ android:background="@drawable/kg_user_switcher_rounded_bg"
+ systemui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
+ systemui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher">
+ <TextView
+ android:id="@+id/user_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="20dp"
+ android:layout_marginEnd="16dp" />
+ <com.android.systemui.statusbar.phone.UserAvatarView
+ android:id="@+id/user_picture"
+ android:layout_width="@dimen/kg_framed_avatar_size"
+ android:layout_height="@dimen/kg_framed_avatar_size"
+ systemui:avatarPadding="0dp"
+ systemui:badgeDiameter="18dp"
+ systemui:badgeMargin="1dp"
+ systemui:frameWidth="0dp"
+ systemui:framePadding="0dp"
+ systemui:frameColor="@color/kg_user_avatar_frame" />
</com.android.systemui.statusbar.policy.KeyguardUserDetailItemView>
diff --git a/packages/SystemUI/res/layout/media_carousel.xml b/packages/SystemUI/res/layout/media_carousel.xml
index 8a47a22ff985..95cee66af536 100644
--- a/packages/SystemUI/res/layout/media_carousel.xml
+++ b/packages/SystemUI/res/layout/media_carousel.xml
@@ -47,7 +47,7 @@
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginBottom="4dp"
- android:tint="@color/media_primary_text"
+ android:tint="?android:attr/textColorPrimary"
android:forceHasOverlappingRendering="false"
/>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index 6b4270531d0b..a4cf5eddb30e 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -48,7 +48,7 @@
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:fontFamily="@*android:string/config_bodyFontFamily"
- android:textColor="@color/media_primary_text"
+ android:textColor="?android:attr/textColorPrimary"
android:gravity="start"
android:textSize="14sp" />
@@ -58,7 +58,7 @@
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:fontFamily="@*android:string/config_bodyFontFamily"
- android:textColor="@color/media_primary_text"
+ android:textColor="?android:attr/textColorPrimary"
android:gravity="end"
android:textSize="14sp" />
</FrameLayout>
@@ -120,13 +120,13 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
- android:gravity="center_vertical|end"
+ android:gravity="center"
+ android:background="@drawable/qs_media_light_source"
android:forceHasOverlappingRendering="false">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:foreground="@drawable/qs_media_seamless_background"
- android:background="@drawable/qs_media_light_source"
+ android:background="@drawable/qs_media_seamless_background"
android:orientation="horizontal"
android:padding="6dp"
android:contentDescription="@string/quick_settings_media_device_label">
@@ -135,7 +135,7 @@
android:layout_width="@dimen/qs_seamless_icon_size"
android:layout_height="@dimen/qs_seamless_icon_size"
android:layout_gravity="center"
- android:tint="@color/media_primary_text"
+ android:tint="?android:attr/colorPrimary"
android:src="@*android:drawable/ic_media_seamless" />
<TextView
android:visibility="gone"
@@ -147,7 +147,7 @@
android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
android:text="@*android:string/ext_media_seamless_action"
- android:textColor="@color/media_primary_text"
+ android:textColor="?android:attr/colorPrimary"
android:textDirection="locale"
android:textSize="14sp" />
</LinearLayout>
@@ -157,7 +157,7 @@
android:id="@+id/media_seamless_fallback"
android:layout_width="@dimen/qs_seamless_icon_size"
android:layout_height="@dimen/qs_seamless_icon_size"
- android:tint="@color/media_primary_text"
+ android:tint="?android:attr/textColorPrimary"
android:src="@drawable/ic_cast_connected"
android:forceHasOverlappingRendering="false" />
@@ -171,15 +171,15 @@
android:clickable="true"
android:maxHeight="@dimen/qs_media_enabled_seekbar_height"
android:paddingVertical="@dimen/qs_media_enabled_seekbar_vertical_padding"
- android:thumbTint="@color/media_primary_text"
- android:progressTint="@color/media_seekbar_progress"
- android:progressBackgroundTint="@color/media_disabled"
+ android:thumbTint="?android:attr/textColorPrimary"
+ android:progressTint="?android:attr/textColorPrimary"
+ android:progressBackgroundTint="?android:attr/colorBackground"
android:splitTrack="false" />
<!-- App name -->
<TextView
android:id="@+id/app_name"
- android:textColor="@color/media_primary_text"
+ android:textColor="?android:attr/textColorPrimary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:singleLine="true"
@@ -194,7 +194,7 @@
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:singleLine="true"
- android:textColor="@color/media_primary_text"
+ android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp" />
<!-- Artist name -->
@@ -204,12 +204,12 @@
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
- android:textColor="@color/media_secondary_text"
+ android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp" />
<com.android.internal.widget.CachingIconView
android:id="@+id/icon"
- android:tint="@color/media_primary_text"
+ android:tint="?android:attr/textColorPrimary"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="6dp" />
@@ -223,7 +223,7 @@
android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
android:id="@+id/media_text"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:textColor="@color/media_primary_text"
+ android:textColor="?android:attr/textColorSecondary"
android:text="@string/controls_media_title"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -238,7 +238,7 @@
android:id="@+id/remove_text"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
- android:textColor="@color/media_primary_text"
+ android:textColor="?android:attr/textColorPrimary"
android:text="@string/controls_media_close_session"
app:layout_constraintTop_toBottomOf="@id/media_text"
app:layout_constraintStart_toStartOf="parent"
@@ -262,7 +262,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:textColor="@android:color/white"
+ android:textColor="?android:attr/textColorPrimary"
android:text="@string/controls_media_settings_button" />
</FrameLayout>
@@ -283,7 +283,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:textColor="@android:color/white"
+ android:textColor="?android:attr/textColorPrimary"
android:text="@string/cancel" />
</FrameLayout>
@@ -304,7 +304,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:textColor="@android:color/white"
+ android:textColor="?android:attr/textColorPrimary"
android:text="@string/controls_media_dismiss_button"
/>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/navigation_layout.xml b/packages/SystemUI/res/layout/navigation_layout.xml
index 0e576fbe2245..64c7422c27ed 100644
--- a/packages/SystemUI/res/layout/navigation_layout.xml
+++ b/packages/SystemUI/res/layout/navigation_layout.xml
@@ -30,7 +30,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
- android:clipToPadding="false">
+ android:clipToPadding="false"
+ systemui:isVertical="false">
<LinearLayout
android:id="@+id/ends_group"
diff --git a/packages/SystemUI/res/layout/navigation_layout_vertical.xml b/packages/SystemUI/res/layout/navigation_layout_vertical.xml
index 4b6770042632..42e93249e95f 100644
--- a/packages/SystemUI/res/layout/navigation_layout_vertical.xml
+++ b/packages/SystemUI/res/layout/navigation_layout_vertical.xml
@@ -30,7 +30,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
- android:clipToPadding="false">
+ android:clipToPadding="false"
+ systemui:isVertical="true">
<com.android.systemui.navigationbar.buttons.ReverseLinearLayout
android:id="@+id/ends_group"
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
index 3c306322d21f..bad582669079 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -22,19 +22,14 @@
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical|end"
- android:focusable="true" >
+ android:focusable="true"
+ android:minWidth="48dp" >
- <FrameLayout
- android:id="@+id/background"
+ <LinearLayout
+ android:id="@+id/icons_container"
android:layout_height="@dimen/ongoing_appops_chip_height"
android:layout_width="wrap_content"
- android:minWidth="48dp"
- android:layout_gravity="center_vertical">
- <LinearLayout
- android:id="@+id/icons_container"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:gravity="center_vertical"
- />
- </FrameLayout>
+ android:gravity="center_vertical"
+ android:layout_gravity="center"
+ />
</com.android.systemui.privacy.OngoingPrivacyChip> \ 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 e9f3424547fa..b1c13287d203 100644
--- a/packages/SystemUI/res/layout/people_space_large_avatar_tile.xml
+++ b/packages/SystemUI/res/layout/people_space_large_avatar_tile.xml
@@ -19,70 +19,78 @@
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
- android:background="@drawable/people_space_round_tile_view_card"
+ android:background="@drawable/people_space_tile_view_card"
android:id="@+id/item"
- android:paddingVertical="6dp"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
- android:gravity="center_vertical"
- android:paddingStart="12dp"
+ android: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"
- android:layout_width="60dp"
- android:layout_height="60dp" />
-
- <LinearLayout
- android:background="@drawable/people_space_rounded_border"
- android:layout_marginStart="-12dp"
- android:layout_marginTop="28dp"
- android:layout_marginBottom="14dp"
- android:layout_width="16dp"
- android:layout_height="16dp">
+ 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:layout_width="60dp"
+ android:layout_height="60dp"/>
- <ImageView
- android:id="@+id/package_icon"
- android:layout_width="12dp"
- android:layout_marginStart="2dp"
- android:layout_marginEnd="2dp"
- android:layout_marginBottom="2dp"
- android:layout_marginTop="2dp"
- android:layout_height="12dp" />
- </LinearLayout>
+ <ImageView
+ android:id="@+id/package_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginStart="-20dp"
+ android:layout_marginTop="22dp"/>
<LinearLayout
android:orientation="vertical"
android:paddingStart="8dp"
- android:paddingEnd="12dp"
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="16sp"
+ android:textSize="14sp"
android:maxLines="1"
android:ellipsize="end"
android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"/>
<TextView
- android:id="@+id/status"
+ android:id="@+id/last_interaction"
+ android:text="@string/empty_status"
android:textColor="?android:attr/textColorSecondary"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:paddingVertical="3dp"
android:textSize="12sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="3"
- android:ellipsize="end" />
+ android:ellipsize="end"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/people_space_notification_content_tile.xml b/packages/SystemUI/res/layout/people_space_notification_content_tile.xml
new file mode 100644
index 000000000000..9ea7aa3c94e5
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_space_notification_content_tile.xml
@@ -0,0 +1,154 @@
+<?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_small_avatar_tile.xml b/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml
index 03589d34efd2..33004952d947 100644
--- a/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml
+++ b/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml
@@ -14,183 +14,144 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<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">
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="start">
- <TextView
- android:id="@+id/punctuation1"
- android:textColor="?android:attr/textColorSecondary"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textSize="36sp"
- android:textStyle="bold"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="5dp"
- android:maxLines="1"
- android:alpha="0.2"
- android:rotation="350" />
- <TextView
- android:id="@+id/punctuation2"
- android:textColor="?android:attr/textColorSecondary"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textSize="36sp"
- android:textStyle="bold"
+ <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">
+ <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">
+ <ImageView
+ android:id="@+id/image"
+ android:gravity="center"
+ android:background="@drawable/people_space_content_background"
+ 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"
- android:layout_marginStart="25dp"
- android:maxLines="1"
- android:alpha="0.2"
- android:rotation="5" />
+ 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/punctuation3"
- android:textColor="?android:attr/textColorSecondary"
+ android:id="@+id/status"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textSize="36sp"
- android:textStyle="bold"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="12sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="5dp"
- android:layout_marginStart="25dp"
- android:maxLines="1"
- android:alpha="0.2"
- android:rotation="355"/>
+ android:maxLines="2"
+ android:ellipsize="end"/>
+ </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">
<TextView
- android:id="@+id/punctuation4"
+ android:id="@+id/time"
android:textColor="?android:attr/textColorSecondary"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textSize="36sp"
- android:textStyle="bold"
+ android:textSize="12sp"
+ android:paddingVertical="2dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="-5dp"
- android:layout_marginStart="25dp"
android:maxLines="1"
- android:alpha="0.2"
- android:rotation="10" />
+ 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/punctuation5"
- android:textColor="?android:attr/textColorSecondary"
+ android:id="@+id/name"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textSize="36sp"
- android:textStyle="bold"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="5dp"
- android:layout_marginStart="25dp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp"
android:maxLines="1"
- android:alpha="0.2"
- android:rotation="15" />
- <TextView
- android:id="@+id/punctuation6"
- android:textColor="?android:attr/textColorSecondary"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textSize="36sp"
- android:textStyle="bold"
+ android:ellipsize="end"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="-5dp"
- android:layout_marginStart="25dp"
- android:maxLines="1"
- android:alpha="0.2"
- android:rotation="345" />
- </LinearLayout>
- <LinearLayout
- android:id="@+id/item"
- android:orientation="vertical"
- android:paddingTop="6dp"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout
- android:orientation="horizontal"
- android:paddingHorizontal="12dp"
- android:paddingBottom="4dp"
- android:gravity="top"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <ImageView
- android:id="@+id/person_icon"
- android:layout_width="34dp"
- android:layout_height="34dp" />
-
- <LinearLayout
- android:background="@drawable/people_space_rounded_border"
- android:layout_marginStart="-5dp"
- android:layout_marginTop="18dp"
- android:layout_width="8dp"
- android:layout_height="8dp">
-
- <ImageView
- android:id="@+id/package_icon"
- android:layout_width="6dp"
- android:layout_marginEnd="1dp"
- android:layout_marginStart="1dp"
- android:layout_marginBottom="1dp"
- android:layout_marginTop="1dp"
- android:layout_height="6dp" />
- </LinearLayout>
-
- <LinearLayout
- android:orientation="vertical"
- android:paddingStart="6dp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <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" />
-
- <TextView
- android:id="@+id/time"
- android:textColor="?android:attr/textColorSecondary"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textSize="10sp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLines="1"
- android:ellipsize="end" />
- </LinearLayout>
- </LinearLayout>
- <LinearLayout
- android:id="@+id/content_background"
- android:background="@drawable/people_space_content_background"
- android:layout_gravity="center"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <TextView
- android:id="@+id/content"
- android:paddingVertical="3dp"
- android:paddingHorizontal="12dp"
- android:gravity="center"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textSize="16sp"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:maxLines="2"
- android:ellipsize="end" />
- <ImageView
- android:id="@+id/image"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:scaleType="centerCrop"/>
- </LinearLayout>
+ android:layout_height="wrap_content"/>
</LinearLayout>
</RelativeLayout>
</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/pip_menu_activity.xml b/packages/SystemUI/res/layout/pip_menu_activity.xml
deleted file mode 100644
index 2b33e17a5fbd..000000000000
--- a/packages/SystemUI/res/layout/pip_menu_activity.xml
+++ /dev/null
@@ -1,93 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/background"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <!-- Menu layout -->
- <FrameLayout
- android:id="@+id/menu_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:forceHasOverlappingRendering="false"
- android:accessibilityTraversalAfter="@id/dismiss">
-
- <!-- The margins for this container is calculated in the code depending on whether the
- actions_container is visible. -->
- <FrameLayout
- android:id="@+id/expand_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <ImageButton
- android:id="@+id/expand_button"
- android:layout_width="60dp"
- android:layout_height="60dp"
- android:layout_gravity="center"
- android:contentDescription="@string/pip_phone_expand"
- android:padding="10dp"
- android:src="@drawable/pip_expand"
- android:background="?android:selectableItemBackgroundBorderless" />
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/actions_container"
- android:layout_width="match_parent"
- android:layout_height="@dimen/pip_action_size"
- android:layout_gravity="bottom"
- android:visibility="invisible">
- <LinearLayout
- android:id="@+id/actions_group"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center_horizontal"
- android:orientation="horizontal"
- android:divider="@android:color/transparent"
- android:showDividers="middle" />
- </FrameLayout>
- </FrameLayout>
-
- <ImageButton
- android:id="@+id/settings"
- android:layout_width="@dimen/pip_action_size"
- android:layout_height="@dimen/pip_action_size"
- android:layout_gravity="top|start"
- android:padding="@dimen/pip_action_padding"
- android:contentDescription="@string/pip_phone_settings"
- android:src="@drawable/ic_settings"
- android:background="?android:selectableItemBackgroundBorderless" />
-
- <ImageButton
- android:id="@+id/dismiss"
- android:layout_width="@dimen/pip_action_size"
- android:layout_height="@dimen/pip_action_size"
- android:layout_gravity="top|end"
- android:padding="@dimen/pip_action_padding"
- android:contentDescription="@string/pip_phone_close"
- android:src="@drawable/ic_close_white"
- android:background="?android:selectableItemBackgroundBorderless" />
-
- <!--TODO (b/156917828): Add content description for a11y purposes?-->
- <ImageButton
- android:id="@+id/resize_handle"
- android:layout_width="@dimen/pip_resize_handle_size"
- android:layout_height="@dimen/pip_resize_handle_size"
- android:layout_gravity="top|start"
- android:layout_margin="@dimen/pip_resize_handle_margin"
- android:src="@drawable/pip_resize_handle"
- android:background="?android:selectableItemBackgroundBorderless" />
-</FrameLayout>
diff --git a/packages/SystemUI/res/layout/punctuation_layout.xml b/packages/SystemUI/res/layout/punctuation_layout.xml
new file mode 100644
index 000000000000..25c7648530d6
--- /dev/null
+++ b/packages/SystemUI/res/layout/punctuation_layout.xml
@@ -0,0 +1,100 @@
+<?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:id="@+id/punctuation_layout"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="start">
+ <TextView
+ android:id="@+id/punctuation1"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textSize="36sp"
+ android:textStyle="bold"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dp"
+ android:maxLines="1"
+ android:alpha="0.2"
+ android:rotation="350"/>
+ <TextView
+ android:id="@+id/punctuation2"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textSize="36sp"
+ android:textStyle="bold"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="25dp"
+ android:maxLines="1"
+ android:alpha="0.2"
+ android:rotation="5"/>
+ <TextView
+ android:id="@+id/punctuation3"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textSize="36sp"
+ android:textStyle="bold"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dp"
+ android:layout_marginStart="25dp"
+ android:maxLines="1"
+ android:alpha="0.2"
+ android:rotation="355"/>
+ <TextView
+ android:id="@+id/punctuation4"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textSize="36sp"
+ android:textStyle="bold"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-5dp"
+ android:layout_marginStart="25dp"
+ android:maxLines="1"
+ android:alpha="0.2"
+ android:rotation="10"/>
+ <TextView
+ android:id="@+id/punctuation5"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textSize="36sp"
+ android:textStyle="bold"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dp"
+ android:layout_marginStart="25dp"
+ android:maxLines="1"
+ android:alpha="0.2"
+ android:rotation="15"/>
+ <TextView
+ android:id="@+id/punctuation6"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textSize="36sp"
+ android:textStyle="bold"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-5dp"
+ android:layout_marginStart="25dp"
+ android:maxLines="1"
+ android:alpha="0.2"
+ android:rotation="345"/>
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index dc34127496f6..059bda3a2376 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -54,16 +54,4 @@
android:paddingBottom="10dp"
android:importantForAccessibility="yes" />
- <TextView
- android:id="@+id/header_debug_info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:fontFamily="sans-serif-condensed"
- android:padding="2dp"
- android:textColor="#00A040"
- android:textSize="11dp"
- android:textStyle="bold"
- android:visibility="invisible"/>
-
</com.android.systemui.qs.QuickStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index d6385ffbcc0c..a8ef7c346b95 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -31,6 +31,18 @@
android:layout_height="match_parent"
android:visibility="gone" />
+ <ViewStub
+ android:id="@+id/keyguard_qs_user_switch_stub"
+ android:layout="@layout/keyguard_qs_user_switch"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent" />
+
+ <ViewStub
+ android:id="@+id/keyguard_user_switcher_stub"
+ android:layout="@layout/keyguard_user_switcher"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent" />
+
<include
layout="@layout/keyguard_status_view"
android:visibility="gone" />
@@ -57,6 +69,13 @@
systemui:layout_constraintEnd_toEndOf="parent"
/>
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/qs_edge_guideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ systemui:layout_constraintGuide_percent="0.5"
+ android:orientation="vertical"/>
+
<com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
android:id="@+id/notification_stack_scroller"
android:layout_marginTop="@dimen/notification_panel_margin_top"
@@ -72,12 +91,6 @@
<include layout="@layout/photo_preview_overlay" />
- <ViewStub
- android:id="@+id/keyguard_user_switcher"
- android:layout="@layout/keyguard_user_switcher"
- android:layout_height="match_parent"
- android:layout_width="match_parent" />
-
<include
layout="@layout/keyguard_status_bar"
android:visibility="invisible" />
diff --git a/packages/SystemUI/res/layout/text_toast.xml b/packages/SystemUI/res/layout/text_toast.xml
new file mode 100644
index 000000000000..de4e062805fe
--- /dev/null
+++ b/packages/SystemUI/res/layout/text_toast.xml
@@ -0,0 +1,51 @@
+<?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="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxWidth="@dimen/toast_width"
+ android:orientation="horizontal"
+ android:background="@drawable/toast_background"
+ android:backgroundTint="?android:attr/colorBackground"
+ android:layout_marginEnd="16dp"
+ android:layout_marginStart="16dp"
+ android:gravity="center_vertical">
+
+ <!-- Icon should be 24x24, make slightly larger to allow for shadowing, adjust via padding -->
+ <ImageView
+ android:id="@+id/icon"
+ android:alpha="@dimen/toast_icon_alpha"
+ android:padding="11.5dp"
+ android:layout_width="@dimen/toast_icon_size"
+ android:layout_height="@dimen/toast_icon_size"/>
+ <TextView
+ android:id="@+id/text"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:paddingStart="0dp"
+ android:paddingEnd="22dp"
+ android:textSize="@dimen/toast_text_size"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+</LinearLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 5db7c25835c8..68c7467b7bf2 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Rollees skermskoot"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Maak skermkiekie toe"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Skermkiekievoorskou"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Boonste grens"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Onderste grens"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skermopnemer"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Verwerk tans skermopname"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Deurlopende kennisgewing vir \'n skermopnamesessie"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Tot sonsopkoms"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aan om <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Tot <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Verminder helderheid"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is gedeaktiveer"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is geaktiveer"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Wys profiel"</string>
<string name="user_add_user" msgid="4336657383006913022">"Voeg gebruiker by"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Nuwe gebruiker"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Beëindig gastesessie?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Beëindig gastesessie"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Verwyder gas?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Beëindig sessie"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Verwyder"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Welkom terug, gas!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Wiil jy jou sessie voortsit?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Begin van voor af"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Jou organisasie het \'n sertifikaatoutoriteit in jou werkprofiel geïnstalleer. Jou veilige netwerkverkeer kan gemonitor of gewysig word."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"\'n Sertifikaatoutoriteit is op hierdie toestel geïnstalleer. Jou veilige netwerkverkeer kan gemonitor of gewysig word."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Jou administrateur het netwerkloginskrywing aangeskakel, wat verkeer op jou toestel monitor."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Jou administrateur het netwerkloglêers aangeskakel wat verkeer in jou werkprofiel maar nie in jou persoonlike profiel monitor nie."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Jy is gekoppel aan <xliff:g id="VPN_APP">%1$s</xliff:g>, wat jou netwerkaktiwiteit, insluitend e-posse, programme en webwerwe, kan monitor."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Jy is gekoppel aan <xliff:g id="VPN_APP_0">%1$s</xliff:g> en <xliff:g id="VPN_APP_1">%2$s</xliff:g>, wat jou netwerkaktiwiteit, insluitend e-posse, programme en webwerwe, kan monitor."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Jou werkprofiel is gekoppel aan <xliff:g id="VPN_APP">%1$s</xliff:g>, wat jou netwerkaktiwiteit, insluitend e-posse, programme en webwerwe, kan monitor."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Hierdie kennisgewing is outomaties deur die stelsel &lt;b&gt;na Stil gedegradeer&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Hierdie kennisgewing is outomaties &lt;b&gt;hoër gegradeer&lt;/b&gt; in jou skakering."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Hierdie kennisgewing is outomaties &lt;b&gt;laer gegradeer&lt;/b&gt; in jou skakering."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Is dit korrek?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Gee jou terugvoer aan die ontwikkelaar deur. Is dit korrek?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Dankie vir jou terugvoer!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kennisgewingkontroles vir <xliff:g id="APP_NAME">%1$s</xliff:g> is oopgemaak"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Skuif na <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Voeg by posisie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Teël is bygevoeg"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Teël is verwyder"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kitsinstellingswysiger."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-kennisgewing: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Maak instellings oop."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> gebruik tans die <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> het onlangs die <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> gebruik"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(onderneming)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Oproep"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Oproep"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(deur <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ligging"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensors is af"</string>
<string name="device_services" msgid="1549944177856658705">"Toesteldienste"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Titelloos"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Tik om hierdie program te herbegin en maak volskerm oop."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Beweeg"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Stelselnavigasie is opgedateer. Gaan na Instellings toe om veranderinge te maak."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Gaan na Instellings toe om stelselnavigasie op te dateer"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 1af18fee0fb9..7618af8cfda4 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"ቅጽበታዊ ገጽ ዕይታን ይሸብልሉ"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"ቅጽበታዊ ገጽ ዕይታን አሰናብት"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"የቅጽበታዊ ገጽ ዕይታ ቅድመ-ዕይታ"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"የላይኛው ወሰን"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"የታችኛው ወሰን"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"የማያ መቅጃ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"የማያ ገጽ ቀረጻን በማሰናዳት ላይ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ለአንድ የማያ ገጽ ቀረጻ ክፍለ-ጊዜ በመካሄድ ያለ ማሳወቂያ"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ጸሐይ እስክትወጣ ድረስ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> ላይ ይበራል"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"እስከ <xliff:g id="TIME">%s</xliff:g> ድረስ"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"ብሩህነትን ይቀንሱ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"ኤንኤፍሲ"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"ኤንኤፍሲ ተሰናክሏል"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"ኤንኤፍሲ ነቅቷል"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"መገለጫ አሳይ"</string>
<string name="user_add_user" msgid="4336657383006913022">"ተጠቃሚ አክል"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"አዲስ ተጠቃሚ"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"የእንግዳ ክፍለ-ጊዜ ይብቃ?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"የእንግዳ ክፍለ-ጊዜ ጨርስ"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"እንግዳ ይወገድ?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ክፍለ-ጊዜን አብቃ"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"አስወግድ"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"እንኳን በደህና ተመለሱ እንግዳ!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"ክፍለ-ጊዜዎን መቀጠል ይፈልጋሉ?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"እንደገና ጀምር"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"የእርስዎ ድርጅት የእውቅና ማረጋገጫ ሰጪ ባለሥልጣን በእርስዎ የሥራ መገለጫ ላይ ጭኗል። የእርስዎ ደኅንነቱ የተጠበቀ አውታረ መረብ ትራፊክ ክትትል ሊደረግበት እና ሊሻሻል ይችላል።"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"የእውቅና ማረጋገጫ ሰጪ ባለሥልጣን በዚህ መሣሪያ ላይ ተጭኗል። የእርስዎ ደኅንነቱ የተጠበቀ አውታረ መረብ ትራፊክ ክትትል ሊደረግበት እና ሊሻሻል ይችላል።"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"የእርስዎ አስተዳዳሪ የአውታረ መረብ ምዝግብ ማስታወሻ መያዝን አብርተዋል፣ ይህም በመሣሪያዎ ላይ ያለውን ትራፊክ ይከታተላል።"</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"የእርስዎ አስተዳዳሪ በስራ መገለጫዎ ውስጥ፣ ግን በግል መገለጫዎ ላይ ሳይሆን፣ ትራፊክን የሚቆጣጠር የአውታረ መረብ ምዝግብ ማስታወሻ አብርተዋል።"</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"እርስዎ ኢሜይሎችን፣ መተግበሪያዎችን እና ድር ጣቢያዎችንም ጨምሮ የግል የአውታረ መረብ እንቅስቃሴዎን መከታተል ከሚችለው <xliff:g id="VPN_APP">%1$s</xliff:g> ጋር ተገናኝተዋል።"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"ኢሜይሎችን፣ መተግበሪያዎችን እና ድር ጣቢያዎችንም ጨምሮ የግል የአውታረ መረብ እንቅስቃሴዎን መከታተል ከሚችሉት <xliff:g id="VPN_APP_0">%1$s</xliff:g> እና <xliff:g id="VPN_APP_1">%2$s</xliff:g> ጋር ተገናኝተዋል።"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"የእርስዎ የሥራ መገለጫ የእርስዎን ኢሜይሎችን፣ መተግበሪያዎችን እና ድር ጣቢያዎችንም ጨምሮ የግል የአውታረ መረብ እንቅስቃሴዎን መከታተል ከሚችለው <xliff:g id="VPN_APP">%1$s</xliff:g> ጋር ተገናኝቷል።"</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"ይህ ማሳወቂያ በሥርዓቱ በራስ-ሰር &lt;b&gt;ወደ ዝምታ ዝቅ ተደርጓል &lt;/b&gt;።"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"ይህ ማሳወቂያ በራስ-ሰር በጥላው ውስጥ &lt;b&gt;ከፍተኛ ደረጃ ተሰጥቶታል&lt;/b&gt;።"</string>
<string name="feedback_demoted" msgid="951884763467110604">"ይህ ማሳወቂያ በራስ-ሰር በጥላው ውስጥ &lt;b&gt;ዝቅተኛ ደረጃ ተሰጥቶታል&lt;/b&gt;።"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"ይህ ትክክል ነበር?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"ግብረመልስዎን ገንቢው እንዲያውቅ ያድርጉ። ይህ ትክክል ነበር?"</string>
<string name="feedback_response" msgid="4671729244976641339">"ለግብረመልስዎ እናመሰግናለን!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"እሺ"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"የ<xliff:g id="APP_NAME">%1$s</xliff:g> ማሳወቂያ መቆጣጠሪያዎች ተከፍተዋል"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ውሰድ"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ቦታ አክል"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"የ<xliff:g id="POSITION">%1$d</xliff:g> አቀማመጥ"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ሰቅ ታክሏል"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ሰቅ ተወግዷል"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"የፈጣን ቅንብሮች አርታዒ።"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"የ<xliff:g id="ID_1">%1$s</xliff:g> ማሳወቂያ፦ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ቅንብሮችን ክፈት።"</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>ን እየተጠቀመ ነው"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> በቅርብ ጊዜ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>ን ይጠቀማል።"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ድርጅት)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"የስልክ ጥሪ"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"የስልክ ጥሪ"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(እስከ <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"ካሜራ"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"አካባቢ"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"ዳሳሾች ጠፍተዋል"</string>
<string name="device_services" msgid="1549944177856658705">"የመሣሪያ አገልግሎቶች"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"ርዕስ የለም"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"ይህን መተግበሪያ እንደገና ለማስጀመር መታ ያድርጉ እና ወደ ሙሉ ማያ ገጽ ይሂዱ።"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"አንቀሳቅስ"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"የስርዓት ዳሰሳ ተዘምኗል። ለውጦችን ለማድረግ ወደ ቅንብሮች ይሂዱ።"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"የስርዓት ዳሰሳን ለማዘመን ወደ ቅንብሮች ይሂዱ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index b34b39a765b1..67622c7eed7b 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"لقطة شاشة موصولة"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"إغلاق لقطة الشاشة"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"معاينة لقطة الشاشة"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"الحد العلوي"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"الحد السفلي"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"مسجّل الشاشة"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"جارٍ معالجة تسجيل الشاشة"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"إشعار مستمر لجلسة تسجيل شاشة"</string>
@@ -418,6 +420,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"حتى شروق الشمس"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"تفعيل الوضع في <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"حتى <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"تقليل السطوع"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"تم إيقاف الاتصال القريب المدى"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"تم تفعيل الاتصال القريب المدى"</string>
@@ -448,7 +451,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"انقر مرة أخرى للفتح"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"يمكنك الفتح بالتمرير سريعًا لأعلى."</string>
<string name="keyguard_retry" msgid="886802522584053523">"مرِّر سريعًا للأعلى لإعادة المحاولة."</string>
- <string name="require_unlock_for_nfc" msgid="1305686454823018831">"‏يجب فتح قفل الشاشة لاستخدام تقنية الاتصال قصير المدى (NFC)."</string>
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"‏افتح قفل الشاشة لاستخدام تقنية NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"هذا الجهاز يخص مؤسستك."</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"هذا الجهاز يخص <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>."</string>
<string name="phone_hint" msgid="6682125338461375925">"يمكنك التمرير سريعًا من الرمز لتشغيل الهاتف"</string>
@@ -471,9 +474,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"عرض الملف الشخصي"</string>
<string name="user_add_user" msgid="4336657383006913022">"إضافة مستخدم"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"مستخدم جديد"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"هل تريد إنهاء جلسة الضيف؟"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"إنهاء جلسة الضيف"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"هل تريد إزالة جلسة الضيف؟"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"سيتم حذف كل التطبيقات والبيانات في هذه الجلسة."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"إنهاء الجلسة"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"إزالة"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"مرحبًا بك مجددًا في جلسة الضيف"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"هل تريد متابعة جلستك؟"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"البدء من جديد"</string>
@@ -552,6 +556,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ثبّتت مؤسستك مرجعًا مصدّقًا في ملفك الشخصي للعمل. قد تتم مراقبة حركة بيانات شبكتك الآمنة أو تعديلها."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"تم تثبيت مرجع مصدّق على هذا الجهاز. قد تتم مراقبة حركة بيانات شبكتك الآمنة أو تعديلها."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"شغَّل المشرف ميزة تسجيل بيانات الشبكة، والتي يتم من خلالها مراقبة حركة البيانات على جهازك."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"شغَّل المشرف ميزة تسجيل بيانات الشبكة، والتي يتم من خلالها مراقبة حركة البيانات في ملفك الشخصي للعمل ولكن لا تتم مراقبتها في ملفك الشخصي."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"لقد اتصلت بتطبيق <xliff:g id="VPN_APP">%1$s</xliff:g>، الذي يمكن أن يراقب نشاط شبكتك، بما في ذلك رسائل البريد الإلكتروني والتطبيقات والمواقع الإلكترونية."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"لقد اتصلت بتطبيق <xliff:g id="VPN_APP_0">%1$s</xliff:g> و<xliff:g id="VPN_APP_1">%2$s</xliff:g> اللذين يمكنهما مراقبة نشاط شبكتك، بما في ذلك رسائل البريد الإلكتروني والتطبيقات والمواقع الإلكترونية."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"تم ربط الملف الشخصي للعمل بـ <xliff:g id="VPN_APP">%1$s</xliff:g>، الذي يمكنه مراقبة أنشطتك الشخصية على الشبكة، بما في ذلك الرسائل الإلكترونية والتطبيقات ومواقع الويب."</string>
@@ -745,7 +750,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"‏تم تلقائيًا &lt;b&gt;خفض ترتيب هذا الإشعار إلى الوضع صامت&lt;/b&gt; من خلال النظام."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"‏تم تلقائيًا &lt;b&gt;زيادة ترتيب&lt;/b&gt; هذا الإشعار في مركز الإشعارات."</string>
<string name="feedback_demoted" msgid="951884763467110604">"‏تم تلقائيًا &lt;b&gt;خفض ترتيب&lt;/b&gt; هذا الإشعار في مركز الإشعارات."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"هل كان هذا صحيحًا؟"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"أخبِر مطوِّر البرامج برأيك. هل كان هذا صحيحًا؟"</string>
<string name="feedback_response" msgid="4671729244976641339">"شكرًا على تعليقك"</string>
<string name="feedback_ok" msgid="6481426753298857144">"حسنًا"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"تم فتح عناصر التحكم في الإشعارات لتطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -900,6 +905,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"الانتقال إلى <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"الإضافة إلى الموضع <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"الموضع: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"تمت إضافة البطاقة."</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"تمت إزالة البطاقة."</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"برنامج تعديل الإعدادات السريعة."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"إشعار <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"فتح الإعدادات."</string>
@@ -987,10 +994,10 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"تستخدم التطبيقات <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"، "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" و "</string>
- <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"يستخدم تطبيق <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> عملية <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> الآن."</string>
- <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"استخدَم تطبيق <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> عملية <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> مؤخرًا."</string>
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"يستخدم تطبيق <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ميزة <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> الآن."</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"استخدَم تطبيق <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ميزة <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> مؤخرًا."</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(للمؤسسات)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"مكالمة هاتفية"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"المكالمات الهاتفية"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(من خلال <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"الكاميرا"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"الموقع"</string>
@@ -998,7 +1005,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"إيقاف أجهزة الاستشعار"</string>
<string name="device_services" msgid="1549944177856658705">"خدمات الأجهزة"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"بلا عنوان"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"انقر لإعادة تشغيل هذا التطبيق والانتقال إلى وضع ملء الشاشة."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"نقل"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"تم تحديث التنقل داخل النظام. لإجراء التغييرات، يُرجى الانتقال إلى \"الإعدادات\"."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"الانتقال إلى \"الإعدادات\" لتعديل التنقل داخل النظام"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 88b2bb278995..b1b28ad130bd 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"স্ক্ৰীনশ্বট স্ক্ৰ’ল কৰক"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"স্ক্ৰীনশ্বট অগ্ৰাহ্য কৰক"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"স্ক্ৰীনশ্বটৰ পূৰ্বদৰ্শন"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"ওপৰৰ সীমাৰেখা"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"তলৰ সীমাৰেখা"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"স্ক্ৰীন ৰেকৰ্ডাৰ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"স্ক্রীন ৰেকৰ্ডিঙৰ প্ৰক্ৰিয়াকৰণ হৈ আছে"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"স্ক্রীণ ৰেকৰ্ডিং ছেশ্বন চলি থকা সময়ত পোৱা জাননী"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"সূৰ্যোদয়লৈকে"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>ত অন কৰক"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> পৰ্যন্ত"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"উজ্জ্বলতা কমাওক"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC নিষ্ক্ৰিয় হৈ আছে"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC সক্ষম হৈ আছে"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"প্ৰ\'ফাইল দেখুৱাওক"</string>
<string name="user_add_user" msgid="4336657383006913022">"ব্যৱহাৰকাৰী যোগ কৰক"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"নতুন ব্যৱহাৰকাৰী"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"অতিথিৰ ছেশ্বন সমাপ্ত কৰিবনে?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"অতিথিৰ ছেশ্বন সমাপ্ত কৰক"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"অতিথি আঁতৰাবনে?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই ছেশ্বনৰ সকলো এপ্ আৰু ডেটা মচা হ\'ব।"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ছেশ্বন সমাপ্ত কৰক"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"আঁতৰাওক"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"আপোনাক পুনৰাই স্বাগতম জনাইছোঁ!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"আপুনি আপোনাৰ ছেশ্বন অব্যাহত ৰাখিব বিচাৰেনে?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আকৌ আৰম্ভ কৰক"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"আপোনাৰ প্ৰতিষ্ঠানে আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইলটোত এটা প্ৰমাণপত্ৰ সম্পৰ্কীয় কৰ্তৃপক্ষ ইনষ্টল কৰিছে। আপোনাৰ সুৰক্ষিত নেটৱৰ্কৰ ট্ৰেফিক পৰ্যবেক্ষণ বা সংশোধন কৰা হ\'ব পাৰে।"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"এই ডিভাইচটোত এটা প্ৰমাণপত্ৰ সম্পৰ্কীয় কৰ্তৃপক্ষ ইনষ্টল কৰা হৈছে। আপোনাৰ সুৰক্ষিত নেটৱৰ্কৰ ট্ৰেফিক পৰ্যবেক্ষণ বা সংশোধন কৰা হ\'ব পাৰে।"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"আপোনাৰ প্ৰশাসকে নেটৱৰ্ক লগিং অন কৰিছে, যিয়ে আপোনাৰ ডিভাইচটোত নেটৱৰ্ক ট্ৰেফিক পৰ্যবেক্ষণ কৰে।"</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"আপোনাৰ প্ৰশাসকে নেটৱৰ্ক লগিং অন কৰিছে, যিয়ে আপোনাৰ কৰ্মস্থানৰ প্ৰ’ফাইলত ট্ৰেফিক নিৰীক্ষণ কৰে কিন্তু আপোনাৰ ব্যক্তিগত প্ৰ’ফাইলত নকৰে।"</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"আপুনি <xliff:g id="VPN_APP">%1$s</xliff:g>ৰে সংযুক্ত হৈ আছে যিয়ে আপোনাৰ ইমেইল, এপ্ আৰু ৱেবছাইটকে ধৰি নেটৱর্কৰ কাৰ্যকলাপ পৰ্যবেক্ষণ কৰিব পাৰে।"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"আপুনি <xliff:g id="VPN_APP_0">%1$s</xliff:g> আৰু <xliff:g id="VPN_APP_1">%2$s</xliff:g>ৰে সংযুক্ত হৈ আছে, যিয়ে আপোনাৰ ইমেইল, এপ্ আৰু ৱেবছাইটকে ধৰি নেটৱর্কৰ কাৰ্যকলাপ পৰ্যবেক্ষণ কৰিব পাৰে।"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"আপুনি <xliff:g id="VPN_APP">%1$s</xliff:g>ৰে সংযুক্ত হৈ আছে যিয়ে আপোনাৰ ইমেইল, এপ্ আৰু ৱেবছাইটকে ধৰি নেটৱর্কৰ কাৰ্যকলাপ পৰ্যবেক্ষণ কৰিব পাৰে।"</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"ছিষ্টেমটোৱে স্বয়ংক্ৰিয়ভাৱে এই জাননীটোৰ ক্ষেত্ৰত দিয়া &lt;b&gt;গুৰুত্ব নীৰৱ&lt;/b&gt;লৈ হ্ৰাস কৰিছে।"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"আপোনাৰ শ্বেডত স্বয়ংক্ৰিয়ভাৱে এই জাননীটোৰ &lt;b&gt;স্থান ওপৰলৈ&lt;/b&gt; কৰা হৈছে।"</string>
<string name="feedback_demoted" msgid="951884763467110604">"আপোনাৰ শ্বেডত স্বয়ংক্ৰিয়ভাৱে এই জাননীটোৰ &lt;b&gt;স্থান তললৈ&lt;/b&gt; কৰা হৈছে।"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"এইটো শুদ্ধ আছিলনে?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"বিকাশকৰ্তাক আপোনাৰ মতামত জনাওক। এইটো শুদ্ধ আছিলনে?"</string>
<string name="feedback_response" msgid="4671729244976641339">"আপোনাৰ মতামতৰ বাবে ধন্যবাদ!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ঠিক আছে"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ জাননী নিয়ন্ত্ৰণসমূহ খোলা অৱস্থাত আছে"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰলৈ স্থানান্তৰ কৰক"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থানত যোগ দিয়ক"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থান"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"টাইল যোগ দিয়া হৈছে"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"টাইল আঁতৰোৱা হৈছে"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ক্ষিপ্ৰ ছেটিংসমূহৰ সম্পাদক।"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> জাননী: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ছেটিংসমূহ খোলক।"</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>এ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ব্যৱহাৰ কৰি আছে"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>এ শেহতীয়াকৈ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ব্যৱহাৰ কৰিছে"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(এণ্টাৰপ্ৰাইজ)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ফ’ন কল"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g>ৰ জৰিয়তে)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"কেমেৰা"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"অৱস্থান"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"ছেন্সৰ অফ হৈ আছে"</string>
<string name="device_services" msgid="1549944177856658705">"ডিভাইচ সেৱা"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"কোনো শিৰোনাম নাই"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"এপ্‌টো ৰিষ্টাৰ্ট কৰক আৰু পূৰ্ণ স্ক্ৰীণ ব্যৱহাৰ কৰক।"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"আঁতৰাওক"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"ছিষ্টেম নেভিগেশ্বন আপডে’ট কৰা হ’ল। সলনি কৰিবলৈ ছেটিংসমূহ-লৈ যাওক।"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ছিষ্টেম নেভিগেশ্বন আপডে’ট কৰিবলৈ ছেটিংসমূহ-লৈ যাওক"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 6f1c347a5987..fed444b9083f 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Sürüşdürülərək çəkilən skrinşot"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ekran şəklini ötürün"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran şəklinə önbaxış"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Yuxarı hüdud"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Aşağı hüdud"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekran Yazıcısı"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran çəkilişi emal edilir"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekranın video çəkimi ərzində silinməyən bildiriş"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Şəfəq vaxtına qədər"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Bu vaxt aktiv olur: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Bu vaxtadək: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Parlaqlığı Azaldın"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC deaktiv edilib"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC aktiv edilib"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string>
<string name="user_add_user" msgid="4336657383006913022">"İstifadəçi əlavə edin"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Yeni istifadəçi"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Qonaq sessiyası bitirilsin?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Qonaq sessiyasını bitirin"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Qonaq silinsin?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Sessiyanı bitirin"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Yığışdır"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Xoş gəlmisiniz!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Sessiya davam etsin?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Yenidən başlayın"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Təşkilat iş profilində sertifikat səlahiyyəti quraşdırdı. Təhlükəsiz şəbəkə ötürülməsinə nəzarət edilə və ya dəyişdirilə bilər."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Bu cihazda sertifikat səlahiyyəti quraşdırıldı. Təhlükəsiz şəbəkə ötürülməsinə nəzarət edilə və ya dəyişdirilə bilər."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Admin cihazda şəbəkə ötürülməsinə nəzarət edən şəbəkə qeydlərini aktiv etdi."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Admininiz şəxsi profilinizdəki deyil, iş profilinizdəki trafikə nəzarət edən şəbəkə qeydiyyatını aktiv edib."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"E-poçt, tətbiq və veb saytlar daxil olmaqla şəbəkə fəaliyyətinə nəzarət edən <xliff:g id="VPN_APP">%1$s</xliff:g> tətbiqinə qoşulusunuz."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"E-poçt, tətbiq və veb saytlar daxil olmaqla şəbəkə fəaliyyətinə nəzarət edən <xliff:g id="VPN_APP_0">%1$s</xliff:g> və <xliff:g id="VPN_APP_1">%2$s</xliff:g> tətbiqlərinə qoşulusunuz."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"İş profili e-poçt, tətbiq və veb saytlar da daxil olmaqla şəbəkə fəaliyyətinə nəzarət edən <xliff:g id="VPN_APP">%1$s</xliff:g> tətbiqinə qoşuludur."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Bu bildiriş sistem tərəfindən avtomatik olaraq &lt;b&gt;Səssiz rejimə keçirilib&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Bu bildiriş avtomatik olaraq siyahıda &lt;b&gt;yuxarı sıraya keçirilib&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Bu bildiriş avtomatik olaraq siyahıda &lt;b&gt;aşağı sıraya keçirilib&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Bu, doğru oldu?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Developerə rəyinizi bildirin. Bu, doğru idi?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Rəyiniz üçün təşəkkür edirik!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün bildiriş kontrolları açıqdır"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə köçürün"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə əlavə edin"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyi"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Mozaik əlavə edilib"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Mozaik silinib"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sürətli ayarlar redaktoru."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildiriş: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ayarları açın."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> istifadə edir"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> bu yaxınlarda <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> istifadə edib"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(korporativ)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefon zəngi"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefon zəngi"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> vasitəsilə)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"məkan"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensorlar deaktivdir"</string>
<string name="device_services" msgid="1549944177856658705">"Cihaz Xidmətləri"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Başlıq yoxdur"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Bu tətbiqi sıfırlayaraq tam ekrana keçmək üçün klikləyin."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Hərəkət etdirin"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Sistem naviqasiyası yeniləndi. Dəyişiklik etmək üçün Ayarlara daxil olun."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Sistem naviqasiyasını yeniləmək üçün Ayarlara keçin"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 2185a66477a0..ded8fa591f0b 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Pomerajte snimak ekrana"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Odbacite snimak ekrana"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimka ekrana"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Gornja granica"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Donja granica"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrađujemo video snimka ekrana"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Obaveštenje o sesiji snimanja ekrana je aktivno"</string>
@@ -412,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do izlaska sunca"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Uključuje se u <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Smanji osvetljenost"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je onemogućen"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je omogućen"</string>
@@ -465,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Prikaži profil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Dodaj korisnika"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Želite da završite sesiju gosta?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Završi sesiju gosta"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite li da uklonite gosta?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Završi sesiju"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Dobro došli nazad, goste!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li da nastavite sesiju?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni iz početka"</string>
@@ -543,6 +547,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizacija je na poslovnom profilu instalirala autoritet za izdavanje sertifikata. Bezbedni mrežni saobraćaj može da se prati ili menja."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Na ovom uređaju je instaliran autoritet za izdavanje sertifikata. Bezbedni mrežni saobraćaj može da se prati ili menja."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administrator je uključio evidentiranje mreže, koje prati saobraćaj na uređaju."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administrator je uključio evidentiranje mreže, koje prati saobraćaj na poslovnom profilu, ali ne i na ličnom profilu."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Povezani ste sa aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>, koja može da nadgleda aktivnosti na mreži, uključujući imejlove, aplikacije i veb-sajtove."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Povezani ste sa aplikacijama <xliff:g id="VPN_APP_0">%1$s</xliff:g> i <xliff:g id="VPN_APP_1">%2$s</xliff:g>, koje mogu da nadgledaju aktivnosti na mreži, uključujući imejlove, aplikacije i veb-sajtove."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Poslovni profil je povezan sa aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>, koja može da nadgleda aktivnosti na mreži, uključujući imejlove, aplikacije i veb-sajtove."</string>
@@ -736,7 +741,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Sistem je ovo obaveštenje automatski &lt;b&gt;degradirao u Nečujno&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Ovo obaveštenje je automatski &lt;b&gt;rangirano više&lt;/b&gt; na traci sa obaveštenjima."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Ovo obaveštenje je automatski &lt;b&gt;rangirano niže&lt;/b&gt; na traci sa obaveštenjima."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Da li je to tačno?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Pošaljite programeru povratne informacije. Da li je to tačno?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Hvala vam na povratnim informacijama!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"Potvrdi"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kontrole obaveštenja za otvaranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -885,6 +890,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premestite na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajte na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. pozicija"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Pločica je dodata"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Pločica je uklonjena"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač za Brza podešavanja."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Obaveštenja za <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvori Podešavanja."</string>
@@ -975,7 +982,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> koristi: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> je nedavno koristila: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(za preduzeća)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonski poziv"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonski poziv"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(preko: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kameru"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokaciju"</string>
@@ -983,7 +990,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Senzori su isključeni"</string>
<string name="device_services" msgid="1549944177856658705">"Usluge za uređaje"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Bez naslova"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Dodirnite da biste restartovali aplikaciju i prešli u režim celog ekrana."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Premesti"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigacija sistema je ažurirana. Da biste uneli izmene, idite u Podešavanja."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Idite u Podešavanja da biste ažurirali navigaciju sistema"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index e5356ae6486c..18756f8c8083 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Здымак экрана з пракруткай"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Адхіліць здымак экрана"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Перадпрагляд здымка экрана"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Верхняя граніца"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Ніжняя граніца"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Запіс экрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Апрацоўваецца запіс экрана"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Бягучае апавяшчэнне для сеанса запісу экрана"</string>
@@ -414,6 +416,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Да ўсходу сонца"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Уключана ў <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Да <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Паменшыць яркасць"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC адключаны"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC уключаны"</string>
@@ -467,9 +470,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Паказаць профіль"</string>
<string name="user_add_user" msgid="4336657383006913022">"Дадаць карыстальніка"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Новы карыстальнік"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Завяршыць гасцявы сеанс?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Завяршыць гасцявы сеанс"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Выдаліць госця?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Завяршыць сеанс"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Выдаліць"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"З вяртаннем, госць!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Хочаце працягнуць сеанс?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Пачаць зноў"</string>
@@ -546,6 +550,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ваша арганізацыя ўсталявала ў вашым працоўным профілі цэнтр сертыфікацыі. Ваш абаронены сеткавы трафік могуць праглядваць ці змяняць."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На гэтай прыладзе ўсталяваны цэнтр сертыфікацыі. Ваш абаронены сеткавы трафік могуць праглядваць ці змяняць."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Ваш адміністратар уключыў вядзенне журнала сеткі, з дапамогай якога адсочваецца трафік на вашай прыладзе."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Ваш адміністратар уключыў вядзенне журнала сеткі, з дапамогай якога адсочваецца трафік у вашым працоўным профілі. Трафік вашага асабістага профілю застаецца недаступным."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Вы падключаны да праграмы <xliff:g id="VPN_APP">%1$s</xliff:g>, якая можа сачыць за вашай сеткавай дзейнасцю, уключаючы электронную пошту, праграмы і вэб-сайты."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Вы падключаны да праграм <xliff:g id="VPN_APP_0">%1$s</xliff:g> і <xliff:g id="VPN_APP_1">%2$s</xliff:g>, якія могуць сачыць за вашай сеткавай дзейнасцю, уключаючы электронную пошту, праграмы і вэб-сайты."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Ваш працоўны профіль падключаны да праграмы <xliff:g id="VPN_APP">%1$s</xliff:g>, якая можа сачыць за вашай сеткавай актыўнасцю, уключаючы электронную пошту, праграмы і вэб-сайты."</string>
@@ -739,7 +744,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Гэта апавяшчэнне аўтаматычна пераведзена сістэмай у &lt;b&gt;рэжым \"Без гуку\"&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Гэта апавяшчэнне аўтаматычна ацэнена як &lt;b&gt;важнае&lt;/b&gt; для вас."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Гэта апавяшчэнне аўтаматычна ацэнена як &lt;b&gt;няважнае&lt;/b&gt; для вас."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Усё правільна?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Напішыце распрацоўшчыку водгук. Усё правільна?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Дзякуй за водгук!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ОК"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Кіраванне апавяшчэннямі для <xliff:g id="APP_NAME">%1$s</xliff:g> адкрыта"</string>
@@ -890,6 +895,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перамясціць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Дадаць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Пазіцыя <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Плітка дададзена"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Плітка выдалена"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Рэдактар хуткіх налад."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Апавяшчэнне <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Адкрыць налады."</string>
@@ -980,7 +987,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Праграма \"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>\" выкарыстоўвае праграму \"<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>\""</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Праграма \"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>\" нядаўна выкарыстоўвала праграму \"<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>\""</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(прадпрыемства)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Тэлефонны выклік"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Тэлефонны выклік"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(праз праграму \"<xliff:g id="ATTRIBUTION">%s</xliff:g>\")"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камера"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"геалакацыя"</string>
@@ -988,7 +995,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Датчыкі выкл."</string>
<string name="device_services" msgid="1549944177856658705">"Сэрвісы прылады"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Без назвы"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Націсніце, каб перазапусціць гэту праграму і перайсці ў поўнаэкранны рэжым."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Перамясціць"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Навігацыя ў сістэме абноўлена. Каб унесці змяненні, перайдзіце ў Налады."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Перайдзіце ў Налады, каб абнавіць параметры навігацыі ў сістэме"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index adf852b5495b..e1efd3bd3c50 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Екранна снимка с превъртане"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Отхвърляне на екранната снимка"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Визуализация на екранната снимка"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Горна граница"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Долна граница"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Запис на екрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Записът на екрана се обработва"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Текущо известие за сесия за записване на екрана"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До изгрев"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ще се включи в <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Намаляване на яркостта"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"КБП е деактивирана"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"КБП е активирана"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Показване на потребителския профил"</string>
<string name="user_add_user" msgid="4336657383006913022">"Добавяне на потребител"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Нов потребител"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Да се прекрати ли сесията като гост?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Прекратяване на сесията като гост"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Да се премахне ли гостът?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Всички приложения и данни в тази сесия ще бъдат изтрити."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Прекратяване на сесията"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Премахване"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Добре дошли отново в сесията като гост!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Искате ли да продължите сесията си?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Започване отначало"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Организацията ви е инсталирала сертифициращ орган в служебния ви потребителски профил. Трафикът в защитената ви мрежа може да бъде наблюдаван или променян."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На това устройство е инсталиран сертифициращ орган. Трафикът в защитената ви мрежа може да бъде наблюдаван или променян."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Администраторът ви е включил функцията за регистриране на мрежовата активност, която следи трафика на устройството ви."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Администраторът ви е включил функцията за регистриране на мрежовата активност, която следи трафика в служебния ви потребителски профил, но не и в личния."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Установена е връзка с приложението <xliff:g id="VPN_APP">%1$s</xliff:g>, което може да наблюдава активността ви в мрежата, включително имейли, приложения и уебсайтове."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Установена е връзка с приложенията <xliff:g id="VPN_APP_0">%1$s</xliff:g> и <xliff:g id="VPN_APP_1">%2$s</xliff:g>, които могат да наблюдават активността ви в мрежата, включително имейли, приложения и уебсайтове."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Служебният ви потребителски профил е свързан с приложението <xliff:g id="VPN_APP">%1$s</xliff:g>, което може да наблюдава активността ви в мрежата, включително имейли, приложения и уебсайтове."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Това известие автоматично бе &lt;b&gt;понижено до беззвучно&lt;/b&gt; от системата."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Това известие автоматично бе &lt;b&gt;класирано по-високо&lt;/b&gt; в панела ви."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Това известие автоматично бе &lt;b&gt;класирано по-ниско&lt;/b&gt; в панела ви."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Правилно ли е това?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Изпратете отзивите си на програмиста. Правилно ли е това?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Благодарим ви за отзивите!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ОК"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Контролите за известията за <xliff:g id="APP_NAME">%1$s</xliff:g> са оттворени"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместване към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавяне към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Панелът е добавен"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Панелът е премахнат"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор за бързи настройки."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Известие от <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отваряне на настройките."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> използва <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> наскоро използва <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(корпоративна версия)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Телефонно обаждане"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Телефонно обаждане"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(чрез <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камерата"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"местополож."</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Сензорите са изключени"</string>
<string name="device_services" msgid="1549944177856658705">"Услуги за устройството"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Няма заглавие"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Докоснете, за да рестартирате това приложение в режим на цял екран."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Преместване"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Режимът за навигиране в системата е актуализиран. За да извършите промени, отворете настройките."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Отворете настройките, за да актуализирате режима за навигиране в системата"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 437abb85b445..12f53454c94e 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -93,6 +93,10 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"স্ক্রিনশট স্ক্রল করুন"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"স্ক্রিনশট বাতিল করুন"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"স্ক্রিনশটের প্রিভিউ"</string>
+ <!-- no translation found for screenshot_top_boundary (1500569103321300856) -->
+ <skip />
+ <!-- no translation found for screenshot_bottom_boundary (5657242629526407311) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"স্ক্রিন রেকর্ডার"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"স্ক্রিন রেকর্ডিং প্রসেস হচ্ছে"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"স্ক্রিন রেকর্ডিং সেশন চলার বিজ্ঞপ্তি"</string>
@@ -410,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"সূর্যোদয় পর্যন্ত"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>-এ চালু হবে"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> পর্যন্ত"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"উজ্জ্বলতা কমান"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC অক্ষম করা আছে"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC সক্ষম করা আছে"</string>
@@ -463,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"প্রোফাইল দেখান"</string>
<string name="user_add_user" msgid="4336657383006913022">"ব্যবহারকারী জুড়ুন"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"নতুন ব্যবহারকারী"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"গেস্ট সেশন শেষ করতে চান?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"গেস্ট সেশন শেষ করুন"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"অতিথি সরাবেন?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই সেশনের সব অ্যাপ্লিকেশান ও ডেটা মুছে ফেলা হবে।"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"সেশন শেষ করুন"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"সরান"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"অতিথি, আপনি ফিরে আসায় আপনাকে স্বাগত!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"আপনি কি আপনার সেশনটি অবিরত রাখতে চান?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আবার শুরু করুন"</string>
@@ -540,6 +546,8 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"আপনার প্রতিষ্ঠান আপনার অফিস প্রোফাইলে একটি সার্টিফিকেট কর্তৃপক্ষ ইনস্টল করেছে। আপনার নিরাপদ নেটওয়ার্ক ট্রাফিকে নজর রাখা হতে পারে বা তাতে পরিবর্তন করা হতে পারে।"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"এই ডিভাইসে একটি সার্টিফিকেট কর্তৃপক্ষ ইনস্টল করা আছে। আপনার নিরাপদ নেটওয়ার্ক ট্রাফিকে নজর রাখা হতে পারে বা তাতে পরিবর্তন করা হতে পারে।"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"আপনার প্রশাসক নেটওয়ার্ক লগিং চালু করেছেন, যা আপনার ডিভাইসের ট্রাফিকের উপরে নজর রাখে।"</string>
+ <!-- no translation found for monitoring_description_managed_profile_network_logging (6932303843097006037) -->
+ <skip />
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"আপনি <xliff:g id="VPN_APP">%1$s</xliff:g> এ সংযুক্ত রয়েছেন, যা আপনার ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক অ্যাক্টিভিটির উপর নজর রাখতে পারে।"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"আপনি <xliff:g id="VPN_APP_0">%1$s</xliff:g> এবং <xliff:g id="VPN_APP_1">%2$s</xliff:g> এর সাথে সংযুক্ত রয়েছেন, যেগুলি আপনার ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক অ্যাক্টিভিটির উপর নজর রাখতে পারে।"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"আপনার কর্মস্থলের প্রোফাইল <xliff:g id="VPN_APP">%1$s</xliff:g> এর সাথে সংযুক্ত রয়েছে, যেটি ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক কার্যকলাপে নজর রাখতে পারে।"</string>
@@ -733,7 +741,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"সিস্টেম অটোমেটিক এই বিজ্ঞপ্তির &lt;b&gt;লেভেল কমিয়ে সাইলেন্ট&lt;/b&gt; করে দিয়েছে।"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"আপনার শেডে অটোমেটিক এই বিজ্ঞপ্তির &lt;b&gt;র‍্যাঙ্ক বাড়িয়ে&lt;/b&gt; দেওয়া হয়েছে।"</string>
<string name="feedback_demoted" msgid="951884763467110604">"আপনার শেডে অটোমেটিক এই বিজ্ঞপ্তির &lt;b&gt;র‍্যাঙ্ক কমিয়ে&lt;/b&gt; দেওয়া হয়েছে।"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"এটি কি সঠিক ছিল?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"ডেভেলপারকে আপনার মতামত জানান। এতে কোনও ভুল দেখতে পেলেন?"</string>
<string name="feedback_response" msgid="4671729244976641339">"মতামতের জন্য ধন্যবাদ!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"বুঝেছি"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> খোলা থাকলে বিজ্ঞপ্তি নিয়ন্ত্রণ"</string>
@@ -880,6 +888,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>-এ সরান"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"অবস্থান <xliff:g id="POSITION">%1$d</xliff:g>-এ যোগ করুন"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"অবস্থান <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"টাইল যোগ করা হয়েছে"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"টাইল সরানো হয়েছে"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"দ্রুত সেটিংস সম্পাদক৷"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> বিজ্ঞপ্তি: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"সেটিংস খুলুন।"</string>
@@ -970,7 +980,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> এখন <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ব্যবহার করছে"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> সম্প্রতি <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ব্যবহার করেছে"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"এন্টারপ্রাইজ"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ফোনকল"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ফোন কল"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g>-এর মাধ্যমে)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"ক্যামেরা"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"লোকেশন"</string>
@@ -978,7 +988,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"সেন্সর বন্ধ"</string>
<string name="device_services" msgid="1549944177856658705">"ডিভাইস সংক্রান্ত পরিষেবা"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"কোনও শীর্ষক নেই"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন ও ফুল-স্ক্রিন ব্যবহার করুন।"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"সরান"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"সিস্টেম নেভিগেশন আপডেট হয়েছে। পরিবর্তন করার জন্য সেটিংসে যান।"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"সিস্টেম নেভিগেশন আপডেট করতে সেটিংসে যান"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index f2b3edca02de..746323386b1c 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Pokretni snimak ekrana"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Odbacite snimak ekrana"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimka ekrana"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Gornja granica"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Donja granica"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrađivanje snimka ekrana"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Obavještenje za sesiju snimanja ekrana je u toku"</string>
@@ -412,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do svitanja"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Uključuje se u <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Smanji osvjetljenje"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je onemogućen"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je omogućen"</string>
@@ -465,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Pokaži profil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Dodaj korisnika"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Završiti sesiju gosta?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Završi sesiju gosta"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite li ukloniti gosta?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i svi podaci iz ove sesije bit će izbrisani."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Završi sesiju"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Zdravo! Lijepo je opet vidjeti goste."</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string>
@@ -543,6 +547,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Vaša organizacija je instalirala CA certifikat na vašem radnom profilu. Vaš saobraćaj preko sigurne mreže može se pratiti."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"CA certifikat je instaliran na ovom uređaju. Vaš saobraćaj preko sigurne mreže može se pratiti."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Vaš administrator je uključio zapisivanje na mreži, čime se prati saobraćaj na vašem uređaju."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administrator je uključio zapisivanje na mreži, čime se nadzire saobraćaj na vašem radnom profilu, ali ne i na ličnom profilu."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Povezani ste s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g> koja može pratiti vašu aktivnost na mreži, uključujući e-poštu i web lokacije."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Povezani ste s aplikacijama <xliff:g id="VPN_APP_0">%1$s</xliff:g> i <xliff:g id="VPN_APP_1">%2$s</xliff:g> koje mogu pratiti vašu aktivnost na mreži, uključujući e-poštu, aplikacije i web lokacije."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Vaš radni profil je povezan s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>, koja može pratiti vašu aktivnost na mreži, uključujući e-poruke i web lokacije."</string>
@@ -736,7 +741,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Sistem je ovo obavještenje automatski &lt;b&gt;unazadio u Nečujno&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Ovo obavještenje je automatski &lt;b&gt;rangirano više&lt;/b&gt; u pozadini."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Ovo obavještenje je automatski &lt;b&gt;rangirano niže&lt;/b&gt; u pozadini."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Je li ovo bilo tačno?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Pošaljite programeru svoje povratne informacije. Je li ovo bilo tačno?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Hvala na povratnim informacijama!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"UREDU"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Otvorene su kontrole obavještenja za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -885,6 +890,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pomjeranje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodavanje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kartica je dodana"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kartica je uklonjena"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivanje brzih postavki"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> obavještenje: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvori postavke."</string>
@@ -975,7 +982,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> koristi aplikaciju <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> je nedavno koristila aplikaciju <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(preduzeće)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonski poziv"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonski poziv"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(putem aplikacije <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kameru"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokaciju"</string>
@@ -983,7 +990,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Senzori su isključeni"</string>
<string name="device_services" msgid="1549944177856658705">"Usluge uređaja"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Bez naslova"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Dodirnite da ponovo pokrenete ovu aplikaciju i aktivirate prikaz preko cijelog ekrana."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Pomjeri"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigiranje sistemom je ažurirano. Da izvršite promjene, idite u Postavke."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Idite u Postavke da ažurirate navigiranje sistemom"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index bdcde318179a..524bb6258774 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Captura de pantalla lliscant"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ignora la captura de pantalla"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Previsualització de la captura de pantalla"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Marge superior"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Marge inferior"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravació de pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processant gravació de pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificació en curs d\'una sessió de gravació de la pantalla"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Fins a l\'alba"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Activat a les <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Fins a les <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Reducció de la brillantor"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"L\'NFC està desactivada"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"L\'NFC està activada"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostra el perfil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Afegeix un usuari"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Usuari nou"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vols finalitzar la sessió de convidat?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Finalitza la sessió de convidat"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vols suprimir el convidat?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Finalitza la sessió"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Suprimeix"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Benvingut de nou, convidat."</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Vols continuar amb la sessió?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Torna a començar"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"La teva organització ha instal·lat una autoritat de certificació al teu perfil de treball. És possible que el trànsit de xarxa segura se supervisi o es modifiqui."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"S\'ha instal·lat una autoritat de certificació en aquest dispositiu. És possible que el trànsit de xarxa segura se supervisi o es modifiqui."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"L\'administrador ha activat el registre de xarxa, que supervisa el trànsit del teu dispositiu."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"L\'administrador ha activat el registre de xarxa, que monitora el trànsit al teu perfil de treball, però no al personal."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Estàs connectat a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pot supervisar la teva activitat a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Estàs connectat a <xliff:g id="VPN_APP_0">%1$s</xliff:g> i <xliff:g id="VPN_APP_1">%2$s</xliff:g>, que poden supervisar la teva activitat a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"El teu perfil de treball està connectat a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pot supervisar la teva activitat a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"El sistema &lt;b&gt;ha disminuït a Silenci&lt;/b&gt; el nivell d\'aquesta notificació."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Aquesta notificació s\'ha classificat automàticament amb un &lt;b&gt;nivell superior&lt;/b&gt; a l\'àrea de notificacions."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Aquesta notificació s\'ha classificat automàticament amb un &lt;b&gt;nivell inferior&lt;/b&gt; a l\'àrea de notificacions."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"La informació ha estat correcta?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Fes saber els teus suggeriments al desenvolupador. La informació ha estat correcta?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Gràcies pels suggeriments."</string>
<string name="feedback_ok" msgid="6481426753298857144">"D\'acord"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"S\'han obert els controls de notificació per a <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mou a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Afegeix a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"El mosaic s\'ha afegit"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"El mosaic s\'ha suprimit"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuració ràpida."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificació de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Obre la configuració."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> està utilitzant: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Recentment <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ha utilitzat: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresa)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Trucada"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Trucada"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(a través de: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"càmera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ubicació"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensors desactivats"</string>
<string name="device_services" msgid="1549944177856658705">"Serveis per a dispositius"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Sense títol"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Toca per reiniciar l\'aplicació i passar a pantalla completa."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mou"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"S\'ha actualitzat el sistema de navegació. Per fer canvis, ves a Configuració."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Ves a Configuració per actualitzar el sistema de navegació"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index aa5a1dd70695..47a673dd4c42 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Posunout snímek obrazovky"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Zavřít snímek obrazovky"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Náhled snímku obrazovky"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Horní hranice"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Dolní hranice"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Záznam obrazovky se zpracovává"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Trvalé oznámení o relaci nahrávání"</string>
@@ -414,6 +416,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do svítání"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Zapnout v <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Snížit jas"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je vypnuto"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je zapnuto"</string>
@@ -467,9 +470,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Zobrazit profil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Přidat uživatele"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Nový uživatel"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Ukončit relaci hosta?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Ukončení relace hosta"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Odstranit hosta?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Ukončit relaci"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstranit"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Vítejte zpět v relaci hosta!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relaci pokračovat?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začít znovu"</string>
@@ -546,6 +550,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizace do vašeho pracovního profilu nainstalovala certifikační autoritu. Zabezpečený síťový provoz může být sledován nebo upravován."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"V zařízení je nainstalována certifikační autorita. Zabezpečený síťový provoz může být sledován nebo upravován."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administrátor zapnul protokolování sítě, které monitoruje síťový provoz v zařízení."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administrátor zapnul protokolování sítě, které monitoruje síťový provoz ve vašem pracovním profilu (ale ne v osobním)."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Jste připojeni k aplikaci <xliff:g id="VPN_APP">%1$s</xliff:g>, která může sledovat vaši aktivitu v síti, včetně e-mailů, aplikací a webů."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Jste připojeni k aplikacím <xliff:g id="VPN_APP_0">%1$s</xliff:g> a <xliff:g id="VPN_APP_1">%2$s</xliff:g>, které mohou sledovat vaši aktivitu v síti, včetně e-mailů, aplikací a webů."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Váš pracovní profil je připojen k aplikaci <xliff:g id="VPN_APP">%1$s</xliff:g>, která může sledovat vaši aktivitu v síti, včetně e-mailů, aplikací a webů."</string>
@@ -739,7 +744,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"U tohoto oznámení systém automaticky &lt;b&gt;snížil prioritu na Tiché&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Toto oznámení bylo na panelu automaticky &lt;b&gt;zařazeno výše&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Toto oznámení bylo na panelu automaticky &lt;b&gt;zařazeno níže&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Udělal to správně?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Sdělte vývojáři svůj názor. Udělal to správně?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Děkujeme za zpětnou vazbu."</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Ovládací prvky oznámení aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> byly otevřeny"</string>
@@ -890,6 +895,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Přesunout na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Přidat dlaždici na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozice <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Karta byla přidána"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Karta byla odstraněna"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rychlého nastavení"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Oznámení aplikace <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otevřít nastavení."</string>
@@ -980,7 +987,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikace <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> používá aplikaci <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikace <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> nedávno použila aplikaci <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(podniková verze)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonní hovor"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonní hovor"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(prostřednictvím aplikace <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"fotoaparát"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"poloha"</string>
@@ -988,7 +995,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Senzory jsou vypnuty"</string>
<string name="device_services" msgid="1549944177856658705">"Služby zařízení"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Bez názvu"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Klepnutím aplikaci restartujete a přejdete na režim celé obrazovky"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Přesunout"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Systémová navigace byla aktualizována. Chcete-li provést změny, přejděte do Nastavení."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Přejděte do Nastavení a aktualizujte systémovou navigaci"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 5630293c1606..e12b91c13ec7 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Rul screenshot"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Luk screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Forhåndsvisning af screenshot"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Øverste kant"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Nederste kant"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skærmoptagelse"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandler skærmoptagelse"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Konstant notifikation om skærmoptagelse"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Indtil solopgang"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Tænd kl. <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Indtil kl. <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Reducer lysstyrken"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC er deaktiveret"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC er aktiveret"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Vis profil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Tilføj bruger"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Ny bruger"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vil du afslutte gæstesessionen?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Afslut gæstesessionen"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vil du fjerne gæsten?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Afslut sessionen"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjern"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkommen tilbage, gæst!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsætte din session?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start forfra"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Din organisation har installeret et nøglecenter på din arbejdsprofil. Din sikre netværkstrafik kan overvåges eller ændres."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Der er installeret et nøglecenter på denne enhed. Din sikre netværkstrafik kan overvåges eller ændres."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Din administrator har aktiveret netværksregistrering, som overvåger trafik på din enhed."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Din administrator har aktiveret netværkslogging, som overvåger trafik på din arbejdsprofil, men ikke på din personlige profil."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Du har forbindelse til <xliff:g id="VPN_APP">%1$s</xliff:g>, som kan overvåge din netværksaktivitet, bl.a. mails, apps og websites."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Du har forbindelse til <xliff:g id="VPN_APP_0">%1$s</xliff:g> og <xliff:g id="VPN_APP_1">%2$s</xliff:g>, som kan overvåge din netværksaktivitet, bl.a. mails, apps og websites."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Din arbejdsprofil har forbindelse til <xliff:g id="VPN_APP">%1$s</xliff:g>, som kan overvåge din netværksaktivitet, bl.a. mails, apps og websites."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Denne notifikation blev automatisk &lt;b&gt;angivet som Lydløs&lt;/b&gt; af systemet."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Denne notifikation blev automatisk &lt;b&gt;rangeret højere&lt;/b&gt; i din skygge."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Denne notifikation blev automatisk &lt;b&gt;rangeret lavere&lt;/b&gt; i din skygge."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Var dette korrekt?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Giv udvikleren feedback. Var dette korrekt?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Tak for din feedback"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Styring af notifikationer for <xliff:g id="APP_NAME">%1$s</xliff:g> blev åbnet"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flyt til <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Føj til placering <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Placering <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kortet blev tilføjet"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kortet blev fjernet"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsværktøj til Kvikmenu."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-notifikation: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Åbn Indstillinger."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> anvender <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> anvendte <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> for nylig"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(til virksomhedsbrug)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonopkald"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonopkald"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(via <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"placering"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Deaktiver sensorer"</string>
<string name="device_services" msgid="1549944177856658705">"Enhedstjenester"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Ingen titel"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Tryk for at genstarte denne app, og gå til fuld skærm."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Flyt"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Systemnavigationen blev opdateret. Gå til Indstillinger for at foretage ændringer."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Gå til Indstillinger for at opdatere systemnavigationen"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index e2468bf04505..6a69ed204b0c 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -93,6 +93,10 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Screenshot scrollen"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Screenshot schließen"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshotvorschau"</string>
+ <!-- no translation found for screenshot_top_boundary (1500569103321300856) -->
+ <skip />
+ <!-- no translation found for screenshot_bottom_boundary (5657242629526407311) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Bildschirmaufzeichnung"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Bildschirmaufzeichnung…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Fortlaufende Benachrichtigung für eine Bildschirmaufzeichnung"</string>
@@ -410,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Bis Sonnenaufgang"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"An um <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Bis <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Helligkeit verringern"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ist deaktiviert"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ist aktiviert"</string>
@@ -463,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profil öffnen"</string>
<string name="user_add_user" msgid="4336657383006913022">"Nutzer hinzufügen"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Neuer Nutzer"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Gastsitzung beenden?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Gastsitzung beenden"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gast entfernen?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Sitzung beenden"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Entfernen"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Willkommen zurück im Gastmodus"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Möchtest du deine Sitzung fortsetzen?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Neu starten"</string>
@@ -540,6 +546,8 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Deine Organisation hat ein Zertifikat einer Zertifizierungsstelle in deinem Arbeitsprofil installiert. Eventuell wird dein sicherer Netzwerkverkehr überwacht oder bearbeitet."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Auf dem Gerät ist das Zertifikat einer Zertifizierungsstelle installiert. Eventuell wird dein sicherer Netzwerkverkehr überwacht oder bearbeitet."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Dein Administrator hat die Netzwerkprotokollierung aktiviert. Damit wird der Netzwerkverkehr auf deinem Gerät überwacht."</string>
+ <!-- no translation found for monitoring_description_managed_profile_network_logging (6932303843097006037) -->
+ <skip />
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Du bist mit <xliff:g id="VPN_APP">%1$s</xliff:g> verbunden. Die App kann deine Netzwerkaktivitäten (E-Mails, Apps und Websites) erfassen."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Du bist mit <xliff:g id="VPN_APP_0">%1$s</xliff:g> und <xliff:g id="VPN_APP_1">%2$s</xliff:g> verbunden. Die Apps können deine Netzwerkaktivitäten (E-Mails, Apps und Websites) erfassen."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Dein Arbeitsprofil ist mit <xliff:g id="VPN_APP">%1$s</xliff:g> verbunden, die deine Netzwerkaktivitäten wie E-Mails, Apps und Websites überwachen kann."</string>
@@ -733,7 +741,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Diese Benachrichtigung wurde durch das System automatisch &lt;b&gt;auf „Lautlos“ herabgestuft&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Diese Benachrichtigung wurde in deiner Leiste automatisch &lt;b&gt;höher eingestuft&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Diese Benachrichtigung wurde in deiner Leiste automatisch &lt;b&gt;niedriger eingestuft&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"War das richtig?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Teile dem Entwickler dein Feedback mit. War das richtig?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Vielen Dank für dein Feedback."</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Benachrichtigungseinstellungen für <xliff:g id="APP_NAME">%1$s</xliff:g> geöffnet"</string>
@@ -880,6 +888,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Auf Position <xliff:g id="POSITION">%1$d</xliff:g> verschieben"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Zur Position <xliff:g id="POSITION">%1$d</xliff:g> hinzufügen"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Ansicht hinzugefügt"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Ansicht entfernt"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor für Schnelleinstellungen."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Benachrichtigung von <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Einstellungen öffnen."</string>
@@ -967,23 +977,17 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apps verwenden gerade Folgendes: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" und "</string>
- <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) -->
- <skip />
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> verwendet gerade die <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>-App"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> verwendete kürzlich die <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>-App"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(Unternehmen)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonanruf"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(über <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"Kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"Standort"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"Mikrofon"</string>
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensoren aus"</string>
<string name="device_services" msgid="1549944177856658705">"Gerätedienste"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Kein Titel"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Tippe, um die App im Vollbildmodus neu zu starten."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Verschieben"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Systemsteuerungseinstellungen wurden angepasst. Änderungen kannst du in den Einstellungen vornehmen."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Gehe zu den Einstellungen, um die Systemsteuerung anzupassen"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index aa747f24a8df..5bed0e5b9ad3 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Στιγμιότυπο κύλισης"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Παράβλεψη στιγμιότυπου οθόνης"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Προεπισκόπηση στιγμιότυπου οθόνης"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Ανώτατο όριο"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Κατώτατο όριο"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Εγγραφή οθόνης"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Επεξεργασία εγγραφής οθόνης"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ειδοποίηση σε εξέλιξη για μια περίοδο λειτουργίας εγγραφής οθόνης"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Μέχρι την ανατολή"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ενεργοποίηση στις <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Έως <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Μείωση φωτεινότητας"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Το NFC είναι απενεργοποιημένο"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Το NFC είναι ενεργοποιημένο"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Εμφάνιση προφίλ"</string>
<string name="user_add_user" msgid="4336657383006913022">"Προσθήκη χρήστη"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Νέος χρήστης"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Λήξη περιόδου σύνδεσης επισκέπτη;"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Λήξη περιόδου σύνδεσης επισκέπτη"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Κατάργηση επισκέπτη;"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Λήξη περιόδου σύνδεσης"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Κατάργηση"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Επισκέπτη , καλώς όρισες ξανά!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Θέλετε να συνεχίσετε την περίοδο σύνδεσής σας;"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Έναρξη από την αρχή"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ο οργανισμός σας εγκατέστησε μια αρχή έκδοσης πιστοποιητικών στο προφίλ εργασίας σας. Η ασφαλής επισκεψιμότητα δικτύου σας μπορεί να παρακολουθείται ή να τροποποιείται."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Μια αρχή έκδοσης πιστοποιητικών έχει εγκατασταθεί σε αυτήν τη συσκευή. Η ασφαλής επισκεψιμότητα δικτύου σας μπορεί να παρακολουθείται ή να τροποποιείται."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Ο διαχειριστής σας ενεργοποίησε την καταγραφή δικτύου, η οποία παρακολουθεί την επισκεψιμότητα στη συσκευή σας."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Ο διαχειριστής σας έχει ενεργοποιήσει την καταγραφή δικτύου, η οποία παρακολουθεί την επισκεψιμότητα στο προφίλ εργασίας σας, αλλά όχι στο προσωπικό προφίλ σας."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Έχετε συνδεθεί στην εφαρμογή <xliff:g id="VPN_APP">%1$s</xliff:g>, η οποία μπορεί να παρακολουθεί τη δραστηριότητα δικτύου σας, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστοτόπων."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Έχετε συνδεθεί στις εφαρμογές <xliff:g id="VPN_APP_0">%1$s</xliff:g> και <xliff:g id="VPN_APP_1">%2$s</xliff:g>, οι οποίες μπορούν να παρακολουθούν τη δραστηριότητα του δικτύου σας, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστοτόπων."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Το προφίλ εργασίας σας είναι συνδεδεμένο στο <xliff:g id="VPN_APP">%1$s</xliff:g>, το οποίο μπορεί να παρακολουθεί τη δραστηριότητα δικτύου σας, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστοτόπων."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Αυτή η ειδοποίηση &lt;b&gt;υποβιβάστηκε σε Αθόρυβη&lt;/b&gt; αυτόματα από το σύστημα."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Αυτή η ειδοποίηση &lt;b&gt;κατατάχθηκε υψηλότερα&lt;/b&gt; στο πλαίσιο σκίασης με αυτόματο τρόπο."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Αυτή η ειδοποίηση &lt;b&gt;κατατάχθηκε χαμηλότερα&lt;/b&gt; στο πλαίσιο σκίασης με αυτόματο τρόπο."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Ήταν σωστό αυτό;"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Μοιραστείτε τα σχόλιά σας με τον προγραμματιστή. Ήταν σωστό αυτό;"</string>
<string name="feedback_response" msgid="4671729244976641339">"Σας ευχαριστούμε για τα σχόλιά σας!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ΟΚ"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Τα στοιχεία ελέγχου ειδοποιήσεων για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> άνοιξαν"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Μετακίνηση στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Προσθήκη στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Το πλακίδιο προστέθηκε"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Το πλακίδιο καταργήθηκε"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Επεξεργασία γρήγορων ρυθμίσεων."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Ειδοποίηση <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Άνοιγμα ρυθμίσεων."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Χρήση <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> από την εφαρμογή <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Πρόσφατη χρήση <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> από την εφαρμογή <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(για επιχειρήσεις)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Τηλεφωνική κλήση"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Τηλεφωνική κλήση"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(μέσω <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"κάμερα"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"τοποθεσία"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Αισθητήρες ανενεργοί"</string>
<string name="device_services" msgid="1549944177856658705">"Υπηρεσίες συσκευής"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Χωρίς τίτλο"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Πατήστε για επανεκκίνηση αυτής της εφαρμογής και ενεργοποίηση πλήρους οθόνης."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Μετακίνηση"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Η πλοήγηση συστήματος ενημερώθηκε. Για να κάνετε αλλαγές, μεταβείτε στις Ρυθμίσεις."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Μεταβείτε στις Ρυθμίσεις για να ενημερώσετε την πλοήγηση συστήματος"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 88a40c346e79..137070fa0437 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Scroll screenshot"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Dismiss screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Top boundary"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Bottom boundary"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Reduce Brightness"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string>
<string name="user_add_user" msgid="4336657383006913022">"Add user"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"New user"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"End Guest session"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Your organisation installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"A certificate authority is installed on this device. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Your admin has turned on network logging, which monitors traffic on your device."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Your admin has turned on network logging, which monitors traffic in your work profile but not in your personal profile."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"You\'re connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"You\'re connected to <xliff:g id="VPN_APP_0">%1$s</xliff:g> and <xliff:g id="VPN_APP_1">%2$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Your work profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"This notification was automatically &lt;b&gt;demoted to silent&lt;/b&gt; by the system."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"This notification was automatically &lt;b&gt;ranked higher&lt;/b&gt; in your shade."</string>
<string name="feedback_demoted" msgid="951884763467110604">"This notification was automatically &lt;b&gt;ranked lower&lt;/b&gt; in your shade."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Was this correct?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Let the developer know your feedback. Was this correct?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Thanks for your feedback!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tile added"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tile removed"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> is using the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> used the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recently"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Phone call"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(through <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"location"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensors off"</string>
<string name="device_services" msgid="1549944177856658705">"Device Services"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"No title"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Tap to restart this app and go full screen."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Move"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"System navigation updated. To make changes, go to Settings."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Go to Settings to update system navigation"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index bf7883af8138..5df29337493a 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Scroll screenshot"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Dismiss screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Top boundary"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Bottom boundary"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Reduce Brightness"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string>
<string name="user_add_user" msgid="4336657383006913022">"Add user"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"New user"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"End Guest session"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Your organisation installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"A certificate authority is installed on this device. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Your admin has turned on network logging, which monitors traffic on your device."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Your admin has turned on network logging, which monitors traffic in your work profile but not in your personal profile."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"You\'re connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"You\'re connected to <xliff:g id="VPN_APP_0">%1$s</xliff:g> and <xliff:g id="VPN_APP_1">%2$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Your work profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"This notification was automatically &lt;b&gt;demoted to silent&lt;/b&gt; by the system."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"This notification was automatically &lt;b&gt;ranked higher&lt;/b&gt; in your shade."</string>
<string name="feedback_demoted" msgid="951884763467110604">"This notification was automatically &lt;b&gt;ranked lower&lt;/b&gt; in your shade."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Was this correct?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Let the developer know your feedback. Was this correct?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Thanks for your feedback!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tile added"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tile removed"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> is using the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> used the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recently"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Phone call"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(through <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"location"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensors off"</string>
<string name="device_services" msgid="1549944177856658705">"Device Services"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"No title"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Tap to restart this app and go full screen."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Move"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"System navigation updated. To make changes, go to Settings."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Go to Settings to update system navigation"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 88a40c346e79..137070fa0437 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Scroll screenshot"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Dismiss screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Top boundary"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Bottom boundary"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Reduce Brightness"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string>
<string name="user_add_user" msgid="4336657383006913022">"Add user"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"New user"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"End Guest session"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Your organisation installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"A certificate authority is installed on this device. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Your admin has turned on network logging, which monitors traffic on your device."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Your admin has turned on network logging, which monitors traffic in your work profile but not in your personal profile."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"You\'re connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"You\'re connected to <xliff:g id="VPN_APP_0">%1$s</xliff:g> and <xliff:g id="VPN_APP_1">%2$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Your work profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"This notification was automatically &lt;b&gt;demoted to silent&lt;/b&gt; by the system."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"This notification was automatically &lt;b&gt;ranked higher&lt;/b&gt; in your shade."</string>
<string name="feedback_demoted" msgid="951884763467110604">"This notification was automatically &lt;b&gt;ranked lower&lt;/b&gt; in your shade."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Was this correct?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Let the developer know your feedback. Was this correct?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Thanks for your feedback!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tile added"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tile removed"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> is using the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> used the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recently"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Phone call"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(through <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"location"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensors off"</string>
<string name="device_services" msgid="1549944177856658705">"Device Services"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"No title"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Tap to restart this app and go full screen."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Move"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"System navigation updated. To make changes, go to Settings."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Go to Settings to update system navigation"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 88a40c346e79..137070fa0437 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Scroll screenshot"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Dismiss screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Top boundary"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Bottom boundary"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Reduce Brightness"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string>
<string name="user_add_user" msgid="4336657383006913022">"Add user"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"New user"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"End Guest session"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Your organisation installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"A certificate authority is installed on this device. Your secure network traffic may be monitored or modified."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Your admin has turned on network logging, which monitors traffic on your device."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Your admin has turned on network logging, which monitors traffic in your work profile but not in your personal profile."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"You\'re connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"You\'re connected to <xliff:g id="VPN_APP_0">%1$s</xliff:g> and <xliff:g id="VPN_APP_1">%2$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Your work profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"This notification was automatically &lt;b&gt;demoted to silent&lt;/b&gt; by the system."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"This notification was automatically &lt;b&gt;ranked higher&lt;/b&gt; in your shade."</string>
<string name="feedback_demoted" msgid="951884763467110604">"This notification was automatically &lt;b&gt;ranked lower&lt;/b&gt; in your shade."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Was this correct?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Let the developer know your feedback. Was this correct?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Thanks for your feedback!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tile added"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tile removed"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> is using the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> used the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recently"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Phone call"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(through <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"location"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensors off"</string>
<string name="device_services" msgid="1549944177856658705">"Device Services"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"No title"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Tap to restart this app and go full screen."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Move"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"System navigation updated. To make changes, go to Settings."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Go to Settings to update system navigation"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index a14566ac0c1a..46c09f668383 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‎‏‎‏‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‏‏‎‎‏‏‎‏‏‏‏‎Scroll screenshot‎‏‎‎‏‎"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‏‏‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‎‎‎‎‏‏‎‎‎‎‏‏‎‎‏‎‎Dismiss screenshot‎‏‎‎‏‎"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‎Screenshot preview‎‏‎‎‏‎"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎Top boundary‎‏‎‎‏‎"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‎‏‎‏‎‎‏‎‎‎‎‎‎‏‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‎‏‏‏‏‎Bottom boundary‎‏‎‎‏‎"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‎Screen Recorder‎‏‎‎‏‎"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‎‎‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‎‎Processing screen recording‎‏‎‎‏‎"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‏‏‎‎‎‏‏‏‏‎‏‏‏‏‎Ongoing notification for a screen record session‎‏‎‎‏‎"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎‏‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‎‎‏‎‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‎‎‎Until sunrise‎‏‎‎‏‎"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‏‏‏‏‎On at ‎‏‎‎‏‏‎<xliff:g id="TIME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‏‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‎Until ‎‏‎‎‏‏‎<xliff:g id="TIME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‎‎‎‎‎‏‏‎‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‎‎‎‎‎‏‏‎‏‏‎‏‏‎Reduce Brightness‎‏‎‎‏‎"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‎‏‎‏‎‏‎NFC‎‏‎‎‏‎"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‎‎‎‏‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‏‎‎‎‏‎‏‎‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎NFC is disabled‎‏‎‎‏‎"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‏‎‎‏‏‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎NFC is enabled‎‏‎‎‏‎"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‎‎‏‎‏‎‏‎‎‏‏‎‏‎‎‎‏‎‎‎‎‎Show profile‎‏‎‎‏‎"</string>
<string name="user_add_user" msgid="4336657383006913022">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎Add user‎‏‎‎‏‎"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‎‏‎‏‏‎‎‎‎‏‎‏‎‎‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‏‏‎‏‎New user‎‏‎‎‏‎"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎End guest session?‎‏‎‎‏‎"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎‏‏‎‎‏‎‎‏‏‏‎‎‏‏‎‏‎‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎‎End guest session‎‏‎‎‏‎"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‏‏‎‎‏‎‏‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎Remove guest?‎‏‎‎‏‎"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‏‎All apps and data in this session will be deleted.‎‏‎‎‏‎"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‏‏‏‏‏‏‎End session‎‏‎‎‏‎"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‎‎‎‎‎‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎‎‎‎‏‏‏‎‎‎‏‏‎‏‎Remove‎‏‎‎‏‎"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‎‎‎‎‏‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‎‎Welcome back, guest!‎‏‎‎‏‎"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‎Do you want to continue your session?‎‏‎‎‏‎"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‎‏‏‏‎‏‎Start over‎‏‎‎‏‎"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎‏‏‎‏‎‏‏‏‎‎‏‏‏‏‏‏‎Your organization installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified.‎‏‎‎‏‎"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎‏‏‎‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎A certificate authority is installed on this device. Your secure network traffic may be monitored or modified.‎‏‎‎‏‎"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‎‏‎‎‏‏‎Your admin has turned on network logging, which monitors traffic on your device.‎‏‎‎‏‎"</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‎‎‏‎‏‎‏‏‎‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎Your admin has turned on network logging, which monitors traffic in your work profile but not in your personal profile.‎‏‎‎‏‎"</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‎‎‏‎‎‏‏‎‏‎‎‏‏‎‎‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎‎‏‏‎You\'re connected to ‎‏‎‎‏‏‎<xliff:g id="VPN_APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎, which can monitor your network activity, including emails, apps, and websites.‎‏‎‎‏‎"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‎‏‏‎‎‎‏‎‏‏‎‏‎‏‎‎‏‎‏‏‏‎You\'re connected to ‎‏‎‎‏‏‎<xliff:g id="VPN_APP_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ and ‎‏‎‎‏‏‎<xliff:g id="VPN_APP_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎, which can monitor your network activity, including emails, apps, and websites.‎‏‎‎‏‎"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‏‎‎‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‏‏‎‏‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎Your work profile is connected to ‎‏‎‎‏‏‎<xliff:g id="VPN_APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎, which can monitor your network activity, including emails, apps, and websites.‎‏‎‎‏‎"</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‎‏‏‏‎‏‎‏‎‎‎‏‏‏‏‎‎‎‎‎‎‎‏‏‎‎‎‏‏‎‎‎‎‏‏‎‏‎‎‎‎‏‏‏‏‎‎‏‎This notification was automatically &lt;b&gt;demoted to Silent&lt;/b&gt; by the system.‎‏‎‎‏‎"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‏‏‏‎This notification was automatically &lt;b&gt;ranked higher&lt;/b&gt; in your shade.‎‏‎‎‏‎"</string>
<string name="feedback_demoted" msgid="951884763467110604">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‏‏‎‏‎‏‏‏‎‎‎‏‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‎‎‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‎‎This notification was automatically &lt;b&gt;ranked lower&lt;/b&gt; in your shade.‎‏‎‎‏‎"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎‏‏‎‎‏‎Was this correct?‎‏‎‎‏‎"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‏‎‎‏‏‎‎‎‎‎‏‏‏‎‏‏‎Let the developer know your feedback. Was this correct?‎‏‎‎‏‎"</string>
<string name="feedback_response" msgid="4671729244976641339">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‏‎Thanks for your feedback!‎‏‎‎‏‎"</string>
<string name="feedback_ok" msgid="6481426753298857144">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‎‏‏‏‎‏‎‎‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‏‏‎‎‏‎‏‏‏‎‎‎‎OK‎‏‎‎‏‎"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎Notification controls for ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ opened‎‏‎‎‏‎"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‎‎‎‏‏‏‎‎‏‎‏‎‎‎‏‏‎‎‎‏‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‎‎‎‎‏‏‏‏‎‎‎Move to ‎‏‎‎‏‏‎<xliff:g id="POSITION">%1$d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‎‎‎‎‏‎‎‏‎‎‏‎‎‎‎‎‎‎‎‏‎‎Add to position ‎‏‎‎‏‏‎<xliff:g id="POSITION">%1$d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‎‏‎‏‎‏‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎‏‏‎‎Position ‎‏‎‎‏‏‎<xliff:g id="POSITION">%1$d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‎‎Tile added‎‏‎‎‏‎"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‎‏‏‎‏‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‎‏‎‎‏‎‎‏‎‏‎‏‏‏‏‎‎‎‏‎‎‎Tile removed‎‏‎‎‏‎"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎‎‏‎Quick settings editor.‎‏‎‎‏‎"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="ID_1">%1$s</xliff:g>‎‏‎‎‏‏‏‎ notification: ‎‏‎‎‏‏‎<xliff:g id="ID_2">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‏‎Open settings.‎‏‎‎‏‎"</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‎‎‏‏‏‎‏‏‏‎‎‎‏‏‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is using the ‎‏‎‎‏‏‎<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ used the ‎‏‎‎‏‏‎<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ recently‎‏‎‎‏‎"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‏‏‎‎‎‏‎‎‏‎(enterprise)‎‏‎‎‏‎"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‎‏‏‎‎‏‎‎‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‏‏‏‏‎Phonecall‎‏‎‎‏‎"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‎‎‏‏‎‏‎‏‏‎‏‏‎‎‏‎‎‏‏‏‏‏‏‎‎‎‎‏‎‎Phone call‎‏‎‎‏‎"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‎‏‏‎‎‏‎‎‎‎‏‏‎‏‎‏‎‏‎‎‎‏‏‎‏‎‎‏‏‎‎(through ‎‏‎‎‏‏‎<xliff:g id="ATTRIBUTION">%s</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‏‎‏‎‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‏‏‎‎‏‎‎‎‎‎‎‎‎‏‎‏‏‎‏‏‏‏‎‎‎camera‎‏‎‎‏‎"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‎‏‏‏‏‎location‎‏‎‎‏‎"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‎‎‏‏‏‎‎‎Sensors off‎‏‎‎‏‎"</string>
<string name="device_services" msgid="1549944177856658705">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‎‎‎‎‎‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‏‎Device Services‎‏‎‎‏‎"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‏‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎No title‎‏‎‎‏‎"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎‎‎‎‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‏‎‎‎‎‎‎Tap to restart this app and go full screen.‎‏‎‎‏‎"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‏‎‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‏‏‎‎‏‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‎‏‎‎Move‎‏‎‎‏‎"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‎‏‏‏‏‎‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‏‎‏‎‎‎‎‎‎‎‎‎System navigation updated. To make changes, go to Settings.‎‏‎‎‏‎"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‏‏‏‏‏‎‎‎‏‎‏‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‎‎Go to Settings to update system navigation‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 4b482b914164..e1ad516254c7 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Desplazar captura de pantalla"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Descartar captura de pantalla"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa de la captura de pantalla"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Límite superior"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Límite inferior"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Grabadora de pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando grabación pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación constante para una sesión de grabación de pantalla"</string>
@@ -115,7 +117,7 @@
<string name="screenrecord_share_label" msgid="5025590804030086930">"Compartir"</string>
<string name="screenrecord_cancel_success" msgid="1775448688137393901">"Se canceló la grabación de pantalla"</string>
<string name="screenrecord_save_message" msgid="490522052388998226">"Se guardó la grabación de pantalla; presiona para verla"</string>
- <string name="screenrecord_delete_error" msgid="2870506119743013588">"Error al borrar la grabación de pantalla"</string>
+ <string name="screenrecord_delete_error" msgid="2870506119743013588">"No se pudo borrar la grabación de pantalla"</string>
<string name="screenrecord_permission_error" msgid="7856841237023137686">"Error al obtener permisos"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Error al iniciar la grabación de pantalla"</string>
<string name="usb_preference_title" msgid="1439924437558480718">"Opciones de transferencia de archivos por USB"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hasta el amanecer"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"A la(s) <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hasta la(s) <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Reducir el brillo"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"La tecnología NFC está inhabilitada"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"La tecnología NFC está habilitada"</string>
@@ -440,7 +443,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Presiona de nuevo para abrir"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Desliza el dedo hacia arriba para abrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Desliza el dedo hacia arriba para volver a intentarlo"</string>
- <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquee el dispositivo para usar NFC"</string>
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquea el dispositivo para usar NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertenece a tu organización"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Desliza el dedo para desbloquear el teléfono."</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Agregar usuario"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Usuario nuevo"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"¿Quieres finalizar la sesión de invitado?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Finalizar sesión de invitado"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"¿Eliminar invitado?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Finalizar sesión"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eliminar"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenido nuevamente, invitado."</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres retomar la sesión?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Tu organización instaló una autoridad de certificación en tu perfil de trabajo. Es posible que se controle o modifique el tráfico de tu red segura."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Hay una autoridad de certificación instalada en este dispositivo. Es posible que se controle o modifique el tráfico de tu red segura."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Tu administrador activó el registro de red, que supervisa el tráfico en tu dispositivo."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"El administrador activó el registro de red, que supervisa el tráfico de tu perfil de trabajo, pero no el de tu perfil personal."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Estás conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que puede controlar la actividad de tu red, incluidos los correos electrónicos, las apps y los sitios web."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Estás conectado a <xliff:g id="VPN_APP_0">%1$s</xliff:g> y <xliff:g id="VPN_APP_1">%2$s</xliff:g>, que pueden controlar tu actividad de red, incluidos los correos electrónicos, las apps y los sitios web."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Tu perfil de trabajo está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que puede controlar tu actividad de red, incluidos los correos electrónicos, las apps y los sitios web."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"El sistema &lt;b&gt;descendió esta notificación a Silenciada&lt;/b&gt; de forma automática."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Se clasificó esta notificación &lt;b&gt;en una posición superior&lt;/b&gt; de forma automática en el panel."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Se clasificó esta notificación &lt;b&gt;en una posición inferior&lt;/b&gt; de forma automática en el panel."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"¿Te parece bien?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Envía tus comentarios al desarrollador. ¿Te parece bien?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Gracias por tus comentarios."</string>
<string name="feedback_ok" msgid="6481426753298857144">"Aceptar"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Se abrieron los controles de notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Agregar a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Se agregó la tarjeta"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Se quitó la tarjeta"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de Configuración rápida"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir Configuración"</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está usando <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> usó <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recientemente"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresarial)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Teléfono"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Llamada telefónica"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(a través de <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"cámara"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ubicación"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Se desactivaron los sensores"</string>
<string name="device_services" msgid="1549944177856658705">"Servicios del dispositivo"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Sin título"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Presiona para reiniciar esta app y acceder al modo de pantalla completa."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mover"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Se actualizó el sistema de navegación. Para hacer cambios, ve a Configuración."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Ve a Configuración para actualizar la navegación del sistema"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 36fee3f89da9..c4b8b24b8e06 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Hacer captura de pantalla continua"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Cerrar captura de pantalla"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa de captura de pantalla"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Margen superior"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Margen inferior"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Grabación de pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando grabación de pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación continua de una sesión de grabación de la pantalla"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hasta el amanecer"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"A las <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hasta las <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Reducir brillo"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"El NFC está desactivado"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"El NFC está activado"</string>
@@ -440,7 +443,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Toca de nuevo para abrir"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Desliza el dedo hacia arriba para abrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Desliza el dedo hacia arriba para volverlo a intentar"</string>
- <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquear para usar NFC"</string>
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquea para usar el NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertenece a tu organización"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Desliza desde el icono para abrir el teléfono"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Añadir usuario"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Nuevo usuario"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"¿Finalizar sesión de invitado?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Finalizar sesión de invitado"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"¿Quitar invitado?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Finalizar sesión"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Quitar"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Hola de nuevo, invitado"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres continuar con la sesión?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Tu organización ha instalado una entidad de certificación en tu perfil de trabajo. Es posible que se supervise o se modifique tu tráfico de red seguro."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Se ha instalado una entidad de certificación en este dispositivo. Es posible que se supervise o se modifique tu tráfico de red seguro."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"El administrador ha activado el registro de la red para supervisar el tráfico en tu dispositivo."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Tu administrador ha activado el registro de la red, por lo que se monitorizará el tráfico de tu perfil de trabajo, aunque no el de tu perfil personal."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Te has conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que puede supervisar tu actividad de red, como los correos electrónicos, las aplicaciones y los sitios web."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Te has conectado a <xliff:g id="VPN_APP_0">%1$s</xliff:g> y <xliff:g id="VPN_APP_1">%2$s</xliff:g>, que pueden supervisar tu actividad de red, como los correos electrónicos, las aplicaciones y los sitios web."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Tu perfil de trabajo está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que puede supervisar tu actividad de red, como los correos electrónicos, las aplicaciones y los sitios web."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"El sistema ha &lt;b&gt;disminuido automáticamente a Silencio&lt;/b&gt; la importancia de esta notificación."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Esta notificación se ha colocado automáticamente en una &lt;b&gt;posición más alta&lt;/b&gt; en tu pantalla de notificaciones."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Esta notificación se ha colocado automáticamente en una &lt;b&gt;posición más baja&lt;/b&gt; en tu pantalla de notificaciones."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"¿Estuvo bien?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Envía tus comentarios al desarrollador. ¿Ha estado bien?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Gracias por tus comentarios."</string>
<string name="feedback_ok" msgid="6481426753298857144">"Aceptar"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Se han abierto los controles de las notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Añadir a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tarjeta añadida"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tarjeta quitada"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de ajustes rápidos."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir ajustes."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está usando este elemento: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ha usado recientemente este elemento: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresa)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Llamada telefónica"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Llamada telefónica"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(a través de <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"cámara"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ubicación"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensores desactivados"</string>
<string name="device_services" msgid="1549944177856658705">"Servicios del dispositivo"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Sin título"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Toca para reiniciar esta aplicación e ir a la pantalla completa."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mover"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Se ha actualizado la navegación del sistema. Para hacer cambios, ve a Ajustes."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Ve a Ajustes para actualizar la navegación del sistema"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index ff043e0872af..5402b6185a76 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Ekraanipildi kerimine"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ekraanipildist loobumine"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekraanipildi eelvaade"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Ülempiir"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Alampiir"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekraanisalvesti"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekraanisalvestuse töötlemine"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pooleli märguanne ekraanikuva salvestamise seansi puhul"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Kuni päikesetõusuni"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Sisse kell <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Kuni <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Ereduse vähendamine"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC on keelatud"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC on lubatud"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Kuva profiil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Lisa kasutaja"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Uus kasutaja"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Kas lõpetada külastajaseanss?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Lõpeta külastajaseanss"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Kas eemaldada külaline?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Lõpeta seanss"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eemalda"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Tere tulemast tagasi, külaline!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Kas soovite seansiga jätkata?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Alusta uuesti"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Teie organisatsioon installis teie tööprofiilile sertifikaadi volituse. Teie turvalist võrguliiklust võidakse jälgida ja muuta."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Sertifikaadi volitus on sellesse seadmesse installitud. Teie turvalist võrguliiklust võidakse jälgida ja muuta."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Teie administraator lülitas sisse võrgu logimise funktsiooni, mis jälgib teie seadmes liiklust."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Teie administraator on sisse lülitanud võrgu logimise funktsiooni, mis jälgib liiklust teie võrguprofiilil, kuid mitte teie isiklikul profiilil."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Teil on ühendus rakendusega <xliff:g id="VPN_APP">%1$s</xliff:g>, mis saab jälgida teie võrgutegevusi, sh meile, rakendusi ja veebisaite."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Teil on ühendus rakendustega <xliff:g id="VPN_APP_0">%1$s</xliff:g> ja <xliff:g id="VPN_APP_1">%2$s</xliff:g>, mis saavad jälgida teie võrgutegevusi, sh meile, rakendusi ja veebisaite."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Teie tööprofiil on ühendatud rakendusega <xliff:g id="VPN_APP">%1$s</xliff:g>, mis saab jälgida teie võrgutegevusi, sh meile, rakendusi ja veebisaite."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Süsteem määras sellele märguandele automaatselt prioriteedi &lt;b&gt;Vaikne&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Sellele märguandele määrati teie märguandealas automaatselt &lt;b&gt;kõrgem prioriteet&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Sellele märguandele määrati teie märguandealas automaatselt &lt;b&gt;madalam prioriteet&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Kas see oli õige?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Andke arendajale tagasisidet. Kas see oli õige?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Täname tagasiside eest!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> märguannete juhtelemendid on avatud"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Teisaldamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Asend <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Paan on lisatud"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Paan on eemaldatud"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kiirseadete redigeerija."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Teenuse <xliff:g id="ID_1">%1$s</xliff:g> märguanne: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ava seaded."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> kasutab järgmist: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> kasutas hiljuti järgmist: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ettevõte)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonikõne"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonikõne"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(üksuse <xliff:g id="ATTRIBUTION">%s</xliff:g> kaudu)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kaamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"asukoht"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Andurid on välja lülitatud"</string>
<string name="device_services" msgid="1549944177856658705">"Seadme teenused"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Pealkiri puudub"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Puudutage rakenduse taaskäivitamiseks ja täisekraanrežiimi aktiveerimiseks."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Teisalda"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Süsteemis navigeerimine on värskendatud. Muutmiseks avage jaotis Seaded."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Süsteemi navigeerimise värskendamiseks avage jaotis Seaded"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 7517f17623fc..510e7f93402d 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Pantaila-argazki etengabea"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Baztertu pantaila-argazkia"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pantaila-argazkiaren aurrebista"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Goiko ertza"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Beheko ertza"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Pantaila-grabagailua"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Pantaila-grabaketa prozesatzen"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pantailaren grabaketa-saioaren jakinarazpen jarraitua"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Egunsentira arte"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktibatze-ordua: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Desaktibatze-ordua: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Murriztu distira"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Desgaituta dago NFC"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Gaituta dago NFC"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Erakutsi profila"</string>
<string name="user_add_user" msgid="4336657383006913022">"Gehitu erabiltzailea"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Erabiltzaile berria"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Gonbidatuentzako saioa amaitu nahi duzu?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Amaitu gonbidatuentzako saioa"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gonbidatua kendu nahi duzu?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Saioko aplikazio eta datu guztiak ezabatuko dira."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Amaitu saioa"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Kendu"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Ongi etorri berriro, gonbidatu hori!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Saioarekin jarraitu nahi duzu?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Hasi berriro"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Erakundeak ziurtagiri-emaile bat instalatu dizu laneko profilean. Baliteke sareko trafiko segurua gainbegiratzea edo aldatzea."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Ziurtagiri-emaile bat dago instalatuta gailuan. Baliteke sareko trafiko segurua gainbegiratzea edo aldatzea."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratzaileak sare-erregistroak aktibatu ditu; horrela, zure gailuko trafikoa gainbegira dezake."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administratzaileak sare-erregistroak aktibatu ditu; horrela, zure laneko profileko trafikoa gainbegira dezake, baina ez zure profil pertsonalekoa."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"<xliff:g id="VPN_APP">%1$s</xliff:g> aplikaziora konektatuta zaude eta hark sareko jarduerak gainbegira ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> eta <xliff:g id="VPN_APP_1">%2$s</xliff:g> aplikazioetara konektatuta zaude, eta haiek sareko jarduerak gainbegira ditzakete, mezu elektronikoak, aplikazioak eta webguneak barne."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"<xliff:g id="VPN_APP">%1$s</xliff:g> aplikaziora dago konektatuta laneko profila, eta aplikazio horrek sareko jarduerak gainbegira ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Jakinarazpen hau automatikoki &lt;b&gt;aldatu da soinurik gabeko modura&lt;/b&gt; du sistemak."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Jakinarazpen hau automatikoki &lt;b&gt;igo da mailaz&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Jakinarazpen hau automatikoki &lt;b&gt;jaitsi da mailaz&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Zuzena al da hau?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Jakinarazi oharrak garatzaileari. Zuzena al da?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Mila esker iritzia emateagatik!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"Ados"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Ireki dira <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak kontrolatzeko aukerak"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Eraman <xliff:g id="POSITION">%1$d</xliff:g>garren lekura"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Gehitu <xliff:g id="POSITION">%1$d</xliff:g>garren lekuan"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>garren lekua"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Gehitu da lauza"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kendu da lauza"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ezarpen bizkorren editorea."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> zerbitzuaren jakinarazpena: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ireki ezarpenak."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> aplikazioa <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> erabiltzen ari da"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> aplikazioak <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> erabili du duela gutxi"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enpresa)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefono-deia"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefono-deia"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> aplikazioaren bidez)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"kokapena"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sentsoreak desaktibatuta daude"</string>
<string name="device_services" msgid="1549944177856658705">"Gailuetarako zerbitzuak"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Ez du izenik"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Berrabiarazi aplikazio hau eta ezarri pantaila osoko modua."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Eraman"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Eguneratu da sistemaren nabigazioa. Aldaketak egiteko, joan Ezarpenak atalera."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Sistemaren nabigazioa eguneratzeko, joan Ezarpenak atalera"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 231eb45fef64..72d7b50ac850 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"نماگرفت پیمایشی"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"رد کردن نماگرفت"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"پیش‌نمایش نماگرفت"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"مرز بالایی"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"مرز پایینی"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ضبط‌کننده صفحه‌نمایش"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"درحال پردازش ضبط صفحه‌نمایش"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"اعلان درحال انجام برای جلسه ضبط صفحه‌نمایش"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"تا طلوع آفتاب"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"ساعت <xliff:g id="TIME">%s</xliff:g> روشن می‌شود"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"تا<xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"کاهش روشنایی"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"‏ارتباط میدان نزدیک (NFC)"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"‏«ارتباط میدان نزدیک» (NFC) غیرفعال است"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"‏«ارتباط میدان نزدیک» (NFC) فعال است"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"نمایش نمایه"</string>
<string name="user_add_user" msgid="4336657383006913022">"افزودن کاربر"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"کاربر جدید"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"جلسه مهمان تمام شود؟"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"پایان دادن به جلسه مهمان"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"مهمان حذف شود؟"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه برنامه‌ها و داده‌های این جلسه حذف خواهد شد."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"پایان جلسه"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"حذف"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"مهمان گرامی، بازگشتتان را خوش آمد می‌گوییم!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"آیا می‌خواهید جلسه‌تان را ادامه دهید؟"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"شروع مجدد"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"سازمان شما مرجع گواهینامه‌ای در نمایه کاری شما نصب کرده است. ممکن است ترافیک امن شبکه شما پایش یا تغییر داده شود."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"مرجع گواهینامه‌ای در این دستگاه نصب شده است. ممکن است ترافیک امن شبکه شما پایش یا تغییر داده شود."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"سرپرست سیستم شما گزارش‌گیری از شبکه را (که ترافیک دستگاه شما را پایش می‌کند) روشن کرده است."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"سرپرست شما گزارش‌گیری شبکه را که بر ترافیک نمایه کاری‌تان نظارت می‌کند، اما بر ترافیک نمایه شخصی‌تان نظارت نمی‌کند روشن کرده است."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل شده‌اید، که می‌تواند فعالیت شبکه شما را (ازجمله ایمیل‌ها، برنامه‌‌ها و وب‌سایت‌ها) پایش کند."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"به <xliff:g id="VPN_APP_0">%1$s</xliff:g> و <xliff:g id="VPN_APP_1">%2$s</xliff:g> متصل شده‌اید، که می‌توانند فعالیت شما را در شبکه (ازجمله ایمیل‌ها، برنامه‌‌ها و وب‌سایت‌ها) پایش کنند."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"نمایه کاری شما به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل است، که می‌تواند فعالیت شما در شبکه (ازجمله ایمیل‌ها، برنامه‌ها و وب‌سایت‌ها) را پایش کند."</string>
@@ -709,9 +714,9 @@
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"سیستم را تنظیم کنید که تشخیص دهد اعلان صدا و لرزش داشته باشد یا نه"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"‏&lt;b&gt;وضعیت:&lt;/b&gt; به «پیش‌فرض» ارتقا یافت"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"‏&lt;b&gt;وضعیت:&lt;/b&gt; به «بی‌صدا» تنزل یافت"</string>
- <string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"‏&lt;b&gt;وضعیت:&lt;/b&gt; در رتبه‌بندی بالاتری قرار گرفت"</string>
- <string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"‏&lt;b&gt;وضعیت:&lt;/b&gt; در رتبه‌بندی پایین‌تری قرار گرفت"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"در بالای بخش مکالمه به‌صورت حبابک شناور نشان داده می‌شود و تصویر نمایه را در صفحه قفل نمایش می‌دهد"</string>
+ <string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"‏&lt;b&gt;وضعیت:&lt;/b&gt; در رده‌بندی بالاتری قرار گرفت"</string>
+ <string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"‏&lt;b&gt;وضعیت:&lt;/b&gt; در رده‌بندی پایین‌تری قرار گرفت"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"در بالای بخش مکالمه به‌صورت حبابک شناور نشان داده می‌شود و عکس نمایه را در صفحه قفل نمایش می‌دهد"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"تنظیمات"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"اولویت"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> از ویژگی‌های مکالمه پشتیبانی نمی‌کند"</string>
@@ -731,9 +736,9 @@
<string name="notification_appops_ok" msgid="2177609375872784124">"تأیید"</string>
<string name="feedback_alerted" msgid="5192459808484271208">"‏سیستم این اعلان را به‌طور خودکار &lt;b&gt;به «پیش‌فرض» ارتقا داد&lt;/b&gt;."</string>
<string name="feedback_silenced" msgid="9116540317466126457">"‏سیستم این اعلان را به‌طور خودکار &lt;b&gt;به «بی‌صدا» تنزل داد&lt;/b&gt;."</string>
- <string name="feedback_promoted" msgid="2125562787759780807">"‏این اعلان به‌طور خودکار در کشوی اعلانات &lt;b&gt;در رتبه‌بندی بالاتری قرار گرفت&lt;/b&gt;."</string>
- <string name="feedback_demoted" msgid="951884763467110604">"‏این اعلان به‌طور خودکار در کشوی اعلانات &lt;b&gt;در رتبه‌بندی پایین‌تری قرار گرفت&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"این مورد درست بود؟"</string>
+ <string name="feedback_promoted" msgid="2125562787759780807">"‏این اعلان به‌طور خودکار در کشوی اعلانات &lt;b&gt;در رده‌بندی بالاتری قرار گرفت&lt;/b&gt;."</string>
+ <string name="feedback_demoted" msgid="951884763467110604">"‏این اعلان به‌طور خودکار در کشوی اعلانات &lt;b&gt;در رده‌بندی پایین‌تری قرار گرفت&lt;/b&gt;."</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"بازخوردتان را به اطلاع توسعه‌دهنده برسانید. این مورد درست بود؟"</string>
<string name="feedback_response" msgid="4671729244976641339">"از بازخوردتان سپاس‌گزاریم!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"تأیید"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"کنترل‌های اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> باز شد"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"انتقال به <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"افزودن به موقعیت <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"موقعیت <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"کاشی اضافه شد"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"کاشی حذف شد"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ویرایشگر تنظیمات سریع."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"اعلان <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"باز کردن تنظیمات."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> درحال استفاده از <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> است"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> اخیراً از <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> استفاده کرده است"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(شرکتی)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"تماس تلفنی"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(ازطریق <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"دوربین"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"مکان"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"حسگرها خاموش است"</string>
<string name="device_services" msgid="1549944177856658705">"سرویس‌های دستگاه"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"بدون عنوان"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"برای بازراه‌اندازی این برنامه و تغییر به حالت تمام‌صفحه، ضربه بزنید."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"انتقال"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"پیمایش سیستم به‌روزرسانی شد. برای انجام تغییرات به «تنظیمات» بروید."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"برای به‌روزرسانی پیمایش سیستم، به «تنظیمات» بروید"</string>
@@ -986,7 +992,7 @@
<string name="priority_onboarding_title" msgid="2893070698479227616">"مکالمه روی اولویت تنظیم شده است"</string>
<string name="priority_onboarding_behavior" msgid="5342816047020432929">"مکالمه‌های اولویت‌دار:"</string>
<string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"نمایش در بالای بخش مکالمه"</string>
- <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"تصویر نمایه را در صفحه قفل نمایش می‌دهد"</string>
+ <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"عکس نمایه را در صفحه قفل نمایش می‌دهد"</string>
<string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"به‌شکل حبابک شناور روی برنامه‌ها ظاهر می‌شود"</string>
<string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"وقفه در «مزاحم نشوید»"</string>
<string name="priority_onboarding_done_button_title" msgid="4569550984286506007">"متوجه‌ام"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index a6c43d96ae81..457d765f641d 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Vieritä kuvakaappausta"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Hylkää kuvakaappaus"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Kuvakaappauksen esikatselu"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Yläraja"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Alaraja"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Näytön tallentaja"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Näytön tallennusta käsitellään"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pysyvä ilmoitus näytön tallentamisesta"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Auringonnousuun"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Päälle klo <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> asti"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Vähennä kirkkautta"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC on poistettu käytöstä"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC on käytössä"</string>
@@ -440,7 +443,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Avaa napauttamalla uudelleen"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Avaa pyyhkäisemällä ylös"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Yritä uudelleen pyyhkäisemällä ylös"</string>
- <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Avaa lukitus käyttääksesi NFC:tä"</string>
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Avaa lukitus, jotta voit käyttää NFC:tä"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Organisaatiosi omistaa tämän laitteen"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g> omistaa tämän laitteen"</string>
<string name="phone_hint" msgid="6682125338461375925">"Avaa puhelu pyyhkäisemällä."</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Näytä profiili"</string>
<string name="user_add_user" msgid="4336657383006913022">"Lisää käyttäjä"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Uusi käyttäjä"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Lopetetaanko Vierailija-käyttökerta?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Lopeta Vierailija-käyttökerta"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Poistetaaanko vieras?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Lopeta käyttökerta"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Poista"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Tervetuloa takaisin!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Haluatko jatkaa istuntoa?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Aloita alusta"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organisaatiosi lisäsi työprofiiliin varmenteen myöntäjän. Suojattua verkkoliikennettäsi voidaan valvoa tai muuttaa."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Laitteeseen on asennettu varmenteen myöntäjä. Suojattua verkkoliikennettäsi voidaan valvoa tai muuttaa."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Järjestelmänvalvoja on ottanut käyttöön verkkolokitietojen tallentamisen, joka valvoo laitteellasi tapahtuvaa liikennettä."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Järjestelmänvalvoja on ottanut käyttöön verkkolokitietojen tallentamisen. Sen avulla seurataan liikennettä työprofiilissasi mutta ei henkilökohtaisessa profiilissasi."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Olet yhteydessä sovellukseen <xliff:g id="VPN_APP">%1$s</xliff:g>, joka voi valvoa verkkotoimintaasi, esimerkiksi sähköposteja, sovelluksia ja verkkosivustoja."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Olet yhteydessä sovelluksiin <xliff:g id="VPN_APP_0">%1$s</xliff:g> ja <xliff:g id="VPN_APP_1">%2$s</xliff:g>, jotka voivat valvoa verkkotoimintaasi, esimerkiksi sähköposteja, sovelluksia ja verkkosivustoja."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Työprofiilisi on yhteydessä sovellukseen <xliff:g id="VPN_APP">%1$s</xliff:g>, joka voi valvoa toimintaasi verkossa, esimerkiksi sähköposteja, sovelluksia ja verkkosivustoja."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Järjestelmä &lt;b&gt;hiljensi&lt;/b&gt; tämän ilmoituksen automaattisesti."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Tämä ilmoitus valittiin automaattisesti &lt;b&gt;tärkeämmäksi&lt;/b&gt; ilmoitusalueella."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Tämä ilmoitus valittiin automaattisesti &lt;b&gt;vähemmän tärkeäksi&lt;/b&gt; ilmoitusalueella."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Oliko tämä oikein?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Anna kehittäjälle palautetta. Oliko tämä oikein?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Kiitos palautteesta!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Sovelluksen <xliff:g id="APP_NAME">%1$s</xliff:g> ilmoitusten hallinta on avattu."</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Siirrä paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisää paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Paikka <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kiekko lisätty"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kiekko poistettu"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Pika-asetusten muokkausnäkymä"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Ilmoitus kohteesta <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Avaa asetukset."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> käyttää kohdetta <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> käytti kohdetta <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> äskettäin"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(yritys)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Puhelu"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Puhelu"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(kautta: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"sijainti"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Anturit pois päältä"</string>
<string name="device_services" msgid="1549944177856658705">"Laitepalvelut"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Ei nimeä"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Napauta, niin sovellus käynnistyy uudelleen ja siirtyy koko näytön tilaan."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Siirrä"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Järjestelmän navigointitapa vaihdettu. Voit muuttaa sitä asetuksista."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Vaihda järjestelmän navigointitapaa asetuksista"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 07f8af5ad4e2..3c4a91085fb3 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Faire défiler la capture d\'écran"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Fermer la capture d\'écran"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Aperçu de la capture d\'écran"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Limite supérieure"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Limite inférieure"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Trait. de l\'enregist. d\'écran…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notification en cours pour une session d\'enregistrement d\'écran"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Jusqu\'à l\'aube"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Actif à <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Jusqu\'à <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Réduire la luminosité"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC désactivée"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC activée"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Afficher le profil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Ajouter un utilisateur"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Nouvel utilisateur"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Mettre fin à la session d\'invité?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Mettre fin à la session d\'invité"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Supprimer l\'invité?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Fermer la session"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Supprimer"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenue à nouveau dans la session Invité"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la session?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recommencer"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Votre entreprise a installé une autorité de certification dans votre profil professionnel. Votre trafic sur le réseau sécurisé peut être contrôlé ou modifié."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Une autorité de certification est installée sur cet appareil. Votre trafic sur le réseau sécurisé peut être contrôlé ou modifié."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Votre administrateur a activé la journalisation réseau, qui surveille le trafic sur votre appareil."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Votre administrateur a activé la journalisation réseau, qui surveille le trafic dans votre profil professionnel, mais pas dans votre profil personnel."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Vous êtes connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>, qui peut contrôler votre activité réseau, y compris les courriels, les applications et les sites Web."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Vous êtes connecté à <xliff:g id="VPN_APP_0">%1$s</xliff:g> et à <xliff:g id="VPN_APP_1">%2$s</xliff:g>, qui peuvent contrôler votre activité sur le réseau, y compris l\'activité relative aux courriels, aux applications et aux sites Web."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Votre profil professionnel est connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>, qui peut contrôler votre activité réseau, y compris les courriels, les applications et les sites Web."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"La notification a été automatiquement &lt;b&gt;abaissée à la catégorie Silencieux&lt;/b&gt; par le système."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"La notification a été automatiquement &lt;b&gt;élevée d\'un niveau&lt;/b&gt; dans votre volet."</string>
<string name="feedback_demoted" msgid="951884763467110604">"La notification a été automatiquement &lt;b&gt;abaissée d\'un niveau&lt;/b&gt; dans votre volet."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Était-ce correct?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Faites part de vos commentaires au concepteur. Était-ce correct?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Merci de vos commentaires!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Les paramètres des notifications pour <xliff:g id="APP_NAME">%1$s</xliff:g> sont ouverts"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Déplacer vers <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ajouter à la position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tuile ajoutée"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tuile retirée"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur de paramètres rapides."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ouvrir les paramètres."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> utilise cet élément : <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> a récemment utilisé cet élément : <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(entreprise)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Appel téléphonique"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Appel téléphonique"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(par l\'intermédiaire de <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"appareil photo"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"position"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Capteurs désactivés"</string>
<string name="device_services" msgid="1549944177856658705">"Services de l\'appareil"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Sans titre"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Touchez pour redémarrer cette application et passer en plein écran."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Déplacer"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"La navigation système a été mise à jour. Pour apporter des modifications, accédez au menu Paramètres."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Accédez au menu Paramètres pour mettre à jour la navigation système"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 7bbbff668710..629cbbabbba3 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Faire défiler la capture d\'écran"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Fermer la capture d\'écran"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Aperçu de la capture d\'écran"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Limite supérieure"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Limite inférieure"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Enregistrement de l\'écran…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notification en cours pour une session d\'enregistrement de l\'écran"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Jusqu\'à l\'aube"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"À partir de <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Jusqu\'à <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Réduire la luminosité"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC désactivée"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"La technologie NFC est activée"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Afficher le profil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Ajouter un utilisateur"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Nouvel utilisateur"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Fermer la session Invité ?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Fermer la session Invité"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Supprimer l\'invité ?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Fermer la session"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Supprimer"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenue à nouveau dans la session Invité"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la dernière session ?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Non, nouvelle session"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Votre entreprise a installé une autorité de certification dans votre profil professionnel. Votre trafic sur le réseau sécurisé peut être contrôlé ou modifié."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Une autorité de certification est installée sur cet appareil. Votre trafic sur le réseau sécurisé peut être contrôlé ou modifié."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Votre administrateur a activé la journalisation du réseau, pour contrôler le trafic sur votre appareil."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Votre administrateur a activé la journalisation réseau, qui surveille le trafic de votre profil professionnel, mais pas celui de votre profil personnel."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Vous êtes connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>, qui peut contrôler votre activité sur le réseau, y compris l\'activité relative aux e-mails, aux applications et aux sites Web."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Vous êtes connecté à <xliff:g id="VPN_APP_0">%1$s</xliff:g> et à <xliff:g id="VPN_APP_1">%2$s</xliff:g>, qui peuvent contrôler votre activité sur le réseau, y compris l\'activité relative aux e-mails, aux applications et aux sites Web."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Votre profil professionnel est connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>, qui peut contrôler votre activité sur le réseau, y compris l\'activité relative aux e-mails, aux applications et aux sites Web."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"La notification a été automatiquement &lt;b&gt;abaissée à la catégorie \"Silencieux\"&lt;/b&gt; par le système."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"La notification a été automatiquement &lt;b&gt;élevée d\'un niveau&lt;/b&gt; dans votre volet."</string>
<string name="feedback_demoted" msgid="951884763467110604">"La notification a été automatiquement &lt;b&gt;abaissée d\'un niveau&lt;/b&gt; dans votre volet."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Est-ce que c\'était correct ?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Faites part de vos commentaires au développeur. Est-ce que c\'était correct ?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Merci de nous avoir envoyé vos commentaires."</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Les commandes de notification sont disponibles pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Déplacer vers <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ajouter à la position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Carte ajoutée"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Carte supprimée"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur de configuration rapide."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ouvrir les paramètres."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> utilise <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> a utilisé <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> récemment"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(Enterprise)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Appel téléphonique"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Appel téléphonique"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(via <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"appareil photo"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"position"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Capteurs désactivés"</string>
<string name="device_services" msgid="1549944177856658705">"Services pour l\'appareil"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Sans titre"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Appuyez pour redémarrer cette application et activer le mode plein écran."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Déplacer"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigation système mise à jour. Pour apporter des modifications, accédez aux paramètres."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Accédez aux paramètres pour mettre à jour la navigation système"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 0ac82616c4aa..9383fb6328ce 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Realizar unha captura de pantalla continua"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ignorar a captura de pantalla"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa da captura de pantalla"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Bordo superior"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Bordo inferior"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravadora da pantalla"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando gravación pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación en curso sobre unha sesión de gravación de pantalla"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Ata o amencer"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Activarase ás: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Utilizarase ata as: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Reducir brillo"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"A opción NFC está desactivada"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"A opción NFC está activada"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Engadir usuario"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Novo usuario"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Queres finalizar a sesión de invitado?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Finalizar sesión de invitado"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Queres eliminar o invitado?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Finalizar sesión"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eliminar"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Benvido de novo, convidado."</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Queres continuar coa túa sesión?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Comezar de novo"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"A túa organización instalou unha autoridade de certificación no teu perfil de traballo. É posible que se controle ou se modifique o teu tráfico de rede segura."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Este dispositivo ten unha autoridade de certificación instalada. É posible que se controle ou se modifique o teu tráfico de rede segura."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"O administrador activou o rexistro na rede, que controla o tráfico do teu dispositivo."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"O administrador activou o rexistro na rede, que supervisa o tráfico do teu perfil de traballo, pero non o do perfil persoal."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Estás conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode controlar a túa actividade na rede, mesmo os correos electrónicos, as aplicacións e os sitios web."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Estás conectado a <xliff:g id="VPN_APP_0">%1$s</xliff:g> e a <xliff:g id="VPN_APP_1">%2$s</xliff:g>, que poden controlar a túa actividade na rede, mesmo os correos electrónicos, as aplicacións e os sitios web."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"O teu perfil de traballo está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode controlar a túa actividade na rede, mesmo os correos electrónicos, as aplicacións e os sitios web."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"O sistema &lt;b&gt;diminuíu a Silencioso&lt;/b&gt; o nivel desta notificación de forma automática."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Esta notificación &lt;b&gt;clasificouse nun nivel superior&lt;/b&gt; de forma automática no teu ton."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Esta notificación &lt;b&gt;clasificouse nun nivel inferior&lt;/b&gt; de forma automática no teu ton."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"A información era correcta?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Faille saber a túa opinión ao programador. A información era correcta?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Grazas polo teu comentario"</string>
<string name="feedback_ok" msgid="6481426753298857144">"Aceptar"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Abríronse os controis de notificacións da aplicación <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engadir á posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Engadiuse a tarxeta"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Quitouse a tarxeta"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuración rápida."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configuración."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está utilizando a aplicación <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> utilizou recentemente a aplicación <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(versión empresarial)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Chamada de teléfono"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Chamada de teléfono"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(mediante <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"a cámara"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"a localiz."</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Desactivar sensores"</string>
<string name="device_services" msgid="1549944177856658705">"Servizos do dispositivo"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Sen título"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Toca o botón para reiniciar esta aplicación e abrila en pantalla completa."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mover"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Actualizouse a navegación do sistema. Para facer cambios, vai a Configuración."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Para actualizar a navegación do sistema, vai a Configuración"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index f4e13332a34a..be62a2943491 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -93,6 +93,10 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"સ્ક્રીનશૉટ પર સ્ક્રોલ કરો"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"સ્ક્રીનશૉટ છોડી દો"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"સ્ક્રીનશૉટનો પ્રીવ્યૂ"</string>
+ <!-- no translation found for screenshot_top_boundary (1500569103321300856) -->
+ <skip />
+ <!-- no translation found for screenshot_bottom_boundary (5657242629526407311) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"સ્ક્રીન રેકૉર્ડર"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"સ્ક્રીન રેકૉર્ડિંગ ચાલુ છે"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"સ્ક્રીન રેકોર્ડિંગ સત્ર માટે ચાલુ નોટિફિકેશન"</string>
@@ -410,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"સૂર્યોદય સુધી"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> વાગ્યે ચાલુ"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> વાગ્યા સુધી"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"બ્રાઇટનેસ ઘટાડો"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC અક્ષમ કરેલ છે"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC સક્ષમ કરેલ છે"</string>
@@ -463,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"પ્રોફાઇલ બતાવો"</string>
<string name="user_add_user" msgid="4336657383006913022">"વપરાશકર્તા ઉમેરો"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"નવો વપરાશકર્તા"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"શું અતિથિ સત્ર સમાપ્ત કરીએ?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"અતિથિ સત્ર સમાપ્ત કરો"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"અતિથિ દૂર કરીએ?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ્લિકેશનો અને ડેટા કાઢી નાખવામાં આવશે."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"સત્ર સમાપ્ત કરો"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"દૂર કરો"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"ફરી સ્વાગત છે, અતિથિ!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"શું તમે તમારું સત્ર ચાલુ કરવા માંગો છો?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"શરૂ કરો"</string>
@@ -540,6 +546,8 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"તમારી સંસ્થાએ તમારી કાર્ય પ્રોફાઇલમાં પ્રમાણપત્ર સત્તાધિકારી ઇન્સ્ટૉલ કર્યું છે. તમારા સુરક્ષિત નેટવર્ક ટ્રાફિકનું નિયમન થઈ શકે છે અથવા તેમાં ફેરફાર કરવામાં આવી શકે છે."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"આ ઉપકરણ પર પ્રમાણપત્ર સત્તાધિકારી ઇન્સ્ટૉલ કરેલ છે. તમારા સુરક્ષિત નેટવર્ક ટ્રાફિકનું નિયમન થઈ શકે છે અથવા તેમાં ફેરફાર કરવામાં આવી શકે છે."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"તમારા વ્યવસ્થાપકે નેટવર્ક લૉગિંગ ચાલુ કર્યું છે, જે તમારા ઉપકરણ પર નેટવર્ક ટ્રાફિકનું નિયમન કરે છે."</string>
+ <!-- no translation found for monitoring_description_managed_profile_network_logging (6932303843097006037) -->
+ <skip />
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"તમે <xliff:g id="VPN_APP">%1$s</xliff:g> સાથે કનેક્ટ થયાં છો, જે ઇમેઇલ, ઍપ્લિકેશનો અને વેબસાઇટ સહિત તમારી નેટવર્ક પ્રવૃત્તિનું નિરીક્ષણ કરી શકે છે."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"તમે <xliff:g id="VPN_APP_0">%1$s</xliff:g> અને <xliff:g id="VPN_APP_1">%2$s</xliff:g> સાથે કનેક્ટ થયાં છો, જે ઇમેઇલ, ઍપ્લિકેશનો અને વેબસાઇટ સહિત તમારી નેટવર્ક પ્રવૃત્તિનું નિરીક્ષણ કરી શકે છે."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"તમારી કાર્યાલયની પ્રોફાઇલ <xliff:g id="VPN_APP">%1$s</xliff:g> સાથે કનેક્ટ કરેલ છે, જે ઇમેઇલ, ઍપ્લિકેશનો અને વેબસાઇટો સહિતની તમારી નેટવર્ક પ્રવૃત્તિનું નિયમન કરી શકે છે."</string>
@@ -733,7 +741,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"સિસ્ટમ દ્વારા આ નોટિફિકેશનને ઑટોમૅટિક રીતે &lt;b&gt;સાઇલન્ટ પર અવનત&lt;/b&gt; કરવામાં આવ્યું."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"તમારા શેડમાં આ નોટિફિકેશનને ઑટોમૅટિક રીતે &lt;b&gt;ઉપલી રેંક&lt;/b&gt; આપવામાં આવી."</string>
<string name="feedback_demoted" msgid="951884763467110604">"તમારા શેડમાં આ નોટિફિકેશનને ઑટોમૅટિક રીતે &lt;b&gt;નીચલી રેંક&lt;/b&gt; આપવામાં આવી."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"શું આ યોગ્ય હતું?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"ડેવલપરને તમારા પ્રતિસાદ વિશે જાણવા દો. શું આ યોગ્ય હતું?"</string>
<string name="feedback_response" msgid="4671729244976641339">"તમારા પ્રતિસાદ બદલ આભાર!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ઓકે"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે સૂચના નિયંત્રણો ચાલુ છે"</string>
@@ -880,6 +888,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> પર ખસેડો"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"જગ્યા પર <xliff:g id="POSITION">%1$d</xliff:g> ઉમેરો"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"જગ્યા <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ટાઇલ ઉમેરી"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ટાઇલ કાઢી નાખી"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ઝડપી સેટિંગ્સ સંપાદક."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> નોટિફિકેશન: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"સેટિંગ્સ ખોલો."</string>
@@ -970,7 +980,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>નો ઉપયોગ કરી રહી છે"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>એ તાજેતરમાં જ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>નો ઉપયોગ કર્યો છે"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(એન્ટરપ્રાઇઝ)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ફોન કૉલ"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ફોન કૉલ"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> મારફતે)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"કૅમેરા"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"સ્થાન"</string>
@@ -978,7 +988,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"સેન્સર બંધ છે"</string>
<string name="device_services" msgid="1549944177856658705">"ડિવાઇસ સેવાઓ"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"કોઈ શીર્ષક નથી"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"આ ઍપ ફરીથી ચાલુ કરવા માટે ટૅપ કરીને પૂર્ણ સ્ક્રીન કરો."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ખસેડો"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"સિસ્ટમ નૅવિગેશન અપડેટ કર્યું. ફેરફારો કરવા માટે, સેટિંગ પર જાઓ."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"સિસ્ટમ નૅવિગેશનને અપડેટ કરવા માટે સેટિંગ પર જાઓ"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 50b078f97ccb..b487689c86c6 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"स्क्रीनशॉट को स्क्रोल करें"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"स्क्रीनशॉट को खारिज करें"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉट की झलक"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"स्क्रीनशाॉट की ऊपर की सीमा"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"स्क्रीनशॉट की नीचे की सीमा"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रिकॉर्डर"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रीन रिकॉर्डिंग को प्रोसेस किया जा रहा है"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"स्क्रीन रिकॉर्ड सेशन के लिए जारी सूचना"</string>
@@ -412,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"सुबह तक चालू रहेगी"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> पर चालू हाेगी"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> तक चालू रहेगी"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"स्क्रीन की चमक कम करें"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"एनएफ़सी"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC बंद है"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC चालू है"</string>
@@ -442,7 +445,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"खोलने के लिए फिर से टैप करें"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"खोलने के लिए ऊपर स्वाइप करें"</string>
<string name="keyguard_retry" msgid="886802522584053523">"फिर से कोशिश करने के लिए ऊपर की ओर स्वाइप करें"</string>
- <string name="require_unlock_for_nfc" msgid="1305686454823018831">"एनएफ़सी इस्तेमाल करने के लिए, स्क्रीन को अनलॉक करें"</string>
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"एनएफ़सी इस्तेमाल करने के लिए स्क्रीन को अनलॉक करें"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"इस डिवाइस का मालिकाना हक आपके संगठन के पास है"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"इस डिवाइस का मालिकाना हक <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> के पास है"</string>
<string name="phone_hint" msgid="6682125338461375925">"फ़ोन के लिए आइकॉन से स्वाइप करें"</string>
@@ -465,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफ़ाइल दिखाएं"</string>
<string name="user_add_user" msgid="4336657383006913022">"उपयोगकर्ता जोड़ें"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"नया उपयोगकर्ता"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"मेहमान के तौर पर ब्राउज़ करने का सेशन खत्म करना चाहते हैं?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"मेहमान के तौर पर ब्राउज़ करने का सेशन खत्म करें"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथि को निकालें?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सत्र के सभी ऐप्स और डेटा को हटा दिया जाएगा."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"सेशन खत्म करें"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"निकालें"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"अतिथि, आपका फिर से स्वागत है!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"क्‍या आप अपना सत्र जारी रखना चाहते हैं?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"फिर से शुरू करें"</string>
@@ -542,6 +546,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"आपके संगठन ने आपकी वर्क प्रोफ़ाइल में एक प्रमाणपत्र अनुमति इंस्टॉल की है. आपके सुरक्षित नेटवर्क ट्रैफ़िक की निगरानी या उसमें बदलाव किया जा सकता है."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"इस डिवाइस पर एक प्रमाणपत्र अनुमति इंस्टॉल की है. आपके सुरक्षित नेटवर्क ट्रैफ़िक की निगरानी या उसमें बदलाव किया जा सकता है."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"आपके व्यवस्थापक ने नेटवर्क लॉगिंग चालू किया है, जो आपके डिवाइस पर ट्रैफ़िक की निगरानी करता है."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"आपके एडमिन ने नेटवर्क लॉगिंग की सुविधा चालू कर दी है, जिससे आपकी वर्क प्रोफ़ाइल पर आने वाले ट्रैफ़िक की निगरानी की जाती है. हालांकि, इससे आपकी निजी प्रोफ़ाइल की निगरानी नहीं की जाती."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"आप <xliff:g id="VPN_APP">%1$s</xliff:g> से कनेक्‍ट हैं, जो ईमेल, ऐप्लिकेशन और वेबसाइटों सहित आपकी नेटवर्क गतिविधि की निगरानी कर सकते हैं."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"आप <xliff:g id="VPN_APP_0">%1$s</xliff:g> और <xliff:g id="VPN_APP_1">%2$s</xliff:g> से कनेक्ट हैं, जो ईमेल, ऐप्लिकेशन और वेबसाइटों सहित आपकी नेटवर्क गतिविधि की निगरानी कर सकते हैं."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"आपकी वर्क प्रोफ़ाइल <xliff:g id="VPN_APP">%1$s</xliff:g> से कनेक्ट है, जो ईमेल, ऐप्लिकेशन और वेबसाइटों सहित आपकी नेटवर्क गतिविधि की निगरानी कर सकता है."</string>
@@ -735,7 +740,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"सिस्टम ने अपने-आप &lt;b&gt;इस सूचना का लेवल घटाकर, इसे साइलेंट&lt;/b&gt; पर सेट किया था."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"आपकी शेड में, इस सूचना को अपने-आप &lt;b&gt;रैंकिंग में ऊपर&lt;/b&gt; किया गया था."</string>
<string name="feedback_demoted" msgid="951884763467110604">"आपकी शेड में, इस सूचना को अपने-आप &lt;b&gt;रैंकिंग में नीचे&lt;/b&gt; किया गया था."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"आपको यह सुविधा कैसी लगी?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"डेवलपर को अपना सुझाव/राय दें. आपको यह सुविधा कैसी लगी?"</string>
<string name="feedback_response" msgid="4671729244976641339">"सुझाव या शिकायत के लिए धन्यवाद!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ठीक है"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> के लिए सूचना नियंत्रण चालू हैं"</string>
@@ -882,6 +887,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"टाइल को <xliff:g id="POSITION">%1$d</xliff:g> पोज़िशन पर ले जाएं"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"टाइल को <xliff:g id="POSITION">%1$d</xliff:g> पोज़िशन पर जोड़ें"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"टाइल की पोज़िशन <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"टाइल जोड़ी गई"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"टाइल हटाई गई"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"त्वरित सेटिंग संपादक."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिंग खोलें."</string>
@@ -955,8 +962,8 @@
<string name="slice_permission_text_1" msgid="6675965177075443714">"- यह <xliff:g id="APP">%1$s</xliff:g> से सूचना पढ़ सकता है"</string>
<string name="slice_permission_text_2" msgid="6758906940360746983">"- यह <xliff:g id="APP">%1$s</xliff:g> में कार्रवाई कर सकता है"</string>
<string name="slice_permission_checkbox" msgid="4242888137592298523">"<xliff:g id="APP">%1$s</xliff:g> को किसी भी ऐप्लिकेशन के हिस्से (स्लाइस) दिखाने की मंज़ूरी दें"</string>
- <string name="slice_permission_allow" msgid="6340449521277951123">"मंज़ूरी दें"</string>
- <string name="slice_permission_deny" msgid="6870256451658176895">"नामंज़ूर करें"</string>
+ <string name="slice_permission_allow" msgid="6340449521277951123">"अनुमति दें"</string>
+ <string name="slice_permission_deny" msgid="6870256451658176895">"अनुमति न दें"</string>
<string name="auto_saver_title" msgid="6873691178754086596">"बैटरी सेवर शेड्यूल करने के लिए टैप करें"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"जब बैटरी खत्म होने वाली हो तब \'बैटरी सेवर\' चालू करें"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"जी नहीं, शुक्रिया"</string>
@@ -971,16 +978,15 @@
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" और "</string>
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> का इस्तेमाल कर रहा है"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ने हाल ही में <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> का इस्तेमाल किया"</string>
- <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"एंटरप्राइज़ वर्शन"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
- <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"<xliff:g id="ATTRIBUTION">%s</xliff:g> के ज़रिए"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(एंटरप्राइज़ वर्शन)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"फ़ोन कॉल"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> के ज़रिए)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"कैमरा"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"जगह"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"माइक्रोफ़ोन"</string>
<string name="sensor_privacy_mode" msgid="4462866919026513692">"सेंसर बंद हैं"</string>
<string name="device_services" msgid="1549944177856658705">"डिवाइस सेवाएं"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"कोई शीर्षक नहीं"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"इस ऐप्लिकेशन को रीस्टार्ट करने और फ़ुल स्क्रीन चालू करने के लिए टैप करें."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ले जाएं"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"सिस्टम नेविगेशन अपडेट हो गया. बदलाव करने के लिए \'सेटिंग\' पर जाएं."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"सिस्टम नेविगेशन अपडेट करने के लिए \'सेटिंग\' में जाएं"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index c80f26260fca..8f3d7c03be75 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Pomicanje snimke zaslona"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Odbacivanje snimke zaslona"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimke zaslona"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Gornja granica"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Donja granica"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snimač zaslona"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrada snimanja zaslona"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Tekuća obavijest za sesiju snimanja zaslona"</string>
@@ -412,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do izlaska sunca"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Uključuje se u <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Smanjenje svjetline"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je onemogućen"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je omogućen"</string>
@@ -465,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Prikaz profila"</string>
<string name="user_add_user" msgid="4336657383006913022">"Dodavanje korisnika"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Završiti gostujuću sesiju?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Završi gostujuću sesiju"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ukloniti gosta?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji bit će izbrisani."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Završi sesiju"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Dobro došli natrag, gostu!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string>
@@ -543,6 +547,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Vaša je organizacija instalirala izdavač certifikata na vašem radnom profilu. Vaš sigurni mrežni promet možda se nadzire ili modificira."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Na ovom je uređaju instaliran izdavač certifikata. Vaš sigurni mrežni promet možda se nadzire ili modificira."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administrator je uključio mrežni zapisnik koji nadzire promet na vašem uređaju."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administrator je uključio mrežni zapisnik koji prati promet na vašem poslovnom profilu, ali ne i na osobnom profilu."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Povezani ste s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g> koja može nadzirati vašu aktivnost na mreži, uključujući e-poštu, aplikacije i web-lokacije."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Povezani ste s aplikacijama <xliff:g id="VPN_APP_0">%1$s</xliff:g> i <xliff:g id="VPN_APP_1">%2$s</xliff:g> koje mogu nadzirati vašu aktivnost na mreži, uključujući e-poruke, aplikacije i web-lokacije."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Vaš je radni profil povezan s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g> koja može nadzirati vašu aktivnost na mreži, uključujući e-poruke, aplikacije i web-lokacije."</string>
@@ -736,7 +741,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Sustav je ovu obavijest automatski &lt;b&gt;prebacio u bešumnu&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Ova obavijest automatski je &lt;b&gt;rangirana više&lt;/b&gt; na vašem zaslonu."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Ova obavijest automatski je &lt;b&gt;rangirana niže&lt;/b&gt; na vašem zaslonu."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Je li to bilo točno?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Recite razvojnom programeru što mislite. Je li to bilo točno?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Zahvaljujemo na povratnim informacijama!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"U redu"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Otvorene su kontrole obavijesti za <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -885,6 +890,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premještanje u prostoriju <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodavanje na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kartica je dodana"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kartica je uklonjena"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač brzih postavki."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> obavijest: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvaranje postavki."</string>
@@ -975,7 +982,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> koristi sljedeće: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> nedavno je koristila sljedeće: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(za poslovne korisnike)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonski poziv"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonski poziv"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(putem aplikacije <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"fotoaparat"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokaciju"</string>
@@ -983,7 +990,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Senzori su isključeni"</string>
<string name="device_services" msgid="1549944177856658705">"Usluge uređaja"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Bez naslova"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Dodirnite da biste ponovo pokrenuli tu aplikaciju i prikazali je na cijelom zaslonu."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Premjesti"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Ažurirana je navigacija sustavom. Možete je promijeniti u Postavkama."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Navigaciju sustavom možete ažurirati u Postavkama"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 868b2e7cbbb0..4fda6679e2d9 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Görgethető képernyőkép"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Képernyőkép elvetése"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Képernyőkép előnézete"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Felső határ"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Alsó határ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Képernyőrögzítő"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Képernyőrögzítés feldolgozása"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Folyamatban lévő értesítés képernyőrögzítési munkamenethez"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Napfelkeltéig"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Be: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Eddig: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Fényerő csökkentése"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Az NFC ki van kapcsolva"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Az NFC be van kapcsolva"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profil megjelenítése"</string>
<string name="user_add_user" msgid="4336657383006913022">"Felhasználó hozzáadása"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Új felhasználó"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Befejezi a vendég munkamenetet?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"A vendég munkamenet befejezése"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Eltávolítja a vendég munkamenetet?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Munkamenet befejezése"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eltávolítás"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Örülünk, hogy visszatért, vendég!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Folytatja a munkamenetet?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Újrakezdés"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Szervezete tanúsítványkibocsátót telepített a munkaprofilba. Ezáltal figyelhetik és befolyásolhatják az Ön biztonságos hálózati forgalmát."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Az eszközre tanúsítványkibocsátó van telepítve. Ezáltal figyelhetik és befolyásolhatják az Ön biztonságos hálózati forgalmát."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"A rendszergazda bekapcsolta az eszköz forgalmát figyelő hálózati naplózást."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"A rendszergazda bekapcsolta a hálózati naplózást, amely a munkaprofilban figyeli a forgalmat, a személyes profilban azonban nem."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Ön kapcsolódik a(z) <xliff:g id="VPN_APP">%1$s</xliff:g> alkalmazáshoz, amely figyelheti hálózati tevékenységét, beleértve a levelezést, valamint az alkalmazás- és webhelyhasználatot."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Ön csatlakozik a(z) <xliff:g id="VPN_APP_0">%1$s</xliff:g> és a(z) <xliff:g id="VPN_APP_1">%2$s</xliff:g> alkalmazásokhoz, amelyek figyelhetik hálózati tevékenységét, beleértve a levelezést, valamint az alkalmazás- és webhelyhasználatot."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Munkaprofilja csatlakozik a(z) <xliff:g id="VPN_APP">%1$s</xliff:g> alkalmazáshoz, amely figyelheti hálózati tevékenységét, beleértve a levelezést, az alkalmazásokat és a webhelyeket."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Ezt az értesítést automatikusan &lt;b&gt;némára állította&lt;/b&gt; a rendszer."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Ezt az értesítést automatikusan &lt;b&gt;előrébb sorolta&lt;/b&gt; a rendszer az értesítési felületen."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Ezt az értesítést automatikusan &lt;b&gt;hátrébb sorolta&lt;/b&gt; a rendszer az értesítési felületen."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Megfelelő ez így?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Ossza meg a fejlesztővel a visszajelzését. Megfelelő ez így?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Köszönjük a visszajelzést!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> értesítésvezérlői megnyitva"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Áthelyezés ide: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Hozzáadás a következő pozícióhoz: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. hely"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kártya hozzáadva"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kártya eltávolítva"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Gyorsbeállítások szerkesztője"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-értesítések: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Beállítások megnyitása."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"A(z) <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> használja a következőt: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"A(z) <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> nemrég használta a következőt: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(vállalati)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonhívás"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonhívás"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(a következőn keresztül: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"helyadatok"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Érzékelők kikapcsolva"</string>
<string name="device_services" msgid="1549944177856658705">"Eszközszolgáltatások"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Nincs cím"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Koppintson az alkalmazás újraindításához és a teljes képernyős mód elindításához."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Áthelyezés"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"A rendszer-navigáció módja megváltozott. Módosításához nyissa meg a Beállításokat."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"A rendszer-navigációs lehetőségeket a Beállításokban módosíthatja"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 3c088749ed57..f0c1f21b505e 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Ոլորել սքրինշոթը"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Փակել սքրինշոթը"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Սքրինշոթի նախադիտում"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Վերևի սահմանագիծ"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Ներքևի սահմանագիծ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Էկրանի տեսագրիչ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Էկրանի տեսագրության մշակում"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Էկրանի տեսագրման աշխատաշրջանի ընթացիկ ծանուցում"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Մինչև լուսաբաց"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Կմիանա՝ <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Մինչև <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Նվազեցնել պայծառությունը"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC-ն անջատված է"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC-ն միացված է"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Ցույց տալ դիտարկումը"</string>
<string name="user_add_user" msgid="4336657383006913022">"Ավելացնել օգտատեր"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Նոր օգտատեր"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Ավարտե՞լ հյուրի աշխատաշրջանը"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Ավարտել հյուրի աշխատաշրջանը"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Հեռացնե՞լ հյուրին:"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Այս աշխատաշրջանի բոլոր ծրագրերն ու տվյալները կջնջվեն:"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Ավարտել աշխատաշրջանը"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Հեռացնել"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Բարի վերադարձ, հյուր:"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Դուք ցանկանու՞մ եք շարունակել ձեր գործողությունը:"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Սկսել"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ձեր կազմակերպությունը ձեր աշխատանքային պրոֆիլում տեղադրել է վկայագրման կենտրոն։ Ձեր ցանցի ապահով թրաֆիկը կարող է վերահսկվել կամ փոփոխվել։"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Այս սարքում տեղադրված է վկայագրման կենտրոն։ Ձեր ցանցի ապահով թրաֆիկը կարող է վերահսկվել կամ փոփոխվել։"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Ձեր ադմինիստրատորը միացրել է ցանցային իրադարձությունների գրանցումը, որը վերահսկում է ձեր սարքի թրաֆիկը։"</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Ձեր ադմինիստրատորը միացրել է ցանցային իրադարձությունների գրանցումը, որը վերահսկում է ձեր աշխատանքային պրոֆիլի թրաֆիկը (այլ ոչ անձնական պրոֆիլը)։"</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Դուք կապակցված եք <xliff:g id="VPN_APP">%1$s</xliff:g> հավելվածին, որը կարող է վերահսկել ձեր ցանցային գործողությունը, այդ թվում նաև էլփոստը, հավելվածները և կայքերը:"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Դուք կապակցված եք <xliff:g id="VPN_APP_0">%1$s</xliff:g> և <xliff:g id="VPN_APP_1">%2$s</xliff:g> հավելվածներին, որոնք կարող են վերահսկել ձեր ցանցային գործունեությունը, այդ թվում նաև էլփոստը, հավելվածները և կայքերը:"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Ձեր աշխատանքային պրոֆիլը կապակցված է <xliff:g id="VPN_APP">%1$s</xliff:g> հավելվածին, որը կարող է վերահսկել ձեր ցանցային գործունեությունը, այդ թվում նաև էլփոստը, հավելվածները և կայքերը:"</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Այս ծանուցման կարևորության մակարդակը ավտոմատ &lt;b&gt;իջեցվել է և դարձել անձայն&lt;/b&gt;։"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Ծանուցումների վահանակում այս ծանուցուման կարևորության մակարդակն ավտոմատ &lt;b&gt;բարձրացվել է&lt;/b&gt;։"</string>
<string name="feedback_demoted" msgid="951884763467110604">"Ծանուցումների վահանակում այս ծանուցուման կարևորության մակարդակն ավտոմատ &lt;b&gt;իջեցվել է&lt;/b&gt;։"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Սա ճի՞շտ էր"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Ուղարկեք ձեր կարծիքը մշակողին։ Սա ճի՞շտ էր։"</string>
<string name="feedback_response" msgid="4671729244976641339">"Շնորհակալություն արձագանքելու համար"</string>
<string name="feedback_ok" msgid="6481426753298857144">"Եղավ"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի ծանուցումների կառավարումը բաց է"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Տեղափոխել դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ավելացնել դիրք <xliff:g id="POSITION">%1$d</xliff:g>-ում"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Սալիկն ավելացվեց"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Սալիկը հեռացվեց"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Արագ կարգավորումների խմբագրիչ:"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ծանուցում՝ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Բացել կարգավորումները:"</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> հավելվածն օգտագործում է «<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>» գործառույթը"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> հավելվածը վերջերս օգտագործել է «<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>» գործառույթը"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(կորպորատիվ տարբերակ)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Հեռախոսազանգ"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Հեռախոսազանգ"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g>-ի միջոցով)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"տեսախցիկը"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"վայրը"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Տվիչներն անջատած են"</string>
<string name="device_services" msgid="1549944177856658705">"Սարքի ծառայություններ"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Անանուն"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Հպեք՝ հավելվածը վերագործարկելու և լիաէկրան ռեժիմին անցնելու համար։"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Տեղափոխել"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Համակարգի նավիգացիան թարմացվեց: Փոփոխություններ անելու համար անցեք կարգավորումներ:"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Թարմացրեք համակարգի նավիգացիան կարգավորումներում"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index a5d8a27cf5a0..71ff39c12c8d 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Men-scroll screenshot"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Menutup screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pratinjau screenshot"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Batas atas"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Batas bawah"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Perekam Layar"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Memproses perekaman layar"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifikasi yang sedang berjalan untuk sesi rekaman layar"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Sampai pagi"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktif pada <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Sampai <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Kurangi Kecerahan"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC dinonaktifkan"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC diaktifkan"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Tampilkan profil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Tambahkan pengguna"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Pengguna baru"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Akhiri sesi tamu?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Akhiri sesi tamu"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Hapus tamu?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data di sesi ini akan dihapus."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Akhiri sesi"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Hapus"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Selamat datang kembali, tamu!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Lanjutkan sesi Anda?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulai ulang"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organisasi Anda menginstal otoritas sertifikat di profil kerja. Traffic jaringan aman Anda mungkin dipantau atau diubah."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Otoritas sertifikat diinstal di perangkat. Traffic jaringan aman Anda mungkin dipantau atau diubah."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Admin telah mengaktifkan pencatatan jaringan, yang memantau traffic di perangkat."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Admin telah mengaktifkan logging jaringan, yang memantau traffic di profil kerja, tetapi tidak di profil pribadi."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Anda tersambung ke <xliff:g id="VPN_APP">%1$s</xliff:g>, yang dapat memantau aktivitas jaringan, termasuk email, aplikasi, dan situs web."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Anda tersambung ke <xliff:g id="VPN_APP_0">%1$s</xliff:g> dan <xliff:g id="VPN_APP_1">%2$s</xliff:g>, yang dapat memantau aktivitas jaringan, termasuk email, aplikasi, dan situs web."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Profil kerja Anda tersambung ke <xliff:g id="VPN_APP">%1$s</xliff:g>, yang dapat memantau aktivitas jaringan, termasuk email, aplikasi, dan situs."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Notifikasi ini otomatis &lt;b&gt;didemosikan menjadi Senyap&lt;/b&gt; oleh sistem."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Notifikasi ini otomatis &lt;b&gt;diberi peringkat lebih tinggi&lt;/b&gt; di shade Anda."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Notifikasi ini otomatis &lt;b&gt;diberi peringkat lebih rendah&lt;/b&gt; di shade Anda."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Sudah benar?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Berikan masukan kepada developer. Sudah benar?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Terima kasih atas masukan Anda"</string>
<string name="feedback_ok" msgid="6481426753298857144">"Oke"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kontrol notifikasi untuk <xliff:g id="APP_NAME">%1$s</xliff:g> dibuka"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pindahkan ke <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Tambahkan ke posisi <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisi <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kartu ditambahkan"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kartu dihapus"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor setelan cepat."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notifikasi <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buka setelan."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> sedang menggunakan <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> menggunakan <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> baru-baru ini"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(perusahaan)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Panggilan telepon"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Panggilan telepon"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(melalui <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokasi"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensor nonaktif"</string>
<string name="device_services" msgid="1549944177856658705">"Layanan Perangkat"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Tanpa judul"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Ketuk untuk memulai ulang aplikasi ini dan membuka layar penuh."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Pindahkan"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigasi sistem diupdate. Untuk melakukan perubahan, buka Setelan."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Buka Setelan untuk mengupdate navigasi sistem"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 5f794e76150f..482338ad3090 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Flettiskjáskot"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Loka skjámynd"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Forskoðun skjámyndar"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Efri mörk"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Neðri mörk"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skjáupptaka"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Vinnur úr skjáupptöku"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Áframhaldandi tilkynning fyrir skjáupptökulotu"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Til sólarupprásar"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Virkt kl. <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Til <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Draga úr birtu"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Slökkt á NFC"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Kveikt á NFC"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Sýna snið"</string>
<string name="user_add_user" msgid="4336657383006913022">"Bæta notanda við"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Nýr notandi"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Ljúka gestalotu?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Ljúka gestalotu"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Fjarlægja gest?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Ljúka lotu"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjarlægja"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkominn aftur, gestur!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Viltu halda áfram með lotuna?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Byrja upp á nýtt"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Fyrirtækið þitt setti upp CA-vottorð á vinnusniðinu þínu. Eftirlit kann að vera haft með öruggri netnotkun þinni eða henni kann að vera breytt."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"CA-vottorð er uppsett á þessu tæki. Eftirlit kann að vera haft með öruggri netnotkun þinni eða henni kann að vera breytt."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Kerfisstjóri hefur kveikt á eftirliti netkerfa, sem fylgist með netumferð á tækinu þínu."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Stjórnandinn kveikti á eftirliti netkerfa sem fylgist með netumferð á vinnusniðinu þínu en ekki á eigin sniði."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Þú ert með tengingu við <xliff:g id="VPN_APP">%1$s</xliff:g>, sem getur fylgst með netnotkun þinni, þ. á m. tölvupósti, forritum og vefsvæðum."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Þú ert með tengingu við <xliff:g id="VPN_APP_0">%1$s</xliff:g> og <xliff:g id="VPN_APP_1">%2$s</xliff:g>, sem geta fylgst með netnotkun þinni, þar á meðal tölvupósti, forritum og vefsvæðum."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Vinnusniðið þitt er tengt <xliff:g id="VPN_APP">%1$s</xliff:g>, sem getur fylgst með netnotkun þinni, þ. á m. tölvupósti, forritum og vefsvæðum."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Stig þessarar tilkynningar var sjálfkrafa &lt;b&gt;lækkað í þögult&lt;/b&gt; af kerfinu."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Stig þessarar tilkynningar var sjálfkrafa &lt;b&gt;hækkað&lt;/b&gt; í tilkynningaglugganum þínum."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Stig þessarar tilkynningar var sjálfkrafa &lt;b&gt;lækkað&lt;/b&gt; í tilkynningaglugganum þínum."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Var þetta rétt?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Sendu framleiðandanum ábendingu. Var þetta rétt?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Takk fyrir að segja þína skoðun!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"Í lagi"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Opnað fyrir tilkynningastýringar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Færa í <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bæta við í stöðu <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Staða <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Reit bætt við"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Reitur fjarlægður"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Flýtistillingaritill."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> tilkynning: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Opna stillingar."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> er að nota <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> notaði <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> nýlega"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(fyrirtæki)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Símtal"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Símtal"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(í gegnum <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"myndavél"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"staðsetning"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Slökkt á skynjurum"</string>
<string name="device_services" msgid="1549944177856658705">"Tækjaþjónusta"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Enginn titill"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Ýttu til að endurræsa forritið og sýna það á öllum skjánum."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Færa"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Kerfisstjórnun uppfærð. Þú getur breytt þessu í stillingunum."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Farðu í stillingar til að uppfæra kerfisstjórnun"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index cfd07970b0cd..0896d0c5f9de 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Scorri screenshot"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ignora screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Anteprima screenshot"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Limite superiore"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Limite inferiore"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Registrazione dello schermo"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Elaboraz. registraz. schermo"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifica costante per una sessione di registrazione dello schermo"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Fino all\'alba"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Attivazione alle <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Fino alle <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Riduci la luminosità"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC non attiva"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC attiva"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostra profilo"</string>
<string name="user_add_user" msgid="4336657383006913022">"Aggiungi utente"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Nuovo utente"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vuoi terminare la sessione Ospite?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Termina sessione Ospite"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Rimuovere l\'ospite?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Termina sessione"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Rimuovi"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Bentornato, ospite."</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Vuoi continuare la sessione?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Ricomincia"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"La tua organizzazione ha installato un\'autorità di certificazione nel tuo profilo di lavoro. Il tuo traffico di rete protetto potrebbe essere monitorato o modificato."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Sul dispositivo è installata un\'autorità di certificazione. Il tuo traffico di rete protetto potrebbe essere monitorato o modificato."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"L\'amministratore ha attivato i log di rete, che consentono di monitorare il traffico sul dispositivo."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"L\'amministratore ha attivato i log di rete, che consentono di monitorare il traffico nel profilo di lavoro, ma non nel profilo personale."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Sei connesso a <xliff:g id="VPN_APP">%1$s</xliff:g>, che consente di monitorare le attività di rete, inclusi siti web, email e app."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Sei connesso a <xliff:g id="VPN_APP_0">%1$s</xliff:g> e <xliff:g id="VPN_APP_1">%2$s</xliff:g>, che consentono di monitorare le attività di rete, inclusi siti web, email e app."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Il tuo profilo di lavoro è collegato a <xliff:g id="VPN_APP">%1$s</xliff:g>, da cui è possibile monitorare la tua attività di rete, inclusi siti web, email e app."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Questa notifica è stata &lt;b&gt;retrocessa automaticamente a Silenziosa&lt;/b&gt; dal sistema."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Questa notifica è stata &lt;b&gt;posizionata automaticamente più in alto&lt;/b&gt; nell\'area notifiche."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Questa notifica è stata &lt;b&gt;posizionata automaticamente più in basso&lt;/b&gt; nell\'area notifiche."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Era corretto?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Fai conoscere la tua opinione allo sviluppatore. La classificazione era corretta?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Grazie per il feedback."</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Controlli di gestione delle notifiche per <xliff:g id="APP_NAME">%1$s</xliff:g> aperti"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Sposta nella posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Aggiungi alla posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Riquadro aggiunto"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Riquadro rimosso"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor di impostazioni rapide."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notifica di <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Apri le impostazioni."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> sta usando: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ha usato di recente: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonata"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(tramite <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"Fotocamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"luogo"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensori disattivati"</string>
<string name="device_services" msgid="1549944177856658705">"Servizi del dispositivo"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Senza titolo"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Tocca per riavviare l\'app e passare a schermo intero."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Sposta"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigazione del sistema aggiornata. Per apportare modifiche, usa le Impostazioni."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Usa le Impostazioni per aggiornare la navigazione del sistema"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index c4d088d24cce..e304dbe66c7a 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -93,6 +93,10 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"צילום מסך נגלל"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"סגירת צילום מסך"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"תצוגה מקדימה של צילום מסך"</string>
+ <!-- no translation found for screenshot_top_boundary (1500569103321300856) -->
+ <skip />
+ <!-- no translation found for screenshot_bottom_boundary (5657242629526407311) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"מקליט המסך"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"מתבצע עיבוד של הקלטת מסך"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"התראה מתמשכת לסשן הקלטת מסך"</string>
@@ -414,6 +418,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"עד הזריחה"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"יתחיל בשעה <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"עד <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"הפחתה של עוצמת הבהירות"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"‏NFC מושבת"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"‏NFC מופעל"</string>
@@ -444,7 +449,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"הקש שוב כדי לפתוח"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"צריך להחליק כדי לפתוח"</string>
<string name="keyguard_retry" msgid="886802522584053523">"יש להחליק למעלה כדי לנסות שוב"</string>
- <string name="require_unlock_for_nfc" msgid="1305686454823018831">"‏יש לבטל נעילה כדי להשתמש ב-NFC"</string>
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"‏יש לבטל את הנעילה כדי להשתמש ב-NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"המכשיר הזה שייך לארגון שלך"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"המכשיר הזה שייך לארגון <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"החלק מהסמל כדי להפעיל את הטלפון"</string>
@@ -467,9 +472,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"הצג פרופיל"</string>
<string name="user_add_user" msgid="4336657383006913022">"הוספת משתמש"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"משתמש חדש"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"להפסיק את הגלישה כאורח?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"הפסקת הגלישה כאורח"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"להסיר אורח?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"כל האפליקציות והנתונים בפעילות זו באתר יימחקו."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"הפסקת הגלישה"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"הסר"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"שמחים לראותך שוב!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"האם ברצונך להמשיך בפעילות באתר?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ברצוני להתחיל מחדש"</string>
@@ -546,6 +552,8 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"הארגון שלך התקין רשות אישורים בפרופיל העבודה. ניתן לעקוב אחר התנועה ברשת המאובטחת או לשנות אותה."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"במכשיר זה מותקנת רשות אישורים. ניתן לעקוב אחר התנועה ברשת המאובטחת או לשנות אותה."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"מנהל המערכת הפעיל את התכונה \'רישום התנועה ברשת\', שמנטרת את תנועת הנתונים במכשיר."</string>
+ <!-- no translation found for monitoring_description_managed_profile_network_logging (6932303843097006037) -->
+ <skip />
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"אתה מחובר לאפליקציה <xliff:g id="VPN_APP">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"אתה מחובר לאפליקציות <xliff:g id="VPN_APP_0">%1$s</xliff:g> ו-<xliff:g id="VPN_APP_1">%2$s</xliff:g>, שיכולות לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"פרופיל העבודה שלך מחובר לאפליקציה <xliff:g id="VPN_APP">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string>
@@ -739,7 +747,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"‏ההתראה הזו &lt;b&gt;הורדה בדרגה ל\'שקט\'&lt;/b&gt; באופן אוטומטי על-ידי המערכת."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"‏ההתראה הזו &lt;b&gt;דורגה גבוה יותר&lt;/b&gt; באופן אוטומטי בהתראות שלך."</string>
<string name="feedback_demoted" msgid="951884763467110604">"‏ההתראה הזו &lt;b&gt;דורגה נמוך יותר&lt;/b&amp;gt באופן אוטומטי בהתראות שלך."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"האם פעולה זו הייתה נכונה?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"אפשר לשלוח למפתח את המשוב שלך. האם פעולה זו הייתה נכונה?"</string>
<string name="feedback_response" msgid="4671729244976641339">"תודה על המשוב!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"אישור"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"פקדי ההודעות של <xliff:g id="APP_NAME">%1$s</xliff:g> נפתחו"</string>
@@ -890,6 +898,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"העברה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"הוספה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"מיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"האריח נוסף"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"האריח הוסר"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"עורך הגדרות מהירות."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"התראות <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"פתיחת הגדרות."</string>
@@ -977,23 +987,17 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"אפליקציות משתמשות ב<xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" וגם "</string>
- <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) -->
- <skip />
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"האפליקציה <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> משתמשת ב<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"האפליקציה <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> השתמשה לאחרונה ב<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(גרסה ארגונית)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"שיחת טלפון"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(באמצעות <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"מצלמה"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"מיקום"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"מיקרופון"</string>
<string name="sensor_privacy_mode" msgid="4462866919026513692">"החיישנים כבויים"</string>
<string name="device_services" msgid="1549944177856658705">"שירותים למכשיר"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"ללא שם"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"צריך להקיש כדי להפעיל מחדש את האפליקציה הזו ולעבור למסך מלא."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"העברה"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"הניווט במערכת עודכן. אפשר לערוך שינויים דרך ההגדרות."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"יש לעבור להגדרות כדי לעדכן את הניווט במערכת"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 3fcc7138d9b3..05b1f7679e59 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"スクリーンショットをスクロールします"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"スクリーンショットを閉じます"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"スクリーンショットのプレビュー"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"上部境界"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"下部境界"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"スクリーン レコーダー"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"画面の録画を処理しています"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"画面の録画セッション中の通知"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"日の出まで"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>にオン"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g>まで"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"明るさを下げる"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC は無効です"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC は有効です"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"プロファイルを表示"</string>
<string name="user_add_user" msgid="4336657383006913022">"ユーザーを追加"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"新しいユーザー"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ゲスト セッションを終了しますか?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"ゲスト セッションを終了する"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ゲストを削除しますか?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"セッションを終了"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"削除"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"おかえりなさい、ゲストさん"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"セッションを続行しますか?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"最初から開始"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"組織によって、あなたの仕事用プロファイルに認証局がインストールされました。保護されたネットワーク トラフィックが監視、変更される場合があります。"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"このデバイスには認証局がインストールされています。保護されたネットワーク トラフィックが監視、変更される可能性があります。"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"管理者がネットワーク ログを有効にしているため、このデバイスのトラフィックが監視されています。"</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"管理者がネットワーク ログを有効にしているため、仕事用プロファイルのトラフィックは監視されています(個人用プロファイルは対象外)。"</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"<xliff:g id="VPN_APP">%1$s</xliff:g> に接続しています。このアプリはあなたのネットワーク アクティビティ(メール、アプリ、ウェブサイトなど)を監視できます。"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> と <xliff:g id="VPN_APP_1">%2$s</xliff:g> に接続しています。これらのアプリは、あなたのネットワーク アクティビティ(メール、アプリ、ウェブサイト)を監視できます。"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"この仕事用プロファイルは <xliff:g id="VPN_APP">%1$s</xliff:g> に接続しています。このアプリはあなたのネットワーク アクティビティ(メール、アプリ、ウェブサイトなど)を監視できます。"</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"この通知は、システムによって自動的に&lt;b&gt;ランクがサイレントに下がり&lt;/b&gt;ました。"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"この通知は、自動的にシェード内の&lt;b&gt;ランクが上がり&lt;/b&gt;ました。"</string>
<string name="feedback_demoted" msgid="951884763467110604">"この通知は、自動的にシェード内の&lt;b&gt;ランクが下がり&lt;/b&gt;ました。"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"間違いありませんか?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"皆様のご意見がデベロッパーに届きます。これで間違いありませんか?"</string>
<string name="feedback_response" msgid="4671729244976641339">"フィードバックをお寄せいただきありがとうございます。"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> の通知管理は開いています"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> に移動"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ポジション <xliff:g id="POSITION">%1$d</xliff:g> に追加"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"タイルを追加しました"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"タイルを削除しました"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"クイック設定エディタ"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> の通知: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"設定を開きます。"</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> は <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> を使用しています"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> は最近 <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> を使用しました"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(エンタープライズ版)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"電話"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"通話"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> 経由)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"カメラ"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"現在地情報"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"センサー OFF"</string>
<string name="device_services" msgid="1549944177856658705">"デバイス サービス"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"タイトルなし"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"タップしてこのアプリを再起動すると、全画面表示になります。"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"移動"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"システム ナビゲーションを更新しました。変更するには [設定] に移動してください。"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"システム ナビゲーションを更新するには [設定] に移動してください"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 5c11fa44f3fc..5404e67a927b 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"ეკრანის ანაბეჭდში გადაადგილება"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"ეკრანის ანაბეჭდის დახურვა"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ეკრანის ანაბეჭდის გადახედვა"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"ზედა საზღვარი"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"ქვედა საზღვარი"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ეკრანის ჩამწერი"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ეკრანის ჩანაწერი მუშავდება"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"უწყვეტი შეტყობინება ეკრანის ჩაწერის სესიისთვის"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"მზის ამოსვლამდე"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"ჩაირთოს <xliff:g id="TIME">%s</xliff:g>-ზე"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g>-მდე"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"სიკაშკაშის შემცირება"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC გათიშულია"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ჩართულია"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"პროფილის ჩვენება"</string>
<string name="user_add_user" msgid="4336657383006913022">"მომხმარებლის დამატება"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"ახალი მომხმარებელი"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"დასრულდეს სტუმრის სესია?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"სტუმრის სესიის დასრულება"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"სტუმრის ამოშლა?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაცემი წაიშლება."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"სესიის დასრულება"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ამოშლა"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"სტუმარო, გვიხარია, რომ დაბრუნდით!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"გსურთ, თქვენი სესიის გაგრძელება?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ხელახლა დაწყება"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"თქვენმა ორგანიზაციამ სამსახურის პროფილში სერტიფიცირების ორგანო დააინსტალირა. თქვენი ქსელის დაცული ტრაფიკი შეიძლება შეიცვალოს, ან მასზე მონიტორინგი განხორციელდეს."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ამ მოწყობილობაზე დაინსტალირებულია სერტიფიცირების ორგანო. თქვენი ქსელის დაცული ტრაფიკი შეიძლება შეიცვალოს, ან მასზე მონიტორინგი განხორციელდეს."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"თქვენმა ადმინისტრატორმა ჩართო ქსელის ჟურნალირება, რომელიც თქვენი მოწყობილობის ტრაფიკის მონიტორინგს ახორციელებს."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"თქვენმა ადმინისტრატორმა ქსელის ჟურნალირება ჩართო, რომელიც ახორციელებს თქვენი სამსახურის პროფილის, მაგრამ არა პირადი პროფილის, ტრაფიკის მონიტორინგს."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"თქვენ დაკავშირებული ხართ <xliff:g id="VPN_APP">%1$s</xliff:g>-თან, რომელსაც შეუძლია თქვენი ქსელის აქტივობის (მათ შორის, ელფოსტის, აპებისა და ვებსაიტების) მონიტორინგი."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"თქვენ დაკავშირებული ხართ <xliff:g id="VPN_APP_0">%1$s</xliff:g>-სა და <xliff:g id="VPN_APP_1">%2$s</xliff:g>-თან, რომელთაც შეუძლია თქვენი ქსელის აქტივობის (მათ შორის, ელფოსტის, აპებისა და ვებსაიტების) მონიტორინგი."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"თქვენი სამსახურის პროფილი დაკავშირებულია <xliff:g id="VPN_APP">%1$s</xliff:g>-თან, რომელსაც შეუძლია თქვენი ქსელის აქტივობის (მათ შორის, ელფოსტის, აპებისა და ვებსაიტების) მონიტორინგი."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"ეს შეტყობინება ავტომატურად &lt;b&gt;გადავიდა „უხმო“ სტატუსზე&lt;/b&gt; სისტემის მიერ."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"ეს შეტყობინება ავტომატურად &lt;b&gt;ჩაითვალა უფრო&lt;/b&gt; პრიორიტეტულად."</string>
<string name="feedback_demoted" msgid="951884763467110604">"ეს შეტყობინება ავტომატურად &lt;b&gt;ჩაითვალა ნაკლებად&lt;/b&gt; პრიორიტეტულად."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"სწორია ეს?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"მიაწოდეთ დეველოპერს თქვენი გამოხმაურება. სწორია ეს?"</string>
<string name="feedback_response" msgid="4671729244976641339">"გმადლობთ გამოხმაურებისთვის!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"კარგი"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"შეტყობინებების მართვა „<xliff:g id="APP_NAME">%1$s</xliff:g>“-ისთვის გახსნილია"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"გადატანა <xliff:g id="POSITION">%1$d</xliff:g>-ზე"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"დამატება პოზიციაზე <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"პოზიცია <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"მოზაიკის ფილა დაემატა"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"მოზაიკის ფილა ამოიშალა"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"სწრაფი პარამეტრების რედაქტორი."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> შეტყობინება: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"პარამეტრების გახსნა."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> იყენებს აპს <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"აპმა <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ახლახან გამოიყენა <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(კორპორაციული)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"სატელეფონო ზარი"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"სატელეფონო ზარი"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(აპის <xliff:g id="ATTRIBUTION">%s</xliff:g> მეშვეობით)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"კამერა"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"მდებარეობა"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"სენსორების გამორთვა"</string>
<string name="device_services" msgid="1549944177856658705">"მოწყობილობის სერვისები"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"უსათაურო"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"შეეხეთ ამ აპის გადასატვირთად და გადადით სრულ ეკრანზე."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"გადატანა"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"სისტემური ნავიგაცია განახლდა. ცვლილებების შესატანად გადადით პარამეტრებზე."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"სისტემური ნავიგაციის გასაახლებლად გადადით პარამეტრებზე"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 89f905733fa6..21c62018bef1 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Скриншотты айналдыру"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Скриншотты жабу"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Скриншотты алдын ала қарау"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Жоғарғы шектік сызық"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Төменгі шектік сызық"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Экран жазғыш"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Экран жазғыш бейнесін өңдеу"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Экранды бейнеге жазудың ағымдағы хабарландыруы"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Күн шыққанға дейін"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Қосылу уақыты: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> дейін"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Жарықтығын азайту"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC өшірулі"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC қосулы"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Профильді көрсету"</string>
<string name="user_add_user" msgid="4336657383006913022">"Пайдаланушы қосу"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Жаңа пайдаланушы"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Қонақ сеансы аяқталсын ба?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Қонақ сеансын аяқтау"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Қонақты жою керек пе?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданбалар мен деректер жойылады."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Сеансты аяқтау"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Алып тастау"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Қош келдіңіз, қонақ"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансты жалғастыру керек пе?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Қайта бастау"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ұйымыңыз жұмыс профиліңізде сертификат орнатқан. Қорғалған желі трафигіңіз бақылануы немесе өзгертілуі мүмкін."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Осы құрылғыда сертификат орнатылған. Қорғалған желі трафигіңіз бақылануы немесе өзгертілуі мүмкін."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Әкімші құрылғыңыздағы трафикті бақылайтын желі журналын жүргізуді қосқан."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Әкімші жеке профильдегі емес, жұмыс профиліндегі трафикті қадағалау үшін желі журналын жүргізуді қосып қойған."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Желідегі әрекеттеріңізді, соның ішінде электрондық хабарларды, қолданбаларды және вебсайттарды бақылай алатын <xliff:g id="VPN_APP">%1$s</xliff:g> желісіне қосылдыңыз."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Желідегі әрекеттеріңізді, соның ішінде электрондық хабарларды, қолданбаларды және вебсайттарды бақылай алатын <xliff:g id="VPN_APP_0">%1$s</xliff:g> және <xliff:g id="VPN_APP_1">%2$s</xliff:g> желілеріне қосылдыңыз."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Жұмыс профиліңіз желідегі белсенділігіңізді, соның ішінде электрондық хабарларды, қолданбаларды және веб-сайттарды бақылай алатын <xliff:g id="VPN_APP">%1$s</xliff:g> қолданбасына қосылған."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Жүйе бұл хабарландырудың деңгейін автоматты түрде &lt;b&gt;\"Үнсіз\" санатына төмендетті&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Жүйе бұл хабарландырудың маңыздылық деңгейін автоматты түрде &lt;b&gt;көтерді&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Жүйе бұл хабарландырудың маңыздылық деңгейін автоматты түрде &lt;b&gt;төмендетті&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Барлығы дұрыс па?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Әзірлеушіге пікіріңізді білдіріңіз. Барлығы дұрыс па?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Пікіріңіз үшін рақмет!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"Жарайды"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> хабарландыруларын басқару элементтері ашылды"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> орнына жылжыту"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> орнына қосу"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> орны"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Бөлшек қосылды."</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Бөлшек өшірілді."</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Жылдам параметрлер өңдегіші."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> хабарландыруы: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Параметрлерді ашу."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> қолданбасы қазір <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> пайдаланады"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> қолданбасы жақында <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> пайдаланды"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(корпоративтік)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Телефон қоңырауы"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Телефон қоңырауы"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> арқылы)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камера"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"геодерек"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Датчиктер өшірулі"</string>
<string name="device_services" msgid="1549944177856658705">"Құрылғы қызметтері"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Атауы жоқ"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Бұл қолданбаны қайта қосып, толық экранға өту үшін түртіңіз."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Жылжыту"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Жүйе навигациясы жаңартылды. Өзгерту енгізу үшін \"Параметрлер\" бөліміне өтіңіз."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Жүйе навигациясын жаңарту үшін \"Параметрлер\" бөліміне өтіңіз."</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 85557070a51a..567bfa87c316 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"មុខងារ​ថតរូបថត​អេក្រង់​រំកិល"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"ច្រានចោល​រូបថត​អេក្រង់"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ការមើល​រូបថត​អេក្រង់​សាកល្បង"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"បន្ទាត់បែងចែក​ខាងលើ"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"បន្ទាត់បែងចែក​ខាងក្រោម"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"មុខងារថត​អេក្រង់"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"កំពុង​ដំណើរការ​ការថតអេក្រង់"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ការជូនដំណឹង​ដែល​កំពុង​ដំណើរការ​សម្រាប់​រយៈពេលប្រើ​ការថត​សកម្មភាព​អេក្រង់"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"រហូត​ដល់​ពេល​ថ្ងៃរះ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"បើកនៅម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"រហូតដល់ម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"បន្ថយពន្លឺ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"បាន​បិទ NFC"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"បាន​បើក NFC"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"បង្ហាញ​ប្រវត្តិរូប"</string>
<string name="user_add_user" msgid="4336657383006913022">"បន្ថែម​អ្នកប្រើ"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"អ្នកប្រើ​ថ្មី"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"បញ្ចប់វគ្គភ្ញៀវឬ?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"បញ្ចប់វគ្គភ្ញៀវ"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"លុប​ភ្ញៀវ​?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ទិន្នន័យ និង​កម្មវិធី​ទាំងអស់​ក្នុង​សម័យ​នេះ​នឹង​ត្រូវ​បាន​លុប។"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"បញ្ចប់វគ្គ"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"លុបចេញ"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"សូម​ស្វាគមន៍​ការ​ត្រឡប់​មកវិញ, ភ្ញៀវ!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"តើ​អ្នក​ចង់​បន្ត​សម័យ​របស់​អ្នក​?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ចាប់ផ្ដើម"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ស្ថាប័នរបស់អ្នក​បានដំឡើង​អាជ្ញាធរវិញ្ញាបនបត្រ​នៅក្នុង​កម្រងព័ត៌មាន​ការងារ។ ចរាចរណ៍​បណ្តាញដែលមាន​សុវត្ថិភាព​របស់អ្នក​អាច​ត្រូវបាន​តាមដាន ឬ​កែសម្រួល។"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"បាន​ដំឡើង​អាជ្ញាធរវិញ្ញាបនបត្រ​នៅលើ​ឧបករណ៍​នេះ។ ចរាចរណ៍​បណ្តាញដែលមានសុវត្ថិភាព​របស់អ្នក​អាច​ត្រូវបាន​តាមដាន ឬ​កែសម្រួល។"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"អ្នក​គ្រប់គ្រង​របស់អ្នក​បាន​បើក​ការ​ធ្វើ​កំណត់ហេតុ​បណ្តាញ​ ដែល​នឹង​តាមដាន​ចរាចរណ៍​នៅលើ​ឧបករណ៍​របស់អ្នក។"</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"អ្នកគ្រប់គ្រង​របស់អ្នក​បានបើក​ការធ្វើ​កំណត់ហេតុ​បណ្តាញ ដែល​តាមដាន​ចរាចរណ៍​នៅក្នុងកម្រងព័ត៌មាន​ការងាររបស់អ្នក ប៉ុន្តែមិនតាមដាន​នៅក្នុងកម្រងព័ត៌មានផ្ទាល់ខ្លួន​របស់អ្នកឡើយ។"</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"អ្នកបានភ្ជាប់ទៅ <xliff:g id="VPN_APP">%1$s</xliff:g> ដែលអាចតាមដានសកម្មភាពក្នុងបណ្តាញរបស់អ្នក រួមទាំងអ៊ីមែល កម្មវិធី និងគេហទំព័រផងដែរ។"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"អ្នកបាន​ភ្ជាប់ទៅ <xliff:g id="VPN_APP_0">%1$s</xliff:g> និង <xliff:g id="VPN_APP_1">%2$s</xliff:g> ដែលអាច​តាមដាន​សកម្មភាព​បណ្តាញ​របស់អ្នក រួមទាំង​អ៊ីមែល កម្មវិធី និង​គេហទំព័រ​ផងដែរ។"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"កម្រងព័ត៌មាន​ការងារ​របស់អ្នក​ត្រូវបាន​ភ្ជាប់​ទៅ <xliff:g id="VPN_APP">%1$s</xliff:g> ដែលអាច​តាមដាន​សកម្មភាព​បណ្តាញ​របស់អ្នក រួមទាំងអ៊ីមែល កម្មវិធី និង​គេហទំព័រ​ផងដែរ។"</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"ការជូនដំណឹងនេះ​ត្រូវបាន&lt;b&gt;បញ្ចុះ​ទៅស្ងាត់&lt;/b&gt;ដោយប្រព័ន្ធដោយស្វ័យប្រវត្តិ។"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"ការជូនដំណឹងនេះ​ត្រូវបាន&lt;b&gt;ចាត់ថ្នាក់ខ្ពស់ជាងមុន&lt;/b&gt;ដោយស្វ័យប្រវត្តិ នៅក្នុង​ផ្ទាំងជូនដំណឹង​របស់អ្នក។"</string>
<string name="feedback_demoted" msgid="951884763467110604">"ការជូនដំណឹងនេះ​ត្រូវបាន&lt;b&gt;ចាត់ថ្នាក់​ទាបជាងមុន&lt;/b&gt;ដោយស្វ័យប្រវត្តិ នៅក្នុង​ផ្ទាំងជូនដំណឹង​របស់អ្នក។"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"តើវា​ត្រឹមត្រូវទេ?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"ប្រាប់ឱ្យអ្នក​អភិវឌ្ឍន៍ដឹងអំពីមតិកែលម្អរបស់អ្នក។ តើវា​ត្រឹមត្រូវដែរទេ?"</string>
<string name="feedback_response" msgid="4671729244976641339">"សូមអរគុណចំពោះ​មតិកែលម្អ​របស់អ្នក!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"យល់ព្រម"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"ការគ្រប់គ្រងការជូនដំណឹងសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> បានបើក"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ផ្លាស់​ទីទៅ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"បញ្ចូលទៅ​ទីតាំងទី <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ទីតាំងទី <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"បានបញ្ចូលប្រអប់"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"បាន​ផ្លាស់ទី​ប្រអប់"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"កម្មវិធីកែការកំណត់រហ័ស"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ការជូនដំណឹង៖ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"បើកការកំណត់"</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> កំពុងប្រើ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> បានប្រើ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ថ្មីៗនេះ"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(សហគ្រាស)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ការហៅទូរសព្ទ"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ការហៅ​ទូរសព្ទ"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(តាមរយៈ <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"កាមេរ៉ា"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ទីតាំង"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"បិទឧបករណ៍​ចាប់សញ្ញា"</string>
<string name="device_services" msgid="1549944177856658705">"សេវាកម្មឧបករណ៍"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"គ្មាន​ចំណងជើង"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"ចុចដើម្បី​ចាប់ផ្ដើម​កម្មវិធី​នេះឡើងវិញ រួចចូលប្រើ​ពេញអេក្រង់។"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ផ្លាស់ទី"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"បានធ្វើ​បច្ចុប្បន្នភាព​ការរុករកក្នុង​ប្រព័ន្ធ។ ដើម្បីធ្វើការផ្លាស់ប្ដូរ សូមចូលទៅ​កាន់ការកំណត់។"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ចូល​ទៅកាន់​ការកំណត់ ដើម្បី​ធ្វើបច្ចុប្បន្នភាព​ការរុករក​ក្នុង​ប្រព័ន្ធ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index c920cc288bc2..e0ea3c223819 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಸ್ಕ್ರಾಲ್ ಮಾಡಿ"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಅನ್ನು ವಜಾಗೊಳಿಸಿ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ಸ್ಕ್ರೀನ್‍ಶಾಟ್‍ನ ಪೂರ್ವವೀಕ್ಷಣೆ"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"ಮೇಲಿನ ಗಡಿರೇಖೆ"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"ಕೆಳಗಿನ ಗಡಿರೇಖೆ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡರ್"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಆಗುತ್ತಿದೆ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಸೆಶನ್‌ಗಾಗಿ ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಅಧಿಸೂಚನೆ"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ಸೂರ್ಯೋದಯದವರೆಗೆ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> ಸಮಯದಲ್ಲಿ"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> ವರೆಗೂ"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"ಪ್ರಖರತೆಯನ್ನು ಕಡಿಮೆ ಮಾಡಿ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ಸಕ್ರಿಯಗೊಂಡಿದೆ"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ಪ್ರೊಫೈಲ್‌ ತೋರಿಸು"</string>
<string name="user_add_user" msgid="4336657383006913022">"ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿ"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"ಹೊಸ ಬಳಕೆದಾರರು"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ಅತಿಥಿ ಸೆಷನ್ ಅಂತ್ಯಗೊಳಿಸುವುದೇ?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"ಅತಿಥಿ ಸೆಷನ್ ಕೊನೆಗೊಳಿಸಿ"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕುವುದೇ?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಷನ್‌ನಲ್ಲಿನ ಎಲ್ಲ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ಸೆಷನ್ ಅಂತ್ಯಗೊಳಿಸಿ"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ತೆಗೆದುಹಾಕಿ"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"ಮತ್ತೆ ಸುಸ್ವಾಗತ, ಅತಿಥಿ!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"ನಿಮ್ಮ ಸೆಷನ್‌ ಮುಂದುವರಿಸಲು ಇಚ್ಚಿಸುವಿರಾ?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ಪ್ರಾರಂಭಿಸಿ"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ನಲ್ಲಿ ಪ್ರಮಾಣಪತ್ರ ಅಂಗೀಕಾರವನ್ನು ಸ್ಥಾಪಿಸಿದೆ. ನಿಮ್ಮ ಸುರಕ್ಷಿತ ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು ಅಥವಾ ಮಾರ್ಪಡಿಸಬಹುದು."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ಈ ಸಾಧನದಲ್ಲಿ ಪ್ರಮಾಣಪತ್ರ ಅಂಗೀಕಾರವನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿದೆ. ನಿಮ್ಮ ಸುರಕ್ಷಿತ ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು ಅಥವಾ ಮಾರ್ಪಡಿಸಬಹುದು."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನೆಟ್‌ವರ್ಕ್ ಲಾಗಿಂಗ್ ಆನ್ ಮಾಡಿದ್ದಾರೆ. ಇದು ನಿಮ್ಮ ಸಾಧನದ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡುತ್ತದೆ."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನೆಟ್‌ವರ್ಕ್‌ ಲಾಗಿಂಗ್ ಆನ್ ಮಾಡಿದ್ದಾರೆ, ಅದು ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ ನಲ್ಲಿ ಇರುವ ಟ್ರಾಫಿಕ್ ಮೇಲೆ ನಿಗಾ ಇರಿಸುತ್ತದೆ ಆದರೆ ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಪ್ರೊಫೈಲ್‌ನಲ್ಲಿ ಇರುವ ಟ್ರಾಫಿಕ್ ಮೇಲೆ ಅಲ್ಲ."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"ನೀವು <xliff:g id="VPN_APP">%1$s</xliff:g> ಗೆ ಸಂಪರ್ಕಗೊಂಡಿದ್ದೀರಿ. ಇದು ನಿಮ್ಮ ಇಮೇಲ್‌ಗಳು, ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವೆಬ್‌ಸೈಟ್‌ಗಳೂ ಸೇರಿದಂತೆ ನಿಮ್ಮ ನೆಟ್‌ವರ್ಕ್ ಚಟುವಟಿಕೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"ನೀವು <xliff:g id="VPN_APP_0">%1$s</xliff:g> ಹಾಗೂ <xliff:g id="VPN_APP_1">%2$s</xliff:g> ಗೆ ಸಂಪರ್ಕಗೊಂಡಿದ್ದೀರಿ. ಇವು ನಿಮ್ಮ ಇಮೇಲ್‌ಗಳು, ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವೆಬ್‌ಸೈಟ್‌ಗಳೂ ಸೇರಿದಂತೆ ನೆಟ್‌ವರ್ಕ್ ಚಟುವಟಿಕೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ <xliff:g id="VPN_APP">%1$s</xliff:g> ಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ. ಇದು ನಿಮ್ಮ ಇಮೇಲ್‌ಗಳು, ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವೆಬ್‌ಸೈಟ್‌ಗಳೂ ಸೇರಿದಂತೆ ನೆಟ್‌ವರ್ಕ್ ಚಟುವಟಿಕೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"ಈ ಅಧಿಸೂಚನೆಯು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಿಸ್ಟಂನಿಂದ &lt;b&gt;ಸೈಲೆಂಟ್‌ಗೆ ಹಿಂಬಡ್ತಿ ಹೊಂದಿದೆ&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"ಈ ಅಧಿಸೂಚನೆಯು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನಿಮ್ಮ ಶೇಡ್‌ನಲ್ಲಿ &lt;b&gt;ಉನ್ನತ ಸ್ಥಾನವನ್ನು ಹೊಂದಿದೆ&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"ಈ ಅಧಿಸೂಚನೆಯು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನಿಮ್ಮ ಶೇಡ್‌ನಲ್ಲಿ &lt;b&gt;ಕಡಿಮೆ ಸ್ಥಾನವನ್ನು ಹೊಂದಿದೆ&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"ಇದು ಸರಿಯಾಗಿತ್ತೇ?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"ನಿಮ್ಮ ಪ್ರತಿಕ್ರಿಯೆಯನ್ನು ಡೆವಲಪರ್‌ಗೆ ತಿಳಿಸಿ. ಇದು ಸರಿಯಾಗಿತ್ತೇ?"</string>
<string name="feedback_response" msgid="4671729244976641339">"ನಿಮ್ಮ ಪ್ರತಿಕ್ರಿಯೆಗೆ ಧನ್ಯವಾದಗಳು!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ಸರಿ"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನ ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳನ್ನು ತೆರೆಯಲಾಗಿದೆ"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ಇಲ್ಲಿಗೆ ಸರಿಸಿ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸೇರಿಸಿ"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ಸ್ಥಾನ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ಟೈಲ್ ಸೇರಿಸಲಾಗಿದೆ"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ಟೈಲ್ ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‍ಗಳ ಎಡಿಟರ್."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ಅಧಿಸೂಚನೆ: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ಅನ್ನು ಬಳಸುತ್ತಿದೆ"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ಇತ್ತೀಚೆಗೆ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ಅನ್ನು ಬಳಸಿದೆ"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ಎಂಟರ್‌ಪ್ರೈಸ್)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ಫೋನ್ ಕರೆ"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ಫೋನ್ ಕರೆ"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> ಮೂಲಕ)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"ಕ್ಯಾಮರಾ"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ಸ್ಥಳ"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"ಸೆನ್ಸರ್‌ಗಳು ಆಫ್"</string>
<string name="device_services" msgid="1549944177856658705">"ಸಾಧನ ಸೇವೆಗಳು"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"ಯಾವುದೇ ಶೀರ್ಷಿಕೆಯಿಲ್ಲ"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಮತ್ತು ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ನೋಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ಸರಿಸಿ"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"ಸಿಸ್ಟಂ ನ್ಯಾವಿಗೇಷನ ಅಪ್‌ಡೇಟ್ ಮಾಡಲಾಗಿದೆ ಬದಲಾವಣೆಗಳನ್ನು ಮಾಡಲು, ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ಹೋಗಿ."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ಸಿಸ್ಟಂ ನ್ಯಾವಿಗೇಷನ್ ಅಪ್‌ಡೇಟ್ ಮಾಡಲು ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ಹೋಗಿ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 362c0bc08707..64e4a69d1db3 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"스크롤 스크린샷"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"스크린샷 닫기"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"스크린샷 미리보기"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"상단 경계"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"하단 경계"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"화면 녹화"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"화면 녹화 처리 중"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"화면 녹화 세션에 관한 지속적인 알림"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"일출까지"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>에 켜짐"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g>까지"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"밝기 낮추기"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 사용 중지됨"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 사용 설정됨"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"프로필 표시"</string>
<string name="user_add_user" msgid="4336657383006913022">"사용자 추가"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"신규 사용자"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"게스트 세션을 종료하시겠습니까?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"게스트 세션 종료"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"게스트를 삭제하시겠습니까?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"세션 종료"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"삭제"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"게스트 세션 다시 시작"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"세션을 계속 진행하시겠습니까?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"다시 시작"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"조직에서 직장 프로필에 인증기관을 설치했습니다. 보안 네트워크 트래픽을 모니터링 또는 수정할 수 있습니다."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"이 기기에는 인증기관이 설치되어 있습니다. 보안 네트워크 트래픽을 모니터링 또는 수정할 수 있습니다."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"관리자가 기기에서 발생하는 트래픽을 모니터링하는 네트워크 로깅을 사용 설정했습니다."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"관리자가 직장 프로필에서 발생하는 트래픽을 모니터링하는 네트워크 로깅을 사용 설정했습니다. 하지만 개인 프로필은 모니터링되지 않습니다."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"<xliff:g id="VPN_APP">%1$s</xliff:g>에 연결되었습니다. 이 앱은 이메일, 앱, 웹사이트와 같은 내 네트워크 활동을 모니터링할 수 있습니다."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> 및 <xliff:g id="VPN_APP_1">%2$s</xliff:g>에 연결되었습니다. 이 앱은 이메일, 앱, 웹사이트와 같은 내 네트워크 활동을 모니터링할 수 있습니다."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"직장 프로필이 <xliff:g id="VPN_APP">%1$s</xliff:g>에 연결되었습니다. 이 앱은 이메일, 앱, 웹사이트와 같은 내 네트워크 활동을 모니터링할 수 있습니다."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"시스템에서 자동으로 이 알림의 순위를 &lt;b&gt;무음으로 낮췄&lt;/b&gt;습니다."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"알림 창에서 자동으로 이 알림의 순위를 &lt;b&gt;높였&lt;/b&gt;습니다."</string>
<string name="feedback_demoted" msgid="951884763467110604">"알림 창에서 자동으로 이 알림의 순위를 &lt;b&gt;낮췄&lt;/b&gt;습니다."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"맞나요?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"개발자에게 의견을 알려주세요. 정보가 정확했나요?"</string>
<string name="feedback_response" msgid="4671729244976641339">"의견을 보내 주셔서 감사합니다."</string>
<string name="feedback_ok" msgid="6481426753298857144">"확인"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> 알림 컨트롤을 열었습니다."</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> 위치로 이동"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> 위치에 추가"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> 위치"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"타일 추가됨"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"타일 삭제됨"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"빠른 설정 편집기"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 알림: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"설정 열기"</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>에서 <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> 사용 중"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>에서 최근에 <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>을(를) 사용함"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(기업용)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"전화 통화"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"전화 통화"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> 사용)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"카메라"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"위치"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"센서 사용 안함"</string>
<string name="device_services" msgid="1549944177856658705">"기기 서비스"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"제목 없음"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"탭하여 이 앱을 다시 시작하고 전체 화면으로 이동합니다."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"이동"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"시스템 탐색이 업데이트되었습니다. 변경하려면 설정으로 이동하세요."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"설정으로 이동하여 시스템 탐색을 업데이트하세요."</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index fe69443c069a..ecbcd55ad962 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -93,11 +93,13 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Скриншотту сыдырып кароо"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Скриншотту четке кагуу"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Скриншотту алдын ала көрүү"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Жогорку чеги"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Баскычтын чеги"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"экрандан видео жаздырып алуу"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Экрандан жаздырылып алынган видео иштетилүүдө"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Экранды жаздыруу сеансы боюнча учурдагы билдирме"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Жаздырып баштайсызбы?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"Жаздыруу учурунда Android тутуму экраныңызда көрүнүп турган жана түзмөктө ойноп жаткан бардык купуя маалыматты жаздырып алат. Буга сырсөздөр, төлөм маалыматы, сүрөттөр, билдирүүлөр жана аудио файлдар кирет."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"Жаздыруу учурунда Android системасы экраныңызда көрүнүп турган жана түзмөктө ойноп жаткан бардык купуя маалыматты жаздырып алат. Буга сырсөздөр, төлөм маалыматы, сүрөттөр, билдирүүлөр жана аудио файлдар кирет."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Аудио жаздыруу"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Түзмөктүн аудиосу"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Музыка, чалуулар жана шыңгырлар сыяктуу түзмөгүңүздөгү добуштар"</string>
@@ -412,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Күн чыкканга чейин"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Саат <xliff:g id="TIME">%s</xliff:g> күйөт"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> чейин"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Экрандын жарыктыгын төмөндөтүү"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC өчүрүлгөн"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC иштетилген"</string>
@@ -421,7 +424,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Улантуу үчүн &lt;b&gt;<xliff:g id="APP">%s</xliff:g>&lt;/b&gt; колдонмосуна түзмөгүңүздүн микрофонун пайдаланууга уруксат беришиңиз керек."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Улантуу үчүн &lt;b&gt;<xliff:g id="APP">%s</xliff:g>&lt;/b&gt; колдонмосуна түзмөгүңүздүн камерасын пайдаланууга уруксат беришиңиз керек."</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"Түзмөк"</string>
- <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Башка колдонмого которулуу үчүн,, өйдө сүрүңүз"</string>
+ <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Башка колдонмого которулуу үчүн өйдө сүрүңүз"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Колдонмолорду тез которуштуруу үчүн, оңго сүйрөңүз"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Сереп салууну өчүрүү/күйгүзүү"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Кубатталды"</string>
@@ -442,12 +445,12 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Ачуу үчүн кайра таптап коюңуз"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Ачуу үчүн өйдө сүрүңүз"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Кайталоо үчүн экранды өйдө сүрүңүз"</string>
- <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC технологиясын колдонуу үчүн кулпуcун ачыңыз"</string>
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC колдонуу үчүн түзмөктүн кулпусун ачыңыз"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Бул түзмөк уюмуңузга таандык"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Бул түзмөк төмөнкүгө таандык: <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
- <string name="phone_hint" msgid="6682125338461375925">"Сүрөтчөнү серпип телефонго өтүңүз"</string>
- <string name="voice_hint" msgid="7476017460191291417">"Сүрөтчөнү серпип үн жардамчысына өтүңүз"</string>
- <string name="camera_hint" msgid="4519495795000658637">"Сүрөтчөнү серпип камерага өтүңүз"</string>
+ <string name="phone_hint" msgid="6682125338461375925">"Сүрөтчөнү сүрүп телефонго өтүңүз"</string>
+ <string name="voice_hint" msgid="7476017460191291417">"Сүрөтчөнү сүрүп үн жардамчысына өтүңүз"</string>
+ <string name="camera_hint" msgid="4519495795000658637">"Сүрөтчөнү сүрүп камерага өтүңүз"</string>
<string name="interruption_level_none_with_warning" msgid="8394434073508145437">"Толук жымжырттык талап кылынат. Бул экрандагыны окугучтарды да тынчтандырат."</string>
<string name="interruption_level_none" msgid="219484038314193379">"Тымтырс"</string>
<string name="interruption_level_priority" msgid="661294280016622209">"Шашылыш билдирүүлөр гана"</string>
@@ -465,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Профилди көрсөтүү"</string>
<string name="user_add_user" msgid="4336657383006913022">"Колдонуучу кошуу"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Жаңы колдонуучу"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Конок сеансы бүтүрүлсүнбү?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Конок сеансын бүтүрүү"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Конокту алып саласызбы?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана дайындар өчүрүлөт."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Сеансты бүтүрүү"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Алып салуу"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Кайтып келишиңиз менен, конок!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансыңызды улантасызбы?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Кайра баштоо"</string>
@@ -542,6 +546,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ишканаңыз жумуш профилиңизге тастыктоочу борборду орнотту. Коопсуз тармагыңыздын трафиги көзөмөлдөнүп же өзгөртүлүшү мүмкүн."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Бул түзмөктө тастыктоочу борбор орнотулган. Коопсуз тармагыңыздын трафиги көзөмөлдөнүп же өзгөртүлүшү мүмкүн."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Администраторуңуз түзмөгүңүздөгү трафикти көзөмөлдөөчү тармактын таржымалын каттоо функциясын иштетти."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Администраторуңуз жумуш профилиндеги трафикти көзөмөлдөгөн тармакка кирүүнү күйгүздү. Буга жеке профилиңиз кирбейт."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Электрондук почта, колдонмолор жана вебсайттар сыяктуу тармактагы аракеттериңизди тескей турган <xliff:g id="VPN_APP">%1$s</xliff:g> колдонмосуна туташып турасыз."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Электрондук почта, колдонмолор жана вебсайттар сыяктуу тармактагы аракеттериңизди көзөмөлдөй турган <xliff:g id="VPN_APP_0">%1$s</xliff:g> жана <xliff:g id="VPN_APP_1">%2$s</xliff:g> колдонмолоруна туташып турасыз."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Жумуш профилиңиз электрондук почта, колдонмолор жана вебсайттар сыяктуу тармактык аракеттериңизди көзөмөлдөй турган <xliff:g id="VPN_APP">%1$s</xliff:g> колдонмосуна туташып турат."</string>
@@ -735,7 +740,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Бул билдирмени тутум автоматтык түрдө &lt;b&gt;Үнсүз абалга төмөндөттү&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Бул билдирменин панелиңиздеги абалы автоматтык түрдө &lt;b&gt;жогорулады&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Бул билдирменин панелиңиздеги абалы автоматтык түрдө &lt;b&gt;төмөндөдү&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Бул туурабы?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Иштеп чыгуучуга пикириңизди билдириңиз. Бул туурабы?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Пикириңиз үчүн рахмат!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"Жарайт"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу үчүн эскертмени көзөмөлдөө функциялары ачылды"</string>
@@ -882,6 +887,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Төмөнкүгө жылдыруу: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g>-позицияга кошуу"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>-позиция"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Карта кошулду"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Карта өчүрүлдү"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ыкчам жөндөөлөр түзөткүчү."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> эскертмеси: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Жөндөөлөрдү ачуу."</string>
@@ -972,7 +979,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> колдонуп жатат"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Жакында <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> колдонулду"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(корпоративдик)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Телефон чалуу"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Телефон чалуу"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> аркылуу)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камера"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"жайгашкан жер"</string>
@@ -980,7 +987,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Сенсорлорду өчүрүү"</string>
<string name="device_services" msgid="1549944177856658705">"Түзмөк кызматтары"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Аталышы жок"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Бул колдонмону өчүрүп күйгүзүп, толук экранга өтүү үчүн, таптап коюңуз."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Жылдыруу"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Тутум чабыттоосу жаңырды. Өзгөртүү үчүн, Жөндөөлөргө өтүңүз."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Тутум чабыттоосун жаңыртуу үчүн Жөндөөлөргө өтүңүз"</string>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 51d7b8eff5fc..24c7655e5ae4 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -52,4 +52,6 @@
<!-- (footer_height -48dp)/2 -->
<dimen name="controls_management_footer_top_margin">4dp</dimen>
<dimen name="controls_management_favorites_top_margin">8dp</dimen>
+
+ <dimen name="toast_y_offset">24dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index e5e89e44b4df..98a75c28ca8a 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"ເລື່ອນຮູບໜ້າຈໍ"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"ປິດຮູບໜ້າຈໍ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ຕົວຢ່າງຮູບໜ້າຈໍ"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"ຂອບເຂດທາງເທິງ"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"ຂອບເຂດທາງລຸ່ມ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ໂປຣແກຣມບັນທຶກໜ້າຈໍ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ກຳລັງປະມວນຜົນການບັນທຶກໜ້າຈໍ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ການແຈ້ງເຕືອນສຳລັບເຊດຊັນການບັນທຶກໜ້າຈໍໃດໜຶ່ງ"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ຈົນກວ່າຕາເວັນຂຶ້ນ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"ເປີດເວລາ <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"ຈົນຮອດ <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"ຫຼຸດຄວາມສະຫວ່າງ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"​ສະ​ແດງ​ໂປຣ​ໄຟລ໌"</string>
<string name="user_add_user" msgid="4336657383006913022">"ເພີ່ມຜູ້ໃຊ້"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"ຜູ່ໃຊ້ໃໝ່"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ສິ້ນສຸດເຊດຊັນແຂກບໍ?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"ສິ້ນສຸດເຊດຊັນແຂກ"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ລຶບ​ແຂກ​ບໍ?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯ​ແລະ​ຂໍ້​ມູນ​ທັງ​ໝົດ​ໃນ​ເຊດ​ຊັນ​ນີ້​ຈະ​ຖືກ​ລຶບ​ອອກ."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ສິ້ນສຸດເຊດຊັນ"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ລຶບ​"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"ຍິນ​ດີ​ຕ້ອນ​ຮັບ​ກັບ​ມາ, ຜູ່​ຢ້ຽມ​ຢາມ!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"ທ່ານ​ຕ້ອງ​ການ​ສືບ​ຕໍ່​ເຊດ​ຊັນ​ຂອງ​ທ່ານບໍ່?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ເລີ່ມຕົ້ນໃຫມ່"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ອົງກອນຂອງທ່ານຕິດຕັ້ງອຳນາດໃບຮັບຮອງໄວ້ໃນໂປຣໄຟລ໌ບ່ອນເຮັດວຽກນີ້. ທຣາບຟິກເຄືອຂ່າຍທີ່ເຂົ້າລະຫັດໄວ້ຂອງທ່ານອາດຖືກຕິດຕາມ ຫຼື ແກ້ໄຂໄດ້."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ມີອຳນາດໃບຮັບຮອງຕິດຕັ້ງຢູ່ໃນອຸປະກອນນີ້. ທຣາບຟິກເຄືອຂ່າຍທີ່ເຂົ້າລະຫັດໄວ້ຂອງທ່ານອາດຖືກຕິດຕາມ ຫຼື ແກ້ໄຂໄດ້."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"ຜູ້ເບິ່ງແຍງຂອງທ່ານໄດ້ເປີດໃຊ້ການບັນທຶກເຄືອຂ່າຍໄວ້, ເຊິ່ງຈະຕິດຕາມທຣາບຟິກໃນອຸປະກອນຂອງທ່ານ."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"ຜູ້ເບິ່ງແຍງຂອງທ່ານໄດ້ເປີດໃຊ້ການບັນທຶກເຄືອຂ່າຍໄວ້, ເຊິ່ງຈະຕິດຕາມທຣາບຟິກໃນໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານ ແຕ່ຈະບໍ່ຕິດຕາມໃນໂປຣໄຟລ໌ສ່ວນຕົວຂອງທ່ານ."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"ທ່ານເຊື່ອມຕໍ່ກັບ <xliff:g id="VPN_APP">%1$s</xliff:g> ແລ້ວ, ເຊິ່ງຈະສາມາດຕິດຕາມການເຄື່ອນໄຫວເຄືອຂ່າຍ, ຮວມທັງອີເມວ, ແອັບ ແລະ ເວັບໄຊຕ່າງໆຂອງທ່ານໄດ້."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"ທ່ານເຊື່ອມຕໍ່ກັບ <xliff:g id="VPN_APP_0">%1$s</xliff:g> ແລະ <xliff:g id="VPN_APP_1">%2$s</xliff:g> ແລ້ວ, ເຊິ່ງຈະສາມາດຕິດຕາມການເຄື່ອນໄຫວເຄືອຂ່າຍ, ຮວມທັງອີເມວ, ແອັບ ແລະ ເວັບໄຊຕ່າງໆຂອງທ່ານໄດ້."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານເຊື່ອມຕໍ່ຫາ <xliff:g id="VPN_APP">%1$s</xliff:g>, ເຊິ່ງສາມາດຕິດຕາມການເຄື່ອນໄຫວເຄືອຂ່າຍຂອງທ່ານ, ຮວມເຖິງອີເມວ, ແອັບ ແລະ ເວັບໄຊ."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"ການແຈ້ງເຕືອນນີ້ &lt;b&gt;ຖືກຫຼຸດລະດັບເປັນປິດສຽງອັດຕະໂນມັດແລ້ວ&lt;/b&gt; ໂດຍລະບົບ."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"ການແຈ້ງເຕືອນນີ້ &lt;b&gt;ມີອັນດັບສູງຂຶ້ນໂດຍອັດຕະໂນມັດແລ້ວ&lt;/b&gt; ໃນໜ້າການແຈ້ງເຕືອນຂອງທ່ານ."</string>
<string name="feedback_demoted" msgid="951884763467110604">"ການແຈ້ງເຕືອນນີ້ &lt;b&gt;ມີອັນດັບຕ່ຳລົງໂດຍອັດຕະໂນມັດແລ້ວ&lt;/b&gt; ໃນໜ້າການແຈ້ງເຕືອນຂອງທ່ານ."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"ສິ່ງນີ້ບໍ່ຖືກຕ້ອງບໍ?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"ສົ່ງຄຳຕິຊົມຂອງທ່ານໃຫ້ນັກພັດທະນາ. ສິ່ງນີ້ບໍ່ຖືກຕ້ອງບໍ?"</string>
<string name="feedback_response" msgid="4671729244976641339">"ຂອບໃຈສຳລັບຄຳເຫັນຂອງທ່ານ!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ຕົກລົງ"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"ເປີດຕົວຄວບຄຸມການແຈ້ງເຕືອນສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ແລ້ວ"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ຍ້າຍໄປ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ເພີ່ມໃສ່ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ເພີ່ມແຜ່ນແລ້ວ"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ລຶບແຜ່ນແລ້ວ"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ຕົວແກ້ໄຂການຕັ້ງຄ່າດ່ວນ"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"ການແຈ້ງເຕືອນ <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ເປີດການຕັ້ງຄ່າ."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ກຳລັງໃຊ້ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ຢູ່"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ໃຊ້ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ເມື່ອບໍ່ດົນມານີ້"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ອົງກອນ)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ໂທລະສັບ"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ໂທລະສັບ"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(ຜ່ານ <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"ກ້ອງຖ່າຍຮູບ"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ສະຖານທີ່"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"ປິດການຮັບຮູ້ຢູ່"</string>
<string name="device_services" msgid="1549944177856658705">"ບໍລິການອຸປະກອນ"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"ບໍ່ມີຊື່"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ ແລະ ໃຊ້ແບບເຕັມຈໍ."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ຍ້າຍ"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"ອັບເດດການນຳທາງລະບົບແລ້ວ. ເພື່ອປ່ຽນແປງ, ກະລຸນາໄປທີ່ການຕັ້ງຄ່າ."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ໄປທີ່ການຕັ້ງຄ່າເພື່ອອັບເດດການນຳທາງລະບົບ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index a5de09533f23..f4a2a0d6cc75 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Viso puslapio ekrano kopija"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Praleisti ekrano kopiją"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekrano kopijos peržiūra"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Viršutinė riba"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Apatinė riba"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekrano vaizdo įrašytuvas"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Apdorojam. ekrano vaizdo įraš."</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Šiuo metu rodomas ekrano įrašymo sesijos pranešimas"</string>
@@ -414,6 +416,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Iki saulėtekio"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Iki <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Šviesumo mažinimas"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"ALR"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"ALR išjungtas"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"ALR įjungtas"</string>
@@ -467,9 +470,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Rodyti profilį"</string>
<string name="user_add_user" msgid="4336657383006913022">"Pridėti naudotoją"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Naujas naudotojas"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Baigti svečio sesiją?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Baigti svečio sesiją"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Pašalinti svečią?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Baigti sesiją"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Pašalinti"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Sveiki sugrįžę, svety!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Ar norite tęsti sesiją?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Pradėti iš naujo"</string>
@@ -546,6 +550,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Jūsų organizacija įdiegė darbo profilyje sertifikato įgaliojimą. Jūsų saugaus tinklo srautas gali būti stebimas arba keičiamas."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Šiame įrenginyje įdiegtas sertifikato įgaliojimas. Jūsų saugaus tinklo srautas gali būti stebimas arba keičiamas."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratorius įjungė tinklo duomenų įrašymą į žurnalą. Įjungus šią funkciją stebimas srautas jūsų įrenginyje."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administratorius įjungė tinklo duomenų įrašymą į žurnalą. Įjungus šią funkciją stebimas srautas jūsų darbo, bet ne asmeniniame profilyje."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Esate prisijungę prie programos „<xliff:g id="VPN_APP">%1$s</xliff:g>“, kuri gali stebėti tinklo veiklą, įskaitant el. laiškus, programas ir svetaines."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Esate prisijungę prie programų „<xliff:g id="VPN_APP_0">%1$s</xliff:g>“ ir „<xliff:g id="VPN_APP_1">%2$s</xliff:g>“, kurios gali stebėti tinklo veiklą, įskaitant el. laiškus, programas ir svetaines."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Darbo profilis susietas su programa „<xliff:g id="VPN_APP">%1$s</xliff:g>“, kuri gali stebėti tinklo veiklą, įskaitant el. laiškus, programas ir svetaines."</string>
@@ -739,7 +744,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Šio pranešimo svarbą sistema automatiškai &lt;b&gt;sumažino iki begarsio lygio&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Šio pranešimo reitingas pranešimų skydelyje automatiškai &lt;b&gt;padidintas&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Šio pranešimo reitingas pranešimų skydelyje automatiškai &lt;b&gt;sumažintas&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Ar tai teisinga?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Pateikite savo atsiliepimą kūrėjui. Ar tai teisinga?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Dėkojame už atsiliepimą!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"Gerai"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ pranešimų valdikliai atidaryti"</string>
@@ -890,6 +895,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Perkelkite į <xliff:g id="POSITION">%1$d</xliff:g> poziciją"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridėkite <xliff:g id="POSITION">%1$d</xliff:g> pozicijoje"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> pozicija"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Išklotinė pridėta"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Išklotinė pašalinta"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sparčiųjų nustatymų redagavimo priemonė."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"„<xliff:g id="ID_1">%1$s</xliff:g>“ pranešimas: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Atidaryti nustatymus."</string>
@@ -980,7 +987,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Programa „<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>“ naudoja: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Programa „<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>“ neseniai naudojo: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(įmonės versija)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefono skambutis"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefono skambutis"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(naud. <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"fotoaparatą"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"vietovę"</string>
@@ -988,7 +995,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Jutikliai išjungti"</string>
<string name="device_services" msgid="1549944177856658705">"Įrenginio paslaugos"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Nėra pavadinimo"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Palieskite, kad paleistumėte iš naujo šią programą arba įjungtumėte viso ekrano režimą."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Perkelti"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Sistemos naršymo funkcijos atnaujintos. Jei norite pakeisti, eikite į skiltį „Nustatymai“."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Eikite į skiltį „Nustatymai“, kad atnaujintumėte sistemos naršymo funkcijas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 019810fe9f84..caca22cc5aba 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Ritināt ekrānuzņēmumu"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Nerādīt ekrānuzņēmumu"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekrānuzņēmuma priekšskatījums"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Augšējā robeža"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Apakšējā robeža"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekrāna ierakstītājs"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekrāna ieraksta apstrāde"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Aktīvs paziņojums par ekrāna ierakstīšanas sesiju"</string>
@@ -412,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Līdz saullēktam"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Plkst. <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Līdz plkst. <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Spilgtuma samazināšana"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ir atspējoti"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ir iespējoti"</string>
@@ -465,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Parādīt profilu"</string>
<string name="user_add_user" msgid="4336657383006913022">"Lietotāja pievienošana"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Jauns lietotājs"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vai beigt viesa sesiju?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Beigt viesa sesiju"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vai noņemt viesi?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šīs sesijas lietotnes un dati."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Beigt sesiju"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Noņemt"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Laipni lūdzam atpakaļ, viesi!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Vai vēlaties turpināt savu sesiju?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Sākt no sākuma"</string>
@@ -543,6 +547,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Jūsu organizācija instalēja sertifikātu jūsu darba profilā. Jūsu drošā tīkla datplūsma var tikt uzraudzīta."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Šajā ierīcē ir instalēts sertifikāts. Drošā tīkla datplūsma var tikt uzraudzīta."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administrators ieslēdza tīkla reģistrēšanu, kuru izmanto, lai pārraudzītu datplūsmu jūsu ierīcē."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administrators ir ieslēdzis tīkla reģistrēšanu, kuru izmanto, lai pārraudzītu datplūsmu jūsu darba profilā, bet ne personīgajā profilā."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Ir izveidots savienojums ar lietotni <xliff:g id="VPN_APP">%1$s</xliff:g>, kas var pārraudzīt jūsu darbības tīklā, tostarp e-pasta ziņojumus, lietotnes un vietnes."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Ir izveidots savienojums ar lietotnēm <xliff:g id="VPN_APP_0">%1$s</xliff:g> un <xliff:g id="VPN_APP_1">%2$s</xliff:g>, kas var pārraudzīt jūsu darbības tīklā, tostarp e-pasta ziņojumus, lietotnes un vietnes."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Jūsu darba profilam ir izveidots savienojums ar lietotni <xliff:g id="VPN_APP">%1$s</xliff:g>, kas var pārraudzīt jūsu darbības tīklā, tostarp saņemtos un nosūtītos e-pasta ziņojumus, instalētās lietotnes un apmeklētās tīmekļa vietnes."</string>
@@ -736,7 +741,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Šī paziņojuma svarīgums sistēmā tika automātiski &lt;b&gt;pazemināts, un paziņojums tiks rādīts bez skaņas&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Šī paziņojums rangs paziņojumu panelī tika automātiski &lt;b&gt;paaugstināts&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Šī paziņojuma rangs paziņojumu panelī tika automātiski &lt;b&gt;pazemināts&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Vai šī informācija ir pareiza?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Sniedziet atsauksmes izstrādātājam. Vai šī informācija ir pareiza?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Paldies par atsauksmēm!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"Labi"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> paziņojumu vadīklas ir atvērtas"</string>
@@ -885,6 +890,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pārvietot uz pozīciju numur <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pievienot elementu pozīcijā numur <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozīcija numur <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Elements ir pievienots"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Elements ir noņemts"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ātro iestatījumu redaktors."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> paziņojums: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Atvērt iestatījumus."</string>
@@ -975,7 +982,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> pašlaik izmanto šādu darbību: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> nesen izmantoja šādu darbību: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(uzņēmumiem)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Tālruņa zvaniem"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Tālruņa zvans"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(izmantojot lietotni <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"atrašanās vieta"</string>
@@ -983,7 +990,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensori izslēgti"</string>
<string name="device_services" msgid="1549944177856658705">"Ierīces pakalpojumi"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Nav nosaukuma"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Pieskarieties, lai restartētu šo lietotni un pārietu pilnekrāna režīmā."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Pārvietot"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Sistēmas navigācija ir atjaunināta. Lai veiktu izmaiņas, atveriet iestatījumus."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Atveriet iestatījumus, lai atjauninātu sistēmas navigāciju"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index c124eefb52cd..b3b4a4531d9e 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Континуирана слика од екранот"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Отфрлете ја сликата од екранот"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Преглед на слика од екранот"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Горна граница"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Долна граница"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Снимач на екран"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Се обработува снимка од екран"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Тековно известување за сесија за снимање на екранот"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До изгрејсонце"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Се вклучува во <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Намали ја осветленоста"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC е оневозможено"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC е овозможено"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Прикажи го профилот"</string>
<string name="user_add_user" msgid="4336657383006913022">"Додај корисник"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Нов корисник"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Да се заврши гостинската сесија?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Заврши ја гостинската сесија"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Да се отстрани гостинот?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијата ќе се избришат."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Заврши ја сесијата"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Отстрани"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Добре дојде пак, гостине!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Дали сакате да продолжите со сесијата?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни одново"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Вашата организација инсталираше авторитет за сертификат на вашиот работен профил. Вашиот безбеден мрежен сообраќај можно е да се следи или изменува."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На уредов е инсталиран авторитет за сертификат. Вашиот безбеден мрежен сообраќај можно е да се следи или изменува."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Вашиот администратор вклучил евиденција на мрежата, што подразбира следење на сообраќајот на вашиот уред."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Вашиот администратор вклучил мрежна евиденција, што подразбира следење на сообраќајот во работниот, но не и во личниот профил."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Поврзани сте на <xliff:g id="VPN_APP">%1$s</xliff:g>, што може да ја следи вашата активност на мрежата, вклучувајќи ги е-пораките, апликациите и веб-сајтовите."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Поврзани сте на <xliff:g id="VPN_APP_0">%1$s</xliff:g> и <xliff:g id="VPN_APP_1">%2$s</xliff:g>, што може да ја следат вашата активност на мрежата, вклучувајќи ги е-пораките, апликациите и веб-сајтовите."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Вашиот работен профил е поврзан на <xliff:g id="VPN_APP">%1$s</xliff:g>, што може да ја следи вашата активност на мрежата, заедно со е-пораките, апликациите и веб-сајтовите."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Известувањево беше автоматски &lt;b&gt;намалено на „Тивко“&lt;/b&gt; од страна на системот."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Известувањево беше автоматски &lt;b&gt;рангирано повисоко&lt;/b&gt; во нијансите за известувања."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Известувањево беше автоматски &lt;b&gt;рангирано пониско&lt;/b&gt; во нијансите за известувања."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Дали ова беше точно?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Дајте му повратни информации на програмерот. Дали ова беше точно?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Фала за повратните информации!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"Во ред"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Контролите за известувањата за <xliff:g id="APP_NAME">%1$s</xliff:g> се отворија"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместување на <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додавање на позиција <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиција <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Додадена е плочка"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Отстранета е плочка"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уредник за брзи поставки."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Известување од <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отворете ги поставките."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> користи <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> користеше <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> неодамна"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(претпријатие)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Телефонски повик"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Телефонски повик"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(преку <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камера"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"локација"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Сензорите се исклучени"</string>
<string name="device_services" msgid="1549944177856658705">"Услуги за уредот"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Без наслов"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Допрете за да ја рестартирате апликацијава и да ја отворите на цел екран."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Премести"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Навигацијата на системот е ажурирана. За да извршите промени, одете во „Поставки“."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Одете во „Поставки“ за да ја ажурирате навигацијата на системот"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 965f4df71d7f..420de5bbcb79 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -93,6 +93,10 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"സ്ക്രീൻഷോട്ട് സ്ക്രോൾ ചെയ്യുക"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"സ്ക്രീൻഷോട്ട് ഡിസ്‌മിസ് ചെയ്യുക"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"സ്‌ക്രീൻഷോട്ട് പ്രിവ്യു"</string>
+ <!-- no translation found for screenshot_top_boundary (1500569103321300856) -->
+ <skip />
+ <!-- no translation found for screenshot_bottom_boundary (5657242629526407311) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"സ്ക്രീൻ റെക്കോർഡർ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"സ്ക്രീൻ റെക്കോർഡിംഗ് പ്രോസസുചെയ്യുന്നു"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ഒരു സ്ക്രീൻ റെക്കോർഡിംഗ് സെഷനായി നിലവിലുള്ള അറിയിപ്പ്"</string>
@@ -410,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"സൂര്യോദയം വരെ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>-ന്"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> വരെ"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"തെളിച്ചം കുറയ്ക്കുക"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC പ്രവർത്തനക്ഷമമാക്കി"</string>
@@ -463,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"പ്രൊഫൈൽ കാണിക്കുക"</string>
<string name="user_add_user" msgid="4336657383006913022">"ഉപയോക്താവിനെ ചേര്‍ക്കുക"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"പുതിയ ഉപയോക്താവ്"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"അതിഥി സെഷൻ അവസാനിപ്പിക്കണോ?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"അതിഥി സെഷൻ അവസാനിപ്പിക്കുക"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"അതിഥിയെ നീക്കംചെയ്യണോ?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ അപ്ലിക്കേഷനുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"സെഷൻ അവസാനിപ്പിക്കുക"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"നീക്കംചെയ്യുക"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"അതിഥിയ്‌ക്ക് വീണ്ടും സ്വാഗതം!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"നിങ്ങളുടെ സെഷൻ തുടരണോ?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"പുനരാംരംഭിക്കുക"</string>
@@ -540,6 +546,8 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലിൽ നിങ്ങളുടെ സ്ഥാപനമൊരു സർട്ടിഫിക്കറ്റ് അതോറിറ്റി ഇൻസ്റ്റാൾ ചെയ്തിരിക്കുന്നു. നിങ്ങളുടെ സുരക്ഷിത നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കപ്പെടുകയോ പരിഷ്കരിക്കപ്പെടുയോ ചെയ്തേക്കാം."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"നിങ്ങളുടെ ഉപകരണത്തിൽ ഒരു സർട്ടിഫിക്കറ്റ് അതോറിറ്റി ഇൻസ്റ്റാൾ ചെയ്തിരിക്കുന്നു. നിങ്ങളുടെ സുരക്ഷിത നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കപ്പെടുകയോ പരിഷ്കരിക്കപ്പെടുയോ ചെയ്തേക്കാം."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"നിങ്ങളുടെ അഡ്‌മിൻ, നെറ്റ്‌വർക്ക് ലോഗിംഗ് ഓണാക്കിയിട്ടുണ്ട്, ഇതിന് നിങ്ങളുടെ ഉപകരണത്തിലെ ട്രാഫിക്ക് നിരീക്ഷിക്കാൻ കഴിയും."</string>
+ <!-- no translation found for monitoring_description_managed_profile_network_logging (6932303843097006037) -->
+ <skip />
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"നിങ്ങൾ <xliff:g id="VPN_APP">%1$s</xliff:g> എന്ന ആപ്പിലേക്ക് കണക്റ്റുചെയ്‌തിരിക്കുന്നു, ഇമെയിലുകൾ, ആപ്പുകൾ, വെബ്‌സൈറ്റുകൾ എന്നിവ ഉൾപ്പെടെ നിങ്ങളുടെ നെറ്റ്‌വർക്ക് ആക്റ്റിവിറ്റി നിരീക്ഷിക്കാൻ ഈ ആപ്പിന് കഴിയും."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"നിങ്ങൾ <xliff:g id="VPN_APP_0">%1$s</xliff:g>, <xliff:g id="VPN_APP_1">%2$s</xliff:g> എന്നീ ആപ്പുകളിലേക്ക് കണക്റ്റുചെയ്‌തിരിക്കുന്നു, ഇമെയിലുകൾ, ആപ്പുകൾ, വെബ്‌സൈറ്റുകൾ എന്നിവ ഉൾപ്പെടെ നിങ്ങളുടെ നെറ്റ്‌വർക്ക് ആക്റ്റിവിറ്റി നിരീക്ഷിക്കാൻ ഈ ആപ്പിന് കഴിയും."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"<xliff:g id="VPN_APP">%1$s</xliff:g> ആപ്പിലേക്ക് നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈൽ കണക്റ്റുചെയ്‌തിരിക്കുന്നു, ഇമെയിലുകൾ, ആപ്‌സ്, വെബ്‌സൈറ്റുകൾ എന്നിവ ഉൾപ്പെടെ നിങ്ങളുടെ നെറ്റ്‌വർക്ക് ആക്റ്റിവിറ്റി നിരീക്ഷിക്കാൻ ഈ ആപ്പിന് കഴിയും."</string>
@@ -733,7 +741,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"ഈ അറിയിപ്പ്, സിസ്‌റ്റം സ്വയമേവ &lt;b&gt;നിശബ്‌ദമാക്കി തരം താഴ്ത്തി&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"ഈ അറിയിപ്പിന് നിങ്ങളുടെ ഷെയ്‌ഡിൽ സ്വയമേവ &lt;b&gt;ഉയർന്ന റാങ്കിംഗ് നൽകി&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"ഈ അറിയിപ്പിന് നിങ്ങളുടെ ഷെയ്‌ഡിൽ സ്വയമേവ &lt;b&gt;താഴ്ന്ന റാങ്കിംഗ് നൽകി&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"ഇത് ശരിയായിരുന്നോ?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"ഡെവലപ്പറിനെ നിങ്ങളുടെ ഫീഡ്ബാക്ക് അറിയിക്കൂ. ഇത് ശരിയായിരുന്നോ?"</string>
<string name="feedback_response" msgid="4671729244976641339">"നിങ്ങളുടെ ഫീഡ്‌ബാക്കിന് നന്ദി!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ശരി"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> ആപ്പിന്റെ അറിയിപ്പ് നിയന്ത്രണങ്ങൾ തുറന്നു"</string>
@@ -880,6 +888,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> എന്നതിലേക്ക് നീക്കുക"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> എന്ന സ്ഥാനത്തേക്ക് ചേർക്കുക"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ടൈൽ ചേർത്തു"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ടൈൽ നീക്കം ചെയ്‌തു"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ദ്രുത ക്രമീകരണ എഡിറ്റർ."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> അറിയിപ്പ്: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ക്രമീകരണം തുറക്കുക."</string>
@@ -967,23 +977,17 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ആപ്പുകൾ നിങ്ങളുടെ <xliff:g id="TYPES_LIST">%s</xliff:g> ഉപയോഗിക്കുന്നു."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" കൂടാതെ "</string>
- <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) -->
- <skip />
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ആപ്പ്, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ഉപയോഗിക്കുന്നു"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> അടുത്തിടെ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ഉപയോഗിച്ചു"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(എന്റർപ്രൈസ്)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ഫോൺ കോൾ"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> എന്നതിലൂടെ)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"ക്യാമറ"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ലൊക്കേഷന്‍"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"മൈക്രോഫോൺ"</string>
<string name="sensor_privacy_mode" msgid="4462866919026513692">"സെൻസറുകൾ ഓഫാണ്"</string>
<string name="device_services" msgid="1549944177856658705">"ഉപകരണ സേവനങ്ങള്‍"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"പേരില്ല"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"ഈ ആപ്പ് റീസ്‌റ്റാർട്ട് ചെയ്യാനും പൂർണ്ണ സ്‌ക്രീനാവാനും ടാപ്പ് ചെയ്യുക."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"നീക്കുക"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"സിസ്‌റ്റം നാവിഗേഷൻ അപ്‌ഡേറ്റ് ചെയ്‌തു. മാറ്റങ്ങൾ വരുത്താൻ ക്രമീകരണത്തിലേക്ക് പോവുക."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"സിസ്‌റ്റം നാവിഗേഷൻ അപ്‌ഡേറ്റ് ചെയ്യാൻ ക്രമീകരണത്തിലേക്ക് പോവുക"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 400352b6473a..63547d59b931 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Бүхэлд нь багтаасан дэлгэцийн агшин"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Дэлгэцийн агшныг хаах"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Дэлгэцийн агшныг урьдчилан үзэх"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Дээд талын хязгаар"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Доод талын хязгаар"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Дэлгэцийн үйлдэл бичигч"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Дэлгэц бичлэг боловсруулж байна"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Дэлгэц бичих горимын үргэлжилж буй мэдэгдэл"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Нар мандах хүртэл"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>-д"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> хүртэл"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Гэрэлтүүлгийг багасгах"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC-г цуцалсан"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC-г идэвхжүүлсэн"</string>
@@ -421,7 +424,7 @@
<string name="media_seamless_remote_device" msgid="177033467332920464">"Төхөөрөмж"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Апп сэлгэхийн тулд дээш шударна уу"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Аппуудыг хурдан сэлгэхийн тулд баруун тийш чирнэ үү"</string>
- <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Тоймыг унтраах/асаах"</string>
+ <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Тоймыг асаах/унтраах"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Цэнэглэгдсэн"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Цэнэглэж байна"</string>
<string name="expanded_header_battery_charging_with_time" msgid="757991461445765011">"дүүргэхэд <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Профайлыг харуулах"</string>
<string name="user_add_user" msgid="4336657383006913022">"Хэрэглэгч нэмэх"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Шинэ хэрэглэгч"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Зочны сургалтыг дуусгах уу?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Зочны сургалтыг дуусгах"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Зочныг хасах уу?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ сешний бүх апп болон дата устах болно."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Сургалтыг дуусгах"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Хасах"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Тавтай морилно уу!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Та үргэлжлүүлэхийг хүсэж байна уу?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Дахин эхлүүлэх"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Таны байгууллага таны ажлын профайлд сертификатын зөвшөөрөл суулгасан байна. Таны аюулгүй сүлжээний ачааллыг өөрчлөх эсвэл хянах боломжтой."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Сертификатын зөвшөөрлийг энэ төхөөрөмжид суулгасан байна. Таны аюулгүй сүлжээний ачааллыг өөрчлөх эсвэл хянах боломжтой."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Таны админ төхөөрөмжийн ачааллыг хянадаг сүлжээний логийг асаасан байна."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Таны админ ажлын профайлын тань ачааллыг хянадаг сүлжээний логийг асаасан бөгөөд энэ нь хувийн профайлын ачааллыг хянахгүй."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Та имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Та имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP_0">%1$s</xliff:g>, <xliff:g id="VPN_APP_1">%2$s</xliff:g>-д холбогдсон байна."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Таны ажлын профайл <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна. Энэ нь таны имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Энэ мэдэгдлийг систем автоматаар &lt;b&gt;Чимээгүй болгож зэрэглэлийг нь бууруулсан&lt;/b&gt; байна."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Энэ мэдэгдлийг таны хураангуй самбарт автоматаар &lt;b&gt;дээгүүр зэрэглэл хийсэн&lt;/b&gt; байна."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Энэ мэдэгдлийг таны хураангуй самбарт автоматаар &lt;b&gt;доогуур зэрэглэл хийсэн&lt;/b&gt; байна."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Энэ зөв байсан уу?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Хөгжүүлэгчид санал хүсэлтээ мэдэгдээрэй. Энэ зөв байсан уу?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Санал хүсэлтээ илгээсэнд баярлалаа!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ОК"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н мэдэгдлийн хяналтыг нээсэн"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> руу зөөнө үү"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> байрлалд нэмнэ үү"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> байрлал"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Хавтан нэмсэн"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Хавтанг хассан"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Түргэн тохиргоо засварлагч."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> мэдэгдэл: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Тохиргоог нээнэ үү."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>-г ашиглаж байна"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>-г саяхан ашигласан"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(байгууллага)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Утасны дуудлага"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Утасны дуудлага"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g>-р)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камер"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"байршил"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Мэдрэгчийг унтраах"</string>
<string name="device_services" msgid="1549944177856658705">"Төхөөрөмжийн үйлчилгээ"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Гарчиггүй"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Энэ аппыг дахин эхлүүлж, бүтэн дэлгэцэд орохын тулд товшино уу."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Зөөх"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Системийн навигацыг шинэчиллээ. Өөрчлөхийн тулд Тохиргоо руу очно уу."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Системийн навигацыг шинэчлэхийн тулд Тохиргоо руу очно уу"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index fe1c910c74ed..de0e7becde88 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -93,6 +93,10 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"स्क्रीनशॉटवर स्क्रोल करा"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"स्क्रीनशॉट डिसमिस करा"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉटचे पूर्वावलोकन"</string>
+ <!-- no translation found for screenshot_top_boundary (1500569103321300856) -->
+ <skip />
+ <!-- no translation found for screenshot_bottom_boundary (5657242629526407311) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रेकॉर्डर"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रीन रेकॉर्डिंग प्रोसेस सुरू"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"स्क्रीन रेकॉर्ड सत्रासाठी सुरू असलेली सूचना"</string>
@@ -410,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"सूर्योदयापर्यंत"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> वाजता सुरू होते"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> पर्यंत"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"ब्राइटनेस कमी करा"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC अक्षम केले आहे"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC सक्षम केले आहे"</string>
@@ -463,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफाईल दर्शवा"</string>
<string name="user_add_user" msgid="4336657383006913022">"वापरकर्ता जोडा"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"नवीन वापरकर्ता"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"अतिथी सत्र संपायचे का?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"अतिथी सत्र संपवा"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथी काढायचे?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अ‍ॅप्स आणि डेटा हटवला जाईल."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"सत्र संपवा"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"काढा"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"अतिथी, तुमचे पुन्‍हा स्‍वागत आहे!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"तुम्ही तुमचे सत्र सुरू ठेवू इच्छिता?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"येथून सुरू करा"</string>
@@ -540,6 +546,8 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"आपल्या संस्थेने आपल्या कार्य प्रोफाइलवर प्रमाणपत्र अधिकार इंस्टॉल केला आहे. आपल्या सुरक्षित नेटवर्क रहदारीचे परीक्षण केले जाऊ शकते किंवा ती सुधारली जाऊ शकते."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"या डिव्हाइसवर प्रमाणपत्र अधिकार इंस्टॉल केला आहे. आपल्या सुरक्षित नेटवर्क रहदारीचे परीक्षण केले जाऊ शकते किंवा ती सुधारली जाऊ शकते."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"आपल्या प्रशासकाने नेटवर्क लॉगिंग सुरू केले आहे, जे आपल्या डिव्हाइसवरील रहदारीचे परीक्षण करते."</string>
+ <!-- no translation found for monitoring_description_managed_profile_network_logging (6932303843097006037) -->
+ <skip />
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"तुम्‍ही <xliff:g id="VPN_APP">%1$s</xliff:g> शी कनेक्‍ट केले आहे, जे ईमेल, अ‍ॅप्स आणि वेबसाइटसहित आपल्‍या नेटवर्क क्रिया मॉनिटर करू शकते."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"तुम्‍ही <xliff:g id="VPN_APP_0">%1$s</xliff:g> आणि <xliff:g id="VPN_APP_1">%2$s</xliff:g> शी कनेक्‍ट केले आहे, जे ईमेल, अ‍ॅप्स आणि वेबसाइटसहित आपल्‍या नेटवर्क क्रिया मॉनिटर करू शकते."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"तुमचे कार्य प्रोफाइल <xliff:g id="VPN_APP">%1$s</xliff:g> शी कनेक्‍ट केले आहे, जे ईमेल, अ‍ॅप्स आणि वेबसाइटसह आपल्‍या नेटवर्क क्रियाकलापाचे परीक्षण करू शकते."</string>
@@ -733,7 +741,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"सिस्टमद्वारे या सूचनेला आपोआप &lt;b&gt;सायलंट म्हणून डीमोट केले&lt;/b&gt; गेले."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"तुमच्या रंगछटेमध्ये या सूचनेला आपोआप &lt;b&gt;थोडी जास्त&lt;/b&gt; म्हणून रँक केले गेले."</string>
<string name="feedback_demoted" msgid="951884763467110604">"तुमच्या रंगछटेमध्ये या सूचनेला आपोआप &lt;b&gt;थोडी कमी&lt;/b&gt; म्हणून रँक केले गेले."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"हे बरोबर होते का?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"डेव्हलपरला तुमचा फीडबॅक कळवा. हे बरोबर होते का?"</string>
<string name="feedback_response" msgid="4671729244976641339">"तुमच्‍या फीडबॅकबद्दल धन्यवाद!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ओके"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी सूचना नियंत्रणे खुली आहेत"</string>
@@ -880,6 +888,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> यावर हलवा"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> स्थानावर जोडा"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"स्थान <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"टाइल जोडली"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"टाइल काढून टाकली"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिंग्ज संपादक."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिंग्ज उघडा."</string>
@@ -967,23 +977,17 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ॲप्लिकेशन्स तुमचे <xliff:g id="TYPES_LIST">%s</xliff:g> वापरत आहे."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" आणि "</string>
- <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) -->
- <skip />
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> हे <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> वापरत आहे"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ने अलीकडे <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> वापरले आहे"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(एंटरप्राइझ)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"फोन कॉल"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> द्वारे)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"कॅमेरा"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"स्थान"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"मायक्रोफोन"</string>
<string name="sensor_privacy_mode" msgid="4462866919026513692">"सेन्सर बंद आहेत"</string>
<string name="device_services" msgid="1549944177856658705">"डिव्हाइस सेवा"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"शीर्षक नाही"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"हे अ‍ॅप रीस्टार्ट करण्यासाठी आणि फुल स्क्रीन करण्यासाठी टॅप करा."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"हलवा"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"सिस्टम नेव्हिगेशन अपडेट केले. बदल करण्यासाठी, सेटिंग्जवर जा."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"सिस्टम नेव्हिगेशन अपडेट करण्यासाठी सेटिंग्जवर जा"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index b1d0b4748b85..319dbde2be5f 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Penatalan tangkapan skrin"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ketepikan tangkapan skrin"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pratonton tangkapan skrin"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Sempadan atas"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Sempadan bawah"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Perakam Skrin"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Memproses rakaman skrin"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pemberitahuan breterusan untuk sesi rakaman skrin"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hingga matahari trbt"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Dihidupkan pada <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hingga <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Kurangkan Kecerahan"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC dilumpuhkan"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC didayakan"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Tunjuk profil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Tambah pengguna"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Pengguna baharu"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Tamatkan sesi tetamu?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Tamatkan sesi tetamu"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Alih keluar tetamu?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Tamatkan sesi"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Alih keluar"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Selamat kembali, tetamu!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Adakah anda ingin meneruskan sesi anda?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulakan semula"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organisasi anda memasang sijil kuasa dalam profil kerja anda. Trafik rangkaian selamat anda mungkin dipantau atau diubah suai."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Sijil kuasa dipasang pada peranti ini. Trafik rangkaian selamat anda mungkin dipantau atau diubah suai."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Pentadbir anda telah menghidupkan pengelogan rangkaian yang memantau trafik pada peranti anda."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Pentadbir anda telah menghidupkan pengelogan rangkaian yang memantau trafik dalam profil kerja anda tetapi bukan dalam profil peribadi anda."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Anda dihubungkan ke <xliff:g id="VPN_APP">%1$s</xliff:g>, yang boleh memantau aktiviti rangkaian anda, termasuk e-mel, apl dan tapak web."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Anda dihubungkan ke <xliff:g id="VPN_APP_0">%1$s</xliff:g> dan <xliff:g id="VPN_APP_1">%2$s</xliff:g>, yang boleh memantau aktiviti rangkaian anda, termasuk e-mel, apl dan tapak web."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Profil kerja anda dihubungkan ke <xliff:g id="VPN_APP">%1$s</xliff:g>, yang dapat memantau aktiviti rangkaian anda, termasuk e-mel, apl dan tapak web."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Pemberitahuan ini secara automatik &lt;b&amp;gtditurunkan taraf kepada Senyap&lt;/b&gt; oleh sistem."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Pemberitahuan ini secara automatik &lt;b&gt;dinilai lebih tinggi&lt;/b&gt; dalam rona warna anda."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Pemberitahuan ini secara automatik &lt;b&gt;dinilai lebih rendah&lt;/b&gt; dalam rona warna anda."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Adakah ini betul?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Beritahu pembangun tentang maklum balas anda. Adakah ini betul?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Terima kasih atas maklum balas anda!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kawalan pemberitahuan untuk <xliff:g id="APP_NAME">%1$s</xliff:g> dibuka"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Alih ke <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Tambahkan pada kedudukan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Kedudukan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Jubin ditambah"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Jubin dialih keluar"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor tetapan pantas."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Pemberitahuan <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buka tetapan."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> sedang menggunakan <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> menggunakan <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> baru-baru ini"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(perusahaan)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Panggilan telefon"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Panggilan telefon"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(melalui <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokasi"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Penderia dimatikan"</string>
<string name="device_services" msgid="1549944177856658705">"Perkhidmatan Peranti"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Tiada tajuk"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Ketik untuk memulakan semula apl ini dan menggunakan skrin penuh."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Alih"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigasi sistem dikemas kini. Untuk membuat perubahan, pergi ke Tetapan."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Pergi ke Tetapan untuk mengemas kini navigasi sistem"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 42b5b230eb6f..15ddf599d788 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"ဖန်သားပြင်ဓာတ်ပုံကို လှိမ့်ရန်"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"ဖန်သားပြင်ဓာတ်ပုံကို ပယ်သည်"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ဖန်သားပြင်ဓာတ်ပုံ အစမ်းကြည့်ရှုခြင်း"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"ထိပ်ပိုင်းအနားသတ်"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"အောက်ခြေအနားသတ်"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ဖန်သားပြင် ရိုက်ကူးမှု"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ဖန်သားပြင်ရိုက်ကူးနေသည်"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ဖန်သားပြင် ရိုက်ကူးသည့် စက်ရှင်အတွက် ဆက်တိုက်လာနေသော အကြောင်းကြားချက်"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"နေထွက်ချိန် အထိ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> တွင် ဖွင့်မည်"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> အထိ"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"တောက်ပမှုကို လျှော့ရန်"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ကို ပိတ်ထားသည်"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ကို ဖွင့်ထားသည်"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ပရိုဖိုင်ကို ပြရန်"</string>
<string name="user_add_user" msgid="4336657383006913022">"အသုံးပြုသူ ထည့်ရန်"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"အသုံးပြုသူ အသစ်"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ဧည့်သည်စက်ရှင်ကို အဆုံးသတ်မလား။"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"ဧည့်သည်ဆက်ရှင်ကို အဆုံးသတ်ရန်"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ဧည့်သည်ကို ဖယ်ထုတ်လိုက်ရမလား?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"သတ်မှတ်ပေးထားသည့်အချိန် ပြီးဆုံးပြီ"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ဖယ်ထုတ်ပါ"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"ပြန်လာတာ ကြိုဆိုပါသည်၊ ဧည့်သည်!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"သင်သည် သင်၏ ချိတ်ဆက်မှုကို ဆက်ပြုလုပ် လိုပါသလား?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"အစမှ ပြန်စပါ"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"သင်၏ အဖွဲ့အစည်းသည် သင်၏ အလုပ်ပရိုဖိုင်တွင် စီမံခန့်ခွဲမှုဆိုင်ရာ အသိအမှတ်ပြုလက်မှတ်ကို ထည့်သွင်းထားပါသည်။ လုံခြုံမှုရှိသော ကွန်ရက်ဒေတာစီးဆင်းမှုကို စောင့်ကြည့်ခြင်း သို့မဟုတ် ပြုပြင်ခြင်းများ ပြုလုပ်နိုင်ပါသည်။"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ဤစက်ပစ္စည်းတွင် စီမံခန့်ခွဲမှုဆိုင်ရာ အသိအမှတ်ပြုလက်မှတ်ကို ထည့်သွင်းထားပါသည်။ လုံခြုံမှုရှိသော ကွန်ရက်ဒေတာစီးဆင်းမှုကို စောင့်ကြည့်ခြင်း သို့မဟုတ် ပြုပြင်ခြင်းများ ပြုလုပ်နိုင်ပါသည်။"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"စက်ပစ္စည်းပေါ်ရှိ ဒေတာစီးဆင်းမှုများကို စောင့်ကြည့်နိုင်သည့် ကွန်ရက်မှတ်တမ်းတင်ခြင်းစနစ်ကို သင်၏ စီမံခန့်ခွဲသူက ဖွင့်ထားပါသည်။"</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"သင်၏စီမံခန့်ခွဲသူက ကွန်ရက်မှတ်တမ်းတင်ခြင်းကို ဖွင့်လိုက်သည်။ ၎င်းသည် သင့်အလုပ်ပရိုဖိုင်ရှိ ဒေတာစီးဆင်းမှုကို စောင့်ကြည့်သော်လည်း ကိုယ်ပိုင်ပရိုဖိုင်တွင် မစောင့်ကြည့်ပါ။"</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"သင်သည် သင်၏ အီးမေးလ်၊ အက်ပ်နှင့် ဝဘ်ဆိုက်များအပါအဝင် ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်သည့် <xliff:g id="VPN_APP">%1$s</xliff:g> သို့ ချိတ်ဆက်ထားပါသည်။"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"သင်သည် သင်၏ အီးမေးလ်၊ အက်ပ်နှင့် ဝဘ်ဆိုက်များအပါအဝင် သင်၏ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်သည့် <xliff:g id="VPN_APP_0">%1$s</xliff:g> နှင့် <xliff:g id="VPN_APP_1">%2$s</xliff:g> သို့ ချိတ်ဆက်ထားပါသည်။"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"သင်၏အလုပ်ပရိုဖိုင်သည် အီးမေးလ်၊ အက်ပ်နှင့် ဝဘ်ဆိုက်များအပါအဝင် သင်၏ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်သည့် <xliff:g id="VPN_APP">%1$s</xliff:g> သို့ ချိတ်ဆက်ထားပါသည်။"</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"ဤအကြောင်းကြားချက်ကို စနစ်က အလိုအလျောက် &lt;b&gt;အသံတိတ်ခြင်းသို့ ပြန်ချိန်ညှိထားသည်&lt;/b&gt;။"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"ဤအကြောင်းကြားချက်ကို သင့်အကြောင်းကြားစာအကွက်တွင် အလိုအလျောက် &lt;b&gt;အဆင့်တိုးထားသည်&lt;/b&gt;။"</string>
<string name="feedback_demoted" msgid="951884763467110604">"ဤအကြောင်းကြားချက်ကို သင့်အကြောင်းကြားစာအကွက်တွင် အလိုအလျောက် &lt;b&gt;အဆင့်လျှော့ထားသည်&lt;/b&gt;။"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"ဤအရာက မှန်ပါသလား။"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"သင့်အကြံပြုချက်ကို ဆော့ဖ်ဝဲအင်ဂျင်နီယာအား အသိပေးပါ။ ဤအရာက မှန်ပါသလား။"</string>
<string name="feedback_response" msgid="4671729244976641339">"အကြံပြုချက်အတွက် ကျေးဇူးတင်ပါသည်"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် အကြောင်းကြားချက်ထိန်းချုပ်မှုများကို ဖွင့်ထားသည်"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> သို့ ရွှေ့ရန်"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> အနေအထားသို့ ပေါင်းထည့်ရန်"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> အနေအထား"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"အကွက်ငယ်ကို ထည့်ပြီးပါပြီ"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"အကွက်ငယ်ကို ဖယ်ရှားပြီးပါပြီ"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"မြန်ဆန်သည့် ဆက်တင်တည်းဖြတ်စနစ်"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> အကြောင်းကြားချက် − <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ဆက်တင်များကို ဖွင့်ပါ။"</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> က <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ကို အသုံးပြုနေသည်"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> က <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ကို မကြာသေးမီက အသုံးပြုထားသည်"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(လုပ်ငန်းသုံး)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ဖုန်းခေါ်ဆိုမှု"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ဖုန်းခေါ်ဆိုမှု"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> မှတစ်ဆင့်)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"ကင်မရာ"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"တည်နေရာ"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"အာရုံခံကိရိယာများ ပိတ်ထားသည်"</string>
<string name="device_services" msgid="1549944177856658705">"စက်ပစ္စည်းဝန်ဆောင်မှုများ"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"ခေါင်းစဉ် မရှိပါ"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"ဤအက်ပ်ကို ပြန်စတင်ပြီး မျက်နှာပြင်အပြည့်လုပ်ရန် တို့ပါ။"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ရွှေ့ရန်"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"စနစ်လမ်းညွှန်ခြင်း အပ်ဒိတ်လုပ်ပြီးပါပြီ။ အပြောင်းအလဲများ ပြုလုပ်ရန် \'ဆက်တင်များ\' သို့သွားပါ။"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"စနစ်လမ်းညွှန်ခြင်း အပ်ဒိတ်လုပ်ရန် \'ဆက်တင်များ\' သို့သွားပါ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index ef48f8fb37c5..1c2fa8a68b11 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Rull skjermdumpen"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Avvis skjermdumpen"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Forhåndsvisning av skjermdump"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Øvre grense"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Nedre grense"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skjermopptaker"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandler skjermopptaket"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Vedvarende varsel for et skjermopptak"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Til soloppgang"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Slås på klokken <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Til <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Reduser lysstyrken"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC er slått av"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC er slått på"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Vis profil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Legg til brukere"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Ny bruker"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vil du avslutte gjesteøkten?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Avslutt gjesteøkten"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vil du fjerne gjesten?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle appene og all informasjon i denne økten slettes."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Avslutt økten"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjern"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkommen tilbake, gjest!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsette økten?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start på nytt"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organisasjonen din installerte en sertifiseringsinstans i jobbprofilen din. Den sikre nettverkstrafikken din kan overvåkes eller endres."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"En sertifiseringsinstans er installert på denne enheten. Den sikre nettverkstrafikken din kan overvåkes eller endres."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratoren din har slått på loggføring av nettverk, som overvåker trafikken på enheten din."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administratoren din har slått på loggføring av nettverk, som overvåker trafikken i jobbprofilen din, men ikke i den personlige profilen din."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Du er koblet til <xliff:g id="VPN_APP">%1$s</xliff:g>, som kan overvåke nettverksaktiviteten din, inkludert e-post, apper og nettsteder."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Du er koblet til <xliff:g id="VPN_APP_0">%1$s</xliff:g> og <xliff:g id="VPN_APP_1">%2$s</xliff:g>, som kan overvåke nettverksaktiviteten din, inkludert e-post, apper og nettsteder."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Jobbprofilen din er koblet til <xliff:g id="VPN_APP">%1$s</xliff:g>, som kan overvåke nettverksaktiviteten din, inkludert e-poster, apper og nettsteder."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Dette varselet ble automatisk &lt;b&gt;nedgradert til lydløst&lt;/b&gt; av systemet."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Dette varselet ble automatisk &lt;b&gt;rangert høyere&lt;/b&gt; i panelet."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Dette varselet ble automatisk &lt;b&gt;rangert lavere&lt;/b&gt; i panelet."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Var det riktig?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Gi utvikleren tilbakemeldingen din. Var det riktig?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Takk for tilbakemeldingen!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Varselinnstillingene for <xliff:g id="APP_NAME">%1$s</xliff:g> er åpnet"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flytt til <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Legg til posisjonen <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisjon <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"En infobrikke er lagt til"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"En infobrikke er fjernet"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsvindu for hurtiginnstillinger."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-varsel: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Åpne innstillingene."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> bruker <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> har brukt <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> nylig"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonsamtale"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonsamtale"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(til og med <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"posisjon"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensorer er av"</string>
<string name="device_services" msgid="1549944177856658705">"Enhetstjenester"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Ingen tittel"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Trykk for å starte denne appen på nytt og vise den i fullskjerm."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Flytt"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Systemnavigeringen er oppdatert. For å gjøre endringer, gå til Innstillinger."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Gå til Innstillinger for å oppdatere systemnavigeringen"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 2594e8c923dd..3ab0136f4d77 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -93,6 +93,10 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"स्क्रिनसट स्क्रोल गर्नुहोस्"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"स्क्रिनसट हटाउनुहोस्"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रिनसटको पूर्वावलोकन"</string>
+ <!-- no translation found for screenshot_top_boundary (1500569103321300856) -->
+ <skip />
+ <!-- no translation found for screenshot_bottom_boundary (5657242629526407311) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रिन रेकर्डर"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रिन रेकर्डिङको प्रक्रिया अघि बढाइँदै"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"कुनै स्क्रिन रेकर्ड गर्ने सत्रका लागि चलिरहेको सूचना"</string>
@@ -410,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"सूर्योदयसम्म"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> मा सक्रिय"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> सम्म"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"स्क्रिनको चमक घटाउनुहोस्"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC लाई असक्षम पारिएको छ"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC लाई सक्षम पारिएको छ"</string>
@@ -463,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफाइल देखाउनुहोस्"</string>
<string name="user_add_user" msgid="4336657383006913022">"प्रयोगकर्ता थप्नुहोस्"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"नयाँ प्रयोगकर्ता"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"अतिथिको सत्र अन्त्य गर्ने हो?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"अतिथिको सत्र अन्त्य गर्नुहोस्"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथि हटाउने हो?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यस सत्रमा सबै एपहरू र डेटा मेटाइनेछ।"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"सत्र अन्त्य गर्नुहोस्"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"हटाउनुहोस्"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"पुनः स्वागत, अतिथि!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"तपाईं आफ्नो सत्र जारी गर्न चाहनुहुन्छ?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"सुरु गर्नुहोस्"</string>
@@ -540,6 +546,8 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"तपाईंको संगठनले तपाईंको कार्य प्रोफाइलमा एउटा प्रमाणपत्र सम्बन्धी अख्तियार सुविधा स्थापना गरेको छ। तपाईंको सुरक्षित नेटवर्क ट्राफिकको अनुगमन वा परिमार्जन हुनसक्छ।"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"यस यन्त्रमा एउटा प्रमाणपत्र सम्बन्धी अख्तियार सुविधा स्थापना गरिएको छ। तपाईंको सुरक्षित नेटवर्कको ट्राफिकको अनुगमन वा परिमार्जन हुनसक्छ।"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"तपाईंका प्रशासकले तपाईंको यन्त्रमा ट्राफिकको अनुगमन गर्ने नेटवर्क लग गर्ने प्रक्रियालाई सक्रिय गर्नुभएको छ।"</string>
+ <!-- no translation found for monitoring_description_managed_profile_network_logging (6932303843097006037) -->
+ <skip />
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"तपाईं इमेल, एप र वेबसाइटहरू लगायत तपाईंको नेटवर्कको गतिविधिको अनुगमन गर्नसक्ने <xliff:g id="VPN_APP">%1$s</xliff:g> मा जडान हुनुहुन्छ।"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"तपाईं इमेल, एप र वेबसाइटहरू लगायत तपाईंको नेटवर्कको गतिविधिको अनुगमन गर्नसक्ने <xliff:g id="VPN_APP_0">%1$s</xliff:g> र <xliff:g id="VPN_APP_1">%2$s</xliff:g> मा जडान हुनुहुन्छ।"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"तपाईंको कार्य प्रोफाइल तपाईंका इमेल, एप र वेबसाइटहरू लगायत तपाईंको नेटवर्कको गतिविधिको अनुगमन गर्नसक्ने <xliff:g id="VPN_APP">%1$s</xliff:g> मा जडान छ।"</string>
@@ -733,7 +741,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"सिस्टमले स्वतः यस सूचनालाई &lt;b&gt;कम महत्त्वपूर्ण ठानी साइलेन्ट मोडमा&lt;/b&gt; सेट गरिदियो।"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"तपाईंको सेडमा यो सूचना स्वतः &lt;b&gt;धेरै महत्त्वपूर्ण सूचनाका रूपमा&lt;/b&gt; सेट गरियो।"</string>
<string name="feedback_demoted" msgid="951884763467110604">"तपाईंको सेडमा यो सूचना स्वतः &lt;b&gt;कम महत्त्वपूर्ण सूचनाका रूपमा&lt;/b&gt; सेट गरियो।"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"के यो सही थियो?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"विकासकर्तालाई तपाईंको प्रतिक्रिया थाहा पाउन दिनुहोस्। यो कुरा सही थियो?"</string>
<string name="feedback_response" msgid="4671729244976641339">"तपाईंको प्रतिक्रियाका लागि धन्यवाद!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ठिक छ"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> का सूचना सम्बन्धी नियन्त्रणहरूलाई खोलियो"</string>
@@ -880,6 +888,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"टाइल सारेर <xliff:g id="POSITION">%1$d</xliff:g> मा लैजानुहोस्"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"टाइल यो अवस्था <xliff:g id="POSITION">%1$d</xliff:g> मा हाल्नुहोस्"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"स्थिति <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"टाइल हालियो"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"टाइल हटाइयो"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिङ सम्पादक।"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> को सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिङहरूलाई खोल्नुहोस्।"</string>
@@ -970,7 +980,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ले <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> प्रयोग गरिरहेको छ"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ले हालसालै <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> प्रयोग गरेको छ"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(इन्टरप्राइज)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"फोन कल"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"फोन कल"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> मार्फत)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"क्यामेरा"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"स्थान"</string>
@@ -978,7 +988,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"सेन्सरहरू निष्क्रिय छन्"</string>
<string name="device_services" msgid="1549944177856658705">"यन्त्रका सेवाहरू"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"शीर्षक छैन"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"यो एप पुनः सुरु गर्न ट्याप गर्नुहोस् र फुल स्क्रिन मोडमा जानुहोस्।"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"सार्नुहोस्"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"प्रणालीको नेभिगेसन अद्यावधिक गरियो। परिवर्तन गर्न सेटिङमा जानुहोस्।"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"प्रणालीको नेभिगेसन अद्यावधिक गर्न सेटिङमा जानुहोस्"</string>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 3153d0d0123d..37ec576be4be 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -89,6 +89,8 @@
<color name="kg_user_switcher_avatar_icon_color">@android:color/background_light</color>
<!-- Icon color for selected user avatars in keyguard user switcher -->
<color name="kg_user_switcher_selected_avatar_icon_color">#202124</color>
+ <!-- Color of background circle of user avatars in keyguard user switcher -->
+ <color name="kg_user_switcher_avatar_background">#3C4043</color>
<!-- Icon color for user avatars in quick settings user switcher -->
<color name="qs_user_switcher_avatar_icon_color">@android:color/background_light</color>
<!-- Icon color for selected user avatars in quick settings user switcher -->
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 02741322f5ac..dcadbf58330f 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Screenshot scrollen"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Screenshot sluiten"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Voorbeeld van screenshot"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Bovengrens"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Ondergrens"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Schermopname"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Schermopname verwerken"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Doorlopende melding voor een schermopname-sessie"</string>
@@ -345,7 +347,7 @@
<string name="quick_settings_location_label" msgid="2621868789013389163">"Locatie"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Locatie uit"</string>
<string name="quick_settings_camera_label" msgid="1367149596242401934">"Camera blokkeren"</string>
- <string name="quick_settings_mic_label" msgid="8245831073612564953">"Microfoon dempen"</string>
+ <string name="quick_settings_mic_label" msgid="8245831073612564953">"Microfoon uitzetten"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media-apparaat"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
<string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Alleen noodoproepen"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Tot zonsopgang"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aan om <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Tot <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Helderheid verlagen"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is uitgeschakeld"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is ingeschakeld"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profiel weergeven"</string>
<string name="user_add_user" msgid="4336657383006913022">"Gebruiker toevoegen"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Nieuwe gebruiker"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Gastsessie beëindigen?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Gastsessie beëindigen"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gast verwijderen?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Sessie beëindigen"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Verwijderen"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Welkom terug, gast!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Wil je doorgaan met je sessie?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Opnieuw starten"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Je organisatie heeft een certificeringsinstantie geïnstalleerd in je werkprofiel. Je beveiligde netwerkverkeer kan worden bijgehouden of aangepast."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Er is een certificeringsinstantie geïnstalleerd op dit apparaat. Je beveiligde netwerkverkeer kan worden bijgehouden of aangepast."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Je beheerder heeft netwerkregistratie ingeschakeld, waarmee het verkeer op je apparaat wordt bijgehouden."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Je beheerder heeft logboekregistratie voor het netwerk aangezet. Hiermee wordt verkeer in je werkprofiel bijgehouden, maar niet in je persoonlijke profiel."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Je bent verbonden met <xliff:g id="VPN_APP">%1$s</xliff:g>, waarmee je netwerkactiviteit (waaronder e-mails, apps en websites) kan worden gecontroleerd."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Je bent verbonden met <xliff:g id="VPN_APP_0">%1$s</xliff:g> en <xliff:g id="VPN_APP_1">%2$s</xliff:g>, waarmee je netwerkactiviteit (waaronder e-mails, apps en websites) kan worden bijgehouden."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Je werkprofiel is verbonden met <xliff:g id="VPN_APP">%1$s</xliff:g>, waarmee je netwerkactiviteit (waaronder e-mails, apps en websites) kan worden bijgehouden."</string>
@@ -612,16 +617,16 @@
<string name="ring_toggle_title" msgid="5973120187287633224">"Gesprekken"</string>
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Bellen"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Trillen"</string>
- <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Dempen"</string>
+ <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Geluid staat uit"</string>
<string name="qs_status_phone_vibrate" msgid="7055409506885541979">"Telefoon op trillen"</string>
- <string name="qs_status_phone_muted" msgid="3763664791309544103">"Telefoon gedempt"</string>
+ <string name="qs_status_phone_muted" msgid="3763664791309544103">"Telefoongeluid staat uit"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tik om dempen op te heffen."</string>
- <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tik om in te stellen op trillen. Toegankelijkheidsservices kunnen zijn gedempt."</string>
- <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tik om te dempen. Toegankelijkheidsservices kunnen zijn gedempt."</string>
+ <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tik om in te stellen op trillen. Het geluid van toegankelijkheidsservices kan hierdoor uitgaan."</string>
+ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tik om te dempen. Het geluid van toegankelijkheidsservices kan hierdoor uitgaan."</string>
<string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tik om in te stellen op trillen."</string>
- <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tik om te dempen."</string>
- <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"dempen"</string>
- <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dempen opheffen"</string>
+ <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tik om geluid uit te zetten."</string>
+ <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"geluid uit"</string>
+ <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"geluid aanzetten"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"trillen"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s-volumeknoppen"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Gesprekken en meldingen gaan over (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
@@ -716,7 +721,7 @@
<string name="notification_priority_title" msgid="2079708866333537093">"Prioriteit"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ondersteunt geen gespreksfuncties"</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"Deze meldingen kunnen niet worden aangepast."</string>
- <string name="notification_multichannel_desc" msgid="7414593090056236179">"Deze groep meldingen kan hier niet worden geconfigureerd"</string>
+ <string name="notification_multichannel_desc" msgid="7414593090056236179">"Deze groep meldingen kan hier niet worden ingesteld"</string>
<string name="notification_delegate_header" msgid="1264510071031479920">"Melding via proxy"</string>
<string name="notification_channel_dialog_title" msgid="6856514143093200019">"Alle meldingen van <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="see_more_title" msgid="7409317011708185729">"Meer weergeven"</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Deze melding is automatisch &lt;b&gt;verlaagd naar Stil&lt;/b&gt; door het systeem."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Deze melding is automatisch &lt;b&gt;hoger gerangschikt&lt;/b&gt; in je meldingenpaneel."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Deze melding is automatisch &lt;b&gt;lager gerangschikt&lt;/b&gt; in je meldingenpaneel."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Is dit juist?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Stuur de ontwikkelaar feedback. Was dit goed?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Bedankt voor je feedback."</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Beheeropties voor meldingen voor <xliff:g id="APP_NAME">%1$s</xliff:g> geopend"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Verplaatsen naar <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Toevoegen aan positie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Positie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tegel toegevoegd"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tegel verwijderd"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor voor \'Snelle instellingen\'."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-melding: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Instellingen openen."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> gebruikt de <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> heeft de <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recent gebruikt"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(zakelijke versie)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefoongesprek"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefoongesprek"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(via <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"locatie"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensoren uit"</string>
<string name="device_services" msgid="1549944177856658705">"Apparaatservices"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Geen titel"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Tik om deze app opnieuw te starten en te openen op het volledige scherm."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Verplaatsen"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Systeemnavigatie geüpdatet. Als je wijzigingen wilt aanbrengen, ga je naar Instellingen."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Ga naar Instellingen om de systeemnavigatie te updaten"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 16d8a2969e27..955c08a35922 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"ସ୍କ୍ରିନସଟ୍ ସ୍କ୍ରୋଲ୍ କରନ୍ତୁ"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"ସ୍କ୍ରିନସଟ୍ ଖାରଜ କରନ୍ତୁ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ସ୍କ୍ରିନସଟର ପ୍ରିଭ୍ୟୁ"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"ଶୀର୍ଷ ସୀମାରେଖା"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"ନିମ୍ନ ସୀମାରେଖା"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ସ୍କ୍ରିନ୍ ରେକର୍ଡର୍"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ସ୍କ୍ରିନ ରେକର୍ଡିଂର ପ୍ରକ୍ରିୟାକରଣ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ଏକ ସ୍କ୍ରି‍ନ୍‍ ରେକର୍ଡ୍‍ ସେସନ୍‍ ପାଇଁ ଚାଲୁଥିବା ବିଜ୍ଞପ୍ତି"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ସକାଳ ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>ରେ ଚାଲୁ ହେବ"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"ଉଜ୍ଜ୍ୱଳତା କମାନ୍ତୁ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ଅକ୍ଷମ କରାଯାଇଛି"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ସକ୍ଷମ କରାଯାଇଛି"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ପ୍ରୋଫାଇଲ୍ ଦେଖାନ୍ତୁ"</string>
<string name="user_add_user" msgid="4336657383006913022">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଯୋଗ କରନ୍ତୁ"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ଅତିଥି ସେସନ୍ ଶେଷ କରିବେ?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"ଅତିଥି ସେସନ୍ ଶେଷ କରନ୍ତୁ"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ଅତିଥିଙ୍କୁ କାଢ଼ିଦେବେ?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ଅବଧିର ସମସ୍ତ ଆପ୍‌ ଓ ଡାଟା ଡିଲିଟ୍‌ ହୋଇଯିବ।"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ସେସନ୍ ଶେଷ କରନ୍ତୁ"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"କାଢ଼ିଦିଅନ୍ତୁ"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"ପୁଣି ସ୍ୱାଗତ, ଅତିଥି!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"ଆପଣ ନିଜର ଅବଧି ଜାରି ରଖିବାକୁ ଚାହାନ୍ତି କି?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ଆରମ୍ଭ କରନ୍ତୁ"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲରେ ଆପଣଙ୍କ ସଂସ୍ଥା ଏକ ସର୍ଟିଫିକେଟ୍‍ ଅଥରିଟି ଇନଷ୍ଟଲ୍‍ କରିଛନ୍ତି। ଆପଣଙ୍କ ସୁରକ୍ଷିତ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କିମ୍ବା ସଂଶୋଧନ କରାଯାଇ ପାରେ।"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ଏହି ଡିଭାଇସରେ ଏକ ସର୍ଟିଫିକେଟ୍‍ ଅଥରିଟି ଇନଷ୍ଟଲ୍‍ କରାଯାଇଛି। ଆପଣଙ୍କ ସୁରକ୍ଷିତ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କିମ୍ବା ସଂଶୋଧନ କରାଯାଇ ପାରେ।"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"ଆପଣଙ୍କ ଆଡମିନ୍‍ ନେଟୱର୍କ ଲଗଇନ୍‍ କରିବା ଅନ୍‍ କରିଛନ୍ତି, ଯାହା ଆପଣଙ୍କ ଡିଭାଇସରେ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କରେ।"</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"ଆପଣଙ୍କ ଆଡମିନ୍ ନେଟୱାର୍କ ଲଗିଂ ଚାଲୁ କରିଛନ୍ତି, ଯାହା ଆପଣଙ୍କ ୱାର୍କ ପ୍ରୋଫାଇଲରେ ଟ୍ରାଫିକ୍ ନିରୀକ୍ଷଣ କରେ କିନ୍ତୁ ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ପ୍ରୋଫାଇଲରେ ନୁହେଁ।"</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"ଆପଣ <xliff:g id="VPN_APP">%1$s</xliff:g>ରେ ସଂଯୁକ୍ତ, ଯାହା ଇମେଲ୍‍, ଆପ୍‌ ଓ ୱେବସାଇଟ୍‍ ସମେତ ଆପଣଙ୍କ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"ଆପଣ <xliff:g id="VPN_APP_0">%1$s</xliff:g> ଏବଂ <xliff:g id="VPN_APP_1">%2$s</xliff:g>ରେ ସଂଯୁକ୍ତ, ଯାହା ଇମେଲ୍‍, ଆପ୍‌ ଓ ୱେବସାଇଟ୍‍ ସମେତ ଆପଣଙ୍କ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲ୍‍ <xliff:g id="VPN_APP">%1$s</xliff:g>ରେ ସଂଯୁକ୍ତ, ଯାହା ଇମେଲ୍‍, ଆପ୍‌ ଓ ୱେବସାଇଟ୍‍ ସମେତ ଆପଣଙ୍କ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"ସିଷ୍ଟମ୍ ଏହି ବିଜ୍ଞପ୍ତିକୁ ସ୍ୱଚାଳିତ ଭାବେ &lt;b&gt;ନୀରବକୁ ଡିମୋଟ୍ କରିଛି&lt;/b&gt;।"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"ଆପଣଙ୍କ ସେଡରେ ସ୍ୱଚାଳିତ ଭାବେ ଏହି ବିଜ୍ଞପ୍ତିର &lt;b&gt;ରେଙ୍କ ଉପରକୁ&lt;/b&gt; କରାଯାଇଛି।"</string>
<string name="feedback_demoted" msgid="951884763467110604">"ଆପଣଙ୍କ ସେଡରେ ସ୍ୱଚାଳିତ ଭାବେ ଏହି ବିଜ୍ଞପ୍ତିର &lt;b&gt;ରେଙ୍କ ତଳକୁ&lt;/b&gt; କରାଯାଇଛି।"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"ଏହା ଠିକ୍ ଥିଲା କି?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"ଡେଭଲପରଙ୍କୁ ଆପଣଙ୍କ ମତାମତ ଜଣାନ୍ତୁ। ଏହା ଠିକ୍ ଥିଲା କି?"</string>
<string name="feedback_response" msgid="4671729244976641339">"ଆପଣଙ୍କ ମତାମତ ପାଇଁ ଧନ୍ୟବାଦ!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ଠିକ୍ ଅଛି"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ବିଜ୍ଞପ୍ତି ନିୟନ୍ତ୍ରଣ ଖୋଲା ଯାଇଛି"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>କୁ ମୁଭ୍ କରନ୍ତୁ"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ଅବସ୍ଥିତିରେ ଯୋଗ କରନ୍ତୁ"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ଅବସ୍ଥିତି <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ଟାଇଲ୍ ଯୋଗ କରାଯାଇଛି"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ଟାଇଲ୍ କାଢ଼ି ଦିଆଯାଇଛି"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ଦ୍ରୁତ ସେଟିଙ୍ଗ ଏଡିଟର୍।"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ବିଜ୍ଞପ୍ତି: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ସେଟିଂସ୍ ଖୋଲନ୍ତୁ।"</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ବ୍ୟବହାର କରୁଛି"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ଏବେ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ବ୍ୟବହାର କରିଛି"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ଏଣ୍ଟରପ୍ରାଇଜ୍)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ଫୋନକଲ୍"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ଫୋନ୍ କଲ୍"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> ମାଧ୍ୟମରେ)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"କ୍ୟାମେରା"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ଲୋକେସନ୍‍"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"ସେନ୍ସର୍‍ଗୁଡ଼ିକ ବନ୍ଦ ଅଛି"</string>
<string name="device_services" msgid="1549944177856658705">"ଡିଭାଇସ୍‍ ସେବାଗୁଡିକ"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"କୌଣସି ଶୀର୍ଷକ ନାହିଁ"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"ଏହି ଆପ୍‌କୁ ରିଷ୍ଟାର୍ଟ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ ଏବଂ ଫୁଲ୍‌ସ୍କ୍ରିନ୍‌କୁ ଯାଆନ୍ତୁ।"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ନିଅନ୍ତୁ"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"ସିଷ୍ଟମ୍ ନାଭିଗେସନ୍ ଅପ୍‌ଡେଟ୍ ହୋଇଛି। ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ, ସେଟିଂସ୍‌କୁ ଯାଆନ୍ତୁ।"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ସିଷ୍ଟମ୍ ନାଭିଗେସନ୍ ଅପ୍‌ଡେଟ୍ କରିବା ପାଇଁ ସେଟିଂସ୍‍କୁ ଯାଆନ୍ତୁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index d33455d2c82c..47f56bd7a09d 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -93,6 +93,10 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਨੂੰ ਸਕ੍ਰੋਲ ਕਰੋ"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਖਾਰਜ ਕਰੋ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਪੂਰਵ-ਝਲਕ"</string>
+ <!-- no translation found for screenshot_top_boundary (1500569103321300856) -->
+ <skip />
+ <!-- no translation found for screenshot_bottom_boundary (5657242629526407311) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਰ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਜਾਰੀ ਹੈ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ਕਿਸੇ ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ ਸੈਸ਼ਨ ਲਈ ਚੱਲ ਰਹੀ ਸੂਚਨਾ"</string>
@@ -410,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ਸੂਰਜ ਚੜ੍ਹਨ ਤੱਕ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> ਵਜੇ ਚਾਲੂ"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> ਵਜੇ ਤੱਕ"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"ਚਮਕ ਘਟਾਓ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ਨੂੰ ਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
@@ -463,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ਪ੍ਰੋਫਾਈਲ ਦਿਖਾਓ"</string>
<string name="user_add_user" msgid="4336657383006913022">"ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ਕੀ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਸਮਾਪਤ ਕਰਨਾ ਹੈ?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਸਮਾਪਤ ਕਰੋ"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ਕੀ ਮਹਿਮਾਨ ਹਟਾਉਣਾ ਹੈ?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿੱਚ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਏਗਾ।"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ਸੈਸ਼ਨ ਸਮਾਪਤ ਕਰੋ"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ਹਟਾਓ"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"ਮਹਿਮਾਨ, ਫਿਰ ਤੁਹਾਡਾ ਸੁਆਗਤ ਹੈ!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"ਕੀ ਤੁਸੀਂ ਆਪਣਾ ਸੈਸ਼ਨ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ਸ਼ੁਰੂ ਕਰੋ"</string>
@@ -540,6 +546,8 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ ਇੱਕ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਅਥਾਰਟੀ ਸਥਾਪਤ ਕੀਤੀ ਗਈ ਹੈ। ਤੁਹਾਡੇ ਸੁਰੱਖਿਅਤ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ ਜਾਂ ਉਸਨੂੰ ਸੋਧਿਆ ਜਾ ਸਕਦਾ ਹੈ।"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ਇੱਕ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਅਥਾਰਟੀ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਸਥਾਪਤ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। ਤੁਹਾਡੇ ਸੁਰੱਖਿਅਤ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ ਜਾਂ ਉਸਨੂੰ ਸੋਧਿਆ ਜਾ ਸਕਦਾ ਹੈ।"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਨੇ ਨੈੱਟਵਰਕ ਲੌਗਿੰਗ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਹੋਇਆ ਹੈ, ਜੋ ਤੁਹਾਡੇ ਡੀਵਾਈਸ \'ਤੇ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰਦਾ ਹੈ।"</string>
+ <!-- no translation found for monitoring_description_managed_profile_network_logging (6932303843097006037) -->
+ <skip />
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"ਤੁਸੀਂ <xliff:g id="VPN_APP">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੋ, ਜੋ ਈਮੇਲਾਂ, ਐਪਾਂ, ਅਤੇ ਵੈੱਬਸਾਈਟਾਂ ਸਮੇਤ ਤੁਹਾਡੀ ਨੈੱਟਵਰਕ ਸਰਗਰਮੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ।"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"ਤੁਸੀਂ <xliff:g id="VPN_APP_0">%1$s</xliff:g> ਅਤੇ <xliff:g id="VPN_APP_1">%2$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੋ, ਜੋ ਈਮੇਲਾਂ, ਐਪਾਂ, ਅਤੇ ਵੈੱਬਸਾਈਟਾਂ ਸਮੇਤ ਤੁਹਾਡੀ ਨੈੱਟਵਰਕ ਸਰਗਰਮੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀਆਂ ਹਨ।"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"ਤੁਹਾਡੀ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ <xliff:g id="VPN_APP">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ, ਜੋ ਈਮੇਲਾਂ, ਐਪਾਂ ਅਤੇ ਵੈੱਬਸਾਈਟਾਂ ਸਮੇਤ ਤੁਹਾਡੀ ਨੈੱਟਵਰਕ ਸਰਗਰਮੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ।"</string>
@@ -733,7 +741,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"ਸਿਸਟਮ ਨੇ ਇਸ ਸੂਚਨਾ ਦਾ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ &lt;b&gt;ਦਰਜਾ ਘਟਾ ਕੇ ਸ਼ਾਂਤ&lt;/b&gt; \'ਤੇ ਸੈੱਟ ਕਰ ਦਿੱਤਾ ਗਿਆ ਸੀ।"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"ਤੁਹਾਡੇ ਸ਼ੇਡ ਵਿੱਚ ਇਸ ਸੂਚਨਾ ਦਾ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ &lt;b&gt;ਦਰਜਾ ਉੱਪਰ&lt;/b&gt; ਕਰ ਦਿੱਤਾ ਗਿਆ ਸੀ।"</string>
<string name="feedback_demoted" msgid="951884763467110604">"ਤੁਹਾਡੇ ਸ਼ੇਡ ਵਿੱਚ ਇਸ ਸੂਚਨਾ ਦਾ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ &lt;b&gt;ਦਰਜਾ ਹੇਠਾਂ&lt;/b&gt; ਕਰ ਦਿੱਤਾ ਗਿਆ ਸੀ।"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"ਕੀ ਇਹ ਸਹੀ ਸੀ?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"ਵਿਕਾਸਕਾਰ ਨੂੰ ਆਪਣੇ ਵਿਚਾਰ ਦੱਸੋ। ਕੀ ਇਹ ਸਹੀ ਸੀ?"</string>
<string name="feedback_response" msgid="4671729244976641339">"ਤੁਹਾਡੇ ਵਿਚਾਰ ਲਈ ਧੰਨਵਾਦ!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ਠੀਕ ਹੈ"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਸੂਚਨਾ ਕੰਟਰੋਲਾਂ ਨੂੰ ਖੋਲ੍ਹਿਆ ਗਿਆ"</string>
@@ -880,6 +888,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> \'ਤੇ ਲਿਜਾਓ"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ਸਥਾਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ਸਥਾਨ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ਟਾਇਲ ਨੂੰ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ਟਾਇਲ ਨੂੰ ਹਟਾ ਦਿੱਤਾ ਗਿਆ"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਸੰਪਾਦਕ।"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ਸੂਚਨਾ: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ।"</string>
@@ -967,23 +977,17 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ਐਪਲੀਕੇਸ਼ਨਾਂ ਤੁਹਾਡੇ <xliff:g id="TYPES_LIST">%s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀਆਂ ਹਨ।"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ਅਤੇ "</string>
- <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) -->
- <skip />
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ਐਪ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀ ਹੈ"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ਨੇ ਹਾਲ ਹੀ ਵਿੱਚ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕੀਤੀ"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ਐਂਟਰਪ੍ਰਾਈਜ਼)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ਫ਼ੋਨ ਕਾਲ"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> ਰਾਹੀਂ)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"ਕੈਮਰਾ"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ਟਿਕਾਣਾ"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string>
<string name="sensor_privacy_mode" msgid="4462866919026513692">"ਸੈਂਸਰ ਬੰਦ ਕਰੋ"</string>
<string name="device_services" msgid="1549944177856658705">"ਡੀਵਾਈਸ ਸੇਵਾਵਾਂ"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"ਕੋਈ ਸਿਰਲੇਖ ਨਹੀਂ"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਪੂਰੀ-ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਜਾਓ।"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ਲਿਜਾਓ"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"ਸਿਸਟਮ ਨੈਵੀਗੇਸ਼ਨ ਅੱਪਡੇਟ ਹੋ ਗਿਆ। ਤਬਦੀਲੀਆਂ ਕਰਨ ਲਈ, ਸੈਟਿੰਗਾਂ \'ਤੇ ਜਾਓ।"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ਸਿਸਟਮ ਨੈਵੀਗੇਸ਼ਨ ਨੂੰ ਅੱਪਡੇਟ ਕਰਨ ਲਈ ਸੈਟਿੰਗਾਂ \'ਤੇ ਜਾਓ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 6440a65a9eaf..59c9f52c4dc2 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -93,6 +93,10 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Przewiń zrzut ekranu"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Zamknij zrzut ekranu"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Podgląd zrzutu ekranu"</string>
+ <!-- no translation found for screenshot_top_boundary (1500569103321300856) -->
+ <skip />
+ <!-- no translation found for screenshot_bottom_boundary (5657242629526407311) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Nagrywanie ekranu"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Przetwarzam nagrywanie ekranu"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Stałe powiadomienie o sesji rejestrowania zawartości ekranu"</string>
@@ -414,6 +418,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do wschodu słońca"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Włącz o <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Zmniejsz jasność"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"Komunikacja NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Komunikacja NFC jest wyłączona"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Komunikacja NFC jest włączona"</string>
@@ -444,7 +449,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Kliknij ponownie, by otworzyć"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Przesuń w górę, by otworzyć"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Przesuń w górę, by spróbować ponownie"</string>
- <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Odblokuj, by użyć NFC"</string>
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Odblokuj, by użyć komunikacji NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"To urządzenie należy do Twojej organizacji"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Właściciel tego urządzenia: <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Aby włączyć telefon, przesuń palcem od ikony"</string>
@@ -467,9 +472,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Pokaż profil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Dodaj użytkownika"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Nowy użytkownik"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Zakończyć sesję gościa?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Zakończ sesję gościa"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Usunąć gościa?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Zakończ sesję"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Usuń"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Witaj ponownie, gościu!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcesz kontynuować sesję?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Rozpocznij nową"</string>
@@ -546,6 +552,8 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Twoja organizacja zainstalowała urząd certyfikacji w Twoim profilu służbowym. Zabezpieczony ruch w sieci może być monitorowany i zmieniany."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Urząd certyfikacji zainstalowany na tym urządzeniu. Twój zabezpieczony ruch w sieci może być monitorowany i zmieniany."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administrator włączył rejestrowanie sieciowe, które pozwala monitorować ruch na Twoim urządzeniu."</string>
+ <!-- no translation found for monitoring_description_managed_profile_network_logging (6932303843097006037) -->
+ <skip />
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Łączysz się z aplikacją <xliff:g id="VPN_APP">%1$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Łączysz się z aplikacjami <xliff:g id="VPN_APP_0">%1$s</xliff:g> i <xliff:g id="VPN_APP_1">%2$s</xliff:g>, które mogą monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Twój profil służbowy jest połączony z aplikacją <xliff:g id="VPN_APP">%1$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string>
@@ -739,7 +747,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"To powiadomienie zostało automatycznie &lt;b&gt;zmienione na Ciche&lt;/b&gt; przez system."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Ważność tego powiadomienia została automatycznie &lt;b&gt;podniesiona&lt;/b&gt; przez system."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Ważność tego powiadomienia została automatycznie &lt;b&gt;obniżona&lt;/b&gt; przez system."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Czy to było prawidłowe?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Daj znać deweloperowi, co o tym sądzisz. Czy to było prawidłowe?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Dziękujemy za opinię"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Sterowanie powiadomieniami aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> otwarte"</string>
@@ -890,6 +898,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Przenieś do pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodaj w pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozycja <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Dodano kartę"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Usunięto kartę"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Edytor szybkich ustawień."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Powiadomienie z aplikacji <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otwórz ustawienia."</string>
@@ -980,7 +990,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikacja <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> używa: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikacja <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> używała ostatnio: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(wersja firmowa)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Rozmowa telefoniczna"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Rozmowa telefoniczna"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(przez: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"aparat"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokalizacja"</string>
@@ -988,7 +998,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Wyłącz czujniki"</string>
<string name="device_services" msgid="1549944177856658705">"Usługi urządzenia"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Bez tytułu"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Kliknij, by uruchomić tę aplikację ponownie i przejść w tryb pełnoekranowy."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Przenieś"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Nawigacja w systemie została zaktualizowana. Aby wprowadzić zmiany, otwórz Ustawienia."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Otwórz Ustawienia, by zaktualizować nawigację w systemie"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 40dc2de30ac4..fca802d4d49f 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Captura de tela da página inteira"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Dispensar captura de tela"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Visualização de captura de tela"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Limite superior"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Limite inferior"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processando gravação de tela"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação contínua para uma sessão de gravação de tela"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Até o nascer do sol"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ativar: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Até: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Reduzir brilho"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"A NFC está desativada"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"A NFC está ativada"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Encerrar sessão de visitante?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Encerrar sessão de visitante"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover convidado?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Encerrar sessão"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo, convidado."</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Sua organização instalou uma autoridade de certificação no seu perfil de trabalho. É possível monitorar ou modificar seu tráfego de rede seguro."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Uma autoridade de certificação foi instalada neste dispositivo. É possível monitorar ou modificar seu tráfego de rede seguro."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Seu administrador ativou o registro de rede, que monitora o tráfego no seu dispositivo."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Seu administrador ativou o registro de rede, que monitora o tráfego no seu perfil de trabalho, mas não no perfil pessoal."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Você está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode monitorar sua atividade na rede, incluindo e-mails, apps e websites."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Você está conectado a <xliff:g id="VPN_APP_0">%1$s</xliff:g> e <xliff:g id="VPN_APP_1">%2$s</xliff:g>, que podem monitorar sua atividade de rede, incluindo e-mails, apps e websites."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Seu perfil de trabalho está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode monitorar sua atividade de rede, incluindo e-mails, apps e websites."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Esta notificação foi automaticamente &lt;b&gt;rebaixada para Silenciosa&lt;/b&gt; pelo sistema."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Esta notificação foi automaticamente &lt;b&gt;classificada com maior prioridade&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Esta notificação foi automaticamente &lt;b&gt;classificada com menor prioridade&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Isso está correto?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Envie seu feedback ao desenvolvedor. Isso está correto?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Agradecemos seu feedback."</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Bloco adicionado"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Bloco removido"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configurações."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"O app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está usando <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"O app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> usou <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recentemente"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresarial)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Chamada telefônica"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Chamada telefônica"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(pelo app <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"câmera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"localização"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensores desativados"</string>
<string name="device_services" msgid="1549944177856658705">"Serviços do dispositivo"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Sem título"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Toque para reiniciar o app e usar tela cheia."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mover"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navegação no sistema atualizada. Se quiser alterá-la, acesse as configurações."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Acesse as configurações para atualizar a navegação no sistema"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 22241f8fc280..9b4d45b615fc 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Deslocar captura de ecrã"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ignorar captura de ecrã"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pré-visualização da captura de ecrã"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Limite superior"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Limite inferior"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravador de ecrã"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"A processar a gravação de ecrã"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação persistente de uma sessão de gravação de ecrã"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Até ao amanhecer"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ativado à(s) <xliff:g id="TIME">%s</xliff:g>."</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Até à(s) <xliff:g id="TIME">%s</xliff:g>."</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Reduzir o brilho"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"O NFC está desativado"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"O NFC está ativado"</string>
@@ -440,7 +443,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Toque novamente para abrir"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Deslize rapidamente para cima para abrir"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Deslize rapidamente para cima para tentar novamente."</string>
- <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloqueie para utilizar o NFC"</string>
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquear para utilizar o NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence à sua entidade."</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertence à entidade <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>."</string>
<string name="phone_hint" msgid="6682125338461375925">"Deslize rapid. a partir do ícone para aceder ao telemóvel"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Adicionar utilizador"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Novo utilizador"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Pretende terminar a sessão de convidado?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Terminar sessão de convidado"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover o convidado?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as aplicações e dados desta sessão serão eliminados."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Terminar sessão"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo de volta, caro(a) convidado(a)!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Pretende continuar a sessão?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"A sua entidade instalou uma autoridade de certificação no seu perfil de trabalho. O tráfego da sua rede segura pode ser monitorizado ou alterado."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Está instalada uma autoridade de certificação neste dispositivo. O tráfego da sua rede segura pode ser monitorizado ou alterado."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"O gestor ativou os registos de rede, que monitorizam o tráfego no seu dispositivo."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"O seu administrador ativou os registos de rede, que monitorizam o tráfego no seu perfil de trabalho, mas não no seu perfil pessoal."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Está ligado à rede <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode monitorizar a sua atividade de rede, incluindo emails, aplicações e Sites."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Está ligado às redes <xliff:g id="VPN_APP_0">%1$s</xliff:g> e <xliff:g id="VPN_APP_1">%2$s</xliff:g>, que podem monitorizar a sua atividade de rede, incluindo emails, aplicações e Sites."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"O seu perfil de trabalho está ligado à rede <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode monitorizar a sua atividade de rede, incluindo emails, aplicações e Sites."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Esta notificação foi automaticamente &lt;b&gt;despromovida para Silenciosa&lt;/b&gt; pelo sistema."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Esta notificação passou automaticamente para uma &lt;b&gt;classificação superior&lt;/b&gt; no seu painel."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Esta notificação passou automaticamente para uma &lt;b&gt;classificação inferior&lt;/b&gt; no seu painel."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Estava correto?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Partilhe o seu feedback com o programador. Estava correto?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Obrigado pelo seu feedback!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Controlos de notificações da app <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mova para <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicione à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Cartão adicionado"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Cartão removido"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de definições rápidas."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir as definições."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"A app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está a utilizar a app <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Recentemente, a app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> utilizou a app <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresarial)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Chamada telefónica"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Chamada"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(através de <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"câmara"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"localização"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensores desativados"</string>
<string name="device_services" msgid="1549944177856658705">"Serviços do dispositivo"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Sem título"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Toque para reiniciar esta app e ficar em ecrã inteiro."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mover"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"A navegação no sistema foi atualizada. Para efetuar alterações, aceda às Definições."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Aceda às Definições para atualizar a navegação no sistema."</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 40dc2de30ac4..fca802d4d49f 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Captura de tela da página inteira"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Dispensar captura de tela"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Visualização de captura de tela"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Limite superior"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Limite inferior"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processando gravação de tela"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação contínua para uma sessão de gravação de tela"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Até o nascer do sol"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ativar: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Até: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Reduzir brilho"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"A NFC está desativada"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"A NFC está ativada"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Encerrar sessão de visitante?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Encerrar sessão de visitante"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover convidado?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Encerrar sessão"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo, convidado."</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Sua organização instalou uma autoridade de certificação no seu perfil de trabalho. É possível monitorar ou modificar seu tráfego de rede seguro."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Uma autoridade de certificação foi instalada neste dispositivo. É possível monitorar ou modificar seu tráfego de rede seguro."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Seu administrador ativou o registro de rede, que monitora o tráfego no seu dispositivo."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Seu administrador ativou o registro de rede, que monitora o tráfego no seu perfil de trabalho, mas não no perfil pessoal."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Você está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode monitorar sua atividade na rede, incluindo e-mails, apps e websites."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Você está conectado a <xliff:g id="VPN_APP_0">%1$s</xliff:g> e <xliff:g id="VPN_APP_1">%2$s</xliff:g>, que podem monitorar sua atividade de rede, incluindo e-mails, apps e websites."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Seu perfil de trabalho está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode monitorar sua atividade de rede, incluindo e-mails, apps e websites."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Esta notificação foi automaticamente &lt;b&gt;rebaixada para Silenciosa&lt;/b&gt; pelo sistema."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Esta notificação foi automaticamente &lt;b&gt;classificada com maior prioridade&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Esta notificação foi automaticamente &lt;b&gt;classificada com menor prioridade&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Isso está correto?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Envie seu feedback ao desenvolvedor. Isso está correto?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Agradecemos seu feedback."</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Bloco adicionado"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Bloco removido"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configurações."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"O app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está usando <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"O app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> usou <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recentemente"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresarial)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Chamada telefônica"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Chamada telefônica"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(pelo app <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"câmera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"localização"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensores desativados"</string>
<string name="device_services" msgid="1549944177856658705">"Serviços do dispositivo"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Sem título"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Toque para reiniciar o app e usar tela cheia."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mover"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navegação no sistema atualizada. Se quiser alterá-la, acesse as configurações."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Acesse as configurações para atualizar a navegação no sistema"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index f32453b9a4d8..3a069210bf5b 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Derulați captura de ecran"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Închideți captura de ecran"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Previzualizare a capturii de ecran"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Marginea superioară"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Marginea inferioară"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Recorder pentru ecran"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Se procesează înregistrarea"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificare în curs pentru o sesiune de înregistrare a ecranului"</string>
@@ -412,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Până la răsărit"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Activată la <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Până la <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Reduceți luminozitatea"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Serviciul NFC este dezactivat"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Serviciul NFC este activat"</string>
@@ -465,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Afișați profilul"</string>
<string name="user_add_user" msgid="4336657383006913022">"Adăugați un utilizator"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Utilizator nou"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Încheiați sesiunea pentru invitați?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Încheiați sesiunea pentru invitați"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ștergeți invitatul?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Încheiați sesiunea"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ștergeți"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Bine ați revenit în sesiunea pentru invitați!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Vreți să continuați sesiunea?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Începeți din nou"</string>
@@ -543,6 +547,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizația dvs. a instalat un certificat CA în profilul dvs. de serviciu. Traficul dvs. sigur de rețea poate fi monitorizat sau modificat."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Pe acest dispozitiv este instalat un certificat CA. Traficul dvs. sigur de rețea poate fi monitorizat sau modificat."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratorul dvs. a activat înregistrarea în jurnal pentru rețea, funcție ce monitorizează traficul de pe dispozitivul dvs."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administratorul a activat înregistrarea în jurnal pentru rețea, funcție ce monitorizează traficul în profilul dvs. de serviciu, dar nu și în profilul personal."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"V-ați conectat la aplicația <xliff:g id="VPN_APP">%1$s</xliff:g>, care vă poate monitoriza activitatea în rețea, inclusiv e-mailurile, aplicațiile și site-urile accesate."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"V-ați conectat la <xliff:g id="VPN_APP_0">%1$s</xliff:g> și la <xliff:g id="VPN_APP_1">%2$s</xliff:g>, care vă pot monitoriza activitatea în rețea, inclusiv e-mailurile, aplicațiile și site-urile accesate."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Profilul dvs. de serviciu este conectat la <xliff:g id="VPN_APP">%1$s</xliff:g>, care vă poate monitoriza activitatea în rețea, inclusiv e-mailurile, aplicațiile și site-urile accesate."</string>
@@ -736,7 +741,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Notificarea a fost &lt;b&gt;setată automat ca Silențioasă&lt;/b&gt; de sistem."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Notificarea a fost &lt;b&gt;clasificată automat mai sus&lt;/b&gt; în umbră."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Notificarea a fost &lt;b&gt;clasificată automat mai jos&lt;/b&gt; în umbră."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Este corect?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Trimiteți feedback dezvoltatorului. Este corect?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Mulțumim pentru feedback!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Opțiunile privind notificările pentru <xliff:g id="APP_NAME">%1$s</xliff:g> sunt afișate"</string>
@@ -885,6 +890,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mutați pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adăugați pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Cardul a fost adăugat"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Cardul a fost eliminat"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editorul pentru setări rapide."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificare <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Deschideți setările."</string>
@@ -975,7 +982,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> folosește <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> a folosit recent <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Apel telefonic"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(prin <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"cameră foto"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"locație"</string>
@@ -983,7 +990,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Senzori dezactivați"</string>
<string name="device_services" msgid="1549944177856658705">"Servicii pentru dispozitiv"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Fără titlu"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Atingeți ca să reporniți aplicația și să treceți în modul ecran complet."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mutați"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigarea în sistem a fost actualizată. Pentru a face modificări, accesați Setările."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Accesați Setările pentru a actualiza navigarea în sistem"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 40fb25865a7c..a81401a60de9 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Прокрутить скриншот"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Закрыть скриншот"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Предварительный просмотр скриншота"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Верхняя граница"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Нижняя граница"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Запись видео с экрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обработка записи с экрана…"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Текущее уведомление для записи видео с экрана"</string>
@@ -414,6 +416,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До рассвета"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Включить в <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Уменьшение яркости"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"Модуль NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Модуль NFC отключен"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Модуль NFC включен"</string>
@@ -467,9 +470,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Показать профиль."</string>
<string name="user_add_user" msgid="4336657383006913022">"Добавить пользователя"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Новый пользователь"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Завершить гостевой сеанс?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Завершить гостевой сеанс"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Удалить аккаунт гостя?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Завершить сеанс"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Удалить"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Рады видеть вас снова!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Продолжить сеанс?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Начать заново"</string>
@@ -546,6 +550,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ваша организация установила сертификат ЦС в рабочем профиле. Она может отслеживать и изменять защищенный сетевой трафик."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На устройстве установлен сертификат ЦС. Ваш защищенный сетевой трафик могут отслеживать и изменять."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Администратор включил ведение сетевого журнала, чтобы отслеживать трафик на вашем устройстве."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Администратор включил ведение сетевого журнала, чтобы отслеживать трафик в вашем рабочем профиле (информация из личного профиля не собирается)."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Запущено приложение \"<xliff:g id="VPN_APP">%1$s</xliff:g>\". Оно может отслеживать ваши действия в сети, включая работу с электронной почтой, приложениями и сайтами."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Запущены приложения \"<xliff:g id="VPN_APP_0">%1$s</xliff:g>\" и \"<xliff:g id="VPN_APP_1">%2$s</xliff:g>\". Они могут отслеживать ваши действия в сети, включая работу с электронной почтой, приложениями и сайтами."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"В рабочем профиле запущено приложение \"<xliff:g id="VPN_APP">%1$s</xliff:g>\", которое может отслеживать ваши действия в сети, включая работу с электронной почтой, приложениями и веб-сайтами."</string>
@@ -739,7 +744,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Уровень важности этого уведомления был автоматически &lt;/b&gt;понижен до \"Без звука\"&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Уровень важности этого уведомления на панели был автоматически &lt;b&gt;повышен&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Уровень важности этого уведомления на панели был автоматически &lt;b&gt;понижен&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Все верно?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Все верно? Поделитесь своим мнением с разработчиком."</string>
<string name="feedback_response" msgid="4671729244976641339">"Спасибо!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ОК"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Настройки уведомлений для приложения <xliff:g id="APP_NAME">%1$s</xliff:g> открыты"</string>
@@ -890,6 +895,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Переместить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Панель добавлена"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Панель удалена"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор быстрых настроек."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Уведомление <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Открыть настройки."</string>
@@ -980,7 +987,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Приложение \"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>\" использует другое (<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>)."</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Приложение \"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>\" недавно использовало другое (<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>)."</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(корпоративная версия)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Приложение для звонков"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Телефонный звонок"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(через приложение \"<xliff:g id="ATTRIBUTION">%s</xliff:g>\")"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камера"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"местоположение"</string>
@@ -988,7 +995,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Датчики отключены"</string>
<string name="device_services" msgid="1549944177856658705">"Сервисы устройства"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Без названия"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Нажмите, чтобы перезапустить приложение и перейти в полноэкранный режим."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Перенести"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Параметры навигации в системе обновлены. Чтобы изменить их, перейдите в настройки."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Чтобы обновить параметры навигации в системе, перейдите в настройки."</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index f3d04091f096..f62eae05e1c0 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"තිර රුව අනුචලනය කරන්න"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"තිර රුව ඉවත ලන්න"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"තිර රූ පෙර දසුන"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"ඉහළම මායිම"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"පහළම මායිම"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"තිර රෙකෝඩරය"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"තිර පටිගත කිරීම සකසමින්"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"තිර පටිගත කිරීමේ සැසියක් සඳහා කෙරෙන දැනුම් දීම"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"හිරු නගින තෙක්"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>ට ක්‍රියාත්මකයි"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> තෙක්"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"දීප්තිය අඩු කරන්න"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC අබලයි"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC සබලයි"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"පැතිකඩ පෙන්වන්න"</string>
<string name="user_add_user" msgid="4336657383006913022">"පරිශීලකයෙක් එක් කරන්න"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"නව පරිශීලකයා"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ආරාධිත සැසිය අවසන් කරන්නද?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"ආරාධිත සැසිය අවසන් කරන්න"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"අමුත්තාන් ඉවත් කරන්නද?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"සැසිය අවසන් කරන්න"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ඉවත් කරන්න"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"නැවත සාදරයෙන් පිළිගනිමු, අමුත්තා!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"ඔබගේ සැසිය දිගටම කරගෙන යෑමට ඔබට අවශ්‍යද?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"යළි මුල සිට අරඹන්න"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ඔබගේ සංවිධානය ඔබගේ කාර්යාල පැතිකඩ තුළ සහතික අධිකාරියක් ස්ථාපනය කර තිබේ. ඔබගේ ආරක්ෂක ජාල තදබදය නිරීක්ෂණය හෝ වෙනස් කිරීමට පුළුවනි."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"මෙම උපාංගය තුළ සහතික අධිකාරියක් ස්ථාපනය කර තිබේ. ඔබගේ ආරක්ෂක ජාල තදබදය නිරීක්ෂණය හෝ වෙනස් කිරීමට පුළුවනි."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"ඔබගේ පරිපාලක ඔබගේ උපාංගය මත තදබදය නිරීක්ෂණය කරන ජාල ලොග් කිරීම ක්‍රියාත්මක කර ඇත."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"ඔබගේ පරිපාලක ඔබගේ පුද්ගලික පැතිකඩෙහි නොව කාර්යාල පැතිකඩෙහි තදබදය නිරීක්ෂණය කරන, ජාල පිරීම ක්‍රියාත්මක කර ඇත."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"ඊ-තැපැල්, යෙදුම් සහ වෙබ් අඩවි ඇතුළු ඔබේ ජාල ක්‍රියාකාරකම් නිරීක්ෂණය කළ හැකි <xliff:g id="VPN_APP">%1$s</xliff:g>, වෙත ඔබ සම්බන්ධ වී ඇත."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"ඊ-තැපැල්, යෙදුම් සහ වෙබ් අඩවි ඇතුළු ඔබේ ජාල ක්‍රියාකාරකම් නිරීක්ෂණය කළ හැකි <xliff:g id="VPN_APP_0">%1$s</xliff:g> සහ <xliff:g id="VPN_APP_1">%2$s</xliff:g> වෙත ඔබ සම්බන්ධ වී ඇත."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"ඊ-තැපැල්, යෙදුම් සහ වෙබ් අඩවි ඇතුළු ඔබේ ජාල ක්‍රියාකාරකම් නිරීක්ෂණය කළ හැකි <xliff:g id="VPN_APP">%1$s</xliff:g>, වෙත ඔබේ කාර්යාල පැතිකඩ සම්බන්ධ වී ඇත."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"පද්ධතිය මගින් මෙම දැනුම්දීම ස්වයංක්‍රියව &lt;b&gt;නිශ්ශබ්ද වෙත පහත දමන ලදි&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"ඔබගේ වැස්ම තුළ මෙම දැනුම්දීම ස්වයංක්‍රියව &lt;b&gt;ඉහළට ශ්‍රේණිගත කරන ලදි&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"ඔබගේ වැස්ම තුළ මෙම දැනුම්දීම ස්වයංක්‍රියව &lt;b&gt;පහළට ශ්‍රේණිගත කරන ලදි&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"මෙය නිවැරදි වුයේද?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"ඔබගේ ප්‍රතිපෝෂණය සංවර්ධකට දන්වන්න. මෙය නිවැරදි වුයේද?"</string>
<string name="feedback_response" msgid="4671729244976641339">"ඔබේ ප්‍රතිපෝෂණයට ස්තූතියි!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"හරි"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා දැනුම්දීම් පාලන විවෘත කරන ලදී"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> වෙත ගෙන යන්න"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ස්ථානයට එක් කරන්න"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ස්ථානය <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ටයිල් එක එක් කරන ලදි"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ටයිල් ඉවත් කරන ලදි"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ඉක්මන් සැකසුම් සංස්කාරකය."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> දැනුම්දීම: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"සැකසීම් විවෘත කරන්න."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> භාවිත කරමින් ඇත"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> මෑතකදී <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> භාවිත කළේය"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ව්‍යවසාය)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"දුරකථන ඇමතුම"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"දුරකථන ඇමතුම"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> හරහා)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"කැමරාව"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ස්ථානය"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"සංවේදක ක්‍රියාවිරහිතයි"</string>
<string name="device_services" msgid="1549944177856658705">"උපාංග සේවා"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"මාතෘකාවක් නැත"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"මෙම යෙදුම යළි ඇරඹීමට සහ පූර්ණ තිරයට යාමට තට්ටු කරන්න"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ගෙන යන්න"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"පද්ධති සංචලනය යාවත්කාලීන කළා. වෙනස්කම් සිදු කිරීමට, සැකසීම් වෙත යන්න."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"පද්ධති සංචලනය යාවත්කාලීන කිරීමට සැකසීම් වෙත යන්න"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 7e3f3c010d68..927b2ac97a20 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Posúvať snímku obrazovky"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Zavrieť snímku obrazovky"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ukážka snímky obrazovky"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Horná hranica"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Dolná hranica"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Spracúva sa záznam obrazovky"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Zobrazuje sa upozornenie týkajúce sa relácie záznamu obrazovky"</string>
@@ -414,6 +416,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do východu slnka"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Zapne sa o <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Znížiť jas"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je deaktivované"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je aktivované"</string>
@@ -467,9 +470,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Zobraziť profil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Pridať používateľa"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Nový používateľ"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Chcete ukončiť reláciu hosťa?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Ukončiť reláciu hosťa"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Odstrániť hosťa?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Ukončiť reláciu"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstrániť"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Hosť, vitajte späť!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relácii pokračovať?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začať odznova"</string>
@@ -546,6 +550,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizácia nainštalovala pre váš pracovný profil certifikačnú autoritu. Zabezpečená sieťová premávka môže byť sledovaná či upravená."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"V tomto zariadení je nainštalovaná certifikačná autorita. Zabezpečená sieťová premávka môže byť sledovaná či upravená."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Správca aktivoval zapisovanie do denníka siete, ktoré sleduje premávku na vašom zariadení."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Správca aktivoval zapisovanie do denníka siete, ktoré sleduje premávku vo vašom pracovnom profile, ale nie osobnom."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Pripojili ste sa k aplikácii <xliff:g id="VPN_APP">%1$s</xliff:g>, ktorá môže sledovať vašu aktivitu v sieti, vrátane správ, aplikácií a webových stránok."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Pripojili ste sa k aplikáciám <xliff:g id="VPN_APP_0">%1$s</xliff:g> a <xliff:g id="VPN_APP_1">%2$s</xliff:g>, ktoré môžu sledovať vašu aktivitu v sieti, vrátane správ, aplikácií a webových stránok."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Váš pracovný profil je pripojený k aplikácii <xliff:g id="VPN_APP">%1$s</xliff:g>, ktorá môže sledovať vašu aktivitu v sieti vrátane správ, aplikácií a webových stránok."</string>
@@ -739,7 +744,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Systém toto upozornenie automaticky &lt;b&gt;preradil nižšie do kategórie Tiché&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Toto upozornenie bolo na vašom paneli automaticky &lt;b&gt;preradené vyššie&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Toto upozornenie bolo na vašom paneli automaticky &lt;b&gt;preradené nižšie&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Bolo toto správne?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Pošlite vývojárovi svoju spätnú väzbu. Bolo toto správne?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Ďakujeme za váš názor."</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Ovládanie upozornení pre aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> je otvorené"</string>
@@ -890,6 +895,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Presunúť na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridať na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. pozícia"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Karta bola pridaná"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Karta bola odstránená"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rýchlych nastavení"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Upozornenie <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvoriť nastavenia"</string>
@@ -980,7 +987,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> používa aplikáciu <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikácia <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> použila nedávno aplikáciu <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(podniková verzia)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonický hovor"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonický hovor"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(prostredníctvom aplikácie <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"fotoaparát"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"poloha"</string>
@@ -988,7 +995,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Senzory sú vypnuté"</string>
<string name="device_services" msgid="1549944177856658705">"Služby zariadenia"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Bez názvu"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Klepnutím reštartujete túto aplikáciu a prejdete do režimu celej obrazovky."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Presunúť"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigácia v systéme bola aktualizovaná. Ak chcete vykonať zmeny, prejdite do Nastavení."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Prejdite do Nastavení a aktualizujte navigáciu v systéme"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index b7ebea9ebb19..881ba91d013e 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Drseče pomikanje po posnetku zaslona"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Opusti posnetek zaslona"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Predogled posnetka zaslona"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Zgornji rob"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Spodnji rob"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snemalnik zaslona"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obdelava videoposnetka zaslona"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Nenehno obveščanje o seji snemanja zaslona"</string>
@@ -414,6 +416,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do sončnega vzhoda"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Vklop ob <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Zmanjšanje svetlosti"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Tehnologija NFC je onemogočena"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Tehnologija NFC je omogočena"</string>
@@ -444,7 +447,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Znova se dotaknite, da odprete"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Povlecite navzgor, da odprete"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Povlecite navzgor za vnovičen poskus"</string>
- <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Odklenite napravo, če želite uporabljati vmesnik NFC."</string>
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Odklenite napravo, če želite uporabljati NFC."</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Ta naprava pripada vaši organizaciji"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Ta naprava pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="phone_hint" msgid="6682125338461375925">"Povlecite z ikone za telefon"</string>
@@ -467,9 +470,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Prikaz profila"</string>
<string name="user_add_user" msgid="4336657383006913022">"Dodajanje uporabnika"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Nov uporabnik"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Želite končati sejo gosta?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Končaj sejo gosta"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite odstraniti gosta?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Končaj sejo"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstrani"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Znova pozdravljeni, gost!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite nadaljevati sejo?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začni znova"</string>
@@ -546,6 +550,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Vaša organizacija je v vaš delovni profil namestila overitelja potrdil. Varni omrežni promet se lahko nadzira ali spreminja."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"V tej napravi je nameščen overitelj potrdil. Varni omrežni promet se lahko nadzira ali spreminja."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Skrbnik je vklopil beleženje omrežnega prometa, ki nadzira promet v napravi."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Skrbnik je vklopil beleženje omrežnega prometa, ki nadzoruje samo promet v delovnem profilu, tistega v osebnem profilu pa ne."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Povezani ste z aplikacijo <xliff:g id="VPN_APP">%1$s</xliff:g>, ki lahko nadzira omrežno dejavnost, vključno z e-pošto, aplikacijami in spletnimi mesti."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Povezani ste z aplikacijama <xliff:g id="VPN_APP_0">%1$s</xliff:g> in <xliff:g id="VPN_APP_1">%2$s</xliff:g>, ki lahko nadzirata omrežno dejavnost, vključno z e-pošto, aplikacijami in spletnimi mesti."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Delovni profil je povezan z aplikacijo <xliff:g id="VPN_APP">%1$s</xliff:g>, ki lahko nadzira omrežno dejavnost, vključno z e-pošto, aplikacijami in spletnimi mesti."</string>
@@ -739,7 +744,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Sistem je to obvestilo samodejno &lt;b&gt;uvrstil nižje – med obvestila brez zvoka&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"To obvestilo je bilo samodejno &lt;b&gt;uvrščeno višje&lt;/b&gt; na zaslonu z obvestili."</string>
<string name="feedback_demoted" msgid="951884763467110604">"To obvestilo je bilo samodejno &lt;b&gt;uvrščeno nižje&lt;/b&gt; na zaslonu z obvestili."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Je bilo to prav?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Posredujte povratne informacije razvijalcu. Je bilo to prav?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Hvala za povratne informacije."</string>
<string name="feedback_ok" msgid="6481426753298857144">"V redu"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kontrolniki obvestil za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g> so odprti"</string>
@@ -890,6 +895,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premik na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajanje na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Ploščica je bila dodana"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Ploščica je bila odstranjena"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Urejevalnik hitrih nastavitev."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Obvestilo za <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Odpri nastavitve."</string>
@@ -980,7 +987,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> uporablja: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> je nedavno uporabila: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(za podjetja)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonski klici"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonski klic"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(prek aplikacije <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"fotoaparat"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokacijo"</string>
@@ -988,7 +995,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Izklop za tipala"</string>
<string name="device_services" msgid="1549944177856658705">"Storitve naprave"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Brez naslova"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Dotaknite se za vnovični zagon te aplikacije in preklop v celozaslonski način."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Premakni"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Krmarjenje po sistemu je posodobljeno. Če želite opraviti spremembe, odprite nastavitve."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Če želite posodobiti krmarjenje po sistemu, odprite nastavitve"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index fede55cd86d8..29d16617571d 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Lëviz në pamjen e ekranit"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Hiq pamjen e ekranit"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pamja paraprake e imazhit"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Kufiri i sipërm"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Kufiri i poshtëm"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Regjistruesi i ekranit"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Regjistrimi i ekranit po përpunohet"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Njoftim i vazhdueshëm për një seancë regjistrimi të ekranit"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Deri në lindje të diellit"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktiv në <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Deri në <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Redukto ndriçimin"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC është çaktivizuar"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC është aktivizuar"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Shfaq profilin"</string>
<string name="user_add_user" msgid="4336657383006913022">"Shto përdorues"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Përdorues i ri"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Dëshiron t\'i japësh fund sesionit të vizitorit?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Jepi fund sesionit të vizitorit"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Të hiqet i ftuari?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Të gjitha aplikacionet dhe të dhënat në këtë sesion do të fshihen."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Jepi fund sesionit"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Hiq"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Mirë se erdhe, i ftuar!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Dëshiron ta vazhdosh sesionin tënd?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Fillo nga e para"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizata jote instaloi një autoritet certifikate në profilin tënd të punës. Trafiku i rrjetit tënd të sigurt mund të monitorohet ose modifikohet."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Në këtë pajisje është instaluar një autoritet certifikate. Trafiku i rrjetit tënd të sigurt mund të monitorohet ose modifikohet."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratori ka aktivizuar regjistrimin e rrjetit, i cili monitoron trafikun në pajisjen tënde."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administratori yt ka aktivizuar regjistrimin e rrjetit, i cili monitoron trafikun në profilin tënd të punës, por jo në profilin tënd personal."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Je lidhur me aplikacionin <xliff:g id="VPN_APP">%1$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd në rrjet, duke përfshirë mail-et, aplikacionet dhe sajtet e uebit."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Je lidhur me aplikacionet <xliff:g id="VPN_APP_0">%1$s</xliff:g> dhe <xliff:g id="VPN_APP_1">%2$s</xliff:g>, të cilat mund të monitorojnë aktivitetin tënd në rrjet, duke përfshirë mail-et, aplikacionet dhe sajtet e uebit."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Profili yt i punës është i lidhur me <xliff:g id="VPN_APP">%1$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd në rrjet, duke përfshirë mail-et, aplikacionet dhe sajtet e uebit."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Ky njoftim është &lt;b&gt;ulur automatikisht në nivel si në heshtje&lt;/b&gt; nga sistemi."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Ky njoftim është &lt;b&gt;renditur automatikisht më lart&lt;/b&gt; në strehën e njoftimeve."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Ky njoftim është &lt;b&gt;renditur automatikisht më poshtë&lt;/b&gt; në strehën e njoftimeve."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"A ishte e saktë kjo?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Tregoji zhvilluesit komentet e tua. A ishte e saktë kjo?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Faleminderit për komentin!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"Në rregull"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kontrollet e njoftimeve për <xliff:g id="APP_NAME">%1$s</xliff:g> janë hapur"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Zhvendos te <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Shto te pozicioni <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozicioni <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Pllakëza u shtua"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Pllakëza u hoq"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redaktori i cilësimeve të shpejta."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Njoftim nga <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Hap cilësimet."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> po përdor <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ka përdorur <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> së fundi"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ndërmarrje)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonata"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(nëpërmjet <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamerën"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"vendndodhjen"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensorët joaktivë"</string>
<string name="device_services" msgid="1549944177856658705">"Shërbimet e pajisjes"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Pa titull"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Trokit për ta rinisur këtë aplikacion dhe për të kaluar në ekranin e plotë."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Zhvendos"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigimi i sistemit u përditësua. Për të bërë ndryshime, shko te \"Cilësimet\"."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Shko te \"Cilësimet\" për të përditësuar navigimin e sistemit"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 0d822464611a..c021bfa8502c 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Померајте снимак екрана"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Одбаците снимак екрана"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Преглед снимка екрана"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Горња граница"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Доња граница"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Снимач екрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обрађујемо видео снимка екрана"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Обавештење о сесији снимања екрана је активно"</string>
@@ -412,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До изласка сунца"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Укључује се у <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Смањи осветљеност"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC је онемогућен"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC је омогућен"</string>
@@ -465,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Прикажи профил"</string>
<string name="user_add_user" msgid="4336657383006913022">"Додај корисника"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Нови корисник"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Желите да завршите сесију госта?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Заврши сесију госта"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Желите ли да уклоните госта?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Заврши сесију"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Уклони"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Добро дошли назад, госте!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Желите ли да наставите сесију?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни из почетка"</string>
@@ -543,6 +547,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Организација је на пословном профилу инсталирала ауторитет за издавање сертификата. Безбедни мрежни саобраћај може да се прати или мења."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На овом уређају је инсталиран ауторитет за издавање сертификата. Безбедни мрежни саобраћај може да се прати или мења."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Администратор је укључио евидентирање мреже, које прати саобраћај на уређају."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Администратор је укључио евидентирање мреже, које прати саобраћај на пословном профилу, али не и на личном профилу."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Повезани сте са апликацијом <xliff:g id="VPN_APP">%1$s</xliff:g>, која може да надгледа активности на мрежи, укључујући имејлове, апликације и веб-сајтове."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Повезани сте са апликацијама <xliff:g id="VPN_APP_0">%1$s</xliff:g> и <xliff:g id="VPN_APP_1">%2$s</xliff:g>, које могу да надгледају активности на мрежи, укључујући имејлове, апликације и веб-сајтове."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Пословни профил је повезан са апликацијом <xliff:g id="VPN_APP">%1$s</xliff:g>, која може да надгледа активности на мрежи, укључујући имејлове, апликације и веб-сајтове."</string>
@@ -736,7 +741,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Систем је ово обавештење аутоматски &lt;b&gt;деградирао у Нечујно&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Ово обавештење је аутоматски &lt;b&gt;рангирано више&lt;/b&gt; на траци са обавештењима."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Ово обавештење је аутоматски &lt;b&gt;рангирано ниже&lt;/b&gt; на траци са обавештењима."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Да ли је то тачно?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Пошаљите програмеру повратне информације. Да ли је то тачно?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Хвала вам на повратним информацијама!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"Потврди"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Контроле обавештења за отварање апликације <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -885,6 +890,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместите на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додајте на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. позиција"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Плочица је додата"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Плочица је уклоњена"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уређивач за Брза подешавања."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Обавештења за <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отвори Подешавања."</string>
@@ -975,7 +982,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Апликација <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> користи: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Апликација <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> је недавно користила: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(за предузећа)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Телефонски позив"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Телефонски позив"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(преко: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камеру"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"локацију"</string>
@@ -983,7 +990,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Сензори су искључени"</string>
<string name="device_services" msgid="1549944177856658705">"Услуге за уређаје"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Без наслова"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Додирните да бисте рестартовали апликацију и прешли у режим целог екрана."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Премести"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Навигација система је ажурирана. Да бисте унели измене, идите у Подешавања."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Идите у Подешавања да бисте ажурирали навигацију система"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 969bdcd7eb17..e38d7cd0a7c3 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Rullande skärmbild"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Stäng skärmbild"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Förhandsgranskning av skärmbild"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Övre gräns"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Nedre gräns"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skärminspelare"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandlar skärminspelning"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Avisering om att skärminspelning pågår"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Till soluppgången"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktivera kl. <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Till <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Minska ljusstyrkan"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC är inaktiverat"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC är aktiverat"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Visa profil"</string>
<string name="user_add_user" msgid="4336657383006913022">"Lägg till användare"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Ny användare"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vill du avsluta gästsessionen?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Avsluta gästsession"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vill du ta bort gästen?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Avsluta session"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ta bort"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Välkommen tillbaka gäst!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Vill du fortsätta sessionen?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Börja om"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organisationen har installerat en certifikatutfärdare i jobbprofilen. Din säkra nätverkstrafik kan övervakas och ändras."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"En certifikatutfärdare är installerad på enheten. Din säkra nätverkstrafik kan övervakas och ändras."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratören har aktiverat nätverksloggning som övervakar trafik på enheten."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administratören har aktiverat nätverksloggning som övervakar trafik i jobbprofilen men inte den privata profilen."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Du är ansluten till <xliff:g id="VPN_APP">%1$s</xliff:g> som kan övervaka din nätverksaktivitet, inklusive e-postmeddelanden, appar och webbplatser."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Du är ansluten till <xliff:g id="VPN_APP_0">%1$s</xliff:g> och <xliff:g id="VPN_APP_1">%2$s</xliff:g> som kan övervaka din nätverksaktivitet, inklusive e-postmeddelanden, appar och webbplatser."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Jobbprofilen är ansluten till <xliff:g id="VPN_APP">%1$s</xliff:g> som kan övervaka din nätverksaktivitet, exempelvis e-post, appar och webbplatser."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Aviseringens relevans &lt;b&gt;ändrades till Tyst&lt;/b&gt; automatiskt av systemet."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Aviseringens relevans i meddelandepanelen &lt;b&gt;höjdes&lt;/b&gt; automatiskt."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Aviseringens relevans i meddelandepanelen &lt;b&gt;sänktes&lt;/b&gt; automatiskt."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Stämmer detta?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Ge utvecklaren din feedback. Stämmer detta?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Tack för din feedback!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Aviseringsinställningarna för <xliff:g id="APP_NAME">%1$s</xliff:g> är öppna"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flytta till <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lägg till på position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kortet har lagts till"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kortet har tagits bort"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigerare för snabbinställningar."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-avisering: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Öppna inställningarna."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> använder <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> använde <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> nyligen"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(företag)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonsamtal"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonsamtal"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(genom <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"plats"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensorer har inaktiverats"</string>
<string name="device_services" msgid="1549944177856658705">"Enhetstjänster"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Ingen titel"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Tryck för att starta om appen i helskärmsläge."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Flytta"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Systemnavigeringen har uppdaterats. Öppna inställningarna om du vill ändra något."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Öppna inställningarna och uppdatera systemnavigeringen"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 4055e8da981d..a775d1426d97 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Fanya picha ya skrini iwe ndefu"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ondoa picha ya skrini"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Onyesho la kukagua picha ya skrini"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Mpaka wa sehemu ya juu"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Mpaka wa sehemu ya chini"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Kinasa Skrini"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Inachakata rekodi ya skrini"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Arifa inayoendelea ya kipindi cha kurekodi skrini"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hadi macheo"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Itawashwa saa <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hadi saa <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Punguza Ung\'aavu"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC imezimwa"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC imewashwa"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Onyesha wasifu"</string>
<string name="user_add_user" msgid="4336657383006913022">"Ongeza mtumiaji"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Mtumiaji mpya"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Ungependa kumaliza kipindi cha mgeni?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Maliza kipindi cha mgeni"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ungependa kumwondoa mgeni?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Maliza kipindi"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ondoa"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Karibu tena, mwalikwa!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Je, unataka kuendelea na kipindi chako?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Anza tena"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Shirika lako limesakinisha mamlaka ya cheti katika wasifu wako wa kazini. Huenda shughuli kwenye mtandao wako salama zikafuatiliwa au kubadilishwa."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Mamlaka ya cheti imesakinishwa kwenye kifaa hiki. Huenda shughuli kwenye mtandao wako salama zikafuatiliwa au kubadilishwa."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Msimamizi wako amewasha kumbukumbu ya kuingia mtandaoni, ambayo hufuatilia shughuli kwenye kifaa chako."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Msimamizi wako amewasha kumbukumbu ya kuingia mtandaoni ambayo hufuatilia shughuli kwenye wasifu wako wa kazini ila si kwenye wasifu wako wa binafsi."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Umeunganishwa kwenye <xliff:g id="VPN_APP">%1$s</xliff:g>, ambayo inaweza kufuatilia shughuli za mtandao wako, ikiwa ni pamoja na barua pepe, programu na tovuti."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Umeunganishwa kwenye <xliff:g id="VPN_APP_0">%1$s</xliff:g> na <xliff:g id="VPN_APP_1">%2$s</xliff:g>, ambazo zinaweza kufuatilia shughuli za mtandao wako, ikiwa ni pamoja na barua pepe, programu na tovuti."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Wasifu wako wa kazini umeunganishwa kwenye <xliff:g id="VPN_APP">%1$s</xliff:g>, ambayo inaweza kufuatilia shughuli za mtandao wako, ikiwa ni pamoja na barua pepe, programu na tovuti."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Arifa hii &lt;b&gt;imeshushwa hadhi kiotomatiki na mfumo kuwa Kimya&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Arifa hii &lt;b&gt;imeorodheshwa kiotomatiki katika nafasi ya juu&lt;/b&gt; katika kiwango chako."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Arifa hii &lt;b&gt;imeorodheshwa kiotomatiki katika nafasi ya chini&lt;/b&gt; katika kiwango chako."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Je, hatua hii ilikuwa sahihi?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Mpe msanidi programu maoni yako. Je, hatua hii ilikuwa sahihi?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Asante kwa maoni yako!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"Sawa"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Vidhibiti vya arifa <xliff:g id="APP_NAME">%1$s</xliff:g> vimefunguliwa"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hamishia kwenye <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ongeza kwenye nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kigae kimewekwa"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kigae kimeondolewa"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kihariri cha Mipangilio ya haraka."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Arifa kutoka <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Fungua mipangilio."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> inatumia <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ilitumia <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> hivi majuzi"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(biashara)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Simu"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Simu"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(kupitia <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"mahali"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Umezima vitambuzi"</string>
<string name="device_services" msgid="1549944177856658705">"Huduma za Kifaa"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Wimbo hauna jina"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Gusa ili uzime na uwashe upya programu hii kisha nenda kwenye skrini nzima."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Sogeza"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Umesasisha usogezaji kwenye mfumo. Ili ubadilishe, nenda kwenye Mipangilio."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Nenda kwenye mipangilio ili usasishe usogezaji kwenye mfumo"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
index 02bd60210e81..ee2b82dca811 100644
--- a/packages/SystemUI/res/values-sw600dp/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp/styles.xml
@@ -23,13 +23,6 @@
<item name="numColumns">4</item>
</style>
- <style name="TextAppearance.StatusBar.Expanded.UserSwitcher">
- <item name="android:textSize">@dimen/kg_user_switcher_text_size</item>
- <item name="android:textStyle">normal</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">?attr/wallpaperTextColor</item>
- </style>
-
<style name="TextAppearance.QS.UserSwitcher">
<item name="android:textSize">@dimen/kg_user_switcher_text_size</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index cbac87e6390a..335a529bb05b 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"ஸ்கிரீன்ஷாட்டைப் பெரிதாக்கும்"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"ஸ்கிரீன்ஷாட்டை நிராகரிக்கும்"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ஸ்கிரீன்ஷாட்டின் மாதிரிக்காட்சி"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"மேற்புற எல்லை"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"கீழ்ப்புற எல்லை"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ஸ்கிரீன் ரெக்கார்டர்"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ஸ்க்ரீன் ரெக்கார்டிங் செயலாக்கப்படுகிறது"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"திரை ரெக்கார்டிங் அமர்விற்கான தொடர் அறிவிப்பு"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"காலை வரை"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>க்கு ஆன் செய்"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> வரை"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"ஒளிர்வைக் குறை"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC முடக்கப்பட்டது"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC இயக்கப்பட்டது"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"சுயவிவரத்தைக் காட்டு"</string>
<string name="user_add_user" msgid="4336657383006913022">"பயனரைச் சேர்"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"புதியவர்"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"விருந்தினர் அமர்வை நிறைவுசெய்யவா?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"விருந்தினர் அமர்வை நிறைவுசெய்"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"கெஸ்ட்டை அகற்றவா?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"இந்த அமர்வின் எல்லா பயன்பாடுகளும், தரவும் நீக்கப்படும்."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"அமர்வை நிறைவுசெய்"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"அகற்று"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"நல்வரவு!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"உங்கள் அமர்வைத் தொடர விருப்பமா?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"மீண்டும் தொடங்கு"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"உங்கள் நிறுவனம், பணிக் கணக்கில் சான்றிதழ் அங்கீகாரத்தை நிறுவியுள்ளது. உங்களின் பாதுகாப்பான நெட்வொர்க் ட்ராஃபிக் கண்காணிக்கப்படலாம் அல்லது மாற்றப்படலாம்."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"இந்தச் சாதனத்தில் சான்றிதழ் அங்கீகாரம் நிறுவப்பட்டுள்ளது. உங்களின் பாதுகாப்பான நெட்வொர்க் ட்ராஃபிக் கண்காணிக்கப்படலாம் அல்லது மாற்றப்படலாம்."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"உங்கள் நிர்வாகி, நெட்வொர்க் பதிவெடுத்தலை இயக்கியுள்ளார். இது சாதனத்தில் ட்ராஃபிக்கைக் கண்காணிக்கும்."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"உங்கள் நிர்வாகி \'நெட்வொர்க் பதிவெடுத்தலை\' இயக்கியுள்ளார், இது உங்கள் பணிக் கணக்கில் டிராஃபிக்கைக் கண்காணிக்கும். ஆனால் தனிப்பட்ட கணக்கில் கண்காணிக்காது."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"மின்னஞ்சல்கள், ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="VPN_APP">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள்."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"மின்னஞ்சல்கள், ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="VPN_APP_0">%1$s</xliff:g> மற்றும் <xliff:g id="VPN_APP_1">%2$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள்."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"மின்னஞ்சல்கள், ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="VPN_APP">%1$s</xliff:g> உடன் உங்கள் பணிக் கணக்கு இணைக்கப்பட்டுள்ளது."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"சிஸ்டத்தால் தானாகவே இந்த அறிவிப்பு &lt;b&gt;நிசப்த நிலைக்குக் குறைத்து அமைக்கப்பட்டது&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"அறிவிப்பு விவரத்தில் தானாகவே இந்த அறிவிப்பின் &lt;b&gt;முக்கியத்துவம் உயர்த்தப்பட்டது&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"அறிவிப்பு விவரத்தில் தானாகவே இந்த அறிவிப்பின் &lt;b&gt;முக்கியத்துவம் குறைக்கப்பட்டது&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"இது சரியானதா?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"உங்கள் கருத்தை டெவெலப்பருக்குத் தெரியப்படுத்துங்கள். இது சரியானதா?"</string>
<string name="feedback_response" msgid="4671729244976641339">"உங்கள் கருத்துக்கு நன்றி!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"சரி"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g>க்கான அறிவிப்புக் கட்டுப்பாடுகள் திறக்கப்பட்டன"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>க்கு நகர்த்தும்"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g>ல் சேர்க்கும்"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"இடம்: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"கட்டம் சேர்க்கப்பட்டது"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"கட்டம் அகற்றப்பட்டது"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"விரைவு அமைப்புகள் திருத்தி."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> அறிவிப்பு: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"அமைப்புகளைத் திற."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ஆப்ஸ் <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> பயன்படுத்துகிறது"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"சமீபத்தில் <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ஆப்ஸ் <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> பயன்படுத்தியுள்ளது"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(நிறுவனப் பதிப்பு)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"மொபைல் அழைப்பு"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"மொபைல் அழைப்பு"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> மூலம்)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"கேமரா"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"இருப்பிடம்"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"சென்சார்களை ஆஃப் செய்தல்"</string>
<string name="device_services" msgid="1549944177856658705">"சாதன சேவைகள்"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"தலைப்பு இல்லை"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கலாம், முழுத்திரையில் பார்க்கலாம்."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"நகர்த்து"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"சிஸ்டம் நேவிகேஷன் மாற்றப்பட்டது. மாற்றங்களைச் செய்ய ‘அமைப்புகளுக்குச்’ செல்லவும்."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"சிஸ்டம் நேவிகேஷனை மாற்ற ’அமைப்புகளுக்குச்’ செல்லவும்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 45ef33900ccd..8013d6ef94d0 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -93,6 +93,10 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"స్క్రీన్‌షాట్‌కు స్క్రోల్ చేయండి"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"స్క్రీన్‌షాట్‌ను విస్మరించు"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"స్క్రీన్‌షాట్ ప్రివ్యూ"</string>
+ <!-- no translation found for screenshot_top_boundary (1500569103321300856) -->
+ <skip />
+ <!-- no translation found for screenshot_bottom_boundary (5657242629526407311) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"స్క్రీన్ రికార్డర్"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"స్క్రీన్ రికార్డింగ్ అవుతోంది"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"స్క్రీన్ రికార్డ్ సెషన్ కోసం ఆన్‌గోయింగ్ నోటిఫికేషన్"</string>
@@ -410,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"సూర్యోదయం వరకు"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> కు ఆన్ అవుతుంది"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> వరకు"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"ప్రకాశాన్ని తగ్గించండి"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC నిలిపివేయబడింది"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ప్రారంభించబడింది"</string>
@@ -440,7 +445,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"తెరవడానికి మళ్లీ నొక్కండి"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"తెరవడానికి, పైకి స్వైప్ చేయండి"</string>
<string name="keyguard_retry" msgid="886802522584053523">"మళ్ళీ ప్రయత్నించడానికి పైకి స్వైప్ చేయండి"</string>
- <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFCను ఉపయోగించడానికి అన్‌లాక్ చేయండి"</string>
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFCని ఉపయోగించడానికి అన్‌లాక్ చేయండి"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"ఈ పరికరం మీ సంస్థకు చెందినది"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>కు చెందినది"</string>
<string name="phone_hint" msgid="6682125338461375925">"ఫోన్ కోసం చిహ్నాన్ని స్వైప్ చేయండి"</string>
@@ -463,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ప్రొఫైల్‌ని చూపు"</string>
<string name="user_add_user" msgid="4336657383006913022">"వినియోగదారుని జోడించండి"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"కొత్త వినియోగదారు"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"గెస్ట్ సెషన్‌ను ముగించాలా?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"గెస్ట్ సెషన్‌ను ముగించు"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"అతిథిని తీసివేయాలా?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్‌లోని అన్ని యాప్‌లు మరియు డేటా తొలగించబడతాయి."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"సెషన్‌ను ముగించు"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"తీసివేయి"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"పునఃస్వాగతం, అతిథి!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"మీరు మీ సెషన్‌ని కొనసాగించాలనుకుంటున్నారా?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"మొదటి నుండి ప్రారంభించు"</string>
@@ -540,6 +546,8 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"మీ కార్యాలయ ప్రొఫైల్‌లో మీ సంస్థ ఒక ప్రమాణపత్ర అధికారాన్ని ఇన్‌స్టాల్ చేసింది. మీ సురక్షిత నెట్‌వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ఈ పరికరంలో ప్రమాణపత్ర అధికారం ఇన్‌స్టాల్ చేయబడింది. మీ సురక్షిత నెట్‌వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్‌ని పర్యవేక్షించగల నెట్‌వర్క్ లాగింగ్‌ని ఆన్ చేసారు."</string>
+ <!-- no translation found for monitoring_description_managed_profile_network_logging (6932303843097006037) -->
+ <skip />
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"మీరు <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"మీరు ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP_0">%1$s</xliff:g> మరియు <xliff:g id="VPN_APP_1">%2$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"మీ కార్యాలయ ప్రొఫైల్ ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
@@ -733,7 +741,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"ఈ నోటిఫికేషన్, సిస్టమ్ ద్వారా ఆటోమేటిక్‌గా &lt;b&gt;నిశ్శబ్దం స్థాయికి తగ్గించబడింది&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"మీ నోటిఫికేషన్ షేడ్‌లో ఈ నోటిఫికేషన్ ఆటోమేటిక్‌గా &lt;b&gt;ఎక్కువ ర్యాంక్‌&lt;/b&gt;కు సర్దుబాటు చేయబడింది."</string>
<string name="feedback_demoted" msgid="951884763467110604">"మీ నోటిఫికేషన్ షేడ్‌లో ఈ నోటిఫికేషన్ ఆటోమేటిక్‌గా &lt;b&gt;తక్కువ ర్యాంక్‌&lt;/b&gt;కు సర్దుబాటు చేయబడింది."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"ఇది సరైనదేనా?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"మీ ఫీడ్‌బ్యాక్‌ను డెవలపర్‌కు తెలియజేయండి. ఇది సరైనదేనా?"</string>
<string name="feedback_response" msgid="4671729244976641339">"మీ ఫీడ్‌బ్యాక్‌ను అందించినందుకు ధన్యవాదాలు!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"సరే"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> యొక్క నోటిఫికేషన్ నియంత్రణలు తెరవబడ్డాయి"</string>
@@ -880,6 +888,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>కు తరలించండి"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> స్థానానికి జోడించండి"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"స్థానం <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"టైల్ జోడించబడింది"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"టైల్ తీసివేయబడింది"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"శీఘ్ర సెట్టింగ్‌ల ఎడిటర్."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> నోటిఫికేషన్: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"సెట్టింగ్‌లను తెరవండి."</string>
@@ -967,23 +977,17 @@
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"అప్లికేషన్‌లు మీ <xliff:g id="TYPES_LIST">%s</xliff:g>ని ఉపయోగిస్తున్నాయి."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
<string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" మరియు "</string>
- <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) -->
- <skip />
- <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) -->
- <skip />
+ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>ను ఉపయోగిస్తోంది"</string>
+ <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, ఇటీవల <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>ను ఉపయోగించింది"</string>
+ <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ఎంటర్‌ప్రైజ్)"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ఫోన్ కాల్"</string>
+ <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> ద్వారా)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"కెమెరా"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"లొకేషన్"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"మైక్రోఫోన్"</string>
<string name="sensor_privacy_mode" msgid="4462866919026513692">"సెన్సార్‌లు ఆఫ్"</string>
<string name="device_services" msgid="1549944177856658705">"పరికర సేవలు"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"శీర్షిక లేదు"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"ఈ యాప్‌ను పునఃప్రారంభించేలా నొక్కి, ఆపై పూర్తి స్క్రీన్‌‍లోకి వెళ్లండి."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"తరలించు"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"సిస్టమ్ నావిగేషన్ అప్‌డేట్ చేయబడింది. మార్పులు చేయడానికి, సెట్టింగ్‌లకు వెళ్లండి."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"సిస్టమ్ నావిగేషన్‌ను అప్‌డేట్ చేయడానికి సెట్టింగ్‌లకు వెళ్లండి"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 8b197acb1958..c55b7f074221 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"เลื่อนจับภาพหน้าจอ"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"ปิดภาพหน้าจอ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ตัวอย่างภาพหน้าจอ"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"ขอบเขตด้านบน"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"ขอบเขตด้านล่าง"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"โปรแกรมบันทึกหน้าจอ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"กำลังประมวลผลการอัดหน้าจอ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการบันทึกหน้าจอ"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"จนพระอาทิตย์ขึ้น"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"เปิดเวลา <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"จนถึง <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"ลดความสว่าง"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ถูกปิดใช้งาน"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"เปิดใช้งาน NFC แล้ว"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"แสดงโปรไฟล์"</string>
<string name="user_add_user" msgid="4336657383006913022">"เพิ่มผู้ใช้"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"ผู้ใช้ใหม่"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"จบเซสชันผู้เยี่ยมชมใช่ไหม"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"จบเซสชันผู้เยี่ยมชม"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ต้องการนำผู้เข้าร่วมออกไหม"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"จบเซสชัน"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"นำออก"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"ยินดีต้อนรับท่านผู้เยี่ยมชมกลับมาอีกครั้ง!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"คุณต้องการอยู่ในเซสชันต่อไปไหม"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"เริ่มต้นใหม่"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"องค์กรของคุณติดตั้งผู้ออกใบรับรองในโปรไฟล์งาน อาจมีการตรวจสอบหรือแก้ไขการจราจรของข้อมูลในเครือข่ายที่ปลอดภัยของคุณ"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"มีการติดตั้งผู้ออกใบรับรองในอุปกรณ์นี้ อาจมีการตรวจสอบหรือแก้ไขการจราจรของข้อมูลในเครือข่ายที่ปลอดภัยของคุณ"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"ผู้ดูแลระบบได้เปิดการบันทึกเครือข่าย ซึ่งจะตรวจสอบการจราจรของข้อมูลในอุปกรณ์ของคุณ"</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"ผู้ดูแลระบบได้เปิดการบันทึกเครือข่าย ซึ่งจะตรวจสอบการรับส่งข้อมูลในโปรไฟล์งาน แต่ไม่ตรวจสอบในโปรไฟล์ส่วนตัวของคุณ"</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"คุณเชื่อมต่ออยู่กับ <xliff:g id="VPN_APP">%1$s</xliff:g> ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่ายของคุณ รวมถึงอีเมล แอป และเว็บไซต์"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"คุณเชื่อมต่ออยู่กับ <xliff:g id="VPN_APP_0">%1$s</xliff:g> และ <xliff:g id="VPN_APP_1">%2$s</xliff:g> ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่ายของคุณ รวมถึงอีเมล แอป และเว็บไซต์"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"โปรไฟล์งานของคุณเชื่อมต่ออยู่กับ <xliff:g id="VPN_APP">%1$s</xliff:g> ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่ายของคุณ รวมถึงอีเมล แอป และเว็บไซต์"</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"ระบบ&lt;b&gt;ลดระดับการแจ้งเตือนนี้เป็นปิดเสียง&lt;/b&gt;โดยอัตโนมัติ"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"การแจ้งเตือนนี้&lt;b&gt;มีอันดับสูงขึ้น&lt;/b&gt;ในหน้าต่างแจ้งเตือนโดยอัตโนมัติ"</string>
<string name="feedback_demoted" msgid="951884763467110604">"การแจ้งเตือนนี้&lt;b&gt;มีอันดับต่ำลง&lt;/b&gt;ในหน้าต่างแจ้งเตือนโดยอัตโนมัติ"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"การดำเนินการนี้ถูกต้องไหม"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"บอกความคิดเห็นให้นักพัฒนาแอปทราบ ถูกต้องไหม"</string>
<string name="feedback_response" msgid="4671729244976641339">"ขอบคุณที่แสดงความคิดเห็น"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ตกลง"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"ส่วนควบคุมการแจ้งเตือนของ <xliff:g id="APP_NAME">%1$s</xliff:g> เปิดอยู่"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ย้ายไปที่ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"เพิ่มไปยังตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"เพิ่มชิ้นส่วนแล้ว"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"นำชิ้นส่วนออกแล้ว"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ตัวแก้ไขการตั้งค่าด่วน"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> การแจ้งเตือน: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"เปิดการตั้งค่า"</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> กำลังใช้<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ใช้<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>เมื่อเร็วๆ นี้"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(องค์กร)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"โทรศัพท์"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"การโทร"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(ผ่านทาง <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"กล้องถ่ายรูป"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"ตำแหน่ง"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"ปิดเซ็นเซอร์"</string>
<string name="device_services" msgid="1549944177856658705">"บริการของอุปกรณ์"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"ไม่มีชื่อ"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"แตะเพื่อรีสตาร์ทแอปนี้และแสดงแบบเต็มหน้าจอ"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ย้าย"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"อัปเดตการไปยังส่วนต่างๆ ของระบบแล้ว หากต้องการเปลี่ยนแปลง ให้ไปที่การตั้งค่า"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ไปที่การตั้งค่าเพื่ออัปเดตการไปยังส่วนต่างๆ ของระบบ"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index a4a768bdc959..33bd6593e08c 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"I-scroll ang screenshot"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"I-dismiss ang screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Preview ng screenshot"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Pinakamataas na limitasyon"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Pinakamababang limitasyon"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Recorder ng Screen"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Pinoproseso screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Kasalukuyang notification para sa session ng pag-record ng screen"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hanggang sunrise"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ma-o-on nang <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hanggang <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Bawasan ang Liwanag"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Naka-disable ang NFC"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Naka-enable ang NFC"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Ipakita ang profile"</string>
<string name="user_add_user" msgid="4336657383006913022">"Magdagdag ng user"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Bagong user"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Tapusin ang session ng bisita?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Tapusin ang session ng bisita"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Alisin ang bisita?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Tapusin ang session"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Alisin"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Maligayang pagbabalik, bisita!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Gusto mo bang ipagpatuloy ang iyong session?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Magsimulang muli"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Nag-install ang iyong organisasyon ng awtoridad sa certificate sa iyong profile sa trabaho. Maaaring subaybayan o baguhin ang iyong ligtas na trapiko sa network."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"May naka-install sa device na ito na isang awtoridad sa certificate. Maaaring subaybayan o baguhin ang iyong ligtas na trapiko sa network."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Na-on ng iyong admin ang pag-log sa network, na sumusubaybay sa trapiko sa device mo."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Na-on ng iyong admin ang pag-log sa network, na sumusubaybay sa trapiko sa profile mo sa trabaho pero hindi sa iyong personal na profile."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Nakakonekta ka sa <xliff:g id="VPN_APP">%1$s</xliff:g>, na maaaring sumubaybay sa iyong aktibidad sa network, kabilang ang mga email, app, at website."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Nakakonekta ka sa <xliff:g id="VPN_APP_0">%1$s</xliff:g> at <xliff:g id="VPN_APP_1">%2$s</xliff:g>, na maaaring sumubaybay sa iyong aktibidad sa network, kabilang ang mga email, app, at website."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Nakakonekta sa <xliff:g id="VPN_APP">%1$s</xliff:g> ang iyong profile sa trabaho, na maaaring sumubaybay sa aktibidad sa iyong network, kasama ang mga email, app, at website."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Awtomatikong &lt;b&gt;na-demote sa Naka-silent&lt;/b&gt; ng system ang notification na ito."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Awtomatikong &lt;b&gt;na-rank nang mas mataas&lt;/b&gt; ang notification na ito sa iyong shade."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Awtomatikong &lt;b&gt;na-rank nang mas mababa&lt;/b&gt; ang notification na ito sa iyong shade."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Tama ba ito?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Ipaalam sa developer ang iyong feedback. Tama ba ito?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Salamat sa iyong feedback!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Binuksan ang mga kontrol sa notification para sa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Ilipat sa <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Idagdag sa posisyong <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisyon <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Idinagdag ang tile"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Inalis ang tile"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor ng Mga mabilisang setting."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification sa <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buksan ang mga setting."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Ginagamit ng <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ang <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Kamakailang ginamit ng <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ang <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Tawag sa telepono"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Tawag sa telepono"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(sa pamamagitan ng <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"lokasyon"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Naka-off ang mga sensor"</string>
<string name="device_services" msgid="1549944177856658705">"Mga Serbisyo ng Device"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Walang pamagat"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"I-tap para i-restart ang app na ito at mag-full screen."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Ilipat"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Na-update na ang pag-navigate ng system. Para gumawa ng mga pagbabago, pumunta sa Mga Setting."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Pumunta sa Mga Setting para i-update ang pag-navigate sa system"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 3040ea0a2c3d..4bacfb9aaee9 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Kayan ekran görüntüsü"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ekran görüntüsünü kapat"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran görüntüsü önizlemesi"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Üst sınır"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Alt sınır"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekran Kaydedicisi"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran kaydı işleniyor"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekran kaydı oturumu için devam eden bildirim"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Sabaha kadar"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Açılacağı saat: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Şu saate kadar: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Parlaklığı Azalt"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC devre dışı"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC etkin"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profili göster"</string>
<string name="user_add_user" msgid="4336657383006913022">"Kullanıcı ekle"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Yeni kullanıcı"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Misafir oturumu sonlandırılsın mı?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Misafir oturumunu sonlandır"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Misafir oturumu kaldırılsın mı?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Oturumu sonlandır"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Kaldır"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Tekrar hoş geldiniz sayın misafir!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Oturumunuza devam etmek istiyor musunuz?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Baştan başla"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Kuruluşunuz iş profilinize bir sertifika yetkilisi yükledi. Güvenli ağ trafiğiniz izlenebilir veya değiştirilebilir."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Bu cihazda bir sertifika yetkilisi yüklü. Güvenli ağ trafiğiniz izlenebilir veya değiştirilebilir."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Yöneticiniz,cihazınızdaki trafiği izleyen ağ günlük kaydını açtı."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Yöneticiniz, iş profilinizdeki trafiği izleyen ancak kişisel profilinizdeki trafiği izlemeyen ağ günlük kaydını açtı."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"E-postalarınız, uygulamalarınız ve web siteleriniz de dahil olmak üzere ağ etkinliğinizi takip edebilen <xliff:g id="VPN_APP">%1$s</xliff:g> ağına bağlısınız."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"E-postalar, uygulamalar ve web siteleri de dahil olmak üzere ağ etkinliğinizi izleyebilen <xliff:g id="VPN_APP_0">%1$s</xliff:g> ve <xliff:g id="VPN_APP_1">%2$s</xliff:g> uygulamalarına bağlısınız."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"İş profiliniz, e-postalar, uygulamalar ve web siteleri dahil olmak üzere ağ etkinliğinizi izleyebilen <xliff:g id="VPN_APP">%1$s</xliff:g> uygulamasına bağlı."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Bu bildirim, sistem tarafından otomatik olarak &lt;b&gt;Sessize düşürüldü&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Bu bildirim, gölgenizde otomatik olarak &lt;b&gt;daha yüksek sıralandı&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Bu bildirim, gölgenizde otomatik olarak &lt;b&gt;daha düşük sıralandı&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Bu doğru muydu?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Görüşlerinizi geliştiriciye bildirin. Bu doğru muydu?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Geri bildiriminiz için teşekkürler!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"Tamam"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> için bildirim kontrolleri açıldı"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna taşı"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna ekle"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Konum: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kart eklendi"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Parça kaldırıldı"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Hızlı ayar düzenleyicisi."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildirimi: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ayarları aç."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> uygulamasını kullanıyor"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, yakın zamanda <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> uygulamasını kullandı"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(kurumsal)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefon çağrısı"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Sesli arama"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> aracılığıyla)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"konum"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensörler kapalı"</string>
<string name="device_services" msgid="1549944177856658705">"Cihaz Hizmetleri"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Başlıksız"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Bu uygulamayı yeniden başlatmak ve tam ekrana geçmek için dokunun."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Taşı"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Sistemde gezinme yöntemi güncellendi. Değişiklik yapmak için Ayarlar\'a gidin."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Sistemde gezinme yöntemini güncellemek için Ayarlar\'a gidin"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index b6b7cee5c4f9..4a0746cdd140 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Прокрутити знімок екрана"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Закрити знімок екрана"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Перегляд знімка екрана"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Верхня межа"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Нижня межа"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Відеозапис екрана"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обробка записування екрана"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Сповіщення про сеанс запису екрана"</string>
@@ -414,6 +416,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До сходу сонця"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Вмикається о <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Зменшувати яскравість"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC вимкнено"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ввімкнено"</string>
@@ -467,9 +470,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Показати профіль"</string>
<string name="user_add_user" msgid="4336657383006913022">"Додати користувача"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Новий користувач"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Завершити сеанс у режимі \"Гість\"?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Завершити сеанс у режимі \"Гість\""</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Вийти з режиму гостя?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усі додатки й дані з цього сеансу буде видалено."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Завершити сеанс"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Вийти"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"З поверненням!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Продовжити сеанс?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почати знову"</string>
@@ -546,6 +550,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Адміністратор організації встановив центр сертифікації у вашому робочому профілі. Захищений мережевий трафік може відстежуватися або змінюватися."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На цьому пристрої встановлено центр сертифікації. Захищений мережевий трафік може відстежуватися або змінюватися."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Ваш адміністратор увімкнув реєстрацію в мережі, під час якої на вашому пристрої відстежується трафік."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Ваш адміністратор увімкнув реєстрацію в журналі мережі, щоб відстежувати трафік вашого робочого профілю (не особистого)."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Під’єднано додаток <xliff:g id="VPN_APP">%1$s</xliff:g>, який може відстежувати вашу активність у мережі, як-от відкривання електронних листів, додатків і веб-сайтів."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Під’єднано додатки <xliff:g id="VPN_APP_1">%2$s</xliff:g> та <xliff:g id="VPN_APP_0">%1$s</xliff:g>, які можуть відстежувати вашу активність у мережі, як-от відкривання електронних листів, додатків і веб-сайтів."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Ваш робочий профіль під’єднано до додатка <xliff:g id="VPN_APP">%1$s</xliff:g>, який може відстежувати вашу активність у мережі, зокрема в електронній пошті, додатках і на веб-сайтах."</string>
@@ -739,7 +744,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Пріоритет цього сповіщення автоматично &lt;b&gt;знижено до \"Без звуку\"&lt;/b&gt; в системі."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Пріоритет цього сповіщення на панелі сповіщень автоматично &lt;b&gt;підвищено&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Пріоритет цього сповіщення на панелі сповіщень автоматично &lt;b&gt;знижено&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Правильно?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Надішліть розробнику свій відгук. Усе правильно?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Дякуємо за відгук!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Елементи керування сповіщеннями для додатка <xliff:g id="APP_NAME">%1$s</xliff:g> відкрито"</string>
@@ -890,6 +895,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перемістити на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додати на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиція <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Опцію додано"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Опцію вилучено"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор швидких налаштувань."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Сповіщення <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Відкрити налаштування."</string>
@@ -980,7 +987,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Додаток <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> використовує функцію \"<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>\""</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Додаток <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> нещодавно використав функцію \"<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>\""</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(корпоративний додаток)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Додаток для телефонних дзвінків"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Телефонний дзвінок"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(через <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"камеру"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"місце"</string>
@@ -988,7 +995,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Датчики вимкнено"</string>
<string name="device_services" msgid="1549944177856658705">"Сервіси на пристрої"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Без назви"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Натисніть, щоб перезапустити додаток і перейти в повноекранний режим."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Перемістити"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Навігацію в системі оновлено. Щоб внести зміни, перейдіть у налаштування."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Перейдіть у налаштування, щоб оновити навігацію в системі"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 15c9607aa78f..f2e0273440a5 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"اسکرین شاٹ پر اسکرول کریں"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"اسکرین شاٹ برخاست کریں"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"اسکرین شاٹ کا پیش منظر"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"اوپر کا احاطہ"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"نیچے کا احاطہ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"سکرین ریکارڈر"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"سکرین ریکارڈنگ پروسیس ہورہی ہے"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"اسکرین ریکارڈ سیشن کیلئے جاری اطلاع"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"طلوع آفتاب تک"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"آن ہوگی بوقت <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> تک"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"چمک کم کریں"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"‏NFC غیر فعال ہے"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"‏NFC فعال ہے"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"پروفائل دکھائیں"</string>
<string name="user_add_user" msgid="4336657383006913022">"صارف کو شامل کریں"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"نیا صارف"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"مہمان سیشن ختم کریں؟"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"مہمان سیشن ختم کریں"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"مہمان کو ہٹائیں؟"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"سیشن ختم کریں"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ہٹائیں"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"مہمان، پھر سے خوش آمدید!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"کیا آپ اپنا سیشن جاری رکھنا چاہتے ہیں؟"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"دوبارہ شروع کریں"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"آپ کی تنظیم نے آپ کے دفتری پروفائل میں ایک سرٹیفکیٹ کی اتھارٹی کو انسٹال کیا ہے۔ آپ کا محفوظ نیٹ ورک ٹریفک مانیٹر ہو سکتا ہے یا اس میں ترمیم کی جا سکتی ہے۔"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ایک سرٹیفکیٹ کی اتھارٹی اس آلہ پر انسٹال ہے۔ آپ کا محفوظ نیٹ ورک ٹریفک مانیٹر ہو سکتا ہے یا اس میں ترمیم کی جا سکتی ہے۔"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"آپ کے منتظم نے نیٹ ورک لاگنگ کو آن کر دیا ہے، جو آپ کے آلے پر ٹریفک مانیٹر کرتی ہے۔"</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"آپ کے منتظم نے نیٹ ورک لاگنگ آن کر دی ہے، جو آپ کے ذاتی پروفائل پر نہیں بلکہ دفتری پروفائل پر ٹریفک کو مانیٹر کرتی ہے۔"</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"آپ <xliff:g id="VPN_APP">%1$s</xliff:g> سے منسلک ہیں جو ای میلز، ایپس اور ویب سائٹس سمیت آپ کے نیٹ ورک کی سرگرمی مانیٹر کر سکتی ہے۔"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"آپ <xliff:g id="VPN_APP_0">%1$s</xliff:g> اور <xliff:g id="VPN_APP_1">%2$s</xliff:g> سے منسلک ہیں، جو ای میلز، ایپس اور ویب سائٹس سمیت آپ کے نیٹ ورک کی سرگرمی مانیٹر کر سکتی ہیں۔"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"آپ کا دفتری پروفائل <xliff:g id="VPN_APP">%1$s</xliff:g> سے منسلک ہے، جو ای میلز، ایپس اور ویب سائٹس سمیت آپ کے نیٹ ورک کی سرگرمی مانیٹر کر سکتی ہے۔"</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"‏اس اطلاع کو خودکار طور پر سسٹم کے ذریعے &lt;b&gt;خاموش پر درجہ بند&lt;b&gt; کیا گیا۔"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"‏اس اطلاع کو آپ کے شیڈ میں خودکار طور پر &lt;b&gt;اعلی درجہ&lt;/b&gt; دیا گیا۔"</string>
<string name="feedback_demoted" msgid="951884763467110604">"‏اس اطلاع کو آپ کے شیڈ میں خودکار طور پر &lt;b&gt;کم درجہ&lt;/b&gt; دیا گیا۔"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"کیا یہ درست تھا؟"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"ڈویلپر کو اپنے تاثرات فراہم کریں کیا یہ درست تھا؟"</string>
<string name="feedback_response" msgid="4671729244976641339">"آپ کے تاثرات کا شکریہ!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"ٹھیک ہے"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> کیلئے اطلاعی کنٹرولز کھلے ہیں"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> میں منتقل کریں"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g> میں شامل کریں"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ٹائل کو شامل کیا گیا"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ٹائل کو ہٹا دیا گیا"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"فوری ترتیبات کا ایڈیٹر۔"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> اطلاع: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ترتیبات کھولیں۔"</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> کا استعمال کر رہی ہے"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> نے حال ہی میں <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> کا استعمال کیا"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(انٹرپرائز)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"فون کال"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"فون کال"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> کے ذریعے)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"کیمرا"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"مقام"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"سینسرز آف ہیں"</string>
<string name="device_services" msgid="1549944177856658705">"آلہ کی سروس"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"کوئی عنوان نہیں ہے"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"یہ ایپ دوبارہ شروع کرنے کے لیے تھپتھپائیں اور پوری اسکرین پر جائیں۔"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"منتقل کریں"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"سسٹم نیویگیشن اپ ڈیٹ کیا گیا۔ تبدیلیاں کرنے کے لیے، ترتیبات پر جائیں۔"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"سسٹم نیویگیشن اپ ڈیٹ کرنے کے لیے ترتیبات پر جائیں"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 3044a98ad814..fab65b116bda 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -93,6 +93,10 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Skrinshotni aylantirish"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Skrinshotni yopish"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Skrinshotga razm solish"</string>
+ <!-- no translation found for screenshot_top_boundary (1500569103321300856) -->
+ <skip />
+ <!-- no translation found for screenshot_bottom_boundary (5657242629526407311) -->
+ <skip />
<string name="screenrecord_name" msgid="2596401223859996572">"Ekrandan yozib olish"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran yozib olinmoqda"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekrandan yozib olish seansi uchun joriy bildirishnoma"</string>
@@ -410,6 +414,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Quyosh chiqqunicha"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> da yoqiladi"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> gacha"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Yorqinlikni pasaytirish"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC o‘chiq"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC yoniq"</string>
@@ -463,9 +468,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profilni ko‘rsatish"</string>
<string name="user_add_user" msgid="4336657383006913022">"Foydalanuvchi"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Yangi foydalanuvchi"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Mehmon seansi yakunlansinmi?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Mehmon seansini yakunlash"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Mehmon hisobi o‘chirib tashlansinmi?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ushbu seansdagi barcha ilovalar va ma’lumotlar o‘chirib tashlanadi."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Seansni yakunlash"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Olib tashlash"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Xush kelibsiz, mehmon!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Seansni davom ettirmoqchimisiz?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Boshidan boshlansin"</string>
@@ -540,6 +546,8 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Tashkilotingiz ishchi profilingizga CA sertifikatini o‘rnatdi. U himoyalangan tarmoq trafigini nazorat qilishi va o‘zgartirishi mumkin."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Qurilmada CA sertifikati o‘rnatilgan. U himoyalangan tarmoq trafigini nazorat qilishi va o‘zgartirishi mumkin."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administrator qurilmangizdagi trafikni nazorat qiluvchi tarmoq jurnalini yoqdi."</string>
+ <!-- no translation found for monitoring_description_managed_profile_network_logging (6932303843097006037) -->
+ <skip />
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"<xliff:g id="VPN_APP">%1$s</xliff:g> ilovasi ishga tushirilgan. U internetdagi harakatlaringiz, jumladan, e-pochta, ilova va veb-saytlardagi xatti-harakatlaringizni kuzatishi mumkin."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> va <xliff:g id="VPN_APP_1">%2$s</xliff:g> ilovalari ishga tushirilgan. Ular tarmoqdagi, jumladan, e-pochta, ilova va veb-saytlardagi xatti-harakatlaringizni kuzatishi mumkin."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Ishchi profilingizda tarmoqdagi, jumladan, e-pochta, ilova va veb-saytlardagi xatti-harakatlaringizni kuzatishi mumkin bo‘lgan <xliff:g id="VPN_APP">%1$s</xliff:g> ilovasi ishga tushirilgan."</string>
@@ -733,7 +741,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Bu bildirishnomaning muhimlik darajasi tizim tomonidan avtomatik ravishda &lt;b&gt;Sokin darajaga tushirildi&lt;/b&gt;."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Bu bildirishnomaning muhimlik darajasi tizim tomonidan avtomatik ravishda &lt;b&gt;yuqori darajaga chiqarildi&lt;/b&gt;."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Bu bildirishnomaning muhimlik darajasi tizim tomonidan avtomatik ravishda &lt;b&gt;quyi darajaga tushirildi&lt;/b&gt;."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Xatolar boʻlmadimi?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Ishlab chiquvchiga fikr-mulohazalaringizni bildiring. Xatolar boʻlmadimi?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Fikr-mulohazangiz uchun tashakkur!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun bildirishnoma sozlamalari ochildi"</string>
@@ -880,6 +888,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Bu joyga olish: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bu joyga kiritish: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Joylashuv: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Katakcha kiritildi"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Katakcha olib tashlandi"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Tezkor sozlamalar muharriri"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildirishnomasi: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Sozlamalarni ochish."</string>
@@ -970,7 +980,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> hozir <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ishlatmoqda"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> yaqinda <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ishlatgan"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(korporativ)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefon chaqiruvi"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefon chaqiruvi"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> orqali)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"joylashuv"</string>
@@ -978,7 +988,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensorlar nofaol"</string>
<string name="device_services" msgid="1549944177856658705">"Qurilma xizmatlari"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Nomsiz"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Bu ilovani qaytadan ishga tushirish va butun ekranga ochish uchun bosing."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Surish"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Tizim navigatsiyasi yangilandi. Buni Sozlamalar orqali oʻzgartirishingiz mumkin."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Tizim navigatsiyasini yangilash uchun Sozlamalarni oching"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 4913be4f9565..366f755638a5 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Cuộn để phóng to ảnh chụp màn hình"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Đóng ảnh chụp màn hình"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Xem trước ảnh chụp màn hình"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Ranh giới trên"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Ranh giới dưới"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Trình ghi màn hình"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Đang xử lý video ghi màn hình"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Thông báo đang diễn ra về phiên ghi màn hình"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Cho đến khi trời sáng"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Bật vào lúc <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Cho đến <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Giảm độ sáng"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC đã được tắt"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC đã được bật"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Hiển thị hồ sơ"</string>
<string name="user_add_user" msgid="4336657383006913022">"Thêm người dùng"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Người dùng mới"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Kết thúc phiên khách?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Kết thúc phiên khách"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Xóa phiên khách?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tất cả ứng dụng và dữ liệu trong phiên này sẽ bị xóa."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Kết thúc phiên"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Xóa"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Chào mừng bạn trở lại!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Bạn có muốn tiếp tục phiên của mình không?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Bắt đầu lại"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Tổ chức của bạn đã cài đặt một tổ chức phát hành chứng chỉ trong hồ cơ công việc của bạn. Lưu lượng truy cập mạng bảo mật của bạn có thể được giám sát hoặc sửa đổi."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Một tổ chức phát hành chứng chỉ được cài đặt trên thiết bị này. Lưu lượng truy cập mạng bảo mật của bạn có thể được giám sát hoặc sửa đổi."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Quản trị viên của bạn đã bật tính năng ghi nhật ký mạng. Tính năng này giám sát lưu lượng truy cập trên thiết bị của bạn."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Quản trị viên của bạn đã bật tính năng ghi nhật ký mạng. Tính năng này giám sát lưu lượng truy cập trong hồ sơ công việc chứ không giám sát lưu lượng truy cập trong hồ sơ cá nhân của bạn."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Bạn đang kết nối với <xliff:g id="VPN_APP">%1$s</xliff:g>. Ứng dụng này có thể giám sát hoạt động mạng của bạn, bao gồm email, ứng dụng và trang web."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Bạn đang kết nối với <xliff:g id="VPN_APP_0">%1$s</xliff:g> và <xliff:g id="VPN_APP_1">%2$s</xliff:g>. Các ứng dụng này có thể giám sát hoạt động mạng của bạn, bao gồm email, ứng dụng và trang web."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Hồ sơ công việc của bạn được kết nối với <xliff:g id="VPN_APP">%1$s</xliff:g>, ứng dụng này có thể giám sát hoạt động mạng của bạn, bao gồm email, ứng dụng và trang web."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Hệ thống đã tự động &lt;b&gt;thay đổi thành Im lặng&lt;/b&gt; theo tầm quan trọng của thông báo này."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Hệ thống đã tự động &lt;b&gt;tăng mức độ quan trọng&lt;/b&gt; của thông báo này trong ngăn thông báo."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Hệ thống đã tự động &lt;b&gt;giảm mức độ quan trọng&lt;/b&gt; của thông báo này trong ngăn thông báo."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Thông tin này có chính xác không?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Chia sẻ ý kiến phản hồi của bạn với nhà phát triển. Thông tin này có chính xác không?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Cảm ơn bạn đã phản hồi!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"OK"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Đã mở điều khiển thông báo đối với <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Di chuyển tới <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Thêm vào vị trí <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Vị trí <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Đã thêm thẻ thông tin"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Đã xóa thẻ thông tin"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Trình chỉnh sửa cài đặt nhanh."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Thông báo của <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Mở phần cài đặt."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> đang sử dụng <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Gần đây, <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> đã sử dụng <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(doanh nghiệp)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Gọi điện thoại"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Cuộc gọi điện thoại"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(thông qua <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"máy ảnh"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"vị trí"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Tắt cảm biến"</string>
<string name="device_services" msgid="1549944177856658705">"Dịch vụ cho thiết bị"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Không có tiêu đề"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Nhấn để khởi động lại ứng dụng này và xem ở chế độ toàn màn hình."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Di chuyển"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Đã cập nhật chế độ di chuyển trên hệ thống. Để thay đổi, hãy chuyển đến phần Cài đặt."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Chuyển đến phần Cài đặt để cập nhật chế độ di chuyển trên hệ thống"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 3479d009804c..c25ad27df294 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"滚动抓取长截图"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"关闭屏幕截图"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"屏幕截图预览"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"顶部边界"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"底部边界"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"屏幕录制器"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"正在处理屏幕录制视频"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"持续显示屏幕录制会话通知"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"在日出时关闭"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"在<xliff:g id="TIME">%s</xliff:g> 开启"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"直到<xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"调低亮度"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 已停用"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 已启用"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"显示个人资料"</string>
<string name="user_add_user" msgid="4336657383006913022">"添加用户"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"新用户"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"要结束访客会话吗?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"结束访客会话"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"要移除访客吗?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"结束会话"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"访客,欢迎回来!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"要继续您的会话吗?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新开始"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"您所在的单位已为您的工作资料安装证书授权中心。您的安全网络流量可能会受到监控或修改。"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"此设备上已安装证书授权中心。您的安全网络流量可能会受到监控或修改。"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"您的管理员已开启网络日志功能(该功能会监控您设备上的流量)。"</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"您的管理员已开启网络日志功能,该功能会监控您的工作资料的流量,而不会监控个人资料的流量。"</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"您已连接到“<xliff:g id="VPN_APP">%1$s</xliff:g>”(该应用能够监控您的网络活动,其中包括收发电子邮件、使用应用和浏览网站)。"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"您已连接到“<xliff:g id="VPN_APP_0">%1$s</xliff:g>”和“<xliff:g id="VPN_APP_1">%2$s</xliff:g>”(这两个应用能够监控您的网络活动,其中包括收发电子邮件、使用应用和浏览网站)。"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"您的工作资料已连接到“<xliff:g id="VPN_APP">%1$s</xliff:g>”(该应用能够监控您的网络活动,其中包括收发电子邮件、使用应用和浏览网站)。"</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"系统已自动将此通知的重要性&lt;b&gt;降低为“静音”&lt;/b&gt;。"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"系统已自动&lt;b&gt;调高&lt;/b&gt;此通知在通知栏中的顺序。"</string>
<string name="feedback_demoted" msgid="951884763467110604">"系统已自动&lt;b&gt;调低&lt;/b&gt;此通知在通知栏中的顺序。"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"是否正确?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"向开发者提供反馈。此信息是否正确?"</string>
<string name="feedback_response" msgid="4671729244976641339">"感谢您提供反馈!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"确定"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g>的通知控件已打开"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"添加到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"已添加卡片"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"已移除卡片"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快捷设置编辑器。"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"打开设置。"</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>正在使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>最近曾使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(企业版)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"电话"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"电话"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(通过<xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"相机"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"位置信息"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"已关闭传感器"</string>
<string name="device_services" msgid="1549944177856658705">"设备服务"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"无标题"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"点按即可重启此应用并进入全屏模式。"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"移动"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"系统导航已更新。要进行更改,请转到“设置”。"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"转到“设置”即可更新系统导航"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 314b728071af..aa7b47aace89 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"整頁截圖"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"關閉螢幕截圖"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"螢幕截圖預覽"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"上方邊界"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"下方邊界"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"螢幕畫面錄影工具"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"正在處理螢幕錄影內容"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"持續顯示錄影畫面工作階段通知"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"在日出時關閉"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"於<xliff:g id="TIME">%s</xliff:g>開啟"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"直至<xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"調低亮度"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 已停用"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 已啟用"</string>
@@ -440,7 +443,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"再次輕按即可開啟"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"向上滑動即可開啟"</string>
<string name="keyguard_retry" msgid="886802522584053523">"請向上滑動以再試一次"</string>
- <string name="require_unlock_for_nfc" msgid="1305686454823018831">"解鎖以使用 NFC"</string>
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"解鎖方可使用 NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"此裝置屬於您的機構"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"此裝置屬於「<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>」"</string>
<string name="phone_hint" msgid="6682125338461375925">"從圖示滑動即可使用手機功能"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"顯示個人檔案"</string>
<string name="user_add_user" msgid="4336657383006913022">"加入使用者"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"新使用者"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"要結束訪客工作階段嗎?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"結束訪客工作階段"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"移除訪客?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"結束工作階段"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"訪客您好,歡迎回來!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"您要繼續您的工作階段嗎?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"您的機構已在您的工作設定檔中安裝憑證授權單位。您的安全網絡流量可能會受監控或修改。"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"此裝置已安裝憑證授權單位。您的安全網絡流量可能會受監控或修改。"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"您的管理員已開啟網絡記錄功能,以監控您裝置上的流量。"</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"您的管理員已開啟網絡記錄功能,可監控您工作設定檔 (而非個人設定檔) 的流量。"</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"您已連接至「<xliff:g id="VPN_APP">%1$s</xliff:g>」,此應用程式可以監控您的網絡活動,包括電郵、應用程式及網站。"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"您已連接至「<xliff:g id="VPN_APP_0">%1$s</xliff:g>」和「<xliff:g id="VPN_APP_1">%2$s</xliff:g>」,這些應用程式可以監控您的網絡活動,包括電郵、應用程式及網站。"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"您的工作設定檔已連結至「<xliff:g id="VPN_APP">%1$s</xliff:g>」,此應用程式可以監控您的網絡活動,包括電郵、應用程式及網站。"</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"系統已自動將此通知的重要性&lt;b&gt;降低為靜音&lt;/b&gt;。"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"系統已自動&lt;b&gt;提高&lt;/b&gt;此通知在通知欄中的次序。"</string>
<string name="feedback_demoted" msgid="951884763467110604">"系統已自動&lt;b&gt;調低&lt;/b&gt;此通知在通知欄中的次序。"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"是否正確?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"與開發人員分享您的意見。是否正確?"</string>
<string name="feedback_response" msgid="4671729244976641339">"多謝您提供意見!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"確定"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"開咗「<xliff:g id="APP_NAME">%1$s</xliff:g>」嘅通知控制項"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移去 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"加去位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"加咗圖塊"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"移除咗圖塊"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯工具。"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"開啟設定。"</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"「<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>」正在使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"「<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>」最近曾使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(企業版本)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"電話"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"電話"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(透過「<xliff:g id="ATTRIBUTION">%s</xliff:g>」)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"相機"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"位置"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"感應器已關閉"</string>
<string name="device_services" msgid="1549944177856658705">"裝置服務"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"無標題"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"輕按即可重新開啟此應用程式並放大至全螢幕。"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"移動"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"系統導覽已更新。如需變更,請前往「設定」。"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"前往「設定」更新系統導覽"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 68b631fff5da..ca8db2eae5d6 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"以捲動畫面的方式拍攝長截圖"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"關閉螢幕截圖"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"螢幕截圖預覽"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"頂端邊界"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"底部邊界"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"螢幕錄影器"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"處理螢幕錄影內容"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"持續顯示螢幕畫面錄製工作階段通知"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"於日出時關閉"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"開啟時間:<xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"關閉時間:<xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"調低亮度"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 已停用"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 已啟用"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"顯示設定檔"</string>
<string name="user_add_user" msgid="4336657383006913022">"新增使用者"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"新使用者"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"要結束訪客工作階段嗎?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"結束訪客工作階段"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"移除訪客?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會遭到刪除。"</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"結束工作階段"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"訪客你好,歡迎回來!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"你要繼續這個工作階段嗎?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"貴機構已為你的工作資料夾安裝憑證授權單位憑證。你的安全網路流量可能會受到監控或修改。"</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"這個裝置已安裝憑證授權單位憑證。你的安全網路流量可能會受到監控或修改。"</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"你的管理員已啟用網路記錄功能,可監控你裝置的流量。"</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"你的管理員已啟用網路記錄功能,可監控你的工作資料夾流量,但不會監控個人資料夾的流量。"</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"由於你已連結至「<xliff:g id="VPN_APP">%1$s</xliff:g>」,因此你的網路活動 (包括收發電子郵件、使用應用程式及瀏覽網站) 可能會受到這個應用程式監控。"</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"由於你已連結至「<xliff:g id="VPN_APP_0">%1$s</xliff:g>」和「<xliff:g id="VPN_APP_1">%2$s</xliff:g>」,因此你的網路活動 (包括收發電子郵件、使用應用程式及瀏覽網站) 可能會受到這兩個應用程式監控。"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"由於你的工作資料夾已連結至「<xliff:g id="VPN_APP">%1$s</xliff:g>」,因此你的網路活動 (包括收發電子郵件、使用應用程式及瀏覽網站) 可能會受到這個應用程式監控。"</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"系統已自動將這則通知的重要性&lt;b&gt;降低為靜音&lt;/b&gt;。"</string>
<string name="feedback_promoted" msgid="2125562787759780807">"系統已自動&lt;b&gt;調高&lt;/b&gt;這則通知在通知欄中的順序。"</string>
<string name="feedback_demoted" msgid="951884763467110604">"系統已自動&lt;b&gt;調降&lt;/b&gt;這則通知在通知欄中的順序。"</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"是否正確?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"與開發人員分享你的意見。是否正確?"</string>
<string name="feedback_response" msgid="4671729244976641339">"感謝你提供意見!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"確定"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」的通知控制項已開啟"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"新增到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"已新增資訊方塊"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"已移除資訊方塊"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯器。"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"開啟設定。"</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"「<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>」正在使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"「<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>」最近曾使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(企業版)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"電話"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"電話"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(透過「<xliff:g id="ATTRIBUTION">%s</xliff:g>」)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"相機"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"位置"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"已關閉感應器"</string>
<string name="device_services" msgid="1549944177856658705">"裝置服務"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"無標題"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"輕觸即可重新啟動這個應用程式並進入全螢幕模式。"</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"移動"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"系統操作機制已更新。如要進行變更,請前往「設定」。"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"請前往「設定」更新系統操作機制"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 78c4f17edf2d..371702fb7494 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -93,6 +93,8 @@
<string name="screenshot_scroll_description" msgid="7855773867093272175">"Isithombe seskrini sokuskrola"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Cashisa isithombe-skrini"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ukubuka kuqala isithombe-skrini"</string>
+ <string name="screenshot_top_boundary" msgid="1500569103321300856">"Umngcele ophezulu"</string>
+ <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Umngcele ophansi"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Irekhoda yesikrini"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Icubungula okokuqopha iskrini"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Isaziso esiqhubekayo seseshini yokurekhoda isikrini"</string>
@@ -410,6 +412,7 @@
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Kuze kube sekuphumeni kwelanga"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Kuvulwe ngo-<xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Kuze kube ngu-<xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_reduce_bright_colors_label" msgid="4782053257950003419">"Nciphisa ukukhanya"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"I-NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"I-NFC ikhutshaziwe"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"I-NFC inikwe amandla"</string>
@@ -463,9 +466,10 @@
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Bonisa iphrofayela"</string>
<string name="user_add_user" msgid="4336657383006913022">"Engeza umsebenzisi"</string>
<string name="user_new_user_name" msgid="2019166282704195789">"Umsebenzisi omusha"</string>
- <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Misa isikhathi sesihambeli?"</string>
+ <string name="guest_exit_button" msgid="3059840571760915762">"Misa isikhathi sesihambeli"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Susa isivakashi?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Zonke izinhlelo zokusebenza nedatha kulesi sikhathi zizosuswa."</string>
- <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Phothula iseshini"</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Susa"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Siyakwamukela futhi, sivakashi!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Ingabe ufuna ukuqhubeka ngesikhathi sakho?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Qala phansi"</string>
@@ -540,6 +544,7 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Inhlangano yakho ifake ukugunyaza kwesitifiketi kuphrofayela yakho yomsebenzi. Ithrafikhi yenethiwekhi yakho evikelekile ingaqashwa noma ilungiswe."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Ukugunyaza kwesitifiketi kufakwe kule divayisi. Ithrafikhi yenethiwekhi yakho evikelekile ingaqashelwa noma ilungiswe."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Umlawuli wakho uvule ukungena kwedivayisi yakho, okuqapha ithrafikhi kudivayisi yakho."</string>
+ <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Umlawuli wakho uvule ukungena kwenethiwekhi, okuhlola ithrafikhi kudivayisi yakho yephrofayela yomsebenzi kodwa hhayi kuphrofayela yakho yomuntu siqu."</string>
<string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Uxhumeke ku-<xliff:g id="VPN_APP">%1$s</xliff:g>, engaqapha umsebenzi wenethiwekhi yakho, ofaka ama-imeyili, izinhlelo zokusebenza, namawebhusayithi."</string>
<string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Uxhumeke ku-<xliff:g id="VPN_APP_0">%1$s</xliff:g> naku-<xliff:g id="VPN_APP_1">%2$s</xliff:g>, okungaqaphela umsebenzi wakho wenethiwekhi, ofaka ama-imeyili, izinhlelo zokusebenza, namawebhusayithi."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Iphrofayela yakho yomsebenzi ixhumeke ku-<xliff:g id="VPN_APP">%1$s</xliff:g>, engaqapha umsebenzi wenethiwekhi yakho, ofaka ama-imeyili, izinhlelo zokusebenza, namawebhusayithi."</string>
@@ -733,7 +738,7 @@
<string name="feedback_silenced" msgid="9116540317466126457">"Lesi saziso sehliswe isikhundla ngokuzenzakalelayo &lt;b&gt;saba Okuthulile&lt;/b&gt; isistimu."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Lesi saziso silinganiselwe phezulu &lt;b&gt;ngokuzenzakalelayo&lt;/b&gt; kumthunzi wakho."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Lesi saziso silinganiselwe phansi &lt;b&gt;ngokuzenzakalelayo&lt;/b&gt; kumthunzi wakho."</string>
- <string name="feedback_prompt" msgid="2278631214125128281">"Ingabe kade kulungile lokhu?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Tshela unjiniyela impendulo yakho. Ingabe kade kulungile lokhu?"</string>
<string name="feedback_response" msgid="4671729244976641339">"Siyabonga ngempendulo!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"KULUNGILE"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Izilawuli zesaziso ze-<xliff:g id="APP_NAME">%1$s</xliff:g> zivuliwe"</string>
@@ -880,6 +885,8 @@
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hambisa ku-<xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engeza kusikhundla se-<xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Isikhundla se-<xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Ithayela lingeziwe"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Ithayela likhishiwe"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Isihleli sezilungiselelo ezisheshayo."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> isaziso: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Vula izilungiselelo."</string>
@@ -970,7 +977,7 @@
<string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"I-<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> isebenzisa i-<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string>
<string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"I-<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> isebenzise i-<xliff:g id="APP_OPP_NAME">%2$s</xliff:g> kamuva nje"</string>
<string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ibhizinisi)"</string>
- <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Ikholi yefoni"</string>
+ <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Ikholi yefoni"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(kuya ku-<xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"ikhamera"</string>
<string name="privacy_type_location" msgid="7991481648444066703">"indawo"</string>
@@ -978,7 +985,6 @@
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Izinzwa zivaliwe"</string>
<string name="device_services" msgid="1549944177856658705">"Amasevisi edivayisi"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Asikho isihloko"</string>
- <string name="restart_button_description" msgid="6916116576177456480">"Thepha ukuze uqale kabusha lolu hlelo lokusebenza uphinde uye kusikrini esigcwele."</string>
<string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Hambisa"</string>
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Ukuzulazula kwesistimu kubuyekeziwe. Ukuze wenze ushintsho, hamba kokuthi Izilungiselelo."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Hamba kuzilungiselelo ukuze ubuyekeze ukuzulazula kwesistimu"</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 8166e35d5b6a..6e458681bbab 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -139,6 +139,12 @@
<!-- Size of shadows/elevations on keyguard -->
<attr name="shadowRadius" format="float" />
+ <attr name="handleThickness" format="dimension" />
+ <attr name="handleColor" format="color" />
+ <attr name="scrimColor" format="color" />
+
+ <attr name="isVertical" format="boolean" />
+
<!-- Used display CarrierText in Keyguard or QS Footer -->
<declare-styleable name="CarrierText">
<attr name="allCaps" format="boolean" />
@@ -173,15 +179,15 @@
</declare-styleable>
<declare-styleable name="CropView">
- <attr name="handleThickness" format="dimension" />
- <attr name="handleColor" format="color" />
- <attr name="scrimColor" format="color" />
+ <attr name="handleThickness" />
+ <attr name="handleColor" />
+ <attr name="scrimColor" />
</declare-styleable>
<declare-styleable name="MagnifierView">
- <attr name="handleThickness" format="dimension" />
- <attr name="handleColor" format="color" />
- <attr name="scrimColor" format="color" />
+ <attr name="handleThickness" />
+ <attr name="handleColor" />
+ <attr name="scrimColor" />
<attr name="borderThickness" format="dimension" />
<attr name="borderColor" format="color" />
</declare-styleable>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 5fb6de7bb588..acd671cb6297 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -66,9 +66,13 @@
<!-- Color for rounded background for activated user in keyguard user switcher -->
<color name="kg_user_switcher_activated_background_color">#26000000</color>
<!-- Icon color for user avatars in keyguard user switcher -->
- <color name="kg_user_switcher_avatar_icon_color">@android:color/background_light</color>
- <!-- Icon color for selected user avatars in keyguard user switcher -->
- <color name="kg_user_switcher_selected_avatar_icon_color">@android:color/background_light</color>
+ <color name="kg_user_switcher_avatar_icon_color">@color/GM2_grey_800</color>
+ <!-- Icon color for user avatars in keyguard user switcher that restricted
+ (e.g. cannot be switched to) -->
+ <color name="kg_user_switcher_restricted_avatar_icon_color">@color/GM2_grey_600</color>
+ <!-- Color of background circle of user avatars in keyguard user switcher -->
+ <color name="kg_user_switcher_avatar_background">@color/GM2_grey_300</color>
+
<!-- Icon color for user avatars in user switcher quick settings -->
<color name="qs_user_switcher_avatar_icon_color">#3C4043</color>
<!-- Icon color for selected user avatars in user switcher quick settings -->
@@ -240,11 +244,8 @@
<color name="magnification_switch_button_color">#7F000000</color>
<!-- media -->
- <color name="media_primary_text">@android:color/white</color>
- <color name="media_secondary_text">#99ffffff</color> <!-- 60% -->
- <color name="media_seekbar_progress">#c0ffffff</color>
<color name="media_disabled">#80ffffff</color>
- <color name="media_seamless_border">#26ffffff</color> <!-- 15% -->
+ <color name="media_seamless_border">?android:attr/colorAccent</color>
<color name="media_divider">#1d000000</color>
<!-- controls -->
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 3f58236d7cf6..149da7f92999 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
+ wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle,controls
</string>
<!-- The tiles to display in QuickSettings -->
@@ -279,6 +279,11 @@
<!-- Whether to show the full screen user switcher. -->
<bool name="config_enableFullscreenUserSwitcher">false</bool>
+ <!-- Whether the multi-user switch on the keyguard opens QS user panel. If false, clicking the
+ user switch on the keyguard will replace the notifications and status area with the user
+ switcher. The multi-user switch is only shown if config_keyguardUserSwitcher=false. -->
+ <bool name="config_keyguard_user_switch_opens_qs_details">false</bool>
+
<!-- SystemUIFactory component -->
<string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIFactory</string>
@@ -307,6 +312,7 @@
<item>com.android.systemui.accessibility.SystemActions</item>
<item>com.android.systemui.toast.ToastUI</item>
<item>com.android.systemui.wmshell.WMShell</item>
+ <item>com.android.systemui.people.widget.PeopleSpaceWidgetEnabler</item>
</string-array>
<!-- QS tile shape store width. negative implies fill configuration instead of stroke-->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2f289d8a0b1f..07fec421ab36 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -163,10 +163,14 @@
<!-- heads up elevation that is added if the view is pinned -->
<dimen name="heads_up_pinned_elevation">16dp</dimen>
- <!-- Height of a messaging notifications with actions at least. Not that this is an upper bound
+ <!-- Height of a messaging notifications with actions at least. Note that this is an upper bound
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>
@@ -199,6 +203,9 @@
<!-- The amount the content shifts upwards when transforming into the shelf -->
<dimen name="shelf_transform_content_shift">32dp</dimen>
+ <!-- The y translation for keyguard indication text animation for rotating text in/out -->
+ <dimen name="keyguard_indication_y_translation">24dp</dimen>
+
<!-- The padding on the bottom of the notifications on the keyguard -->
<dimen name="keyguard_indication_bottom_padding">12sp</dimen>
@@ -685,6 +692,11 @@
<!-- The amount to shift the clocks during a small/large transition -->
<dimen name="keyguard_clock_switch_y_shift">10dp</dimen>
+ <!-- Default line spacing multiplier between hours and minutes of the keyguard clock -->
+ <item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item>
+ <!-- Burmese line spacing multiplier between hours and minutes of the keyguard clock -->
+ <item name="keyguard_clock_line_spacing_scale_burmese" type="dimen" format="float">1</item>
+
<item name="scrim_behind_alpha" format="float" type="dimen">0.62</item>
<!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
@@ -731,9 +743,6 @@
<!-- end margin for system icons if multi user switch is hidden -->
<dimen name="system_icons_switcher_hidden_expanded_margin">16dp</dimen>
- <!-- The thickness of the colored border around the current user. -->
- <dimen name="keyguard_user_switcher_border_thickness">2dp</dimen>
-
<dimen name="data_usage_graph_marker_width">4dp</dimen>
<!-- The padding bottom of the clock group when QS is expanded. -->
@@ -793,7 +802,7 @@
<!-- Size of user icon + frame in the qs user picker (incl. frame) -->
<dimen name="qs_framed_avatar_size">54dp</dimen>
<!-- Size of user icon + frame in the keyguard user picker (incl. frame) -->
- <dimen name="kg_framed_avatar_size">54dp</dimen>
+ <dimen name="kg_framed_avatar_size">48dp</dimen>
<!-- Margin on the left side of the carrier text on Keyguard -->
<dimen name="keyguard_carrier_text_margin">16dp</dimen>
@@ -1157,7 +1166,7 @@
<dimen name="logout_button_layout_height">32dp</dimen>
<dimen name="logout_button_padding_horizontal">16dp</dimen>
<dimen name="logout_button_margin_bottom">12dp</dimen>
- <dimen name="logout_button_corner_radius">2dp</dimen>
+ <dimen name="logout_button_corner_radius">4dp</dimen>
<!-- Blur radius on status bar window and power menu -->
<dimen name="min_window_blur_radius">1px</dimen>
@@ -1167,17 +1176,13 @@
<dimen name="display_cutout_margin_consumption">0px</dimen>
<!-- Height of the Ongoing App Ops chip -->
- <dimen name="ongoing_appops_chip_height">32dp</dimen>
- <!-- Padding between background of Ongoing App Ops chip and content -->
- <dimen name="ongoing_appops_chip_bg_padding">8dp</dimen>
+ <dimen name="ongoing_appops_chip_height">24dp</dimen>
<!-- Side padding between background of Ongoing App Ops chip and content -->
<dimen name="ongoing_appops_chip_side_padding">8dp</dimen>
- <!-- Margin between icons of Ongoing App Ops chip when QQS-->
- <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen>
- <!-- Margin between icons of Ongoing App Ops chip when QS-->
- <dimen name="ongoing_appops_chip_icon_margin_expanded">2dp</dimen>
+ <!-- Margin between icons of Ongoing App Ops chip -->
+ <dimen name="ongoing_appops_chip_icon_margin">4dp</dimen>
<!-- Icon size of Ongoing App Ops chip -->
- <dimen name="ongoing_appops_chip_icon_size">@dimen/status_bar_icon_drawing_size</dimen>
+ <dimen name="ongoing_appops_chip_icon_size">16dp</dimen>
<!-- Radius of Ongoing App Ops chip corners -->
<dimen name="ongoing_appops_chip_bg_corner_radius">16dp</dimen>
@@ -1316,8 +1321,16 @@
<dimen name="screenrecord_status_icon_height">17.5dp</dimen>
<dimen name="screenrecord_status_icon_bg_radius">8dp</dimen>
+ <!-- Keyguard user switcher -->
<dimen name="kg_user_switcher_text_size">16sp</dimen>
+ <!-- End guest session button -->
+ <dimen name="end_guest_button_layout_height">32dp</dimen>
+ <dimen name="end_guest_button_padding_horizontal">16dp</dimen>
+ <dimen name="end_guest_button_margin_bottom">96dp</dimen>
+ <dimen name="end_guest_button_border_size">1dp</dimen>
+ <dimen name="end_guest_button_corner_radius">16dp</dimen>
+
<!-- Opacity at which the background for the shutdown UI will be drawn. -->
<item name="shutdown_scrim_behind_alpha" format="float" type="dimen">0.95</item>
@@ -1335,8 +1348,8 @@
<dimen name="media_output_dialog_icon_corner_radius">16dp</dimen>
<dimen name="media_output_dialog_title_anim_y_delta">12.5dp</dimen>
- <dimen name="people_space_widget_radius">24dp</dimen>
- <dimen name="people_space_widget_round_radius">100dp</dimen>
+ <dimen name="people_space_widget_radius">28dp</dimen>
+ <dimen name="people_space_image_radius">20dp</dimen>
<dimen name="people_space_widget_background_padding">6dp</dimen>
<dimen name="rounded_slider_height">48dp</dimen>
@@ -1346,4 +1359,11 @@
<dimen name="rounded_slider_icon_size">24dp</dimen>
<!-- rounded_slider_icon_size / 2 -->
<dimen name="rounded_slider_icon_inset">12dp</dimen>
+
+ <dimen name="toast_width">296dp</dimen>
+ <item name="toast_icon_alpha" format="float" type="dimen">1</item>
+ <dimen name="toast_text_size">14sp</dimen>
+ <dimen name="toast_y_offset">48dp</dimen>
+ <dimen name="toast_icon_size">48dp</dimen>
+ <dimen name="toast_bg_radius">28dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 01e54ff60582..d4bb128120e9 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -18,11 +18,12 @@
<resources>
<bool name="are_flags_overrideable">false</bool>
- <bool name="flag_notification_pipeline2">false</bool>
+ <bool name="flag_notification_pipeline2">true</bool>
<bool name="flag_notification_pipeline2_rendering">false</bool>
<bool name="flag_notif_updates">false</bool>
<bool name="flag_shade_is_opaque">false</bool>
+ <bool name="flag_monet">false</bool>
<!-- b/171917882 -->
<bool name="flag_notification_twocolumn">false</bool>
@@ -36,4 +37,9 @@
<!-- People Tile flag -->
<bool name="flag_conversations">false</bool>
+
+ <!-- The new animations to/from lockscreen and AOD! -->
+ <bool name="flag_lockscreen_animations">false</bool>
+
+ <bool name="flag_toast_style">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d93239531038..3b42600076ac 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -247,6 +247,10 @@
<string name="screenshot_dismiss_description">Dismiss screenshot</string>
<!-- Content description indicating that the view is a preview of the screenshot that was just taken [CHAR LIMIT=NONE] -->
<string name="screenshot_preview_description">Screenshot preview</string>
+ <!-- Content description for the top boundary of the screenshot being cropped [CHAR LIMIT=NONE] -->
+ <string name="screenshot_top_boundary">Top boundary</string>
+ <!-- Content description for the bottom boundary of the screenshot being cropped [CHAR LIMIT=NONE] -->
+ <string name="screenshot_bottom_boundary">Bottom boundary</string>
<!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
<string name="screenrecord_name">Screen Recorder</string>
@@ -954,11 +958,8 @@
<string name="quick_settings_dark_mode_secondary_label_on_at">On at <xliff:g id="time" example="10 pm">%s</xliff:g></string>
<!-- QuickSettings: Secondary text for when the Dark theme or some other tile will be on until some user-selected time. [CHAR LIMIT=20] -->
<string name="quick_settings_dark_mode_secondary_label_until">Until <xliff:g id="time" example="7 am">%s</xliff:g></string>
- <!-- TODO(b/170970602): remove translatable=false when RBC has official name and strings -->
- <!-- QuickSettings: Label for the toggle that controls whether Reduce Bright Colors is enabled. [CHAR LIMIT=NONE] -->
- <string name="quick_settings_reduce_bright_colors_label" translatable="false">Reduce Bright Colors</string>
- <!-- QuickSettings: Secondary text for intensity level of Reduce Bright Colors. [CHAR LIMIT=NONE] -->
- <string name="quick_settings_reduce_bright_colors_secondary_label" translatable="false"> <xliff:g id="intensity" example="50">%d</xliff:g>%% reduction</string>
+ <!-- QuickSettings: Label for the toggle that controls whether Reduce Brightness is enabled. [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_reduce_bright_colors_label">Reduce Brightness</string>
<!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] -->
<string name="quick_settings_nfc_label">NFC</string>
@@ -1115,14 +1116,17 @@
<!-- Name for a freshly added user [CHAR LIMIT=30] -->
<string name="user_new_user_name">New user</string>
+ <!-- Label for button that exits guest session and clears the guest user data [CHAR LIMIT=50]-->
+ <string name="guest_exit_button">End guest session</string>
+
<!-- Title of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] -->
- <string name="guest_exit_guest_dialog_title">End guest session?</string>
+ <string name="guest_exit_guest_dialog_title">Remove guest?</string>
<!-- Message of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] -->
<string name="guest_exit_guest_dialog_message">All apps and data in this session will be deleted.</string>
<!-- Label for button in confirmation dialog when exiting guest session [CHAR LIMIT=35] -->
- <string name="guest_exit_guest_dialog_remove">End session</string>
+ <string name="guest_exit_guest_dialog_remove">Remove</string>
<!-- Title of the notification when resuming an existing guest session [CHAR LIMIT=NONE] -->
<string name="guest_wipe_session_title">Welcome back, guest!</string>
@@ -1341,6 +1345,9 @@
<!-- Monitoring dialog: Description of Network Logging. [CHAR LIMIT=NONE]-->
<string name="monitoring_description_management_network_logging">Your admin has turned on network logging, which monitors traffic on your device.</string>
+ <!-- Monitoring dialog: Description of Network Logging in the work profile. [CHAR LIMIT=NONE]-->
+ <string name="monitoring_description_managed_profile_network_logging">Your admin has turned on network logging, which monitors traffic in your work profile but not in your personal profile.</string>
+
<!-- Monitoring dialog: Description of an active VPN. [CHAR LIMIT=NONE]-->
<string name="monitoring_description_named_vpn">You\'re connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.</string>
@@ -2256,6 +2263,12 @@
<!-- Accessibility description indicating the currently selected tile's position. Only used for tiles that are currently in use [CHAR LIMIT=NONE] -->
<string name="accessibility_qs_edit_position">Position <xliff:g id="position" example="5">%1$d</xliff:g></string>
+ <!-- Accessibility announcement after a tile has been added [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_tile_added">Tile added</string>
+
+ <!-- Accessibility announcement after a tile has been added [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_tile_removed">Tile removed</string>
+
<!-- Accessibility label for window when QS editing is happening [CHAR LIMIT=NONE] -->
<string name="accessibility_desc_quick_settings_edit">Quick settings editor.</string>
@@ -2804,8 +2817,26 @@
<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=120] -->
- <string name="birthday_status" translatable="false">Today is their birthday!</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>
<!-- Title to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false
[CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 7c72548a7252..14b376a8bf6c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -114,12 +114,12 @@
<style name="TextAppearance.StatusBar.Expanded.UserSwitcher">
<item name="android:textSize">@dimen/kg_user_switcher_text_size</item>
<item name="android:textStyle">normal</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">?attr/wallpaperTextColor</item>
</style>
<style name="TextAppearance.StatusBar.Expanded.UserSwitcher.Activated">
<item name="android:fontWeight">700</item>
- <item name="android:textStyle">bold</item>
</style>
<style name="TextAppearance" />
@@ -582,7 +582,7 @@
<style name="MediaPlayer.Button" parent="@android:style/Widget.Material.Button.Borderless.Small">
<item name="android:background">@drawable/qs_media_light_source</item>
- <item name="android:tint">@android:color/white</item>
+ <item name="android:tint">?android:attr/textColorPrimary</item>
<item name="android:stateListAnimator">@anim/media_button_state_list_animator</item>
</style>
@@ -764,6 +764,7 @@
<style name="TextAppearance.PrivacyDialog">
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="UdfpsProgressBarStyle"
diff --git a/packages/SystemUI/res/xml/media_collapsed.xml b/packages/SystemUI/res/xml/media_collapsed.xml
index f834d6df15c2..f83e3a1d9b26 100644
--- a/packages/SystemUI/res/xml/media_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_collapsed.xml
@@ -36,25 +36,23 @@
app:layout_constraintTop_toTopOf="@id/icon"
app:layout_constraintBottom_toBottomOf="@id/icon"
app:layout_constraintStart_toEndOf="@id/icon"
- app:layout_constraintEnd_toStartOf="@id/media_seamless"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
+ app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline"
app:layout_constrainedWidth="true"
app:layout_constraintHorizontal_bias="0"
/>
<Constraint
android:id="@+id/media_seamless"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toEndOf="@id/app_name"
+ app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintHorizontal_bias="1"
app:layout_constrainedWidth="true"
app:layout_constraintWidth_min="48dp"
app:layout_constraintHeight_min="48dp"
- android:layout_marginEnd="@dimen/qs_center_guideline_padding"
android:layout_marginStart="@dimen/qs_center_guideline_padding"
/>
diff --git a/packages/SystemUI/res/xml/media_expanded.xml b/packages/SystemUI/res/xml/media_expanded.xml
index d89e0eb4df63..7c6772059695 100644
--- a/packages/SystemUI/res/xml/media_expanded.xml
+++ b/packages/SystemUI/res/xml/media_expanded.xml
@@ -36,25 +36,23 @@
app:layout_constraintTop_toTopOf="@id/icon"
app:layout_constraintBottom_toBottomOf="@id/icon"
app:layout_constraintStart_toEndOf="@id/icon"
- app:layout_constraintEnd_toStartOf="@id/media_seamless"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
+ app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline"
app:layout_constrainedWidth="true"
app:layout_constraintHorizontal_bias="0"
/>
<Constraint
android:id="@+id/media_seamless"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toEndOf="@id/app_name"
+ app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintHorizontal_bias="1"
app:layout_constrainedWidth="true"
app:layout_constraintWidth_min="48dp"
app:layout_constraintHeight_min="48dp"
- android:layout_marginEnd="@dimen/qs_center_guideline_padding"
android:layout_marginStart="@dimen/qs_center_guideline_padding"
/>
diff --git a/packages/SystemUI/res/xml/people_space_widget_info.xml b/packages/SystemUI/res/xml/people_space_widget_info.xml
index d0c63a8b180c..fbdac5e1789b 100644
--- a/packages/SystemUI/res/xml/people_space_widget_info.xml
+++ b/packages/SystemUI/res/xml/people_space_widget_info.xml
@@ -16,11 +16,11 @@
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="140dp"
- android:minHeight="40dp"
+ android:minHeight="55dp"
android:minResizeWidth="110dp"
- android:minResizeHeight="40dp"
+ android:minResizeHeight="55dp"
android:updatePeriodMillis="60000"
- android:previewImage="@drawable/ic_android"
+ android:previewImage="@drawable/ic_person"
android:resizeMode="horizontal|vertical"
android:configure="com.android.systemui.people.PeopleSpaceActivity"
android:initialLayout="@layout/people_space_widget">
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 606fd2c1848e..09e9675a3277 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
genrule {
name: "statslog-SystemUI-java-gen",
tools: ["stats-log-api-gen"],
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 7f04f28198c0..72e4061829fa 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
@@ -32,8 +32,29 @@ public class PipSurfaceTransactionHelper {
private final Matrix mTmpTransform = new Matrix();
private final float[] mTmpFloat9 = new float[9];
private final RectF mTmpSourceRectF = new RectF();
+ private final RectF mTmpDestinationRectF = new RectF();
private final Rect mTmpDestinationRect = new Rect();
+ public void scale(SurfaceControl.Transaction tx, SurfaceControl leash,
+ Rect sourceBounds, Rect destinationBounds) {
+ mTmpSourceRectF.set(sourceBounds);
+ mTmpDestinationRectF.set(destinationBounds);
+ mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
+ tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
+ .setPosition(leash, mTmpDestinationRectF.left, mTmpDestinationRectF.top);
+ }
+
+ public void scale(SurfaceControl.Transaction tx, SurfaceControl leash,
+ Rect sourceBounds, Rect destinationBounds,
+ float degree, float positionX, float positionY) {
+ mTmpSourceRectF.set(sourceBounds);
+ mTmpDestinationRectF.set(destinationBounds);
+ mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
+ mTmpTransform.postRotate(degree, 0, 0);
+ tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
+ .setPosition(leash, positionX, positionY);
+ }
+
public void scaleAndCrop(SurfaceControl.Transaction tx, SurfaceControl leash,
Rect sourceBounds, Rect destinationBounds, Rect insets) {
mTmpSourceRectF.set(sourceBounds);
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 e38cf237d36b..5c943f63a9a1 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
@@ -245,9 +245,11 @@ interface ISystemUiProxy {
void setSideStageVisibility(in boolean visible) = 36;
/** Removes the split-screen stages. */
void exitSplitScreen() = 37;
- void startTask(in int taskId, in int stage, in int position, in Bundle options) = 38;
+ /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */
+ void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 38;
+ void startTask(in int taskId, in int stage, in int position, in Bundle options) = 39;
void startShortcut(in String packageName, in String shortcutId, in int stage, in int position,
- in Bundle options, in UserHandle user) = 39;
+ in Bundle options, in UserHandle user) = 40;
void startIntent(
- in PendingIntent intent, in int stage, in int position, in Bundle options) = 40;
+ in PendingIntent intent, in int stage, in int position, in Bundle options) = 41;
}
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 186379af4b1d..ebb6e30d4b3b 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
@@ -43,17 +43,6 @@ public class Task {
public static final String TAG = "Task";
- /* Task callbacks */
- @Deprecated
- public interface TaskCallbacks {
- /* Notifies when a task has been bound */
- void onTaskDataLoaded(Task task, ThumbnailData thumbnailData);
- /* Notifies when a task has been unbound */
- void onTaskDataUnloaded();
- /* Notifies when a task's windowing mode has changed. */
- void onTaskWindowingModeChanged();
- }
-
/**
* The Task Key represents the unique primary key for the task
*/
@@ -209,12 +198,6 @@ public class Task {
public TaskKey key;
/**
- * The temporary sort index in the stack, used when ordering the stack.
- */
- @Deprecated
- public int temporarySortIndexInStack;
-
- /**
* The icon is the task description icon (if provided), which falls back to the activity icon,
* which can then fall back to the application icon.
*/
@@ -229,45 +212,24 @@ public class Task {
public int colorPrimary;
@ViewDebug.ExportedProperty(category="recents")
public int colorBackground;
- @ViewDebug.ExportedProperty(category="recents")
- @Deprecated
- public boolean useLightOnPrimaryColor;
/**
* The task description for this task, only used to reload task icons.
*/
public TaskDescription taskDescription;
- /**
- * The state isLaunchTarget will be set for the correct task upon launching Recents.
- */
- @ViewDebug.ExportedProperty(category="recents")
- @Deprecated
- public boolean isLaunchTarget;
- @ViewDebug.ExportedProperty(category="recents")
- @Deprecated
- public boolean isStackTask;
- @ViewDebug.ExportedProperty(category="recents")
- @Deprecated
- public boolean isSystemApp;
@ViewDebug.ExportedProperty(category="recents")
public boolean isDockable;
- /**
- * Resize mode. See {@link ActivityInfo#resizeMode}.
- */
- @ViewDebug.ExportedProperty(category="recents")
- @Deprecated
- public int resizeMode;
-
@ViewDebug.ExportedProperty(category="recents")
public ComponentName topActivity;
@ViewDebug.ExportedProperty(category="recents")
public boolean isLocked;
- @Deprecated
- private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
+ // Last snapshot data, only used for recent tasks
+ public ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
+ new ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData();
public Task() {
// Do nothing
@@ -289,6 +251,16 @@ public class Task {
this.taskDescription = new TaskDescription();
}
+ public Task(Task other) {
+ this(other.key, other.colorPrimary, other.colorBackground, other.isDockable,
+ other.isLocked, other.taskDescription, other.topActivity);
+ lastSnapshotData.set(other.lastSnapshotData);
+ }
+
+ /**
+ * Use {@link Task#Task(Task)}.
+ */
+ @Deprecated
public Task(TaskKey key, int colorPrimary, int colorBackground,
boolean isDockable, boolean isLocked, TaskDescription taskDescription,
ComponentName topActivity) {
@@ -301,110 +273,32 @@ public class Task {
this.topActivity = topActivity;
}
- @Deprecated
- public Task(TaskKey key, Drawable icon, ThumbnailData thumbnail, String title,
- String titleDescription, int colorPrimary, int colorBackground, boolean isLaunchTarget,
- boolean isStackTask, boolean isSystemApp, boolean isDockable,
- TaskDescription taskDescription, int resizeMode, ComponentName topActivity,
- boolean isLocked) {
- this.key = key;
- this.icon = icon;
- this.thumbnail = thumbnail;
- this.title = title;
- this.titleDescription = titleDescription;
- this.colorPrimary = colorPrimary;
- this.colorBackground = colorBackground;
- this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
- Color.WHITE) > 3f;
- this.taskDescription = taskDescription;
- this.isLaunchTarget = isLaunchTarget;
- this.isStackTask = isStackTask;
- this.isSystemApp = isSystemApp;
- this.isDockable = isDockable;
- this.resizeMode = resizeMode;
- this.topActivity = topActivity;
- this.isLocked = isLocked;
- }
-
/**
- * Copies the metadata from another task, but retains the current callbacks.
+ * Returns the top activity component.
*/
- @Deprecated
- public void copyFrom(Task o) {
- this.key = o.key;
- this.icon = o.icon;
- this.thumbnail = o.thumbnail;
- this.title = o.title;
- this.titleDescription = o.titleDescription;
- this.colorPrimary = o.colorPrimary;
- this.colorBackground = o.colorBackground;
- this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
- this.taskDescription = o.taskDescription;
- this.isLaunchTarget = o.isLaunchTarget;
- this.isStackTask = o.isStackTask;
- this.isSystemApp = o.isSystemApp;
- this.isDockable = o.isDockable;
- this.resizeMode = o.resizeMode;
- this.isLocked = o.isLocked;
- this.topActivity = o.topActivity;
+ public ComponentName getTopComponent() {
+ return topActivity != null
+ ? topActivity
+ : key.baseIntent.getComponent();
}
- /**
- * Add a callback.
- */
- @Deprecated
- public void addCallback(TaskCallbacks cb) {
- if (!mCallbacks.contains(cb)) {
- mCallbacks.add(cb);
- }
+ public void setLastSnapshotData(ActivityManager.RecentTaskInfo rawTask) {
+ lastSnapshotData.set(rawTask.lastSnapshotData);
}
/**
- * Remove a callback.
+ * Returns the visible width to height ratio. Returns 0f if snapshot data is not available.
*/
- @Deprecated
- public void removeCallback(TaskCallbacks cb) {
- mCallbacks.remove(cb);
- }
-
- /** Updates the task's windowing mode. */
- @Deprecated
- public void setWindowingMode(int windowingMode) {
- key.setWindowingMode(windowingMode);
- int callbackCount = mCallbacks.size();
- for (int i = 0; i < callbackCount; i++) {
- mCallbacks.get(i).onTaskWindowingModeChanged();
+ public float getVisibleThumbnailRatio() {
+ if (lastSnapshotData.taskSize == null || lastSnapshotData.contentInsets == null) {
+ return 0f;
}
- }
- /** Notifies the callback listeners that this task has been loaded */
- @Deprecated
- public void notifyTaskDataLoaded(ThumbnailData thumbnailData, Drawable applicationIcon) {
- this.icon = applicationIcon;
- this.thumbnail = thumbnailData;
- int callbackCount = mCallbacks.size();
- for (int i = 0; i < callbackCount; i++) {
- mCallbacks.get(i).onTaskDataLoaded(this, thumbnailData);
- }
- }
-
- /** Notifies the callback listeners that this task has been unloaded */
- @Deprecated
- public void notifyTaskDataUnloaded(Drawable defaultApplicationIcon) {
- icon = defaultApplicationIcon;
- thumbnail = null;
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).onTaskDataUnloaded();
- }
- }
-
- /**
- * Returns the top activity component.
- */
- public ComponentName getTopComponent() {
- return topActivity != null
- ? topActivity
- : key.baseIntent.getComponent();
+ float availableWidth = lastSnapshotData.taskSize.x - (lastSnapshotData.contentInsets.left
+ + lastSnapshotData.contentInsets.right);
+ float availableHeight = lastSnapshotData.taskSize.y - (lastSnapshotData.contentInsets.top
+ + lastSnapshotData.contentInsets.bottom);
+ return availableWidth / availableHeight;
}
@Override
@@ -424,9 +318,6 @@ public class Task {
if (!isDockable) {
writer.print(" dockable=N");
}
- if (isLaunchTarget) {
- writer.print(" launchTarget=Y");
- }
if (isLocked) {
writer.print(" locked=Y");
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java
index ffde84128549..259cca8c01e2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java
@@ -16,13 +16,12 @@
package com.android.systemui.shared.system;
-import android.os.Bundle;
+import android.graphics.Matrix;
import android.os.Looper;
import android.view.BatchedInputEventReceiver;
import android.view.Choreographer;
import android.view.InputChannel;
import android.view.InputEvent;
-import android.view.InputEventSender;
import android.view.MotionEvent;
/**
@@ -53,6 +52,12 @@ public class InputChannelCompat {
return target.addBatch(src);
}
+ /** @see MotionEvent#createRotateMatrix */
+ public static Matrix createRotationMatrix(
+ /*@Surface.Rotation*/ int rotation, int displayW, int displayH) {
+ return MotionEvent.createRotateMatrix(rotation, displayW, displayH);
+ }
+
/**
* @see BatchedInputEventReceiver
*/
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
index 19e7d7ea4e6e..bec9220028b1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -17,7 +17,6 @@
package com.android.systemui.shared.system;
import android.annotation.IntDef;
-import android.annotation.NonNull;
import android.view.View;
import com.android.internal.jank.InteractionJankMonitor;
@@ -37,6 +36,10 @@ public final class InteractionJankMonitorWrapper {
InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP;
public static final int CUJ_QUICK_SWITCH =
InteractionJankMonitor.CUJ_LAUNCHER_QUICK_SWITCH;
+ public static final int CUJ_OPEN_ALL_APPS =
+ InteractionJankMonitor.CUJ_LAUNCHER_OPEN_ALL_APPS;
+ public static final int CUJ_ALL_APPS_SCROLL =
+ InteractionJankMonitor.CUJ_LAUNCHER_ALL_APPS_SCROLL;
@IntDef({
CUJ_APP_LAUNCH_FROM_RECENTS,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PeopleProviderUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PeopleProviderUtils.java
new file mode 100644
index 000000000000..15cf3696fec7
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PeopleProviderUtils.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.system;
+
+/**
+ * These strings are part of the {@link com.android.systemui.people.PeopleProvider} API
+ * contract. The API returns a People Tile preview that can be displayed by calling packages.
+ * The provider is part of the SystemUI service, and the strings live here for shared access with
+ * Launcher (caller).
+ */
+public class PeopleProviderUtils {
+ /**
+ * ContentProvider URI scheme.
+ * @hide
+ */
+ public static final String PEOPLE_PROVIDER_SCHEME = "content://";
+
+ /**
+ * ContentProvider URI authority.
+ * @hide
+ */
+ public static final String PEOPLE_PROVIDER_AUTHORITY =
+ "com.android.systemui.people.PeopleProvider";
+
+ /**
+ * Method name for getting People Tile preview.
+ * @hide
+ */
+ public static final String GET_PEOPLE_TILE_PREVIEW_METHOD = "get_people_tile_preview";
+
+ /**
+ * Extras bundle key specifying shortcut Id of the People Tile preview requested.
+ * @hide
+ */
+ public static final String EXTRAS_KEY_SHORTCUT_ID = "shortcut_id";
+
+ /**
+ * Extras bundle key specifying package name of the People Tile preview requested.
+ * @hide
+ */
+ public static final String EXTRAS_KEY_PACKAGE_NAME = "package_name";
+
+ /**
+ * Extras bundle key specifying {@code UserHandle} of the People Tile preview requested.
+ * @hide
+ */
+ public static final String EXTRAS_KEY_USER_HANDLE = "user_handle";
+
+ /**
+ * Response bundle key to access the returned People Tile preview.
+ * @hide
+ */
+ public static final String RESPONSE_KEY_REMOTE_VIEWS = "remote_views";
+
+ /**
+ * Name of the permission needed to get a People Tile preview for a given conversation shortcut.
+ * @hide
+ */
+ public static final String GET_PEOPLE_TILE_PREVIEW_PERMISSION =
+ "android.permission.GET_PEOPLE_TILE_PREVIEW";
+
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index 70021b6f3d45..fbabaa489d74 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -114,10 +114,13 @@ public class SyncRtSurfaceTransactionApplierCompat {
for (int i = params.length - 1; i >= 0; i--) {
SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams =
params[i];
- t.deferTransactionUntil(surfaceParams.surface, mBarrierSurfaceControl, frame);
surfaceParams.applyTo(t);
}
- t.apply();
+ if (mTargetViewRootImpl != null) {
+ mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
+ } else {
+ t.apply();
+ }
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
.sendToTarget();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 6c77af7bbddd..c5d54391959a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -100,4 +100,10 @@ public abstract class TaskStackChangeListener {
/** @see ITaskStackListener#onActivityRotation(int)*/
public void onActivityRotation(int displayId) { }
+
+ /**
+ * Called when the lock task mode changes. See ActivityManager#LOCK_TASK_MODE_* and
+ * LockTaskController.
+ */
+ public void onLockTaskModeChanged(int mode) { }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 8f08f5a51143..d2698ee9fe3b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -93,6 +93,7 @@ public class TaskStackChangeListeners {
private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 20;
private static final int ON_TASK_DESCRIPTION_CHANGED = 21;
private static final int ON_ACTIVITY_ROTATION = 22;
+ private static final int ON_LOCK_TASK_MODE_CHANGED = 23;
/**
* List of {@link TaskStackChangeListener} registered from {@link #addListener}.
@@ -273,6 +274,11 @@ public class TaskStackChangeListeners {
}
@Override
+ public void onLockTaskModeChanged(int mode) {
+ mHandler.obtainMessage(ON_LOCK_TASK_MODE_CHANGED, mode, 0 /* unused */).sendToTarget();
+ }
+
+ @Override
public boolean handleMessage(Message msg) {
synchronized (mTaskStackListeners) {
switch (msg.what) {
@@ -421,6 +427,12 @@ public class TaskStackChangeListeners {
}
break;
}
+ case ON_LOCK_TASK_MODE_CHANGED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onLockTaskModeChanged(msg.arg1);
+ }
+ break;
+ }
}
}
if (msg.obj instanceof SomeArgs) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
index 4a28d56a41e1..89c60f1d3f06 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
@@ -56,4 +56,12 @@ public class ViewRootImplCompat {
});
}
}
+
+ public void mergeWithNextTransaction(SurfaceControl.Transaction t, long frame) {
+ if (mViewRoot != null) {
+ mViewRoot.mergeWithNextTransaction(t, frame);
+ } else {
+ t.apply();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 59e81cf96bb2..0a117c17a354 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -16,37 +16,72 @@
package com.android.keyguard;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.graphics.Color;
import android.graphics.Paint;
+import android.icu.text.NumberFormat;
import android.util.MathUtils;
import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.util.ViewController;
+import java.util.Locale;
+import java.util.Objects;
import java.util.TimeZone;
/**
- * Controls the color of a GradientTextClock.
+ * Controller for an AnimatableClockView.
*/
public class AnimatableClockController extends ViewController<AnimatableClockView> {
+ private static final int FORMAT_NUMBER = 1234567890;
private final StatusBarStateController mStatusBarStateController;
- private final int[] mDozingColors = new int[] {Color.WHITE, Color.WHITE};
- private int[] mLockScreenColors = new int[2];
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final int mDozingColor = Color.WHITE;
+ private int mLockScreenColor;
private boolean mIsDozing;
+ private Locale mLocale;
+
+ private final NumberFormat mBurmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"));
+ private final String mBurmeseNumerals;
+ private final float mBurmeseLineSpacing;
+ private final float mDefaultLineSpacing;
public AnimatableClockController(
AnimatableClockView view,
- StatusBarStateController statusBarStateController) {
+ StatusBarStateController statusBarStateController,
+ BroadcastDispatcher broadcastDispatcher) {
super(view);
mStatusBarStateController = statusBarStateController;
mIsDozing = mStatusBarStateController.isDozing();
+ mBroadcastDispatcher = broadcastDispatcher;
+
+ mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
+ mBurmeseLineSpacing = getContext().getResources().getFloat(
+ R.dimen.keyguard_clock_line_spacing_scale_burmese);
+ mDefaultLineSpacing = getContext().getResources().getFloat(
+ R.dimen.keyguard_clock_line_spacing_scale);
}
+ private BroadcastReceiver mLocaleBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateLocale();
+ }
+ };
+
@Override
protected void onViewAttached() {
+ updateLocale();
+ mBroadcastDispatcher.registerReceiver(mLocaleBroadcastReceiver,
+ new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
mStatusBarStateController.addCallback(mStatusBarStateListener);
mIsDozing = mStatusBarStateController.isDozing();
refreshTime();
@@ -55,6 +90,7 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
@Override
protected void onViewDetached() {
+ mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
}
@@ -84,11 +120,23 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
mView.refreshFormat();
}
+ private void updateLocale() {
+ Locale currLocale = Locale.getDefault();
+ if (!Objects.equals(currLocale, mLocale)) {
+ mLocale = currLocale;
+ NumberFormat nf = NumberFormat.getInstance(mLocale);
+ if (nf.format(FORMAT_NUMBER).equals(mBurmeseNumerals)) {
+ mView.setLineSpacingScale(mBurmeseLineSpacing);
+ } else {
+ mView.setLineSpacingScale(mDefaultLineSpacing);
+ }
+ }
+ }
+
private void initColors() {
- mLockScreenColors[0] = Utils.getColorAttrDefaultColor(getContext(),
+ mLockScreenColor = Utils.getColorAttrDefaultColor(getContext(),
com.android.systemui.R.attr.wallpaperTextColor);
- mLockScreenColors[1] = mLockScreenColors[0]; // same color
- mView.setColors(mDozingColors, mLockScreenColors);
+ mView.setColors(mDozingColor, mLockScreenColor);
mView.animateDoze(mIsDozing, false);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
index ca99563986b4..64b3d7356d3c 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
@@ -44,12 +44,13 @@ public class AnimatableClockView extends TextView {
private final Calendar mTime = Calendar.getInstance();
- private CharSequence mFormat;
- private CharSequence mDescFormat;
- private int[] mDozingColors;
- private int[] mLockScreenColors;
private final int mDozingWeight;
private final int mLockScreenWeight;
+ private CharSequence mFormat;
+ private CharSequence mDescFormat;
+ private int mDozingColor;
+ private int mLockScreenColor;
+ private float mLineSpacingScale = 1f;
private TextAnimator mTextAnimator = null;
private Runnable mOnTextAnimatorInitialized;
@@ -111,8 +112,7 @@ public class AnimatableClockView extends TextView {
() -> {
invalidate();
return Unit.INSTANCE;
- },
- 2 /* number of lines (each can have a unique Paint) */);
+ });
if (mOnTextAnimatorInitialized != null) {
mOnTextAnimatorInitialized.run();
mOnTextAnimatorInitialized = null;
@@ -127,15 +127,20 @@ public class AnimatableClockView extends TextView {
mTextAnimator.draw(canvas);
}
- void setColors(int[] dozingColors, int[] lockScreenColors) {
- mDozingColors = dozingColors;
- mLockScreenColors = lockScreenColors;
+ void setLineSpacingScale(float scale) {
+ mLineSpacingScale = scale;
+ setLineSpacing(0, mLineSpacingScale);
+ }
+
+ void setColors(int dozingColor, int lockScreenColor) {
+ mDozingColor = dozingColor;
+ mLockScreenColor = lockScreenColor;
}
void animateDoze(boolean isDozing, boolean animate) {
setTextStyle(isDozing ? mDozingWeight : mLockScreenWeight /* weight */,
-1,
- isDozing ? mDozingColors : mLockScreenColors,
+ isDozing ? mDozingColor : mLockScreenColor,
animate);
}
@@ -152,15 +157,15 @@ public class AnimatableClockView extends TextView {
private void setTextStyle(
@IntRange(from = 0, to = 1000) int weight,
@FloatRange(from = 0) float textSize,
- int[] colors,
+ int color,
boolean animate) {
if (mTextAnimator != null) {
- mTextAnimator.setTextStyle(weight, textSize, colors, animate, ANIM_DURATION, null);
+ mTextAnimator.setTextStyle(weight, textSize, color, animate, ANIM_DURATION, null);
} else {
// when the text animator is set, update its start values
mOnTextAnimatorInitialized =
() -> mTextAnimator.setTextStyle(
- weight, textSize, colors, false, ANIM_DURATION, null);
+ weight, textSize, color, false, ANIM_DURATION, null);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index e0de180e657c..e375877ed6cf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -28,6 +28,7 @@ import android.widget.FrameLayout;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ClockPlugin;
@@ -56,6 +57,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private final ClockManager mClockManager;
private final KeyguardSliceViewController mKeyguardSliceViewController;
private final NotificationIconAreaController mNotificationIconAreaController;
+ private final BroadcastDispatcher mBroadcastDispatcher;
/**
* Gradient clock for usage when mode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL.
@@ -101,7 +103,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
SysuiColorExtractor colorExtractor, ClockManager clockManager,
KeyguardSliceViewController keyguardSliceViewController,
NotificationIconAreaController notificationIconAreaController,
- ContentResolver contentResolver) {
+ ContentResolver contentResolver,
+ BroadcastDispatcher broadcastDispatcher) {
super(keyguardClockSwitch);
mResources = resources;
mStatusBarStateController = statusBarStateController;
@@ -109,6 +112,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mClockManager = clockManager;
mKeyguardSliceViewController = keyguardSliceViewController;
mNotificationIconAreaController = notificationIconAreaController;
+ mBroadcastDispatcher = broadcastDispatcher;
mTimeFormat = Settings.System.getString(contentResolver, Settings.System.TIME_12_24);
}
@@ -231,12 +235,14 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mNewLockScreenClockViewController =
new AnimatableClockController(
mView.findViewById(R.id.animatable_clock_view),
- mStatusBarStateController);
+ mStatusBarStateController,
+ mBroadcastDispatcher);
mNewLockScreenClockViewController.init();
mNewLockScreenLargeClockViewController =
new AnimatableClockController(
mView.findViewById(R.id.animatable_clock_view_large),
- mStatusBarStateController);
+ mStatusBarStateController,
+ mBroadcastDispatcher);
mNewLockScreenLargeClockViewController.init();
}
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 1c691e7098a1..957882dc9c6b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -28,6 +28,7 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -157,6 +158,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
private LiftToActivateListener mLiftToActivateListener;
private TelephonyManager mTelephonyManager;
private final FalsingCollector mFalsingCollector;
+ private final boolean mIsNewLayoutEnabled;
@Inject
public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -165,7 +167,9 @@ 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;
mLatencyTracker = latencyTracker;
@@ -176,6 +180,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
mLiftToActivateListener = liftToActivateListener;
mTelephonyManager = telephonyManager;
mFalsingCollector = falsingCollector;
+ mIsNewLayoutEnabled = featureFlags.isKeyguardLayoutEnabled();
}
/** Create a new {@link KeyguardInputViewController}. */
@@ -194,17 +199,19 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
- mLiftToActivateListener, mFalsingCollector);
+ mLiftToActivateListener, mFalsingCollector, mIsNewLayoutEnabled);
} else if (keyguardInputView instanceof KeyguardSimPinView) {
return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
- mLiftToActivateListener, mTelephonyManager, mFalsingCollector);
+ mLiftToActivateListener, mTelephonyManager,
+ mFalsingCollector, mIsNewLayoutEnabled);
} else if (keyguardInputView instanceof KeyguardSimPukView) {
return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
- mLiftToActivateListener, mTelephonyManager, mFalsingCollector);
+ mLiftToActivateListener, mTelephonyManager,
+ mFalsingCollector, mIsNewLayoutEnabled);
}
throw new RuntimeException("Unable to find controller for " + keyguardInputView);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 6a6b964c2a8f..4e06491621cb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -24,14 +24,12 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
import com.android.internal.widget.LockscreenCredential;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
/**
@@ -167,6 +165,20 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
}
/**
+ * By default, the new layout will be enabled. When false, revert to the old style.
+ */
+ public void setIsNewLayoutEnabled(boolean isEnabled) {
+ if (!isEnabled) {
+ for (int i = 0; i < mButtons.length; i++) {
+ mButtons[i].disableNewLayout();
+ }
+ mDeleteButton.disableNewLayout();
+ mOkButton.disableNewLayout();
+ reloadColors();
+ }
+ }
+
+ /**
* Reload colors from resources.
**/
public void reloadColors() {
@@ -174,10 +186,6 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
key.reloadColors();
}
mPasswordEntry.reloadColors();
- int deleteColor = Utils.getColorAttr(getContext(), android.R.attr.textColorSecondary)
- .getDefaultColor();
- mDeleteButton.setImageTintList(ColorStateList.valueOf(deleteColor));
-
mDeleteButton.reloadColors();
mOkButton.reloadColors();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index c0aa2af00a74..49099fa18323 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -34,11 +34,12 @@ public class KeyguardPinViewController
KeyguardSecurityCallback keyguardSecurityCallback,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
- FalsingCollector falsingCollector) {
+ FalsingCollector falsingCollector, boolean isNewLayoutEnabled) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
falsingCollector);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ view.setIsNewLayoutEnabled(isNewLayoutEnabled);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index c182fd178202..5f6fd30ffa1b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -29,17 +29,12 @@ import android.app.AlertDialog;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
-import android.provider.Settings;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.util.TypedValue;
-import android.view.Gravity;
import android.view.MotionEvent;
-import android.view.OrientationEventListener;
import android.view.VelocityTracker;
-import android.view.View;
import android.view.ViewConfiguration;
-import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimationControlListener;
@@ -60,7 +55,6 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.util.List;
@@ -105,12 +99,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
private boolean mDisappearAnimRunning;
private SwipeListener mSwipeListener;
- private boolean mIsSecurityViewLeftAligned = true;
- private boolean mOneHandedMode = false;
- private SecurityMode mSecurityMode = SecurityMode.Invalid;
- private ViewPropertyAnimator mRunningOneHandedAnimator;
- private final OrientationEventListener mOrientationEventListener;
-
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -169,20 +157,16 @@ public class KeyguardSecurityContainer extends FrameLayout {
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
-
void userActivity();
-
void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
/**
- * @param strongAuth wheher the user has authenticated with strong authentication like
- * pattern, password or PIN but not by trust agents or fingerprint
+ * @param strongAuth wheher the user has authenticated with strong authentication like
+ * pattern, password or PIN but not by trust agents or fingerprint
* @param targetUserId a user that needs to be the foreground user at the finish completion.
*/
void finish(boolean strongAuth, int targetUserId);
-
void reset();
-
void onCancelClicked();
}
@@ -240,136 +224,12 @@ public class KeyguardSecurityContainer extends FrameLayout {
super(context, attrs, defStyle);
mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
mViewConfiguration = ViewConfiguration.get(context);
-
- mOrientationEventListener = new OrientationEventListener(context) {
- @Override
- public void onOrientationChanged(int orientation) {
- updateLayoutForSecurityMode(mSecurityMode);
- }
- };
}
void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
- mSecurityMode = securityMode;
mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
updateBiometricRetry(securityMode, faceAuthEnabled);
- updateLayoutForSecurityMode(securityMode);
- mOrientationEventListener.enable();
- }
-
- void updateLayoutForSecurityMode(SecurityMode securityMode) {
- mSecurityMode = securityMode;
- mOneHandedMode = canUseOneHandedBouncer();
-
- if (mOneHandedMode) {
- mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext);
- }
-
- updateSecurityViewGravity();
- updateSecurityViewLocation(false);
- }
-
- /** Return whether the one-handed keyguard should be enabled. */
- private boolean canUseOneHandedBouncer() {
- // Is it enabled?
- if (!getResources().getBoolean(
- com.android.internal.R.bool.config_enableOneHandedKeyguard)) {
- return false;
- }
-
- if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) {
- return false;
- }
-
- return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
- }
-
- /** Read whether the one-handed keyguard should be on the left/right from settings. */
- private boolean isOneHandedKeyguardLeftAligned(Context context) {
- try {
- return Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
- == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
- } catch (Settings.SettingNotFoundException ex) {
- return true;
- }
- }
-
- private void updateSecurityViewGravity() {
- View securityView = findKeyguardSecurityView();
-
- if (securityView == null) {
- return;
- }
-
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams();
-
- if (mOneHandedMode) {
- lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
- } else {
- lp.gravity = Gravity.CENTER;
- }
-
- securityView.setLayoutParams(lp);
- }
-
- /**
- * Moves the inner security view to the correct location (in one handed mode) with animation.
- * This is triggered when the user taps on the side of the screen that is not currently occupied
- * by the security view .
- */
- private void updateSecurityViewLocation(boolean animate) {
- View securityView = findKeyguardSecurityView();
-
- if (securityView == null) {
- return;
- }
-
- if (!mOneHandedMode) {
- securityView.setTranslationX(0);
- return;
- }
-
- if (mRunningOneHandedAnimator != null) {
- mRunningOneHandedAnimator.cancel();
- mRunningOneHandedAnimator = null;
- }
-
- int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f);
-
- if (animate) {
- mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation);
- mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mRunningOneHandedAnimator = null;
- }
- });
-
- mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- mRunningOneHandedAnimator.start();
- } else {
- securityView.setTranslationX(targetTranslation);
- }
- }
-
- @Nullable
- private KeyguardSecurityViewFlipper findKeyguardSecurityView() {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
-
- if (isKeyguardSecurityView(child)) {
- return (KeyguardSecurityViewFlipper) child;
- }
- }
-
- return null;
- }
-
- private boolean isKeyguardSecurityView(View view) {
- return view instanceof KeyguardSecurityViewFlipper;
}
public void onPause() {
@@ -378,7 +238,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
mAlertDialog = null;
}
mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
- mOrientationEventListener.disable();
}
@Override
@@ -460,44 +319,19 @@ public class KeyguardSecurityContainer extends FrameLayout {
if (mSwipeListener != null) {
mSwipeListener.onSwipeUp();
}
- } else {
- if (!mIsDragging) {
- handleTap(event);
- }
}
}
return true;
}
- private void handleTap(MotionEvent event) {
- // If we're using a fullscreen security mode, skip
- if (!mOneHandedMode) {
- return;
- }
-
- // Did the tap hit the "other" side of the bouncer?
- if ((mIsSecurityViewLeftAligned && (event.getX() > getWidth() / 2f))
- || (!mIsSecurityViewLeftAligned && (event.getX() < getWidth() / 2f))) {
- mIsSecurityViewLeftAligned = !mIsSecurityViewLeftAligned;
-
- Settings.Global.putInt(
- mContext.getContentResolver(),
- Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
- mIsSecurityViewLeftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
- : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
-
- updateSecurityViewLocation(true);
- }
- }
-
void setSwipeListener(SwipeListener swipeListener) {
mSwipeListener = swipeListener;
}
private void startSpringAnimation(float startVelocity) {
mSpringAnimation
- .setStartVelocity(startVelocity)
- .animateToFinalPosition(0);
+ .setStartVelocity(startVelocity)
+ .animateToFinalPosition(0);
}
public void startDisappearAnimation(SecurityMode securitySelection) {
@@ -607,17 +441,18 @@ public class KeyguardSecurityContainer extends FrameLayout {
return insets.inset(0, 0, 0, inset);
}
+
private void showDialog(String title, String message) {
if (mAlertDialog != null) {
mAlertDialog.dismiss();
}
mAlertDialog = new AlertDialog.Builder(mContext)
- .setTitle(title)
- .setMessage(message)
- .setCancelable(false)
- .setNeutralButton(R.string.ok, null)
- .create();
+ .setTitle(title)
+ .setMessage(message)
+ .setCancelable(false)
+ .setNeutralButton(R.string.ok, null)
+ .create();
if (!(mContext instanceof Activity)) {
mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
}
@@ -655,44 +490,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int maxHeight = 0;
- int maxWidth = 0;
- int childState = 0;
-
- int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(widthMeasureSpec) / 2,
- MeasureSpec.getMode(widthMeasureSpec));
-
- for (int i = 0; i < getChildCount(); i++) {
- final View view = getChildAt(i);
- if (view.getVisibility() != GONE) {
- if (mOneHandedMode && isKeyguardSecurityView(view)) {
- measureChildWithMargins(view, halfWidthMeasureSpec, 0,
- heightMeasureSpec, 0);
- } else {
- measureChildWithMargins(view, widthMeasureSpec, 0,
- heightMeasureSpec, 0);
- }
- final LayoutParams lp = (LayoutParams) view.getLayoutParams();
- maxWidth = Math.max(maxWidth,
- view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
- maxHeight = Math.max(maxHeight,
- view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
- childState = combineMeasuredStates(childState, view.getMeasuredState());
- }
- }
-
- // Check against our minimum height and width
- maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
- maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
-
- setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
- resolveSizeAndState(maxHeight, heightMeasureSpec,
- childState << MEASURED_HEIGHT_STATE_SHIFT));
- }
-
void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
String message = null;
switch (userType) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index fdab8db67431..1a8d420fb394 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -404,7 +404,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
if (newView != null) {
newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
mSecurityViewFlipperController.show(newView);
- mView.updateLayoutForSecurityMode(securityMode);
}
mSecurityCallback.onSecurityModeChanged(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index 2b73894e3fd8..29d09914a9b4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -93,13 +93,4 @@ public class KeyguardSecurityModel {
throw new IllegalStateException("Unknown security quality:" + security);
}
}
-
- /**
- * Returns whether the given security view should be used in a "one handed" way. This can be
- * used to change how the security view is drawn (e.g. take up less of the screen, and align to
- * one side).
- */
- public static boolean isSecurityViewOneHanded(SecurityMode securityMode) {
- return securityMode == SecurityMode.Pattern || securityMode == SecurityMode.PIN;
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 107fd772d68b..84a705f0738e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -79,13 +79,15 @@ public class KeyguardSimPinViewController
KeyguardSecurityCallback keyguardSecurityCallback,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
- TelephonyManager telephonyManager, FalsingCollector falsingCollector) {
+ TelephonyManager telephonyManager,
+ FalsingCollector falsingCollector, boolean isNewLayoutEnabled) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
falsingCollector);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
+ view.setIsNewLayoutEnabled(isNewLayoutEnabled);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 4a1b57d64c89..5d6aeaa938ed 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -85,13 +85,15 @@ public class KeyguardSimPukViewController
KeyguardSecurityCallback keyguardSecurityCallback,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
- TelephonyManager telephonyManager, FalsingCollector falsingCollector) {
+ TelephonyManager telephonyManager,
+ FalsingCollector falsingCollector, boolean isNewLayoutEnabled) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
falsingCollector);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
+ view.setIsNewLayoutEnabled(isNewLayoutEnabled);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 97aa26fb7f68..fea152abe36a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -56,8 +56,10 @@ public class KeyguardStatusView extends GridLayout {
private final IActivityManager mIActivityManager;
private TextView mLogoutView;
+ private boolean mCanShowLogout = true; // by default, try to show the logout button here
private KeyguardClockSwitch mClockView;
private TextView mOwnerInfo;
+ private boolean mCanShowOwnerInfo = true; // by default, try to show the owner information here
private KeyguardSliceView mKeyguardSlice;
private View mNotificationIcons;
private Runnable mPendingMarqueeStart;
@@ -114,6 +116,25 @@ public class KeyguardStatusView extends GridLayout {
if (mOwnerInfo != null) mOwnerInfo.setSelected(enabled);
}
+ void setCanShowOwnerInfo(boolean canShowOwnerInfo) {
+ mCanShowOwnerInfo = canShowOwnerInfo;
+ mOwnerInfo = findViewById(R.id.owner_info);
+ if (mOwnerInfo != null) {
+ if (mCanShowOwnerInfo) {
+ mOwnerInfo.setVisibility(VISIBLE);
+ updateOwnerInfo();
+ } else {
+ mOwnerInfo.setVisibility(GONE);
+ mOwnerInfo = null;
+ }
+ }
+ }
+
+ void setCanShowLogout(boolean canShowLogout) {
+ mCanShowLogout = canShowLogout;
+ updateLogoutView();
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -128,7 +149,10 @@ public class KeyguardStatusView extends GridLayout {
if (KeyguardClockAccessibilityDelegate.isNeeded(mContext)) {
mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext));
}
- mOwnerInfo = findViewById(R.id.owner_info);
+ if (mCanShowOwnerInfo) {
+ mOwnerInfo = findViewById(R.id.owner_info);
+ }
+
mKeyguardSlice = findViewById(R.id.keyguard_status_area);
mTextColor = mClockView.getCurrentTextColor();
@@ -189,7 +213,7 @@ public class KeyguardStatusView extends GridLayout {
if (mLogoutView == null) {
return;
}
- mLogoutView.setVisibility(shouldShowLogout() ? VISIBLE : GONE);
+ mLogoutView.setVisibility(mCanShowLogout && shouldShowLogout() ? VISIBLE : GONE);
// Logout button will stay in language of user 0 if we don't set that manually.
mLogoutView.setText(mContext.getResources().getString(
com.android.internal.R.string.global_action_logout));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 973b49384c09..6fb6760be653 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -16,13 +16,9 @@
package com.android.keyguard;
-import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-
import android.util.Slog;
import android.view.View;
-import com.android.systemui.Interpolators;
-import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -50,13 +46,12 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
private final KeyguardSliceViewController mKeyguardSliceViewController;
private final KeyguardClockSwitchController mKeyguardClockSwitchController;
- private final KeyguardStateController mKeyguardStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
private final NotificationIconAreaController mNotificationIconAreaController;
private final DozeParameters mDozeParameters;
+ private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
- private boolean mKeyguardStatusViewVisibilityAnimating;
private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
@Inject
@@ -72,11 +67,12 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
super(keyguardStatusView);
mKeyguardSliceViewController = keyguardSliceViewController;
mKeyguardClockSwitchController = keyguardClockSwitchController;
- mKeyguardStateController = keyguardStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mConfigurationController = configurationController;
mNotificationIconAreaController = notificationIconAreaController;
mDozeParameters = dozeParameters;
+ mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController,
+ dozeParameters);
}
@Override
@@ -134,7 +130,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
/**
- * Get the height of the logout button.
+ * Get the height of the owner information view.
*/
public int getOwnerInfoHeight() {
return mView.getOwnerInfoHeight();
@@ -144,7 +140,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
* Set keyguard status view alpha.
*/
public void setAlpha(float alpha) {
- if (!mKeyguardStatusViewVisibilityAnimating) {
+ if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
mView.setAlpha(alpha);
}
}
@@ -200,7 +196,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
public void updatePosition(int x, int y, float scale, boolean animate) {
// We animate the status view visible/invisible using Y translation, so don't change it
// while the animation is running.
- if (!mKeyguardStatusViewVisibilityAnimating) {
+ if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES,
animate);
}
@@ -230,69 +226,8 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
boolean keyguardFadingAway,
boolean goingToFullShade,
int oldStatusBarState) {
- mView.animate().cancel();
- mKeyguardStatusViewVisibilityAnimating = false;
- if ((!keyguardFadingAway && oldStatusBarState == KEYGUARD
- && statusBarState != KEYGUARD) || goingToFullShade) {
- mKeyguardStatusViewVisibilityAnimating = true;
- mView.animate()
- .alpha(0f)
- .setStartDelay(0)
- .setDuration(160)
- .setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction(
- mAnimateKeyguardStatusViewGoneEndRunnable);
- if (keyguardFadingAway) {
- mView.animate()
- .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
- .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration())
- .start();
- }
- } else if (oldStatusBarState == StatusBarState.SHADE_LOCKED && statusBarState == KEYGUARD) {
- mView.setVisibility(View.VISIBLE);
- mKeyguardStatusViewVisibilityAnimating = true;
- mView.setAlpha(0f);
- mView.animate()
- .alpha(1f)
- .setStartDelay(0)
- .setDuration(320)
- .setInterpolator(Interpolators.ALPHA_IN)
- .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
- } else if (statusBarState == KEYGUARD) {
- if (keyguardFadingAway) {
- mKeyguardStatusViewVisibilityAnimating = true;
- mView.animate()
- .alpha(0)
- .translationYBy(-getHeight() * 0.05f)
- .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
- .setDuration(125)
- .setStartDelay(0)
- .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable)
- .start();
- } else if (mDozeParameters.shouldControlUnlockedScreenOff()) {
- mKeyguardStatusViewVisibilityAnimating = true;
-
- mView.setVisibility(View.VISIBLE);
- mView.setAlpha(0f);
-
- float curTranslationY = mView.getTranslationY();
- mView.setTranslationY(curTranslationY - getHeight() * 0.1f);
- mView.animate()
- .setStartDelay((int) (StackStateAnimator.ANIMATION_DURATION_WAKEUP * .6f))
- .setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .alpha(1f)
- .translationY(curTranslationY)
- .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable)
- .start();
- } else {
- mView.setVisibility(View.VISIBLE);
- mView.setAlpha(1f);
- }
- } else {
- mView.setVisibility(View.GONE);
- mView.setAlpha(1f);
- }
+ mKeyguardVisibilityHelper.setViewVisibility(
+ statusBarState, keyguardFadingAway, goingToFullShade, oldStatusBarState);
}
private void refreshTime() {
@@ -335,9 +270,13 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
// of the top of the view
mKeyguardSliceViewController.updateTopMargin(
mKeyguardClockSwitchController.getClockTextTopPadding());
+ mView.setCanShowOwnerInfo(false);
+ mView.setCanShowLogout(false);
} else {
// reset margin
mKeyguardSliceViewController.updateTopMargin(0);
+ mView.setCanShowOwnerInfo(true);
+ mView.setCanShowLogout(false);
}
updateAodIcons();
}
@@ -389,19 +328,4 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
mView.updateLogoutView();
}
};
-
- private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = () -> {
- mKeyguardStatusViewVisibilityAnimating = false;
- mView.setVisibility(View.INVISIBLE);
- };
-
-
- private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = () -> {
- mKeyguardStatusViewVisibilityAnimating = false;
- mView.setVisibility(View.GONE);
- };
-
- private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = () -> {
- mKeyguardStatusViewVisibilityAnimating = false;
- };
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
new file mode 100644
index 000000000000..724e1f660fb9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -0,0 +1,137 @@
+/*
+ * 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.keyguard;
+
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+
+import android.view.View;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+/**
+ * Helper class for updating visibility of keyguard views based on keyguard and status bar state.
+ * This logic is shared by both the keyguard status view and the keyguard user switcher.
+ */
+public class KeyguardVisibilityHelper {
+
+ private View mView;
+ private final KeyguardStateController mKeyguardStateController;
+ private final DozeParameters mDozeParameters;
+ private boolean mKeyguardViewVisibilityAnimating;
+
+ public KeyguardVisibilityHelper(View view, KeyguardStateController keyguardStateController,
+ DozeParameters dozeParameters) {
+ mView = view;
+ mKeyguardStateController = keyguardStateController;
+ mDozeParameters = dozeParameters;
+ }
+
+ public boolean isVisibilityAnimating() {
+ return mKeyguardViewVisibilityAnimating;
+ }
+
+ /**
+ * Set the visibility of a keyguard view based on some new state.
+ */
+ public void setViewVisibility(
+ int statusBarState,
+ boolean keyguardFadingAway,
+ boolean goingToFullShade,
+ int oldStatusBarState) {
+ mView.animate().cancel();
+ mKeyguardViewVisibilityAnimating = false;
+ if ((!keyguardFadingAway && oldStatusBarState == KEYGUARD
+ && statusBarState != KEYGUARD) || goingToFullShade) {
+ mKeyguardViewVisibilityAnimating = true;
+ mView.animate()
+ .alpha(0f)
+ .setStartDelay(0)
+ .setDuration(160)
+ .setInterpolator(Interpolators.ALPHA_OUT)
+ .withEndAction(
+ mAnimateKeyguardStatusViewGoneEndRunnable);
+ if (keyguardFadingAway) {
+ mView.animate()
+ .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
+ .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration())
+ .start();
+ }
+ } else if (oldStatusBarState == StatusBarState.SHADE_LOCKED && statusBarState == KEYGUARD) {
+ mView.setVisibility(View.VISIBLE);
+ mKeyguardViewVisibilityAnimating = true;
+ mView.setAlpha(0f);
+ mView.animate()
+ .alpha(1f)
+ .setStartDelay(0)
+ .setDuration(320)
+ .setInterpolator(Interpolators.ALPHA_IN)
+ .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
+ } else if (statusBarState == KEYGUARD) {
+ if (keyguardFadingAway) {
+ mKeyguardViewVisibilityAnimating = true;
+ mView.animate()
+ .alpha(0)
+ .translationYBy(-mView.getHeight() * 0.05f)
+ .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
+ .setDuration(125)
+ .setStartDelay(0)
+ .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable)
+ .start();
+ } else if (mDozeParameters.shouldControlUnlockedScreenOff()) {
+ mKeyguardViewVisibilityAnimating = true;
+
+ mView.setVisibility(View.VISIBLE);
+ mView.setAlpha(0f);
+
+ float curTranslationY = mView.getTranslationY();
+ mView.setTranslationY(curTranslationY - mView.getHeight() * 0.1f);
+ mView.animate()
+ .setStartDelay((int) (StackStateAnimator.ANIMATION_DURATION_WAKEUP * .6f))
+ .setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .alpha(1f)
+ .translationY(curTranslationY)
+ .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable)
+ .start();
+ } else {
+ mView.setVisibility(View.VISIBLE);
+ mView.setAlpha(1f);
+ }
+ } else {
+ mView.setVisibility(View.GONE);
+ mView.setAlpha(1f);
+ }
+ }
+
+ private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = () -> {
+ mKeyguardViewVisibilityAnimating = false;
+ mView.setVisibility(View.INVISIBLE);
+ };
+
+ private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = () -> {
+ mKeyguardViewVisibilityAnimating = false;
+ mView.setVisibility(View.GONE);
+ };
+
+ private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = () -> {
+ mKeyguardViewVisibilityAnimating = false;
+ };
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index cdf98581e29b..97d6e9750716 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -17,14 +17,16 @@ package com.android.keyguard;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.TypedArray;
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;
-import com.android.internal.graphics.ColorUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -34,13 +36,18 @@ import com.android.systemui.R;
class NumPadAnimator {
private ValueAnimator mAnimator;
private GradientDrawable mBackground;
+ private RippleDrawable mRipple;
+ private GradientDrawable mRippleMask;
private int mMargin;
private int mNormalColor;
private int mHighlightColor;
private int mStyle;
- NumPadAnimator(Context context, final GradientDrawable background, @StyleRes int style) {
- mBackground = (GradientDrawable) background.mutate();
+ NumPadAnimator(Context context, LayerDrawable drawable, @StyleRes int style) {
+ LayerDrawable ld = (LayerDrawable) drawable.mutate();
+ mBackground = (GradientDrawable) ld.findDrawableByLayerId(R.id.background);
+ mRipple = (RippleDrawable) ld.findDrawableByLayerId(R.id.ripple);
+ mRippleMask = (GradientDrawable) mRipple.findDrawableByLayerId(android.R.id.mask);
mStyle = style;
reloadColors(context);
@@ -49,13 +56,14 @@ class NumPadAnimator {
// Actual values will be updated later, usually during an onLayout() call
mAnimator = ValueAnimator.ofFloat(0f);
- mAnimator.setDuration(250);
- mAnimator.setInterpolator(Interpolators.LINEAR);
+ mAnimator.setDuration(100);
+ mAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+ mAnimator.setRepeatMode(ValueAnimator.REVERSE);
+ mAnimator.setRepeatCount(1);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator anim) {
mBackground.setCornerRadius((float) anim.getAnimatedValue());
- mBackground.setColor(ColorUtils.blendARGB(mHighlightColor, mNormalColor,
- anim.getAnimatedFraction()));
+ mRippleMask.setCornerRadius((float) anim.getAnimatedValue());
}
});
@@ -66,9 +74,9 @@ class NumPadAnimator {
}
void onLayout(int height) {
- float startRadius = height / 10f;
- float endRadius = height / 2f;
- mBackground.setCornerRadius(endRadius);
+ float startRadius = height / 2f;
+ float endRadius = height / 4f;
+ mBackground.setCornerRadius(startRadius);
mAnimator.setFloatValues(startRadius, endRadius);
}
@@ -91,6 +99,7 @@ class NumPadAnimator {
a.recycle();
mBackground.setColor(mNormalColor);
+ mRipple.setColor(ColorStateList.valueOf(mHighlightColor));
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index 372810677649..8cb1bc4878a5 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -16,45 +16,60 @@
package com.android.keyguard;
import android.content.Context;
-import android.graphics.drawable.GradientDrawable;
+import android.content.res.ColorStateList;
+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 com.android.settingslib.Utils;
+import com.android.systemui.R;
+
/**
* Similar to the {@link NumPadKey}, but displays an image.
*/
public class NumPadButton extends AlphaOptimizedImageButton {
- private final NumPadAnimator mAnimator;
+ private NumPadAnimator mAnimator;
public NumPadButton(Context context, AttributeSet attrs) {
super(context, attrs);
- mAnimator = new NumPadAnimator(context, (GradientDrawable) getBackground(),
+ mAnimator = new NumPadAnimator(context, (LayerDrawable) getBackground(),
attrs.getStyleAttribute());
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mAnimator.updateMargin((ViewGroup.MarginLayoutParams) getLayoutParams());
+ 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);
- // Set width/height to the same value to ensure a smooth circle for the bg
- setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
+ // Set width/height to the same value to ensure a smooth circle for the bg, but shrink
+ // the height to match the old pin bouncer
+ int width = getMeasuredWidth();
+ int height = mAnimator == null ? (int) (width * .75f) : width;
+
+ setMeasuredDimension(getMeasuredWidth(), height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
- mAnimator.onLayout(b - t);
+ if (mAnimator != null) mAnimator.onLayout(b - t);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
- mAnimator.start();
+ if (mAnimator != null) mAnimator.start();
return super.onTouchEvent(event);
}
@@ -62,6 +77,23 @@ public class NumPadButton extends AlphaOptimizedImageButton {
* Reload colors from resources.
**/
public void reloadColors() {
- mAnimator.reloadColors(getContext());
+ if (mAnimator != null) {
+ mAnimator.reloadColors(getContext());
+ } else {
+ // Needed for old style pin
+ int textColor = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary)
+ .getDefaultColor();
+ ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(textColor));
+ }
+ }
+
+ /**
+ * 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()));
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index 756d6107570a..a4a781dc6ff5 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -18,10 +18,11 @@ package com.android.keyguard;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -47,7 +48,7 @@ public class NumPadKey extends ViewGroup {
private int mTextViewResId;
private PasswordTextView mTextView;
- private final NumPadAnimator mAnimator;
+ private NumPadAnimator mAnimator;
private View.OnClickListener mListener = new View.OnClickListener() {
@Override
@@ -126,11 +127,22 @@ public class NumPadKey extends ViewGroup {
setContentDescription(mDigitText.getText().toString());
- mAnimator = new NumPadAnimator(context, (GradientDrawable) getBackground(),
+ mAnimator = new NumPadAnimator(context, (LayerDrawable) getBackground(),
R.style.NumPadKey);
}
/**
+ * By default, the new layout will be enabled. Invoking will revert to the old style
+ */
+ 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()));
+ }
+
+ /**
* Reload colors from resources.
**/
public void reloadColors() {
@@ -141,7 +153,7 @@ public class NumPadKey extends ViewGroup {
mDigitText.setTextColor(textColor);
mKlondikeText.setTextColor(klondikeColor);
- mAnimator.reloadColors(getContext());
+ if (mAnimator != null) mAnimator.reloadColors(getContext());
}
@Override
@@ -150,14 +162,14 @@ public class NumPadKey extends ViewGroup {
doHapticKeyClick();
}
- mAnimator.start();
+ if (mAnimator != null) mAnimator.start();
return super.onTouchEvent(event);
}
@Override
public void setLayoutParams(ViewGroup.LayoutParams params) {
- mAnimator.updateMargin((ViewGroup.MarginLayoutParams) params);
+ if (mAnimator != null) mAnimator.updateMargin((ViewGroup.MarginLayoutParams) params);
super.setLayoutParams(params);
}
@@ -167,8 +179,12 @@ public class NumPadKey extends ViewGroup {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
- // Set width/height to the same value to ensure a smooth circle for the bg
- setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
+ // Set width/height to the same value to ensure a smooth circle for the bg, but shrink
+ // the height to match the old pin bouncer
+ int width = getMeasuredWidth();
+ int height = mAnimator == null ? (int) (width * .75f) : width;
+
+ setMeasuredDimension(getMeasuredWidth(), height);
}
@Override
@@ -187,7 +203,7 @@ public class NumPadKey extends ViewGroup {
left = centerX - mKlondikeText.getMeasuredWidth() / 2;
mKlondikeText.layout(left, top, left + mKlondikeText.getMeasuredWidth(), bottom);
- mAnimator.onLayout(b - t);
+ if (mAnimator != null) mAnimator.onLayout(b - t);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
index f2d36d19afcf..5735a4fc0172 100644
--- a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
+++ b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
@@ -54,11 +54,10 @@ private const val DEFAULT_ANIMATION_DURATION: Long = 300
*/
class TextAnimator(
layout: Layout,
- private val invalidateCallback: () -> Unit,
- private val numLines: Int = 1
+ private val invalidateCallback: () -> Unit
) {
// Following two members are for mutable for testing purposes.
- internal var textInterpolator: TextInterpolator = TextInterpolator(layout, numLines)
+ internal var textInterpolator: TextInterpolator = TextInterpolator(layout)
internal var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply {
duration = DEFAULT_ANIMATION_DURATION
addUpdateListener {
@@ -99,7 +98,7 @@ class TextAnimator(
fun setTextStyle(
weight: Int = -1,
textSize: Float = -1f,
- colors: IntArray? = null,
+ color: Int? = null,
animate: Boolean = true,
duration: Long = -1L,
interpolator: TimeInterpolator? = null
@@ -110,21 +109,13 @@ class TextAnimator(
}
if (textSize >= 0) {
- for (targetPaint in textInterpolator.targetPaint)
- targetPaint.textSize = textSize
+ textInterpolator.targetPaint.textSize = textSize
}
if (weight >= 0) {
- for (targetPaint in textInterpolator.targetPaint)
- targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
+ textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
}
- if (colors != null) {
- require(colors.size == textInterpolator.targetPaint.size) {
- "colors size (${colors.size}) must be the same size as" +
- " targetPaints size (${textInterpolator.targetPaint.size})," +
- " which was initialized as numLines ($numLines)"
- }
- for ((index, targetPaint) in textInterpolator.targetPaint.withIndex())
- targetPaint.color = colors[index]
+ if (color != null) {
+ textInterpolator.targetPaint.color = color
}
textInterpolator.onTargetPaintModified()
diff --git a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
index 0d41a2f56618..5d5797cbbb3d 100644
--- a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
+++ b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
@@ -30,8 +30,7 @@ import java.lang.Math.max
* Provide text style linear interpolation for plain text.
*/
class TextInterpolator(
- layout: Layout,
- lines: Int = 1
+ layout: Layout
) {
/**
@@ -40,11 +39,9 @@ class TextInterpolator(
* Once you modified the style parameters, you have to call reshapeText to recalculate base text
* layout.
*
- * @return an array list of paint objects representing one paint per line of text. If this
- * list has a smaller size than the number of lines, all extra lines will use the Paint an
- * index 0.
+ * @return a paint object
*/
- val basePaint = createDefaultPaint(layout.paint, lines)
+ val basePaint = TextPaint(layout.paint)
/**
* Returns target paint used for interpolation.
@@ -52,18 +49,9 @@ class TextInterpolator(
* Once you modified the style parameters, you have to call reshapeText to recalculate target
* text layout.
*
- * @return an array list of paint objects representing one paint per line of text. If this
- * list has a smaller size than the number of lines, all extra lines will use the Paint an
- * index 0.
+ * @return a paint object
*/
- val targetPaint = createDefaultPaint(layout.paint, lines)
-
- private fun createDefaultPaint(paint: TextPaint, lines: Int): ArrayList<TextPaint> {
- val paintList = ArrayList<TextPaint>()
- for (i in 0 until lines)
- paintList.add(TextPaint(paint))
- return paintList
- }
+ val targetPaint = TextPaint(layout.paint)
/**
* A class represents a single font run.
@@ -102,7 +90,7 @@ class TextInterpolator(
private val fontInterpolator = FontInterpolator()
// Recycling object for glyph drawing. Will be extended for the longest font run if needed.
- private val tmpDrawPaints = ArrayList<TextPaint>()
+ private val tmpDrawPaint = TextPaint()
private var tmpPositionArray = FloatArray(20)
/**
@@ -216,10 +204,10 @@ class TextInterpolator(
if (progress == 0f) {
return
} else if (progress == 1f) {
- updatePaint(basePaint, targetPaint)
+ basePaint.set(targetPaint)
} else {
- lerp(basePaint, targetPaint, progress, tmpDrawPaints)
- updatePaint(basePaint, tmpDrawPaints)
+ lerp(basePaint, targetPaint, progress, tmpDrawPaint)
+ basePaint.set(tmpDrawPaint)
}
lines.forEach { line ->
@@ -237,21 +225,13 @@ class TextInterpolator(
progress = 0f
}
- companion object {
- fun updatePaint(toUpdate: ArrayList<TextPaint>, newValues: ArrayList<TextPaint>) {
- toUpdate.clear()
- for (paint in newValues)
- toUpdate.add(TextPaint(paint))
- }
- }
-
/**
* Draws interpolated text at the given progress.
*
* @param canvas a canvas.
*/
fun draw(canvas: Canvas) {
- lerp(basePaint, targetPaint, progress, tmpDrawPaints)
+ lerp(basePaint, targetPaint, progress, tmpDrawPaint)
lines.forEachIndexed { lineNo, line ->
line.runs.forEach { run ->
canvas.save()
@@ -261,10 +241,7 @@ class TextInterpolator(
canvas.translate(origin, layout.getLineBaseline(lineNo).toFloat())
run.fontRuns.forEach { fontRun ->
- if (lineNo >= tmpDrawPaints.size)
- drawFontRun(canvas, run, fontRun, tmpDrawPaints[0])
- else
- drawFontRun(canvas, run, fontRun, tmpDrawPaints[lineNo])
+ drawFontRun(canvas, run, fontRun, tmpDrawPaint)
}
} finally {
canvas.restore()
@@ -430,27 +407,19 @@ class TextInterpolator(
}
// Linear interpolate the paint.
- private fun lerp(
- from: ArrayList<TextPaint>,
- to: ArrayList<TextPaint>,
- progress: Float,
- out: ArrayList<TextPaint>
- ) {
- out.clear()
+ private fun lerp(from: Paint, to: Paint, progress: Float, out: Paint) {
+ out.set(from)
+
// Currently only font size & colors are interpolated.
// TODO(172943390): Add other interpolation or support custom interpolator.
- for (index in from.indices) {
- val paint = TextPaint(from[index])
- paint.textSize = MathUtils.lerp(from[index].textSize, to[index].textSize, progress)
- paint.color = ColorUtils.blendARGB(from[index].color, to[index].color, progress)
- out.add(paint)
- }
+ out.textSize = MathUtils.lerp(from.textSize, to.textSize, progress)
+ out.color = ColorUtils.blendARGB(from.color, to.color, progress)
}
// Shape the text and stores the result to out argument.
private fun shapeText(
layout: Layout,
- paints: ArrayList<TextPaint>
+ paint: TextPaint
): List<List<PositionedGlyphs>> {
val out = mutableListOf<List<PositionedGlyphs>>()
for (lineNo in 0 until layout.lineCount) { // Shape all lines.
@@ -458,7 +427,7 @@ class TextInterpolator(
val count = layout.getLineEnd(lineNo) - lineStart
val runs = mutableListOf<PositionedGlyphs>()
TextShaper.shapeText(layout.text, lineStart, count, layout.textDirectionHeuristic,
- paints[lineNo]) { _, _, glyphs, _ ->
+ paint) { _, _, glyphs, _ ->
runs.add(glyphs)
}
out.add(runs)
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java
new file mode 100644
index 000000000000..3a0357d2a284
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java
@@ -0,0 +1,40 @@
+/*
+ * 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.keyguard.dagger;
+
+import com.android.systemui.statusbar.phone.UserAvatarView;
+import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Subcomponent for helping work with KeyguardQsUserSwitch and its children.
+ */
+@Subcomponent(modules = {KeyguardUserSwitcherModule.class})
+@KeyguardUserSwitcherScope
+public interface KeyguardQsUserSwitchComponent {
+ /** Simple factory for {@link KeyguardUserSwitcherComponent}. */
+ @Subcomponent.Factory
+ interface Factory {
+ KeyguardQsUserSwitchComponent build(
+ @BindsInstance UserAvatarView userAvatarView);
+ }
+
+ /** Builds a {@link KeyguardQsUserSwitchController}. */
+ KeyguardQsUserSwitchController getKeyguardQsUserSwitchController();
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherComponent.java
new file mode 100644
index 000000000000..730c14dc9600
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherComponent.java
@@ -0,0 +1,40 @@
+/*
+ * 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.keyguard.dagger;
+
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Subcomponent for helping work with KeyguardUserSwitcher and its children.
+ */
+@Subcomponent(modules = {KeyguardUserSwitcherModule.class})
+@KeyguardUserSwitcherScope
+public interface KeyguardUserSwitcherComponent {
+ /** Simple factory for {@link KeyguardUserSwitcherComponent}. */
+ @Subcomponent.Factory
+ interface Factory {
+ KeyguardUserSwitcherComponent build(
+ @BindsInstance KeyguardUserSwitcherView keyguardUserSwitcherView);
+ }
+
+ /** Builds a {@link com.android.systemui.statusbar.policy.KeyguardUserSwitcherController}. */
+ KeyguardUserSwitcherController getKeyguardUserSwitcherController();
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherModule.java
new file mode 100644
index 000000000000..b9184f405bf9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherModule.java
@@ -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.
+ */
+
+package com.android.keyguard.dagger;
+
+import dagger.Module;
+
+/** Dagger module for {@link KeyguardUserSwitcherComponent}. */
+@Module
+public abstract class KeyguardUserSwitcherModule {
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherScope.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherScope.java
new file mode 100644
index 000000000000..864472e53ce7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardUserSwitcherScope.java
@@ -0,0 +1,32 @@
+/*
+ * 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.keyguard.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Scope;
+
+/**
+ * Scope annotation for singleton items within the KeyguardUserSwitcherComponent.
+ */
+@Documented
+@Retention(RUNTIME)
+@Scope
+public @interface KeyguardUserSwitcherScope {}
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index caaee5fd3f37..176562799838 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -49,7 +49,6 @@ import android.widget.TextView;
import androidx.annotation.StyleRes;
-import com.android.settingslib.Utils;
import com.android.settingslib.graph.ThemedBatteryDrawable;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher;
@@ -105,11 +104,6 @@ public class BatteryMeterView extends LinearLayout implements
private DualToneHandler mDualToneHandler;
private int mUser;
- /**
- * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings.
- */
- private boolean mUseWallpaperTextColors;
-
private int mNonAdaptedSingleToneColor;
private int mNonAdaptedForegroundColor;
private int mNonAdaptedBackgroundColor;
@@ -242,31 +236,6 @@ public class BatteryMeterView extends LinearLayout implements
mIsSubscribedForTunerUpdates = false;
}
- /**
- * Sets whether the battery meter view uses the wallpaperTextColor. If we're not using it, we'll
- * revert back to dark-mode-based/tinted colors.
- *
- * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for all
- * components
- */
- public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) {
- if (shouldUseWallpaperTextColor == mUseWallpaperTextColors) {
- return;
- }
-
- mUseWallpaperTextColors = shouldUseWallpaperTextColor;
-
- if (mUseWallpaperTextColors) {
- updateColors(
- Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor),
- Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColorSecondary),
- Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor));
- } else {
- updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor,
- mNonAdaptedSingleToneColor);
- }
- }
-
public void setColorsFromContext(Context context) {
if (context == null) {
return;
@@ -476,13 +445,19 @@ public class BatteryMeterView extends LinearLayout implements
mNonAdaptedForegroundColor = mDualToneHandler.getFillColor(intensity);
mNonAdaptedBackgroundColor = mDualToneHandler.getBackgroundColor(intensity);
- if (!mUseWallpaperTextColors) {
- updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor,
- mNonAdaptedSingleToneColor);
- }
+ updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor,
+ mNonAdaptedSingleToneColor);
}
- private void updateColors(int foregroundColor, int backgroundColor, int singleToneColor) {
+ /**
+ * Sets icon and text colors. This will be overridden by {@code onDarkChanged} events,
+ * if registered.
+ *
+ * @param foregroundColor
+ * @param backgroundColor
+ * @param singleToneColor
+ */
+ public void updateColors(int foregroundColor, int backgroundColor, int singleToneColor) {
mDrawable.setColors(foregroundColor, backgroundColor, singleToneColor);
mTextColor = singleToneColor;
if (mBatteryPercentView != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 71ec33e16e0e..a9b4c492e10c 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -16,7 +16,10 @@
package com.android.systemui;
+import android.app.WallpaperColors;
+import android.graphics.Bitmap;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
@@ -26,13 +29,16 @@ import android.util.Log;
import android.util.Size;
import android.view.SurfaceHolder;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.glwallpaper.EglHelper;
-import com.android.systemui.glwallpaper.GLWallpaperRenderer;
import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
import javax.inject.Inject;
@@ -45,8 +51,13 @@ public class ImageWallpaper extends WallpaperService {
// We delayed destroy render context that subsequent render requests have chance to cancel it.
// This is to avoid destroying then recreating render context in a very short time.
private static final int DELAY_FINISH_RENDERING = 1000;
+ 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 HandlerThread mWorker;
+ // scaled down version
+ private Bitmap mMiniBitmap;
@Inject
public ImageWallpaper() {
@@ -70,6 +81,7 @@ public class ImageWallpaper extends WallpaperService {
super.onDestroy();
mWorker.quitSafely();
mWorker = null;
+ mMiniBitmap = null;
}
class GLEngine extends Engine {
@@ -80,7 +92,7 @@ public class ImageWallpaper extends WallpaperService {
@VisibleForTesting
static final int MIN_SURFACE_HEIGHT = 64;
- private GLWallpaperRenderer mRenderer;
+ private ImageWallpaperRenderer mRenderer;
private EglHelper mEglHelper;
private final Runnable mFinishRenderingTask = this::finishRendering;
private boolean mNeedRedraw;
@@ -101,6 +113,10 @@ public class ImageWallpaper extends WallpaperService {
setFixedSizeAllowed(true);
setOffsetNotificationsEnabled(false);
updateSurfaceSize();
+ mMiniBitmap = null;
+ if (mWorker != null && mWorker.getThreadHandler() != null) {
+ mWorker.getThreadHandler().post(this::updateMiniBitmap);
+ }
}
EglHelper getEglHelperInstance() {
@@ -111,6 +127,20 @@ public class ImageWallpaper extends WallpaperService {
return new ImageWallpaperRenderer(getDisplayContext());
}
+ private void updateMiniBitmap() {
+ mRenderer.useBitmap(b -> {
+ int size = Math.min(b.getWidth(), b.getHeight());
+ float scale = 1.0f;
+ if (size > MIN_SURFACE_WIDTH) {
+ scale = (float) MIN_SURFACE_WIDTH / (float) size;
+ }
+ mMiniBitmap = Bitmap.createScaledBitmap(b, Math.round(scale * b.getWidth()),
+ Math.round(scale * b.getHeight()), false);
+ computeAndNotifyLocalColors(mLocalColorsToAdd, mMiniBitmap);
+ mLocalColorsToAdd.clear();
+ });
+ }
+
private void updateSurfaceSize() {
SurfaceHolder holder = getSurfaceHolder();
Size frameSize = mRenderer.reportSurfaceSize();
@@ -126,6 +156,7 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void onDestroy() {
+ mMiniBitmap = null;
mWorker.getThreadHandler().post(() -> {
mRenderer.finish();
mRenderer = null;
@@ -135,6 +166,59 @@ public class ImageWallpaper extends WallpaperService {
}
@Override
+ public boolean supportsLocalColorExtraction() {
+ return true;
+ }
+
+ @Override
+ public void addLocalColorsAreas(@NonNull List<RectF> regions) {
+ mWorker.getThreadHandler().post(() -> {
+ Bitmap bitmap = mMiniBitmap;
+ if (bitmap == null) {
+ mLocalColorsToAdd.addAll(regions);
+ } else {
+ computeAndNotifyLocalColors(regions, bitmap);
+ }
+ });
+ }
+
+ private void computeAndNotifyLocalColors(@NonNull List<RectF> regions, Bitmap b) {
+ List<WallpaperColors> colors = getLocalWallpaperColors(regions, b);
+ try {
+ notifyLocalColorsChanged(regions, colors);
+ } catch (RuntimeException e) {
+ Log.e(TAG, e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public void removeLocalColorsAreas(@NonNull List<RectF> regions) {
+ // No-OP
+ }
+
+ private List<WallpaperColors> getLocalWallpaperColors(@NonNull List<RectF> areas,
+ Bitmap b) {
+ List<WallpaperColors> colors = new ArrayList<>(areas.size());
+ for (int i = 0; i < areas.size(); i++) {
+ RectF area = areas.get(i);
+ if (area == null || !LOCAL_COLOR_BOUNDS.contains(area)) {
+ colors.add(null);
+ continue;
+ }
+ Rect subImage = new Rect(
+ Math.round(area.left * b.getWidth()),
+ Math.round(area.top * b.getHeight()),
+ Math.round(area.right * b.getWidth()),
+ Math.round(area.bottom * b.getHeight()));
+ Bitmap colorImg = Bitmap.createBitmap(b,
+ subImage.left, subImage.top, subImage.width(), subImage.height());
+ WallpaperColors color = WallpaperColors.fromBitmap(colorImg);
+ colors.add(color);
+ }
+ return colors;
+ }
+
+ @Override
public void onSurfaceCreated(SurfaceHolder holder) {
if (mWorker == null) return;
mWorker.getThreadHandler().post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 78f7966558ab..865ca40b1f4c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -19,18 +19,15 @@ package com.android.systemui;
import android.app.ActivityThread;
import android.app.Application;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Process;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
import android.util.TimingsTraceLog;
import android.view.SurfaceControl;
@@ -40,7 +37,6 @@ import com.android.systemui.dagger.ContextComponentHelper;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import com.android.systemui.shared.system.ThreadedRendererCompat;
import com.android.systemui.util.NotificationChannels;
@@ -125,21 +121,6 @@ public class SystemUIApplication extends Application implements
mServices[i].onBootCompleted();
}
}
-
- // If SHOW_PEOPLE_SPACE is true, enable People Space widget provider.
- // TODO(b/170396074): Migrate to new feature flag (go/silk-flags-howto)
- try {
- int showPeopleSpace = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.SHOW_PEOPLE_SPACE, 1);
- context.getPackageManager().setComponentEnabledSetting(
- new ComponentName(context, PeopleSpaceWidgetProvider.class),
- showPeopleSpace == 1
- ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
- : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP);
- } catch (Exception e) {
- Log.w(TAG, "Error enabling People Space widget:", e);
- }
}
}, bootCompletedFilter);
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
index 38a82f8c9908..36937d622b5b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
@@ -16,10 +16,19 @@
package com.android.systemui.controls.dagger
+import android.content.ContentResolver
+import android.content.Context
+import android.database.ContentObserver
+import android.provider.Settings
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.settings.SecureSettings
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
import dagger.Lazy
import java.util.Optional
import javax.inject.Inject
@@ -28,15 +37,43 @@ import javax.inject.Inject
* Pseudo-component to inject into classes outside `com.android.systemui.controls`.
*
* If `featureEnabled` is false, all the optionals should be empty. The controllers will only be
- * instantiated if `featureEnabled` is true.
+ * instantiated if `featureEnabled` is true. Can also be queried for the availability of controls.
*/
@SysUISingleton
class ControlsComponent @Inject constructor(
@ControlsFeatureEnabled private val featureEnabled: Boolean,
+ private val context: Context,
private val lazyControlsController: Lazy<ControlsController>,
private val lazyControlsUiController: Lazy<ControlsUiController>,
- private val lazyControlsListingController: Lazy<ControlsListingController>
+ private val lazyControlsListingController: Lazy<ControlsListingController>,
+ private val lockPatternUtils: LockPatternUtils,
+ private val keyguardStateController: KeyguardStateController,
+ private val userTracker: UserTracker,
+ private val secureSettings: SecureSettings
) {
+
+ private val contentResolver: ContentResolver
+ get() = context.contentResolver
+
+ private var canShowWhileLockedSetting = false
+
+ val showWhileLockedObserver = object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ updateShowWhileLocked()
+ }
+ }
+
+ init {
+ if (featureEnabled) {
+ secureSettings.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT),
+ false, /* notifyForDescendants */
+ showWhileLockedObserver
+ )
+ updateShowWhileLocked()
+ }
+ }
+
fun getControlsController(): Optional<ControlsController> {
return if (featureEnabled) Optional.of(lazyControlsController.get()) else Optional.empty()
}
@@ -52,4 +89,37 @@ class ControlsComponent @Inject constructor(
Optional.empty()
}
}
-} \ No newline at end of file
+
+ /**
+ * @return true if controls are feature-enabled and have available services to serve controls
+ */
+ fun isEnabled() = featureEnabled && lazyControlsController.get().available
+
+ /**
+ * Returns one of 3 states:
+ * * AVAILABLE - Controls can be made visible
+ * * AVAILABLE_AFTER_UNLOCK - Controls can be made visible only after device unlock
+ * * UNAVAILABLE - Controls are not enabled
+ */
+ fun getVisibility(): Visibility {
+ if (!isEnabled()) return Visibility.UNAVAILABLE
+ if (lockPatternUtils.getStrongAuthForUser(userTracker.userHandle.identifier)
+ == STRONG_AUTH_REQUIRED_AFTER_BOOT) {
+ return Visibility.AVAILABLE_AFTER_UNLOCK
+ }
+ if (!canShowWhileLockedSetting && !keyguardStateController.isUnlocked()) {
+ return Visibility.AVAILABLE_AFTER_UNLOCK
+ }
+
+ return Visibility.AVAILABLE
+ }
+
+ private fun updateShowWhileLocked() {
+ canShowWhileLockedSetting = secureSettings.getInt(
+ Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, 0) != 0
+ }
+
+ enum class Visibility {
+ AVAILABLE, AVAILABLE_AFTER_UNLOCK, UNAVAILABLE
+ }
+}
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 40c238680a45..fc89783018bc 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -32,6 +32,8 @@ 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.ControlsUiController
import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.settings.CurrentUserTracker
import com.android.systemui.util.LifecycleActivity
@@ -42,9 +44,10 @@ import javax.inject.Inject
*/
class ControlsEditingActivity @Inject constructor(
private val controller: ControlsControllerImpl,
- broadcastDispatcher: BroadcastDispatcher,
+ private val broadcastDispatcher: BroadcastDispatcher,
private val globalActionsComponent: GlobalActionsComponent,
- private val customIconCache: CustomIconCache
+ private val customIconCache: CustomIconCache,
+ private val uiController: ControlsUiController
) : LifecycleActivity() {
companion object {
@@ -59,6 +62,7 @@ class ControlsEditingActivity @Inject constructor(
private lateinit var model: FavoritesModel
private lateinit var subtitle: TextView
private lateinit var saveButton: View
+ private var backToGlobalActions = true
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
private val startingUser = controller.currentUserId
@@ -82,6 +86,11 @@ class ControlsEditingActivity @Inject constructor(
structure = it
} ?: run(this::finish)
+ backToGlobalActions = intent.getBooleanExtra(
+ ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
+ true
+ )
+
bindViews()
bindButtons()
@@ -100,7 +109,11 @@ class ControlsEditingActivity @Inject constructor(
}
override fun onBackPressed() {
- globalActionsComponent.handleShowGlobalActionsMenu()
+ if (backToGlobalActions) {
+ globalActionsComponent.handleShowGlobalActionsMenu()
+ } else {
+ ControlsDialog(applicationContext, broadcastDispatcher).show(uiController)
+ }
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 b2821579c389..1c2f17c55671 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -40,6 +40,8 @@ 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.ControlsUiController
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.settings.CurrentUserTracker
@@ -53,8 +55,9 @@ class ControlsFavoritingActivity @Inject constructor(
@Main private val executor: Executor,
private val controller: ControlsControllerImpl,
private val listingController: ControlsListingController,
- broadcastDispatcher: BroadcastDispatcher,
- private val globalActionsComponent: GlobalActionsComponent
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val globalActionsComponent: GlobalActionsComponent,
+ private val uiController: ControlsUiController
) : LifecycleActivity() {
companion object {
@@ -89,6 +92,7 @@ class ControlsFavoritingActivity @Inject constructor(
private lateinit var comparator: Comparator<StructureContainer>
private var cancelLoadRunnable: Runnable? = null
private var isPagerLoaded = false
+ private var backToGlobalActions = true
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
private val startingUser = controller.currentUserId
@@ -114,7 +118,7 @@ class ControlsFavoritingActivity @Inject constructor(
override fun onBackPressed() {
if (!fromProviderSelector) {
- globalActionsComponent.handleShowGlobalActionsMenu()
+ openControlsOrigin()
}
animateExitAndFinish()
}
@@ -129,6 +133,11 @@ class ControlsFavoritingActivity @Inject constructor(
component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
fromProviderSelector = intent.getBooleanExtra(EXTRA_FROM_PROVIDER_SELECTOR, false)
+ backToGlobalActions = intent.getBooleanExtra(
+ ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
+ true
+ )
+
bindViews()
}
@@ -330,11 +339,19 @@ class ControlsFavoritingActivity @Inject constructor(
)
}
animateExitAndFinish()
- globalActionsComponent.handleShowGlobalActionsMenu()
+ openControlsOrigin()
}
}
}
+ private fun openControlsOrigin() {
+ if (backToGlobalActions) {
+ globalActionsComponent.handleShowGlobalActionsMenu()
+ } else {
+ ControlsDialog(applicationContext, broadcastDispatcher).show(uiController)
+ }
+ }
+
override fun onPause() {
super.onPause()
mTooltipManager?.hide(false)
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 429f67fdc706..d06568a7caf9 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
@@ -24,6 +24,9 @@ import android.service.controls.Control
*/
interface ControlActionCoordinator {
+ // Handle actions launched from GlobalActionsDialog or ControlDialog
+ var startedFromGlobalActions: Boolean
+
/**
* 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 7cd7e18965c2..6b300f4e07e4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -30,6 +30,8 @@ import android.service.controls.actions.CommandAction
import android.service.controls.actions.FloatAction
import android.util.Log
import android.view.HapticFeedbackConstants
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.globalactions.GlobalActionsComponent
@@ -37,6 +39,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.wm.shell.TaskViewFactory
+import dagger.Lazy
import java.util.Optional
import javax.inject.Inject
@@ -48,13 +51,17 @@ class ControlActionCoordinatorImpl @Inject constructor(
private val activityStarter: ActivityStarter,
private val keyguardStateController: KeyguardStateController,
private val globalActionsComponent: GlobalActionsComponent,
- private val taskViewFactory: Optional<TaskViewFactory>
+ private val taskViewFactory: Optional<TaskViewFactory>,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val lazyUiController: Lazy<ControlsUiController>
) : 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
+
companion object {
private const val RESPONSE_TIMEOUT_IN_MILLIS = 3000L
}
@@ -65,7 +72,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
}
override fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
- bouncerOrRun(Action(cvh.cws.ci.controlId, {
+ bouncerOrRun(createAction(cvh.cws.ci.controlId, {
cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
cvh.action(BooleanAction(templateId, !isChecked))
}, true /* blockable */))
@@ -73,7 +80,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
override fun touch(cvh: ControlViewHolder, templateId: String, control: Control) {
val blockable = cvh.usePanel()
- bouncerOrRun(Action(cvh.cws.ci.controlId, {
+ bouncerOrRun(createAction(cvh.cws.ci.controlId, {
cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
if (cvh.usePanel()) {
showDialog(cvh, control.getAppIntent().getIntent())
@@ -92,13 +99,13 @@ class ControlActionCoordinatorImpl @Inject constructor(
}
override fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) {
- bouncerOrRun(Action(cvh.cws.ci.controlId, {
+ bouncerOrRun(createAction(cvh.cws.ci.controlId, {
cvh.action(FloatAction(templateId, newValue))
}, false /* blockable */))
}
override fun longPress(cvh: ControlViewHolder) {
- bouncerOrRun(Action(cvh.cws.ci.controlId, {
+ 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)
@@ -108,6 +115,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
}
override fun runPendingAction(controlId: String) {
+ if (!keyguardStateController.isUnlocked()) return
if (pendingAction?.controlId == controlId) {
pendingAction?.invoke()
pendingAction = null
@@ -129,10 +137,11 @@ class ControlActionCoordinatorImpl @Inject constructor(
false
}
- private fun bouncerOrRun(action: Action) {
+ @VisibleForTesting
+ fun bouncerOrRun(action: Action) {
if (keyguardStateController.isShowing()) {
- var closeGlobalActions = !keyguardStateController.isUnlocked()
- if (closeGlobalActions) {
+ var closeDialog = !keyguardStateController.isUnlocked()
+ if (closeDialog) {
context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
// pending actions will only run after the control state has been refreshed
@@ -141,8 +150,12 @@ class ControlActionCoordinatorImpl @Inject constructor(
activityStarter.dismissKeyguardThenExecute({
Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action")
- if (closeGlobalActions) {
- globalActionsComponent.handleShowGlobalActionsMenu()
+ if (closeDialog) {
+ if (startedFromGlobalActions) {
+ globalActionsComponent.handleShowGlobalActionsMenu()
+ } else {
+ ControlsDialog(context, broadcastDispatcher).show(lazyUiController.get())
+ }
} else {
action.invoke()
}
@@ -180,6 +193,10 @@ class ControlActionCoordinatorImpl @Inject constructor(
}
}
+ @VisibleForTesting
+ fun createAction(controlId: String, f: () -> Unit, blockable: Boolean) =
+ Action(controlId, f, blockable)
+
inner class Action(val controlId: String, val f: () -> Unit, val blockable: Boolean) {
fun invoke() {
if (!blockable || shouldRunAction(controlId)) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
index 8e878cf76ad9..537334aeb2f7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
@@ -28,11 +28,12 @@ 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(
+class ControlsDialog @Inject constructor(
thisContext: Context,
val broadcastDispatcher: BroadcastDispatcher
) : Dialog(thisContext, R.style.Theme_SystemUI_Dialog_Control_LockScreen) {
@@ -66,7 +67,7 @@ class ControlsDialog(
val vg = requireViewById<ViewGroup>(com.android.systemui.R.id.global_actions_controls)
vg.alpha = 0f
- controller.show(vg, { /* do nothing */ })
+ controller.show(vg, { dismiss() }, false /* startedFromGlobalActions */)
vg.animate()
.alpha(1f)
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 4e4c82cabaa0..20bdf609357e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -27,9 +27,10 @@ interface ControlsUiController {
companion object {
public const val TAG = "ControlsUiController"
public const val EXTRA_ANIMATE = "extra_animate"
+ public const val BACK_TO_GLOBAL_ACTIONS = "back_to_global_actions"
}
- fun show(parent: ViewGroup, dismissGlobalActions: Runnable)
+ fun show(parent: ViewGroup, onDismiss: Runnable, startedFromGlobalActions: Boolean)
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 2b529f9a6cde..c94d85aa58c4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -102,7 +102,7 @@ class ControlsUiControllerImpl @Inject constructor (
private lateinit var lastItems: List<SelectionItem>
private var popup: ListPopupWindow? = null
private var hidden = true
- private lateinit var dismissGlobalActions: Runnable
+ private lateinit var onDismiss: Runnable
private val popupThemedContext = ContextThemeWrapper(context, R.style.Control_ListPopupWindow)
private var retainCache = false
@@ -145,13 +145,19 @@ class ControlsUiControllerImpl @Inject constructor (
}
}
- override fun show(parent: ViewGroup, dismissGlobalActions: Runnable) {
+ override fun show(
+ parent: ViewGroup,
+ onDismiss: Runnable,
+ startedFromGlobalActions: Boolean
+ ) {
Log.d(ControlsUiController.TAG, "show()")
this.parent = parent
- this.dismissGlobalActions = dismissGlobalActions
+ this.onDismiss = onDismiss
hidden = false
retainCache = false
+ controlActionCoordinator.startedFromGlobalActions = startedFromGlobalActions
+
allStructures = controlsController.get().getFavorites()
selectedStructure = loadPreference(allStructures)
@@ -187,7 +193,7 @@ class ControlsUiControllerImpl @Inject constructor (
controlViewsById.clear()
controlsById.clear()
- show(parent, dismissGlobalActions)
+ show(parent, onDismiss, controlActionCoordinator.startedFromGlobalActions)
val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f)
showAnim.setInterpolator(DecelerateInterpolator(1.0f))
showAnim.setDuration(FADE_IN_MILLIS)
@@ -260,7 +266,11 @@ class ControlsUiControllerImpl @Inject constructor (
private fun startActivity(context: Context, intent: Intent) {
// Force animations when transitioning from a dialog to an activity
intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
- dismissGlobalActions.run()
+ intent.putExtra(
+ ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
+ controlActionCoordinator.startedFromGlobalActions
+ )
+ onDismiss.run()
activityStarter.dismissKeyguardThenExecute({
shadeController.collapsePanel(false)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index ec4a91c24464..2b362b94d1f5 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import com.android.systemui.ForegroundServicesDialog;
import com.android.systemui.keyguard.WorkLockActivity;
+import com.android.systemui.people.PeopleSpaceActivity;
import com.android.systemui.screenrecord.ScreenRecordDialog;
import com.android.systemui.settings.brightness.BrightnessDialog;
import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
@@ -92,4 +93,10 @@ public abstract class DefaultActivityBinder {
@IntoMap
@ClassKey(TvNotificationPanelActivity.class)
public abstract Activity bindTvNotificationPanelActivity(TvNotificationPanelActivity activity);
+
+ /** Inject into PeopleSpaceActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(PeopleSpaceActivity.class)
+ public abstract Activity bindPeopleSpaceActivity(PeopleSpaceActivity activity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 062410abb689..ffb8446f3e21 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -33,7 +33,7 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
import java.util.Optional;
@@ -86,7 +86,7 @@ public interface SysUIComponent {
Builder setShellCommandHandler(Optional<ShellCommandHandler> shellDump);
@BindsInstance
- Builder setTransitions(Transitions t);
+ Builder setTransitions(RemoteTransitions t);
SysUIComponent build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index e5c9d104ea93..ec3188a6144f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -27,6 +27,7 @@ import com.android.systemui.globalactions.GlobalActionsComponent;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.media.systemsounds.HomeSoundEffectController;
+import com.android.systemui.people.widget.PeopleSpaceWidgetEnabler;
import com.android.systemui.power.PowerUI;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsModule;
@@ -177,4 +178,10 @@ public abstract class SystemUIBinder {
@IntoMap
@ClassKey(HomeSoundEffectController.class)
public abstract SystemUI bindHomeSoundEffectController(HomeSoundEffectController sysui);
+
+ /** Inject into PeopleSpaceWidgetEnabler. */
+ @Binds
+ @IntoMap
+ @ClassKey(PeopleSpaceWidgetEnabler.class)
+ public abstract SystemUI bindPeopleSpaceWidgetEnabler(PeopleSpaceWidgetEnabler sysui);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 60b665f0a51a..f3726a37bb65 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -16,6 +16,11 @@
package com.android.systemui.dagger;
+import android.content.Context;
+
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.tv.TvWMComponent;
+import com.android.systemui.wmshell.TvWMShellModule;
import com.android.systemui.wmshell.WMShellModule;
import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.ShellInit;
@@ -27,14 +32,20 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
import java.util.Optional;
import dagger.Subcomponent;
/**
- * Dagger Subcomponent for WindowManager.
+ * Dagger Subcomponent for WindowManager. This class explicitly describes the interfaces exported
+ * from the WM component into the SysUI component (in
+ * {@link SystemUIFactory#init(Context, boolean)}), and references the specific dependencies
+ * provided by its particular device/form-factor SystemUI implementation.
+ *
+ * ie. {@link WMComponent} includes {@link WMShellModule}
+ * and {@link TvWMComponent} includes {@link TvWMShellModule}
*/
@WMSingleton
@Subcomponent(modules = {WMShellModule.class})
@@ -55,16 +66,12 @@ public interface WMComponent {
getShellInit().init();
}
- // Gets the Shell init instance
@WMSingleton
ShellInit getShellInit();
- // Gets the Shell dump instance
@WMSingleton
Optional<ShellCommandHandler> getShellCommandHandler();
- // TODO(b/162923491): We currently pass the instances through to SysUI, but that may change
- // depending on the threading mechanism we go with
@WMSingleton
Optional<OneHanded> getOneHanded();
@@ -89,7 +96,6 @@ public interface WMComponent {
@WMSingleton
Optional<TaskViewFactory> getTaskViewFactory();
- /** Gets transitions */
@WMSingleton
- Transitions getTransitions();
+ RemoteTransitions getTransitions();
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 5c8c9f22d585..8ab135ced97e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -23,6 +23,7 @@ import android.app.AlarmManager;
import android.content.Context;
import android.os.Handler;
import android.os.SystemClock;
+import android.provider.Settings;
import android.text.format.Formatter;
import android.util.Log;
@@ -32,6 +33,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.wakelock.WakeLock;
@@ -43,7 +45,7 @@ import javax.inject.Inject;
* The policy controlling doze.
*/
@DozeScope
-public class DozeUi implements DozeMachine.Part {
+public class DozeUi implements DozeMachine.Part, TunerService.Tunable {
private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min
private final Context mContext;
@@ -73,7 +75,7 @@ public class DozeUi implements DozeMachine.Part {
public DozeUi(Context context, AlarmManager alarmManager,
WakeLock wakeLock, DozeHost host, @Main Handler handler,
DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor,
- DozeLog dozeLog) {
+ DozeLog dozeLog, TunerService tunerService) {
mContext = context;
mWakeLock = wakeLock;
mHost = host;
@@ -83,6 +85,8 @@ public class DozeUi implements DozeMachine.Part {
mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler);
keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
mDozeLog = dozeLog;
+
+ tunerService.addTunable(this, Settings.Secure.DOZE_ALWAYS_ON);
}
@Override
@@ -238,4 +242,11 @@ public class DozeUi implements DozeMachine.Part {
KeyguardUpdateMonitorCallback getKeyguardCallback() {
return mKeyguardVisibilityCallback;
}
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ if (key.equals(Settings.Secure.DOZE_ALWAYS_ON)) {
+ updateAnimateScreenOff();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
index b77fcc822b88..073586e88a8b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
@@ -16,24 +16,18 @@
package com.android.systemui.flags;
-import android.annotation.NonNull;
import android.content.res.Resources;
-import android.provider.DeviceConfig;
import android.util.SparseArray;
import androidx.annotation.BoolRes;
import androidx.annotation.Nullable;
import com.android.systemui.R;
-import com.android.systemui.assist.DeviceConfigHelper;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.util.wrapper.BuildInfo;
-import java.util.concurrent.Executor;
-
import javax.inject.Inject;
/**
* Reads and caches feature flags for quick access
@@ -60,23 +54,19 @@ import javax.inject.Inject;
@SysUISingleton
public class FeatureFlagReader {
private final Resources mResources;
- private final DeviceConfigHelper mDeviceConfig;
private final boolean mAreFlagsOverrideable;
-
+ private final SystemPropertiesHelper mSystemPropertiesHelper;
private final SparseArray<CachedFlag> mCachedFlags = new SparseArray<>();
@Inject
public FeatureFlagReader(
@Main Resources resources,
BuildInfo build,
- DeviceConfigHelper deviceConfig,
- @Background Executor executor) {
+ SystemPropertiesHelper systemPropertiesHelper) {
mResources = resources;
- mDeviceConfig = deviceConfig;
+ mSystemPropertiesHelper = systemPropertiesHelper;
mAreFlagsOverrideable =
build.isDebuggable() && mResources.getBoolean(R.bool.are_flags_overrideable);
-
- mDeviceConfig.addOnPropertiesChangedListener(executor, this::onPropertiesChanged);
}
/**
@@ -93,7 +83,7 @@ public class FeatureFlagReader {
String name = resourceIdToFlagName(resId);
boolean value = mResources.getBoolean(resId);
if (mAreFlagsOverrideable) {
- value = mDeviceConfig.getBoolean(flagNameToStorageKey(name), value);
+ value = mSystemPropertiesHelper.getBoolean(flagNameToStorageKey(name), value);
}
cachedFlag = new CachedFlag(name, value);
@@ -104,27 +94,6 @@ public class FeatureFlagReader {
}
}
- private void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
- synchronized (mCachedFlags) {
- for (String key : properties.getKeyset()) {
- String flagName = storageKeyToFlagName(key);
- if (flagName != null) {
- clearCachedFlag(flagName);
- }
- }
- }
- }
-
- private void clearCachedFlag(String flagName) {
- for (int i = 0; i < mCachedFlags.size(); i++) {
- CachedFlag flag = mCachedFlags.valueAt(i);
- if (flag.name.equals(flagName)) {
- mCachedFlags.removeAt(i);
- break;
- }
- }
- }
-
private String resourceIdToFlagName(@BoolRes int resId) {
String resName = mResources.getResourceEntryName(resId);
if (resName.startsWith(RESNAME_PREFIX)) {
@@ -160,6 +129,6 @@ public class FeatureFlagReader {
}
}
- private static final String STORAGE_KEY_PREFIX = "flag_";
+ private static final String STORAGE_KEY_PREFIX = "persist.systemui.flag_";
private static final String RESNAME_PREFIX = "flag_";
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
new file mode 100644
index 000000000000..28f63b07e584
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.flags
+
+import android.os.SystemProperties
+import com.android.systemui.dagger.SysUISingleton
+
+import javax.inject.Inject
+
+/**
+ * Proxy to make {@link SystemProperties} easily testable.
+ */
+@SysUISingleton
+class SystemPropertiesHelper @Inject constructor() {
+ fun getBoolean(name: String, default: Boolean): Boolean {
+ return SystemProperties.getBoolean(name, default)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index ad4c44767995..d85b10167697 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -25,6 +25,8 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOM
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
+import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
import android.animation.Animator;
@@ -250,6 +252,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final IWindowManager mIWindowManager;
private final Executor mBackgroundExecutor;
private List<ControlsServiceInfo> mControlsServiceInfos = new ArrayList<>();
+ private ControlsComponent mControlsComponent;
private Optional<ControlsController> mControlsControllerOptional;
private final RingerModeTracker mRingerModeTracker;
private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
@@ -338,6 +341,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mSysuiColorExtractor = colorExtractor;
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mControlsComponent = controlsComponent;
mControlsUiControllerOptional = controlsComponent.getControlsUiController();
mIWindowManager = iWindowManager;
mBackgroundExecutor = backgroundExecutor;
@@ -387,7 +391,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
if (mDialog.mWalletViewController != null) {
mDialog.mWalletViewController.onDeviceLockStateChanged(!unlocked);
}
- if (!mDialog.isShowingControls() && shouldShowControls()) {
+ if (!mDialog.isShowingControls()
+ && mControlsComponent.getVisibility() == AVAILABLE) {
mDialog.showControls(mControlsUiControllerOptional.get());
}
if (unlocked) {
@@ -397,14 +402,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
});
- if (controlsComponent.getControlsListingController().isPresent()) {
- controlsComponent.getControlsListingController().get()
+ if (mControlsComponent.getControlsListingController().isPresent()) {
+ mControlsComponent.getControlsListingController().get()
.addCallback(list -> {
mControlsServiceInfos = list;
// This callback may occur after the dialog has been shown. If so, add
// controls into the already visible space or show the lock msg if needed.
if (mDialog != null) {
- if (!mDialog.isShowingControls() && shouldShowControls()) {
+ if (!mDialog.isShowingControls()
+ && mControlsComponent.getVisibility() == AVAILABLE) {
mDialog.showControls(mControlsUiControllerOptional.get());
} else if (shouldShowLockMessage(mDialog)) {
mDialog.showLockMessage();
@@ -704,7 +710,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mDepthController.setShowingHomeControls(true);
ControlsUiController uiController = null;
- if (mControlsUiControllerOptional.isPresent() && shouldShowControls()) {
+ if (mControlsComponent.getVisibility() == AVAILABLE) {
uiController = mControlsUiControllerOptional.get();
}
ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, mOverflowAdapter,
@@ -2229,7 +2235,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private void showControls(ControlsUiController controller) {
mControlsUiController = controller;
- mControlsUiController.show(mControlsView, this::dismissForControlsActivity);
+ mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
+ true /* startedFromGlobalActions */);
}
private boolean isWalletViewAvailable() {
@@ -2457,7 +2464,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return WindowInsets.CONSUMED;
});
if (mControlsUiController != null) {
- mControlsUiController.show(mControlsView, this::dismissForControlsActivity);
+ mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
+ true /* startedFromGlobalActions */);
}
mBackgroundDrawable.setAlpha(0);
@@ -2632,7 +2640,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
initializeLayout();
mGlobalActionsLayout.updateList();
if (mControlsUiController != null) {
- mControlsUiController.show(mControlsView, this::dismissForControlsActivity);
+ mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
+ true /* startedFromGlobalActions */);
}
}
@@ -2684,26 +2693,24 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return isPanelDebugModeEnabled(context);
}
- private boolean shouldShowControls() {
- boolean showOnLockScreen = mShowLockScreenCardsAndControls && mLockPatternUtils
- .getStrongAuthForUser(getCurrentUser().id) != STRONG_AUTH_REQUIRED_AFTER_BOOT;
- return controlsAvailable()
- && (mKeyguardStateController.isUnlocked() || showOnLockScreen);
- }
-
private boolean controlsAvailable() {
return mDeviceProvisioned
- && mControlsUiControllerOptional.isPresent()
- && mControlsUiControllerOptional.get().getAvailable()
+ && mControlsComponent.isEnabled()
&& !mControlsServiceInfos.isEmpty();
}
private boolean shouldShowLockMessage(ActionsDialog dialog) {
+ return mControlsComponent.getVisibility() == AVAILABLE_AFTER_UNLOCK
+ || isWalletAvailableAfterUnlock(dialog);
+ }
+
+ // Temporary while we move items out of the power menu
+ private boolean isWalletAvailableAfterUnlock(ActionsDialog dialog) {
boolean isLockedAfterBoot = mLockPatternUtils.getStrongAuthForUser(getCurrentUser().id)
== STRONG_AUTH_REQUIRED_AFTER_BOOT;
return !mKeyguardStateController.isUnlocked()
&& (!mShowLockScreenCardsAndControls || isLockedAfterBoot)
- && (controlsAvailable() || dialog.isWalletViewAvailable());
+ && dialog.isWalletViewAvailable();
}
private void onPowerMenuLockScreenSettingsChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index 1a0356c4446d..01a353ce8f1f 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -58,6 +58,14 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer {
mWallpaper = new ImageGLWallpaper(mProgram);
}
+ /**
+ * @hide
+ * @return
+ */
+ public void useBitmap(Consumer<Bitmap> c) {
+ mTexture.use(c);
+ }
+
@Override
public boolean isWcgContent() {
return mTexture.isWcgContent();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
new file mode 100644
index 000000000000..2873cd36409d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
@@ -0,0 +1,172 @@
+/*
+ * 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.keyguard;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.view.View;
+
+/**
+ * Data class containing display information (message, icon, styling) for indication to show at
+ * the bottom of the keyguard.
+ *
+ * See {@link com.android.systemui.statusbar.phone.KeyguardBottomAreaView}.
+ */
+public class KeyguardIndication {
+ @Nullable
+ private final CharSequence mMessage;
+ @NonNull
+ private final ColorStateList mTextColor;
+ @Nullable
+ private final Drawable mIcon;
+ @Nullable
+ private final View.OnClickListener mOnClickListener;
+ @Nullable
+ private final Drawable mBackground;
+
+ private KeyguardIndication(
+ CharSequence message,
+ ColorStateList textColor,
+ Drawable icon,
+ View.OnClickListener onClickListener,
+ Drawable background) {
+ mMessage = message;
+ mTextColor = textColor;
+ mIcon = icon;
+ mOnClickListener = onClickListener;
+ mBackground = background;
+ }
+
+ /**
+ * Message to display
+ */
+ public @Nullable CharSequence getMessage() {
+ return mMessage;
+ }
+
+ /**
+ * TextColor to display the message.
+ */
+ public @NonNull ColorStateList getTextColor() {
+ return mTextColor;
+ }
+
+ /**
+ * Icon to display.
+ */
+ public @Nullable Drawable getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * Click listener for messsage.
+ */
+ public @Nullable View.OnClickListener getClickListener() {
+ return mOnClickListener;
+ }
+
+ /**
+ * Background for textView.
+ */
+ public @Nullable Drawable getBackground() {
+ return mBackground;
+ }
+
+ @Override
+ public String toString() {
+ String str = "KeyguardIndication{";
+ if (!TextUtils.isEmpty(mMessage)) str += "mMessage=" + mMessage;
+ if (mIcon != null) str += " mIcon=" + mIcon;
+ if (mOnClickListener != null) str += " mOnClickListener=" + mOnClickListener;
+ if (mBackground != null) str += " mBackground=" + mBackground;
+ str += "}";
+ return str;
+ }
+
+ /**
+ * KeyguardIndication Builder
+ */
+ public static class Builder {
+ private CharSequence mMessage;
+ private Drawable mIcon;
+ private View.OnClickListener mOnClickListener;
+ private ColorStateList mTextColor;
+ private Drawable mBackground;
+
+ public Builder() { }
+
+ /**
+ * Message to display. Indication requires a non-null message or icon.
+ */
+ public Builder setMessage(@NonNull CharSequence message) {
+ this.mMessage = message;
+ return this;
+ }
+
+ /**
+ * Required field. Text color to use to display the message.
+ */
+ public Builder setTextColor(@NonNull ColorStateList textColor) {
+ this.mTextColor = textColor;
+ return this;
+ }
+
+ /**
+ * Icon to show next to the text. Indication requires a non-null icon or message.
+ * Icon location changes based on language display direction. For LTR, icon shows to the
+ * left of the message. For RTL, icon shows to the right of the message.
+ */
+ public Builder setIcon(Drawable icon) {
+ this.mIcon = icon;
+ return this;
+ }
+
+ /**
+ * Optional. Set a click listener on the message.
+ */
+ public Builder setClickListener(View.OnClickListener onClickListener) {
+ this.mOnClickListener = onClickListener;
+ return this;
+ }
+
+ /**
+ * Optional. Set a custom background on the TextView.
+ */
+ public Builder setBackground(Drawable background) {
+ this.mBackground = background;
+ return this;
+ }
+
+ /**
+ * Build the KeyguardIndication.
+ */
+ public KeyguardIndication build() {
+ if (TextUtils.isEmpty(mMessage) && mIcon == null) {
+ throw new IllegalStateException("message or icon must be set");
+ }
+ if (mTextColor == null) {
+ throw new IllegalStateException("text color must be set");
+ }
+
+ return new KeyguardIndication(
+ mMessage, mTextColor, mIcon, mOnClickListener, mBackground);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
new file mode 100644
index 000000000000..d4678f39e404
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -0,0 +1,323 @@
+/*
+ * 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.keyguard;
+
+import android.annotation.Nullable;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.view.View;
+
+import androidx.annotation.IntDef;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
+import com.android.systemui.util.ViewController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Rotates through messages to show on the keyguard bottom area on the lock screen
+ * NOTE: This controller should not be used on AoD to avoid waking up the AP too often.
+ */
+public class KeyguardIndicationRotateTextViewController extends
+ ViewController<KeyguardIndicationTextView> implements Dumpable {
+ public static String TAG = "KgIndicationRotatingCtrl";
+ private static final long DEFAULT_INDICATION_SHOW_LENGTH = 3500; // milliseconds
+
+ private final StatusBarStateController mStatusBarStateController;
+ private final float mMaxAlpha;
+ private final ColorStateList mInitialTextColorState;
+
+ // Stores @IndicationType => KeyguardIndication messages
+ private final Map<Integer, KeyguardIndication> mIndicationMessages = new HashMap<>();
+
+ // Executor that will show the next message after a delay
+ private final DelayableExecutor mExecutor;
+ @Nullable private ShowNextIndication mShowNextIndicationRunnable;
+
+ // List of indication types to show. The next indication to show is always at index 0
+ private final List<Integer> mIndicationQueue = new LinkedList<>();
+ private @IndicationType int mCurrIndicationType = INDICATION_TYPE_NONE;
+
+ private boolean mIsDozing;
+
+ public KeyguardIndicationRotateTextViewController(
+ KeyguardIndicationTextView view,
+ @Main DelayableExecutor executor,
+ StatusBarStateController statusBarStateController,
+ int lockScreenMode
+ ) {
+ super(view);
+ mMaxAlpha = view.getAlpha();
+ mExecutor = executor;
+ mInitialTextColorState = mView != null
+ ? mView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
+ mStatusBarStateController = statusBarStateController;
+ mView.setLockScreenMode(lockScreenMode);
+ init();
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mStatusBarStateController.removeCallback(mStatusBarStateListener);
+ cancelScheduledIndication();
+ }
+
+ /**
+ * Update the indication type with the given String.
+ * @param type of indication
+ * @param newIndication message to associate with this indication type
+ * @param showImmediately if true: shows this indication message immediately. Else, the text
+ * associated with this type is updated and will show when its turn in
+ * the IndicationQueue comes around.
+ */
+ public void updateIndication(@IndicationType int type, KeyguardIndication newIndication,
+ boolean showImmediately) {
+ final boolean hasPreviousIndication = mIndicationMessages.get(type) != null;
+ final boolean hasNewIndication = newIndication != null;
+ if (!hasNewIndication) {
+ mIndicationMessages.remove(type);
+ mIndicationQueue.removeIf(x -> x == type);
+ } else {
+ if (!hasPreviousIndication) {
+ mIndicationQueue.add(type);
+ }
+
+ mIndicationMessages.put(type, newIndication);
+ }
+
+ if (mIsDozing) {
+ return;
+ }
+
+ final boolean showNow = showImmediately
+ || mCurrIndicationType == INDICATION_TYPE_NONE
+ || mCurrIndicationType == type;
+ if (hasNewIndication) {
+ if (showNow) {
+ showIndication(type);
+ } else if (!isNextIndicationScheduled()) {
+ scheduleShowNextIndication();
+ }
+ return;
+ }
+
+ if (mCurrIndicationType == type
+ && !hasNewIndication
+ && showImmediately) {
+ if (mShowNextIndicationRunnable != null) {
+ mShowNextIndicationRunnable.runImmediately();
+ } else {
+ showIndication(INDICATION_TYPE_NONE);
+ }
+ }
+ }
+
+ /**
+ * Stop showing the following indication type.
+ *
+ * If the current indication is of this type, immediately stops showing the message.
+ */
+ public void hideIndication(@IndicationType int type) {
+ updateIndication(type, null, true);
+ }
+
+ /**
+ * Show a transient message.
+ * Transient messages:
+ * - show immediately
+ * - will continue to be in the rotation of messages shown until hideTransient is called.
+ * - can be presented with an "error" color if isError is true
+ */
+ public void showTransient(CharSequence newIndication, boolean isError) {
+ updateIndication(INDICATION_TYPE_TRANSIENT,
+ new KeyguardIndication.Builder()
+ .setMessage(newIndication)
+ .setTextColor(isError
+ ? Utils.getColorError(getContext())
+ : mInitialTextColorState)
+ .build(),
+ /* showImmediately */true);
+ }
+
+ /**
+ * Hide a transient message immediately.
+ */
+ public void hideTransient() {
+ hideIndication(INDICATION_TYPE_TRANSIENT);
+ }
+
+ /**
+ * @return true if there are available indications to show
+ */
+ public boolean hasIndications() {
+ return mIndicationMessages.keySet().size() > 0;
+ }
+
+ /**
+ * Immediately show the passed indication type and schedule the next indication to show.
+ * Will re-add this indication to be re-shown after all other indications have been
+ * rotated through.
+ */
+ private void showIndication(@IndicationType int type) {
+ cancelScheduledIndication();
+
+ mCurrIndicationType = type;
+ mIndicationQueue.removeIf(x -> x == type);
+ if (mCurrIndicationType == INDICATION_TYPE_NONE) {
+ mView.setVisibility(View.GONE);
+ } else {
+ mView.setVisibility(View.VISIBLE);
+ mIndicationQueue.add(type); // re-add to show later
+ }
+
+ mView.switchIndication(mIndicationMessages.get(type));
+
+ // only schedule next indication if there's more than just this indication in the queue
+ if (mCurrIndicationType != INDICATION_TYPE_NONE && mIndicationQueue.size() > 1) {
+ scheduleShowNextIndication();
+ }
+ }
+
+ protected boolean isNextIndicationScheduled() {
+ return mShowNextIndicationRunnable != null;
+ }
+
+ private void scheduleShowNextIndication() {
+ cancelScheduledIndication();
+ mShowNextIndicationRunnable = new ShowNextIndication(DEFAULT_INDICATION_SHOW_LENGTH);
+ }
+
+ private void cancelScheduledIndication() {
+ if (mShowNextIndicationRunnable != null) {
+ mShowNextIndicationRunnable.cancelDelayedExecution();
+ mShowNextIndicationRunnable = null;
+ }
+ }
+
+ private StatusBarStateController.StateListener mStatusBarStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onDozeAmountChanged(float linear, float eased) {
+ mView.setAlpha((1 - linear) * mMaxAlpha);
+ }
+
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ if (isDozing == mIsDozing) return;
+ mIsDozing = isDozing;
+ if (mIsDozing) {
+ showIndication(INDICATION_TYPE_NONE);
+ } else if (mIndicationQueue.size() > 0) {
+ showIndication(mIndicationQueue.remove(0));
+ }
+ }
+ };
+
+ /**
+ * Shows the next indication in the IndicationQueue after an optional delay.
+ * This wrapper has the ability to cancel itself (remove runnable from DelayableExecutor) or
+ * immediately run itself (which also removes itself from the DelayableExecutor).
+ */
+ class ShowNextIndication {
+ private final Runnable mShowIndicationRunnable;
+ private Runnable mCancelDelayedRunnable;
+
+ ShowNextIndication(long delay) {
+ mShowIndicationRunnable = () -> {
+ int type = mIndicationQueue.size() == 0
+ ? INDICATION_TYPE_NONE : mIndicationQueue.remove(0);
+ showIndication(type);
+ };
+ mCancelDelayedRunnable = mExecutor.executeDelayed(mShowIndicationRunnable, delay);
+ }
+
+ public void runImmediately() {
+ cancelDelayedExecution();
+ mShowIndicationRunnable.run();
+ }
+
+ public void cancelDelayedExecution() {
+ if (mCancelDelayedRunnable != null) {
+ mCancelDelayedRunnable.run();
+ mCancelDelayedRunnable = null;
+ }
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("KeyguardIndicationRotatingTextViewController:");
+ pw.println(" currentMessage=" + mView.getText());
+ pw.println(" dozing:" + mIsDozing);
+ pw.println(" queue:" + mIndicationQueue.toString());
+ pw.println(" showNextIndicationRunnable:" + mShowNextIndicationRunnable);
+
+ if (hasIndications()) {
+ pw.println(" All messages:");
+ for (int type : mIndicationMessages.keySet()) {
+ pw.println(" type=" + type + " " + mIndicationMessages.get(type));
+ }
+ }
+ }
+
+ private static final int INDICATION_TYPE_NONE = -1;
+ public static final int INDICATION_TYPE_OWNER_INFO = 0;
+ public static final int INDICATION_TYPE_DISCLOSURE = 1;
+ public static final int INDICATION_TYPE_LOGOUT = 2;
+ public static final int INDICATION_TYPE_BATTERY = 3;
+ public static final int INDICATION_TYPE_ALIGNMENT = 4;
+ public static final int INDICATION_TYPE_TRANSIENT = 5;
+ public static final int INDICATION_TYPE_TRUST = 6;
+ public static final int INDICATION_TYPE_RESTING = 7;
+ public static final int INDICATION_TYPE_USER_LOCKED = 8;
+ public static final int INDICATION_TYPE_NOW_PLAYING = 9;
+ public static final int INDICATION_TYPE_REVERSE_CHARGING = 10;
+
+ @IntDef({
+ INDICATION_TYPE_NONE,
+ INDICATION_TYPE_DISCLOSURE,
+ INDICATION_TYPE_OWNER_INFO,
+ INDICATION_TYPE_LOGOUT,
+ INDICATION_TYPE_BATTERY,
+ INDICATION_TYPE_ALIGNMENT,
+ INDICATION_TYPE_TRANSIENT,
+ INDICATION_TYPE_TRUST,
+ INDICATION_TYPE_RESTING,
+ INDICATION_TYPE_USER_LOCKED,
+ INDICATION_TYPE_NOW_PLAYING,
+ INDICATION_TYPE_REVERSE_CHARGING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface IndicationType{}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 0c4802486c16..f9b0dff377d2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -67,6 +67,7 @@ import android.telephony.TelephonyManager;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.RemoteAnimationTarget;
@@ -308,6 +309,13 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
private final SparseIntArray mLastSimStates = new SparseIntArray();
private static SparseIntArray mUnlockTrackSimStates = new SparseIntArray();
+ /**
+ * Indicates if a SIM card had the SIM PIN enabled during the initialization, before
+ * reaching the SIM_STATE_READY state. The flag is reset to false at SIM_STATE_READY.
+ * Index is the slotId - in case of multiple SIM cards.
+ */
+ private final SparseBooleanArray mSimWasLocked = new SparseBooleanArray();
+
private boolean mDeviceInteractive;
private boolean mGoingToSleep;
@@ -409,6 +417,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
@Override
public void onUserSwitching(int userId) {
+ if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId));
// Note that the mLockPatternUtils user has already been updated from setCurrentUser.
// We need to force a reset of the views, since lockNow (called by
// ActivityManagerService) will not reconstruct the keyguard if it is already showing.
@@ -426,6 +435,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
@Override
public void onUserSwitchComplete(int userId) {
+ if (DEBUG) Log.d(TAG, String.format("onUserSwitchComplete %d", userId));
if (userId != UserHandle.USER_SYSTEM) {
UserInfo info = UserManager.get(mContext).getUserInfo(userId);
// Don't try to dismiss if the user has Pin/Patter/Password set
@@ -479,10 +489,10 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
}
}
- boolean simWasLocked;
+ boolean lastSimStateWasLocked;
synchronized (KeyguardViewMediator.this) {
int lastState = mLastSimStates.get(slotId);
- simWasLocked = (lastState == TelephonyManager.SIM_STATE_PIN_REQUIRED
+ lastSimStateWasLocked = (lastState == TelephonyManager.SIM_STATE_PIN_REQUIRED
|| lastState == TelephonyManager.SIM_STATE_PUK_REQUIRED);
mLastSimStates.append(slotId, simState);
@@ -519,17 +529,19 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
if (simState == TelephonyManager.SIM_STATE_ABSENT) {
// MVNO SIMs can become transiently NOT_READY when switching networks,
// so we should only lock when they are ABSENT.
- if (simWasLocked) {
+ if (lastSimStateWasLocked) {
if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to ABSENT when the "
+ "previous state was locked. Reset the state.");
resetStateLocked();
}
+ mSimWasLocked.append(slotId, false);
}
}
break;
case TelephonyManager.SIM_STATE_PIN_REQUIRED:
case TelephonyManager.SIM_STATE_PUK_REQUIRED:
synchronized (KeyguardViewMediator.this) {
+ mSimWasLocked.append(slotId, true);
if (!mShowing) {
if (DEBUG_SIM_STATES) Log.d(TAG,
"INTENT_VALUE_ICC_LOCKED and keygaurd isn't "
@@ -556,9 +568,10 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
case TelephonyManager.SIM_STATE_READY:
synchronized (KeyguardViewMediator.this) {
if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
- if (mShowing && simWasLocked) {
+ if (mShowing && mSimWasLocked.get(slotId, false)) {
if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to READY when the "
- + "previous state was locked. Reset the state.");
+ + "previously was locked. Reset the state.");
+ mSimWasLocked.append(slotId, false);
resetStateLocked();
}
}
@@ -2401,6 +2414,9 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
return;
}
mDozing = dozing;
+ if (!dozing) {
+ mAnimatingScreenOff = false;
+ }
setShowingLocked(mShowing);
}
@@ -2410,7 +2426,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
// is 1f), then show the activity lock screen.
if (mAnimatingScreenOff && mDozing && linear == 1f) {
mAnimatingScreenOff = false;
- setShowingLocked(mShowing);
+ setShowingLocked(mShowing, true);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 76281d8c0f00..de2e7c476e18 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -29,7 +29,9 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardDisplayManager;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardViewController;
+import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
+import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
@@ -61,7 +63,8 @@ import dagger.Provides;
/**
* Dagger Module providing {@link StatusBar}.
*/
-@Module(subcomponents = {KeyguardStatusViewComponent.class},
+@Module(subcomponents = {KeyguardStatusViewComponent.class,
+ KeyguardQsUserSwitchComponent.class, KeyguardUserSwitcherComponent.class},
includes = {FalsingModule.class})
public class KeyguardModule {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index e8dba8f3b585..c1db8edf4119 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -17,7 +17,6 @@
package com.android.systemui.log
import android.util.Log
-import com.android.systemui.dump.DumpManager
import com.android.systemui.log.dagger.LogModule
import java.io.PrintWriter
import java.text.SimpleDateFormat
@@ -58,7 +57,7 @@ import java.util.Locale
* In either case, `level` can be any of `verbose`, `debug`, `info`, `warn`, `error`, `assert`, or
* the first letter of any of the previous.
*
- * Buffers are provided by [LogModule].
+ * Buffers are provided by [LogModule]. Instances should be created using a [LogBufferFactory].
*
* @param name The name of this buffer
* @param maxLogs The maximum number of messages to keep in memory at any one time, including the
@@ -77,10 +76,6 @@ class LogBuffer(
var frozen = false
private set
- fun attach(dumpManager: DumpManager) {
- dumpManager.registerBuffer(name, this)
- }
-
/**
* Logs a message to the log buffer
*
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
new file mode 100644
index 000000000000..0622df3dbb43
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.log
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import javax.inject.Inject
+
+@SysUISingleton
+class LogBufferFactory @Inject constructor(
+ private val dumpManager: DumpManager,
+ private val logcatEchoTracker: LogcatEchoTracker
+) {
+ @JvmOverloads
+ fun create(name: String, maxPoolSize: Int, flexSize: Int = 10): LogBuffer {
+ val buffer = LogBuffer(name, maxPoolSize, flexSize, logcatEchoTracker)
+ dumpManager.registerBuffer(name, buffer)
+ return buffer
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index fff185b99a1e..19193f9eceb2 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -22,8 +22,8 @@ import android.os.Looper;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.LogBufferFactory;
import com.android.systemui.log.LogcatEchoTracker;
import com.android.systemui.log.LogcatEchoTrackerDebug;
import com.android.systemui.log.LogcatEchoTrackerProd;
@@ -40,96 +40,64 @@ public class LogModule {
@Provides
@SysUISingleton
@DozeLog
- public static LogBuffer provideDozeLogBuffer(
- LogcatEchoTracker bufferFilter,
- DumpManager dumpManager) {
- LogBuffer buffer = new LogBuffer("DozeLog", 100, 10, bufferFilter);
- buffer.attach(dumpManager);
- return buffer;
+ public static LogBuffer provideDozeLogBuffer(LogBufferFactory factory) {
+ return factory.create("DozeLog", 100);
}
/** Provides a logging buffer for all logs related to the data layer of notifications. */
@Provides
@SysUISingleton
@NotificationLog
- public static LogBuffer provideNotificationsLogBuffer(
- LogcatEchoTracker bufferFilter,
- DumpManager dumpManager) {
- LogBuffer buffer = new LogBuffer("NotifLog", 1000, 10, bufferFilter);
- buffer.attach(dumpManager);
- return buffer;
+ public static LogBuffer provideNotificationsLogBuffer(LogBufferFactory factory) {
+ return factory.create("NotifLog", 1000);
}
/** Provides a logging buffer for all logs related to managing notification sections. */
@Provides
@SysUISingleton
@NotificationSectionLog
- public static LogBuffer provideNotificationSectionLogBuffer(
- LogcatEchoTracker bufferFilter,
- DumpManager dumpManager) {
- LogBuffer buffer = new LogBuffer("NotifSectionLog", 1000, 10, bufferFilter);
- buffer.attach(dumpManager);
- return buffer;
+ public static LogBuffer provideNotificationSectionLogBuffer(LogBufferFactory factory) {
+ return factory.create("NotifSectionLog", 1000);
}
/** Provides a logging buffer for all logs related to the data layer of notifications. */
@Provides
@SysUISingleton
@NotifInteractionLog
- public static LogBuffer provideNotifInteractionLogBuffer(
- LogcatEchoTracker echoTracker,
- DumpManager dumpManager) {
- LogBuffer buffer = new LogBuffer("NotifInteractionLog", 50, 10, echoTracker);
- buffer.attach(dumpManager);
- return buffer;
+ public static LogBuffer provideNotifInteractionLogBuffer(LogBufferFactory factory) {
+ return factory.create("NotifInteractionLog", 50);
}
/** Provides a logging buffer for all logs related to Quick Settings. */
@Provides
@SysUISingleton
@QSLog
- public static LogBuffer provideQuickSettingsLogBuffer(
- LogcatEchoTracker bufferFilter,
- DumpManager dumpManager) {
- LogBuffer buffer = new LogBuffer("QSLog", 500, 10, bufferFilter);
- buffer.attach(dumpManager);
- return buffer;
+ public static LogBuffer provideQuickSettingsLogBuffer(LogBufferFactory factory) {
+ return factory.create("QSLog", 500);
}
/** Provides a logging buffer for {@link com.android.systemui.broadcast.BroadcastDispatcher} */
@Provides
@SysUISingleton
@BroadcastDispatcherLog
- public static LogBuffer provideBroadcastDispatcherLogBuffer(
- LogcatEchoTracker bufferFilter,
- DumpManager dumpManager) {
- LogBuffer buffer = new LogBuffer("BroadcastDispatcherLog", 500, 10, bufferFilter);
- buffer.attach(dumpManager);
- return buffer;
+ public static LogBuffer provideBroadcastDispatcherLogBuffer(LogBufferFactory factory) {
+ return factory.create("BroadcastDispatcherLog", 500);
}
/** Provides a logging buffer for all logs related to Toasts shown by SystemUI. */
@Provides
@SysUISingleton
@ToastLog
- public static LogBuffer provideToastLogBuffer(
- LogcatEchoTracker bufferFilter,
- DumpManager dumpManager) {
- LogBuffer buffer = new LogBuffer("ToastLog", 50, 10, bufferFilter);
- buffer.attach(dumpManager);
- return buffer;
+ public static LogBuffer provideToastLogBuffer(LogBufferFactory factory) {
+ return factory.create("ToastLog", 50);
}
/** Provides a logging buffer for all logs related to privacy indicators in SystemUI. */
@Provides
@SysUISingleton
@PrivacyLog
- public static LogBuffer providePrivacyLogBuffer(
- LogcatEchoTracker bufferFilter,
- DumpManager dumpManager) {
- LogBuffer buffer = new LogBuffer(("PrivacyLog"), 100, 10, bufferFilter);
- buffer.attach(dumpManager);
- return buffer;
+ public static LogBuffer providePrivacyLogBuffer(LogBufferFactory factory) {
+ return factory.create("PrivacyLog", 100);
}
/** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 935352665314..a3ff3753cbf9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -1,7 +1,9 @@
package com.android.systemui.media
+import android.animation.ArgbEvaluator
import android.content.Context
import android.content.Intent
+import android.content.res.ColorStateList
import android.content.res.Configuration
import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
import android.util.Log
@@ -112,6 +114,9 @@ class MediaCarouselController @Inject constructor(
private val visualStabilityCallback: VisualStabilityManager.Callback
private var needsReordering: Boolean = false
private var keysNeedRemoval = mutableSetOf<String>()
+ private var bgColor = getBackgroundColor()
+ private var fgColor = com.android.settingslib.Utils.getColorAttr(context,
+ com.android.internal.R.attr.textColorPrimary).defaultColor
private var isRtl: Boolean = false
set(value) {
if (value != field) {
@@ -147,7 +152,7 @@ class MediaCarouselController @Inject constructor(
}
override fun onUiModeChanged() {
- // Only settings button needs to update for dark theme
+ recreatePlayers()
inflateSettingsButton()
}
}
@@ -249,6 +254,11 @@ class MediaCarouselController @Inject constructor(
}
private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData) {
+ data.actions.forEach {
+ it.icon?.setTintList(ColorStateList.valueOf(fgColor))
+ }
+ data.appIcon?.setTintList(ColorStateList.valueOf(fgColor))
+ val dataCopy = data.copy(backgroundColor = bgColor)
val existingPlayer = MediaPlayerData.getMediaPlayer(key, oldKey)
if (existingPlayer == null) {
var newPlayer = mediaControlPanelFactory.get()
@@ -257,14 +267,14 @@ class MediaCarouselController @Inject constructor(
val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
newPlayer.view?.player?.setLayoutParams(lp)
- newPlayer.bind(data, key)
+ newPlayer.bind(dataCopy, key)
newPlayer.setListening(currentlyExpanded)
- MediaPlayerData.addMediaPlayer(key, data, newPlayer)
+ MediaPlayerData.addMediaPlayer(key, dataCopy, newPlayer)
updatePlayerToState(newPlayer, noAnimation = true)
reorderAllPlayers()
} else {
- existingPlayer.bind(data, key)
- MediaPlayerData.addMediaPlayer(key, data, existingPlayer)
+ existingPlayer.bind(dataCopy, key)
+ MediaPlayerData.addMediaPlayer(key, dataCopy, existingPlayer)
if (visualStabilityManager.isReorderingAllowed) {
reorderAllPlayers()
} else {
@@ -298,12 +308,27 @@ class MediaCarouselController @Inject constructor(
}
private fun recreatePlayers() {
+ bgColor = getBackgroundColor()
+
+ fgColor = com.android.settingslib.Utils.getColorAttr(context,
+ com.android.internal.R.attr.textColorPrimary).defaultColor
+ pageIndicator.tintList = ColorStateList.valueOf(fgColor)
+
MediaPlayerData.mediaData().forEach { (key, data) ->
removePlayer(key, dismissMediaData = false)
addOrUpdatePlayer(key = key, oldKey = null, data = data)
}
}
+ private fun getBackgroundColor(): Int {
+ val themeAccent = com.android.settingslib.Utils.getColorAttr(context,
+ com.android.internal.R.attr.colorAccent).defaultColor
+ val themeBackground = com.android.settingslib.Utils.getColorAttr(context,
+ com.android.internal.R.attr.colorBackground).defaultColor
+ // Simulate transparency - cannot be actually transparent because of lockscreen
+ return ArgbEvaluator().evaluate(0.25f, themeBackground, themeAccent) as Int
+ }
+
private fun updatePageIndicator() {
val numPages = mediaContent.getChildCount()
pageIndicator.setNumPages(numPages)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 3629d4d6141c..55c55b97db51 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -25,7 +25,6 @@ import android.content.Intent
import android.content.IntentFilter
import android.graphics.Bitmap
import android.graphics.Canvas
-import android.graphics.Color
import android.graphics.ImageDecoder
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
@@ -38,7 +37,6 @@ import android.os.UserHandle
import android.service.notification.StatusBarNotification
import android.text.TextUtils
import android.util.Log
-import com.android.internal.graphics.ColorUtils
import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -48,7 +46,6 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
-import com.android.systemui.statusbar.notification.MediaNotificationProcessor
import com.android.systemui.statusbar.notification.row.HybridGroupManager
import com.android.systemui.util.Assert
import com.android.systemui.util.Utils
@@ -68,10 +65,6 @@ private val ART_URIS = arrayOf(
private const val TAG = "MediaDataManager"
private const val DEBUG = true
-private const val DEFAULT_LUMINOSITY = 0.25f
-private const val LUMINOSITY_THRESHOLD = 0.05f
-private const val SATURATION_MULTIPLIER = 0.8f
-const val DEFAULT_COLOR = Color.DKGRAY
private val LOADING = MediaData(-1, false, 0, null, null, null, null, null,
emptyList(), emptyList(), "INVALID", null, null, null, true, null)
@@ -110,6 +103,11 @@ class MediaDataManager(
private val useQsMediaPlayer: Boolean
) : Dumpable {
+ private val themeText = com.android.settingslib.Utils.getColorAttr(context,
+ com.android.internal.R.attr.textColorPrimary).defaultColor
+ private val bgColor = com.android.settingslib.Utils.getColorAttr(context,
+ com.android.internal.R.attr.colorBackground).defaultColor
+
// Internal listeners are part of the internal pipeline. External listeners (those registered
// with [MediaDeviceManager.addListener]) receive events after they have propagated through
// the internal pipeline.
@@ -395,7 +393,6 @@ class MediaDataManager(
} else {
null
}
- val bgColor = artworkBitmap?.let { computeBackgroundColor(it) } ?: DEFAULT_COLOR
val mediaAction = getResumeMediaAction(resumeAction)
foregroundExecutor.execute {
@@ -449,7 +446,6 @@ class MediaDataManager(
}
}
}
- val bgColor = computeBackgroundColor(artworkBitmap)
// App name
val builder = Notification.Builder.recoverBuilder(context, notif)
@@ -506,7 +502,7 @@ class MediaDataManager(
Icon.createWithResource(packageContext, action.getIcon()!!.getResId())
} else {
action.getIcon()
- }
+ }.setTint(themeText)
val mediaAction = MediaAction(
mediaActionIcon,
runnable,
@@ -589,38 +585,9 @@ class MediaDataManager(
}
}
- private fun computeBackgroundColor(artworkBitmap: Bitmap?): Int {
- var color = Color.WHITE
- if (artworkBitmap != null && artworkBitmap.width > 1 && artworkBitmap.height > 1) {
- // If we have valid art, get colors from that
- val p = MediaNotificationProcessor.generateArtworkPaletteBuilder(artworkBitmap)
- .generate()
- val swatch = MediaNotificationProcessor.findBackgroundSwatch(p)
- color = swatch.rgb
- } else {
- return DEFAULT_COLOR
- }
- // Adapt background color, so it's always subdued and text is legible
- val tmpHsl = floatArrayOf(0f, 0f, 0f)
- ColorUtils.colorToHSL(color, tmpHsl)
-
- val l = tmpHsl[2]
- // Colors with very low luminosity can have any saturation. This means that changing the
- // luminosity can make a black become red. Let's remove the saturation of very light or
- // very dark colors to avoid this issue.
- if (l < LUMINOSITY_THRESHOLD || l > 1f - LUMINOSITY_THRESHOLD) {
- tmpHsl[1] = 0f
- }
- tmpHsl[1] *= SATURATION_MULTIPLIER
- tmpHsl[2] = DEFAULT_LUMINOSITY
-
- color = ColorUtils.HSLToColor(tmpHsl)
- return color
- }
-
private fun getResumeMediaAction(action: Runnable): MediaAction {
return MediaAction(
- Icon.createWithResource(context, R.drawable.lb_ic_play),
+ Icon.createWithResource(context, R.drawable.lb_ic_play).setTint(themeText),
action,
context.getString(R.string.controls_media_resume)
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 4c96de232810..553b6d8679db 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -163,7 +163,8 @@ public class RingtonePlayer extends SystemUI {
}
@Override
- public void setPlaybackProperties(IBinder token, float volume, boolean looping) {
+ public void setPlaybackProperties(IBinder token, float volume, boolean looping,
+ boolean hapticGeneratorEnabled) {
Client client;
synchronized (mClients) {
client = mClients.get(token);
@@ -171,6 +172,7 @@ public class RingtonePlayer extends SystemUI {
if (client != null) {
client.mRingtone.setVolume(volume);
client.mRingtone.setLooping(looping);
+ client.mRingtone.setHapticGeneratorEnabled(hapticGeneratorEnabled);
}
// else no client for token when setting playback properties but will be set at play()
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index dab4d0bb00c5..553623702aed 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -855,6 +855,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Override
public void onRotationProposal(final int rotation, boolean isValid) {
+ if (mNavigationBarView == null) {
+ if (RotationContextButton.DEBUG_ROTATION) {
+ Log.v(TAG, "onRotationProposal proposedRotation=" +
+ Surface.rotationToString(rotation) + ", mNavigationBarView is null");
+ }
+ return;
+ }
+
final int winRotation = mNavigationBarView.getDisplay().getRotation();
final boolean rotateSuggestionsDisabled = RotationButtonController
.hasDisable2RotateSuggestionFlag(mDisabledFlags2);
@@ -1116,7 +1124,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
// If an incoming call is ringing, HOME is totally disabled.
// (The user is already on the InCallUI at this point,
- // and his ONLY options are to answer or reject the call.)
+ // and their ONLY options are to answer or reject the call.)
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mHomeBlockedThisTouch = false;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index c07404c2e34d..8e75bec72c15 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -76,6 +76,7 @@ import com.android.systemui.navigationbar.buttons.ContextualButton;
import com.android.systemui.navigationbar.buttons.ContextualButtonGroup;
import com.android.systemui.navigationbar.buttons.DeadZone;
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
+import com.android.systemui.navigationbar.buttons.NearestTouchFrame;
import com.android.systemui.navigationbar.buttons.RotationContextButton;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.navigationbar.gestural.FloatingRotationButton;
@@ -97,6 +98,8 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
import java.util.function.Consumer;
public class NavigationBarView extends FrameLayout implements
@@ -129,6 +132,7 @@ public class NavigationBarView extends FrameLayout implements
private final Region mTmpRegion = new Region();
private final int[] mTmpPosition = new int[2];
private Rect mTmpBounds = new Rect();
+ private Map<View, Rect> mButtonFullTouchableRegions = new HashMap<>();
private KeyButtonDrawable mBackIcon;
private KeyButtonDrawable mHomeDefaultIcon;
@@ -973,9 +977,18 @@ public class NavigationBarView extends FrameLayout implements
getButtonLocations(true /* includeFloatingRotationButton */, true /* inScreen */));
}
+ private void updateButtonTouchRegionCache() {
+ FrameLayout navBarLayout = mIsVertical
+ ? mNavigationInflaterView.mVertical
+ : mNavigationInflaterView.mHorizontal;
+ mButtonFullTouchableRegions = ((NearestTouchFrame) navBarLayout
+ .findViewById(R.id.nav_buttons)).getFullTouchableChildRegions();
+ }
+
private Region getButtonLocations(boolean includeFloatingRotationButton,
boolean inScreenSpace) {
mTmpRegion.setEmpty();
+ updateButtonTouchRegionCache();
updateButtonLocation(getBackButton(), inScreenSpace);
updateButtonLocation(getHomeButton(), inScreenSpace);
updateButtonLocation(getRecentsButton(), inScreenSpace);
@@ -999,6 +1012,12 @@ public class NavigationBarView extends FrameLayout implements
if (view == null || !button.isVisible()) {
return;
}
+ // If the button is tappable from perspective of NearestTouchFrame, then we'll
+ // include the regions where the tap is valid instead of just the button layout location
+ if (mButtonFullTouchableRegions.containsKey(view)) {
+ mTmpRegion.op(mButtonFullTouchableRegions.get(view), Op.UNION);
+ return;
+ }
updateButtonLocation(view, inScreenSpace);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java
index 453e85ae25c7..517f8e7f0dac 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java
@@ -51,7 +51,7 @@ public class ContextualButton extends ButtonDispatcher {
* Reload the drawable from resource id, should reapply the previous dark intensity.
*/
public void updateIcon(int lightIconColor, int darkIconColor) {
- if (getCurrentView() == null || !getCurrentView().isAttachedToWindow() || mIconResId == 0) {
+ if (mIconResId == 0) {
return;
}
final KeyButtonDrawable currentDrawable = getImageDrawable();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java
index 88c8fea085fb..b1c85b5c530e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java
@@ -18,8 +18,9 @@ package com.android.systemui.navigationbar.buttons;
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -27,8 +28,13 @@ import android.widget.FrameLayout;
import androidx.annotation.VisibleForTesting;
+import com.android.systemui.R;
+
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
/**
* Redirects touches that aren't handled by any child view to the nearest
@@ -36,11 +42,32 @@ import java.util.Comparator;
*/
public class NearestTouchFrame extends FrameLayout {
- private final ArrayList<View> mClickableChildren = new ArrayList<>();
+ private final List<View> mClickableChildren = new ArrayList<>();
+ private final List<View> mAttachedChildren = new ArrayList<>();
private final boolean mIsActive;
private final int[] mTmpInt = new int[2];
private final int[] mOffset = new int[2];
+ private boolean mIsVertical;
private View mTouchingChild;
+ private final Map<View, Rect> mTouchableRegions = new HashMap<>();
+ /**
+ * Used to sort all child views either by their left position or their top position,
+ * depending on if this layout is used horizontally or vertically, respectively
+ */
+ private final Comparator<View> mChildRegionComparator =
+ (view1, view2) -> {
+ int leftTopIndex = 0;
+ if (mIsVertical) {
+ // Compare view bound's "top" values
+ leftTopIndex = 1;
+ }
+ view1.getLocationInWindow(mTmpInt);
+ int startingCoordView1 = mTmpInt[leftTopIndex] - mOffset[leftTopIndex];
+ view2.getLocationInWindow(mTmpInt);
+ int startingCoordView2 = mTmpInt[leftTopIndex] - mOffset[leftTopIndex];
+
+ return startingCoordView1 - startingCoordView2;
+ };
public NearestTouchFrame(Context context, AttributeSet attrs) {
this(context, attrs, context.getResources().getConfiguration());
@@ -50,13 +77,20 @@ public class NearestTouchFrame extends FrameLayout {
NearestTouchFrame(Context context, AttributeSet attrs, Configuration c) {
super(context, attrs);
mIsActive = c.smallestScreenWidthDp < 600;
+ int[] attrsArray = new int[] {R.attr.isVertical};
+ TypedArray ta = context.obtainStyledAttributes(attrs, attrsArray);
+ mIsVertical = ta.getBoolean(0, false);
+ ta.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mClickableChildren.clear();
+ mAttachedChildren.clear();
+ mTouchableRegions.clear();
addClickableChildren(this);
+ cacheClosestChildLocations();
}
@Override
@@ -65,6 +99,85 @@ public class NearestTouchFrame extends FrameLayout {
getLocationInWindow(mOffset);
}
+ /**
+ * Populates {@link #mTouchableRegions} with the regions where each clickable child is the
+ * closest for a given point on this layout.
+ */
+ private void cacheClosestChildLocations() {
+ if (getWidth() == 0 || getHeight() == 0) {
+ return;
+ }
+
+ // Sort by either top or left depending on mIsVertical, then take out all children
+ // that are not attached to window
+ mClickableChildren.sort(mChildRegionComparator);
+ mClickableChildren.stream()
+ .filter(View::isAttachedToWindow)
+ .forEachOrdered(mAttachedChildren::add);
+
+ // Cache bounds of children
+ // Mark coordinates where the actual child layout resides in this frame's window
+ for (int i = 0; i < mAttachedChildren.size(); i++) {
+ View child = mAttachedChildren.get(i);
+ if (!child.isAttachedToWindow()) {
+ continue;
+ }
+ Rect childRegion = getChildsBounds(child);
+
+ // We compute closest child from this child to the previous one
+ if (i == 0) {
+ // First child, nothing to the left/top of it
+ if (mIsVertical) {
+ childRegion.top = 0;
+ } else {
+ childRegion.left = 0;
+ }
+ mTouchableRegions.put(child, childRegion);
+ continue;
+ }
+
+ View previousChild = mAttachedChildren.get(i - 1);
+ Rect previousChildBounds = mTouchableRegions.get(previousChild);
+ int midPoint;
+ if (mIsVertical) {
+ int distance = childRegion.top - previousChildBounds.bottom;
+ midPoint = distance / 2;
+ childRegion.top -= midPoint;
+ previousChildBounds.bottom += midPoint - ((distance % 2) == 0 ? 1 : 0);
+ } else {
+ int distance = childRegion.left - previousChildBounds.right;
+ midPoint = distance / 2;
+ childRegion.left -= midPoint;
+ previousChildBounds.right += midPoint - ((distance % 2) == 0 ? 1 : 0);
+ }
+
+ if (i == mClickableChildren.size() - 1) {
+ // Last child, nothing to right/bottom of it
+ if (mIsVertical) {
+ childRegion.bottom = getHeight();
+ } else {
+ childRegion.right = getWidth();
+ }
+ }
+
+ mTouchableRegions.put(child, childRegion);
+ }
+ }
+
+ @VisibleForTesting
+ void setIsVertical(boolean isVertical) {
+ mIsVertical = isVertical;
+ }
+
+ private Rect getChildsBounds(View child) {
+ child.getLocationInWindow(mTmpInt);
+ int left = mTmpInt[0] - mOffset[0];
+ int top = mTmpInt[1] - mOffset[1];
+ int right = left + child.getWidth();
+ int bottom = top + child.getHeight();
+ return new Rect(left, top, right, bottom);
+ }
+
private void addClickableChildren(ViewGroup group) {
final int N = group.getChildCount();
for (int i = 0; i < N; i++) {
@@ -77,47 +190,45 @@ public class NearestTouchFrame extends FrameLayout {
}
}
+ /**
+ * @return A Map where the key is the view object of the button and the value
+ * is the Rect where that button will receive a touch event if pressed. This Rect will
+ * usually be larger than the layout bounds for the button.
+ * The Rect is in screen coordinates.
+ */
+ public Map<View, Rect> getFullTouchableChildRegions() {
+ Map<View, Rect> fullTouchRegions = new HashMap<>(mTouchableRegions.size());
+ getLocationOnScreen(mTmpInt);
+ for (Map.Entry<View, Rect> entry : mTouchableRegions.entrySet()) {
+ View child = entry.getKey();
+ Rect screenRegion = new Rect(entry.getValue());
+ screenRegion.offset(mTmpInt[0], mTmpInt[1]);
+ fullTouchRegions.put(child, screenRegion);
+ }
+ return fullTouchRegions;
+ }
+
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mIsActive) {
+ int x = (int) event.getX();
+ int y = (int) event.getY();
if (event.getAction() == MotionEvent.ACTION_DOWN) {
- mTouchingChild = findNearestChild(event);
+ mTouchingChild = mClickableChildren
+ .stream()
+ .filter(View::isAttachedToWindow)
+ .filter(view -> mTouchableRegions.get(view).contains(x, y))
+ .findFirst()
+ .orElse(null);
+
}
if (mTouchingChild != null) {
- event.offsetLocation(mTouchingChild.getWidth() / 2 - event.getX(),
- mTouchingChild.getHeight() / 2 - event.getY());
+ event.offsetLocation(mTouchingChild.getWidth() / 2 - x,
+ mTouchingChild.getHeight() / 2 - y);
return mTouchingChild.getVisibility() == VISIBLE
&& mTouchingChild.dispatchTouchEvent(event);
}
}
return super.onTouchEvent(event);
}
-
- private View findNearestChild(MotionEvent event) {
- if (mClickableChildren.isEmpty()) {
- return null;
- }
- return mClickableChildren
- .stream()
- .filter(View::isAttachedToWindow)
- .map(v -> new Pair<>(distance(v, event), v))
- .min(Comparator.comparingInt(f -> f.first))
- .map(data -> data.second)
- .orElse(null);
- }
-
- private int distance(View v, MotionEvent event) {
- v.getLocationInWindow(mTmpInt);
- int left = mTmpInt[0] - mOffset[0];
- int top = mTmpInt[1] - mOffset[1];
- int right = left + v.getWidth();
- int bottom = top + v.getHeight();
-
- int x = Math.min(Math.abs(left - (int) event.getX()),
- Math.abs((int) event.getX() - right));
- int y = Math.min(Math.abs(top - (int) event.getY()),
- Math.abs((int) event.getY() - bottom));
-
- return Math.max(x, y);
- }
}
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 d7a3537a4a5f..292cc7a7deaa 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -38,6 +38,7 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Choreographer;
+import android.view.Display;
import android.view.ISystemGestureExclusionListener;
import android.view.InputDevice;
import android.view.InputEvent;
@@ -96,6 +97,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
static final boolean DEBUG_MISSING_GESTURE = true;
static final String DEBUG_MISSING_GESTURE_TAG = "NoBackGesture";
+ private static final boolean ENABLE_PER_WINDOW_INPUT_ROTATION =
+ SystemProperties.getBoolean("persist.debug.per_window_input_rotation", false);
+
private ISystemGestureExclusionListener mGestureExclusionListener =
new ISystemGestureExclusionListener.Stub() {
@Override
@@ -505,9 +509,19 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
}
private void onInputEvent(InputEvent ev) {
- if (ev instanceof MotionEvent) {
- onMotionEvent((MotionEvent) ev);
+ if (!(ev instanceof MotionEvent)) return;
+ MotionEvent event = (MotionEvent) ev;
+ if (ENABLE_PER_WINDOW_INPUT_ROTATION) {
+ final Display display = mContext.getDisplay();
+ int rotation = display.getRotation();
+ if (rotation != Surface.ROTATION_0) {
+ Point sz = new Point();
+ display.getRealSize(sz);
+ event = MotionEvent.obtain(event);
+ event.transform(MotionEvent.createRotateMatrix(rotation, sz.x, sz.y));
+ }
}
+ onMotionEvent(event);
}
private void updateMLModelState() {
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
new file mode 100644
index 000000000000..e7458a3df801
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.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.people;
+
+import android.app.people.ConversationChannel;
+import android.app.people.IPeopleManager;
+import android.app.people.PeopleSpaceTile;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Log;
+import android.widget.RemoteViews;
+
+import com.android.systemui.shared.system.PeopleProviderUtils;
+
+/** API that returns a People Tile preview. */
+public class PeopleProvider extends ContentProvider {
+
+ LauncherApps mLauncherApps;
+ IPeopleManager mPeopleManager;
+
+ private static final String TAG = "PeopleProvider";
+ private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
+ private static final String EMPTY_STRING = "";
+
+ @Override
+ public Bundle call(String method, String arg, Bundle extras) {
+ if (!doesCallerHavePermission()) {
+ String callingPackage = getCallingPackage();
+ Log.w(TAG, "API not accessible to calling package: " + callingPackage);
+ throw new SecurityException("API not accessible to calling package: " + callingPackage);
+ }
+ if (!PeopleProviderUtils.GET_PEOPLE_TILE_PREVIEW_METHOD.equals(method)) {
+ Log.w(TAG, "Invalid method");
+ throw new IllegalArgumentException("Invalid method");
+ }
+
+ // If services are not set as mocks in tests, fetch them now.
+ mPeopleManager = mPeopleManager != null ? mPeopleManager
+ : IPeopleManager.Stub.asInterface(
+ ServiceManager.getService(Context.PEOPLE_SERVICE));
+ mLauncherApps = mLauncherApps != null ? mLauncherApps
+ : getContext().getSystemService(LauncherApps.class);
+
+ if (mPeopleManager == null || mLauncherApps == null) {
+ Log.w(TAG, "Null system managers");
+ return null;
+ }
+
+ if (extras == null) {
+ Log.w(TAG, "Extras can't be null");
+ throw new IllegalArgumentException("Extras can't be null");
+ }
+
+ String shortcutId = extras.getString(
+ PeopleProviderUtils.EXTRAS_KEY_SHORTCUT_ID, EMPTY_STRING);
+ String packageName = extras.getString(
+ PeopleProviderUtils.EXTRAS_KEY_PACKAGE_NAME, EMPTY_STRING);
+ UserHandle userHandle = extras.getParcelable(
+ PeopleProviderUtils.EXTRAS_KEY_USER_HANDLE);
+ if (shortcutId.isEmpty()) {
+ Log.w(TAG, "Invalid shortcut id");
+ throw new IllegalArgumentException("Invalid shortcut id");
+ }
+
+ if (packageName.isEmpty()) {
+ Log.w(TAG, "Invalid package name");
+ throw new IllegalArgumentException("Invalid package name");
+ }
+ if (userHandle == null) {
+ Log.w(TAG, "Null user handle");
+ throw new IllegalArgumentException("Null user handle");
+ }
+
+ ConversationChannel channel;
+ try {
+ channel = mPeopleManager.getConversation(
+ packageName, userHandle.getIdentifier(), shortcutId);
+ } catch (Exception e) {
+ Log.w(TAG, "Exception getting tiles: " + e);
+ return null;
+ }
+ PeopleSpaceTile tile = PeopleSpaceUtils.getTile(channel, mLauncherApps);
+
+ if (tile == null) {
+ if (DEBUG) Log.i(TAG, "No tile was returned");
+ return null;
+ }
+
+ if (DEBUG) Log.i(TAG, "Returning tile preview for shortcutId: " + shortcutId);
+ RemoteViews view = PeopleSpaceUtils.createRemoteViews(getContext(), tile, 0);
+ final Bundle bundle = new 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())
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ throw new IllegalArgumentException("Invalid method");
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new IllegalArgumentException("Invalid method");
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues initialValues) {
+ throw new IllegalArgumentException("Invalid method");
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new IllegalArgumentException("Invalid method");
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new IllegalArgumentException("Invalid method");
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index 580cbcf8ce47..2f9b17aece8e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -19,6 +19,8 @@ package com.android.systemui.people;
import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID;
+import static com.android.systemui.people.PeopleSpaceUtils.getUserHandle;
+
import android.app.Activity;
import android.app.INotificationManager;
import android.app.people.IPeopleManager;
@@ -37,15 +39,20 @@ import android.view.ViewGroup;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import java.util.Collections;
import java.util.List;
+import javax.inject.Inject;
+
/**
* Shows the user their tiles for their priority People (go/live-status).
*/
public class PeopleSpaceActivity extends Activity {
private static final String TAG = "PeopleSpaceActivity";
+ private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
private ViewGroup mPeopleSpaceLayout;
private IPeopleManager mPeopleManager;
@@ -54,10 +61,17 @@ public class PeopleSpaceActivity extends Activity {
private LauncherApps mLauncherApps;
private Context mContext;
private AppWidgetManager mAppWidgetManager;
+ private NotificationEntryManager mNotificationEntryManager;
private int mAppWidgetId;
private boolean mShowSingleConversation;
private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
+ @Inject
+ public PeopleSpaceActivity(NotificationEntryManager notificationEntryManager) {
+ super();
+ mNotificationEntryManager = notificationEntryManager;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -91,8 +105,8 @@ public class PeopleSpaceActivity extends Activity {
*/
private void setTileViewsWithPriorityConversations() {
try {
- List<PeopleSpaceTile> tiles = PeopleSpaceUtils.getTiles(
- mContext, mNotificationManager, mPeopleManager, mLauncherApps);
+ List<PeopleSpaceTile> tiles = PeopleSpaceUtils.getTiles(mContext, mNotificationManager,
+ mPeopleManager, mLauncherApps, mNotificationEntryManager);
for (PeopleSpaceTile tile : tiles) {
PeopleSpaceTileView tileView = new PeopleSpaceTileView(mContext, mPeopleSpaceLayout,
tile.getId());
@@ -124,9 +138,11 @@ public class PeopleSpaceActivity extends Activity {
/** Stores the user selected configuration for {@code mAppWidgetId}. */
private void storeWidgetConfiguration(PeopleSpaceTile tile) {
if (PeopleSpaceUtils.DEBUG) {
- Log.d(TAG, "Put " + tile.getUserName() + "'s shortcut ID: "
- + tile.getId() + " for widget ID: "
- + mAppWidgetId);
+ if (DEBUG) {
+ Log.d(TAG, "Put " + tile.getUserName() + "'s shortcut ID: "
+ + tile.getId() + " for widget ID: "
+ + mAppWidgetId);
+ }
}
PeopleSpaceUtils.setStorageForTile(mContext, tile, mAppWidgetId);
@@ -134,12 +150,22 @@ public class PeopleSpaceActivity extends Activity {
// TODO: Populate new widget with existing conversation notification, if there is any.
PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager,
mPeopleManager);
+ if (mLauncherApps != null) {
+ try {
+ if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId());
+ mLauncherApps.cacheShortcuts(tile.getPackageName(),
+ Collections.singletonList(tile.getId()),
+ getUserHandle(tile), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
+ } catch (Exception e) {
+ Log.w(TAG, "Exception caching shortcut:" + e);
+ }
+ }
finishActivity();
}
/** Finish activity with a successful widget configuration result. */
private void finishActivity() {
- if (PeopleSpaceUtils.DEBUG) Log.d(TAG, "Widget added!");
+ if (DEBUG) Log.d(TAG, "Widget added!");
mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_ADDED);
setActivityResult(RESULT_OK);
finish();
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 994dc6d78507..cd1131ba3e79 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -17,12 +17,21 @@
package com.android.systemui.people;
import static android.app.Notification.EXTRA_MESSAGES;
+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 android.app.people.ConversationStatus.ACTIVITY_LOCATION;
+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 android.annotation.Nullable;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.people.ConversationChannel;
+import android.app.people.ConversationStatus;
import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
@@ -37,6 +46,7 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.icu.text.MeasureFormat;
import android.icu.util.Measure;
import android.icu.util.MeasureUnit;
@@ -49,6 +59,7 @@ 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.Log;
import android.view.View;
import android.widget.RemoteViews;
@@ -60,13 +71,17 @@ 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.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
@@ -137,7 +152,7 @@ public class PeopleSpaceUtils {
/** Returns a list of map entries corresponding to user's conversations. */
public static List<PeopleSpaceTile> getTiles(
Context context, INotificationManager notificationManager, IPeopleManager peopleManager,
- LauncherApps launcherApps)
+ LauncherApps launcherApps, NotificationEntryManager notificationEntryManager)
throws Exception {
boolean showOnlyPriority = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 1;
@@ -173,6 +188,8 @@ public class PeopleSpaceUtils {
getSortedTiles(peopleManager, launcherApps, mergedStream);
tiles.addAll(recentTiles);
}
+
+ tiles = augmentTilesFromVisibleNotifications(tiles, notificationEntryManager);
return tiles;
}
@@ -258,7 +275,8 @@ public class PeopleSpaceUtils {
ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
IPeopleManager.Stub.asInterface(
ServiceManager.getService(Context.PEOPLE_SERVICE)),
- context.getSystemService(LauncherApps.class));
+ context.getSystemService(LauncherApps.class),
+ Dependency.get(NotificationEntryManager.class));
Optional<PeopleSpaceTile> entry = tiles.stream().filter(
e -> e.getId().equals(shortcutId)).findFirst();
if (entry.isPresent()) {
@@ -280,7 +298,7 @@ public class PeopleSpaceUtils {
SharedPreferences.Editor widgetEditor = widgetSp.edit();
widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, tile.getPackageName());
widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, tile.getId());
- int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+ int userId = getUserId(tile);
widgetEditor.putInt(PeopleSpaceUtils.USER_ID, userId);
widgetEditor.apply();
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
@@ -339,6 +357,41 @@ public class PeopleSpaceUtils {
&& storedUserId == userId;
}
+ static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(List<PeopleSpaceTile> tiles,
+ NotificationEntryManager notificationEntryManager) {
+ if (notificationEntryManager == null) {
+ Log.w(TAG, "NotificationEntryManager is null");
+ return tiles;
+ }
+ Map<String, NotificationEntry> visibleNotifications = notificationEntryManager
+ .getVisibleNotifications()
+ .stream()
+ .filter(entry -> entry.getRanking() != null
+ && entry.getRanking().getConversationShortcutInfo() != null)
+ .collect(Collectors.toMap(PeopleSpaceUtils::getKey, e -> e));
+ if (DEBUG) {
+ Log.d(TAG, "Number of visible notifications:" + visibleNotifications.size());
+ }
+ return tiles
+ .stream()
+ .map(entry -> augmentTileFromVisibleNotifications(entry, visibleNotifications))
+ .collect(Collectors.toList());
+ }
+
+ static PeopleSpaceTile augmentTileFromVisibleNotifications(PeopleSpaceTile tile,
+ Map<String, NotificationEntry> visibleNotifications) {
+ String shortcutId = tile.getId();
+ String packageName = tile.getPackageName();
+ int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+ String key = getKey(shortcutId, packageName, userId);
+ if (!visibleNotifications.containsKey(key)) {
+ if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key);
+ return tile;
+ }
+ if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key);
+ return augmentTileFromNotification(tile, visibleNotifications.get(key).getSbn());
+ }
+
/**
* If incoming notification changed tile, store the changes in the tile options.
*/
@@ -355,17 +408,7 @@ public class PeopleSpaceUtils {
}
if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
- Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(sbn);
- if (message == null) {
- if (DEBUG) Log.i(TAG, "Notification doesn't have content, skipping.");
- return;
- }
- storedTile = storedTile
- .toBuilder()
- .setNotificationKey(sbn.getKey())
- .setNotificationContent(message.getText())
- .setNotificationDataUri(message.getDataUri())
- .build();
+ storedTile = augmentTileFromNotification(storedTile, sbn);
} else {
if (DEBUG) {
Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
@@ -380,6 +423,21 @@ public class PeopleSpaceUtils {
updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile);
}
+ static PeopleSpaceTile augmentTileFromNotification(PeopleSpaceTile tile,
+ StatusBarNotification sbn) {
+ Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(sbn);
+ if (message == null) {
+ if (DEBUG) Log.i(TAG, "Notification doesn't have content, skipping.");
+ return tile;
+ }
+ return tile
+ .toBuilder()
+ .setNotificationKey(sbn.getKey())
+ .setNotificationContent(message.getText())
+ .setNotificationDataUri(message.getDataUri())
+ .build();
+ }
+
private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId,
PeopleSpaceTile tile) {
if (tile == null) {
@@ -392,23 +450,147 @@ public class PeopleSpaceUtils {
}
/** Creates a {@link RemoteViews} for {@code tile}. */
- private static RemoteViews createRemoteViews(Context context,
+ public static RemoteViews createRemoteViews(Context context,
PeopleSpaceTile tile, int appWidgetId) {
- RemoteViews views;
+ RemoteViews viewsForTile = getViewForTile(context, tile);
+ RemoteViews views = setCommonRemoteViewsFields(context, viewsForTile, tile);
+ return setLaunchIntents(context, views, tile, appWidgetId);
+ }
+
+ /**
+ * 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) {
if (tile.getNotificationKey() != null) {
- views = createNotificationRemoteViews(context, tile);
- } else if (tile.getBirthdayText() != null) {
- views = createStatusRemoteViews(context, tile);
+ if (DEBUG) Log.d(TAG, "Create notification view");
+ return createNotificationRemoteViews(context, tile);
+ }
+
+ // TODO: Add sorting when we expose timestamp of statuses.
+ List<ConversationStatus> statusesForEntireView =
+ tile.getStatuses() == null ? Arrays.asList() : tile.getStatuses().stream().filter(
+ c -> isStatusValidForEntireStatusView(c)).collect(Collectors.toList());
+ ConversationStatus birthdayStatus = getBirthdayStatus(tile, statusesForEntireView);
+ if (birthdayStatus != null) {
+ if (DEBUG) Log.d(TAG, "Create birthday view");
+ return createStatusRemoteViews(context, birthdayStatus);
+ }
+
+ if (!statusesForEntireView.isEmpty()) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "Create status view for: " + statusesForEntireView.get(0).getActivity());
+ }
+ return createStatusRemoteViews(context, statusesForEntireView.get(0));
+ }
+
+ return createLastInteractionRemoteViews(context, tile);
+ }
+
+ @Nullable
+ private static ConversationStatus getBirthdayStatus(PeopleSpaceTile tile,
+ List<ConversationStatus> statuses) {
+ Optional<ConversationStatus> birthdayStatus = statuses.stream().filter(
+ c -> c.getActivity() == ACTIVITY_BIRTHDAY).findFirst();
+ if (birthdayStatus.isPresent()) {
+ return birthdayStatus.get();
+ }
+ if (!TextUtils.isEmpty(tile.getBirthdayText())) {
+ return new ConversationStatus.Builder(tile.getId(), ACTIVITY_BIRTHDAY).build();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns whether a {@code status} should have its own entire templated view.
+ *
+ * <p>A status may still be shown on the view (for example, as a new story ring) even if it's
+ * not valid to compose an entire view.
+ */
+ private static boolean isStatusValidForEntireStatusView(ConversationStatus status) {
+ switch (status.getActivity()) {
+ // Birthday & Anniversary don't require text provided or icon provided.
+ case ACTIVITY_BIRTHDAY:
+ case ACTIVITY_ANNIVERSARY:
+ return true;
+ default:
+ // For future birthday, location, new story, video, music, game, and other, the
+ // app must provide either text or an icon.
+ return !TextUtils.isEmpty(status.getDescription())
+ || status.getIcon() != null;
+ }
+ }
+
+ private static RemoteViews createStatusRemoteViews(Context context, ConversationStatus status) {
+ RemoteViews views = new RemoteViews(
+ context.getPackageName(), R.layout.people_space_small_avatar_tile);
+ CharSequence statusText = status.getDescription();
+ if (TextUtils.isEmpty(statusText)) {
+ statusText = getStatusTextByType(context, status.getActivity());
+ }
+ views.setTextViewText(R.id.status, statusText);
+ Icon statusIcon = status.getIcon();
+ if (statusIcon != null) {
+ views.setImageViewIcon(R.id.image, statusIcon);
+ views.setBoolean(R.id.content_background, "setClipToOutline", true);
} else {
- views = createLastInteractionRemoteViews(context, tile);
+ views.setViewVisibility(R.id.content_background, View.GONE);
+ }
+ // TODO: Set status pre-defined icons
+ return views;
+ }
+
+ private static String getStatusTextByType(Context context, int activity) {
+ switch (activity) {
+ case ACTIVITY_BIRTHDAY:
+ return context.getString(R.string.birthday_status);
+ case ACTIVITY_UPCOMING_BIRTHDAY:
+ return context.getString(R.string.upcoming_birthday_status);
+ case ACTIVITY_ANNIVERSARY:
+ return context.getString(R.string.anniversary_status);
+ case ACTIVITY_LOCATION:
+ return context.getString(R.string.location_status);
+ case ACTIVITY_NEW_STORY:
+ return context.getString(R.string.new_story_status);
+ case ACTIVITY_MEDIA:
+ return context.getString(R.string.video_status);
+ case ACTIVITY_GAME:
+ return context.getString(R.string.game_status);
+ default:
+ return EMPTY_STRING;
}
- return setCommonRemoteViewsFields(context, views, tile, appWidgetId);
}
private static RemoteViews setCommonRemoteViewsFields(Context context, RemoteViews views,
- PeopleSpaceTile tile, int appWidgetId) {
+ PeopleSpaceTile tile) {
try {
+ boolean isAvailable =
+ tile.getStatuses() != null && tile.getStatuses().stream().anyMatch(
+ c -> c.getAvailability() == AVAILABILITY_AVAILABLE);
+ if (isAvailable) {
+ views.setViewVisibility(R.id.availability, View.VISIBLE);
+ } else {
+ views.setViewVisibility(R.id.availability, View.GONE);
+ }
+ 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);
+
views.setImageViewBitmap(
R.id.package_icon,
PeopleSpaceUtils.convertDrawableToBitmap(
@@ -416,9 +598,16 @@ public class PeopleSpaceUtils {
tile.getPackageName())
)
);
- views.setImageViewIcon(R.id.person_icon, tile.getUserIcon());
- views.setBoolean(R.id.content_background, "setClipToOutline", true);
+ return views;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to set common fields: " + e);
+ }
+ return views;
+ }
+ private static RemoteViews setLaunchIntents(Context context, RemoteViews views,
+ PeopleSpaceTile tile, int appWidgetId) {
+ try {
Intent activityIntent = new Intent(context, LaunchConversationActivity.class);
activityIntent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK
@@ -436,48 +625,42 @@ public class PeopleSpaceUtils {
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE));
return views;
} catch (Exception e) {
- Log.e(TAG, "Failed to set common fields: " + e);
+ Log.e(TAG, "Failed to add launch intents: " + e);
}
- return null;
+ return views;
}
private static RemoteViews createNotificationRemoteViews(Context context,
PeopleSpaceTile tile) {
RemoteViews views = new RemoteViews(
- context.getPackageName(), R.layout.people_space_small_avatar_tile);
+ context.getPackageName(), R.layout.people_space_notification_content_tile);
Uri image = tile.getNotificationDataUri();
if (image != null) {
- //TODO: Use NotificationInlineImageCache
+ // TODO: Use NotificationInlineImageCache
views.setImageViewUri(R.id.image, image);
- views.setViewVisibility(R.id.image, View.VISIBLE);
+ views.setViewVisibility(R.id.content_background, View.VISIBLE);
+ views.setBoolean(R.id.content_background, "setClipToOutline", true);
views.setViewVisibility(R.id.content, View.GONE);
} else {
CharSequence content = tile.getNotificationContent();
views = setPunctuationRemoteViewsFields(views, content);
views.setTextViewText(R.id.content, content);
views.setViewVisibility(R.id.content, View.VISIBLE);
- views.setViewVisibility(R.id.image, View.GONE);
+ views.setViewVisibility(R.id.content_background, View.GONE);
}
- views.setTextViewText(R.id.time, PeopleSpaceUtils.getLastInteractionString(
+ // TODO: Set subtext as Group Sender name once storing the name in PeopleSpaceTile.
+ views.setTextViewText(R.id.subtext, PeopleSpaceUtils.getLastInteractionString(
context, tile.getLastInteractionTimestamp(), false));
return views;
}
- private static RemoteViews createStatusRemoteViews(Context context,
- PeopleSpaceTile tile) {
- RemoteViews views = new RemoteViews(
- context.getPackageName(), R.layout.people_space_large_avatar_tile);
- views.setTextViewText(R.id.status, tile.getBirthdayText());
- return views;
- }
-
private static RemoteViews createLastInteractionRemoteViews(Context context,
PeopleSpaceTile tile) {
RemoteViews views = new RemoteViews(
context.getPackageName(), R.layout.people_space_large_avatar_tile);
String status = PeopleSpaceUtils.getLastInteractionString(
context, tile.getLastInteractionTimestamp(), true);
- views.setTextViewText(R.id.status, status);
+ views.setTextViewText(R.id.last_interaction, status);
return views;
}
@@ -566,11 +749,27 @@ public class PeopleSpaceUtils {
.collect(Collectors.toList());
}
+ /** Returns {@code PeopleSpaceTile} based on provided {@ConversationChannel}. */
+ public static PeopleSpaceTile getTile(ConversationChannel channel, LauncherApps launcherApps) {
+ if (channel == null) {
+ Log.i(TAG, "ConversationChannel is null");
+ return null;
+ }
+
+ PeopleSpaceTile tile = new PeopleSpaceTile.Builder(channel, launcherApps).build();
+ if (!PeopleSpaceUtils.shouldKeepConversation(tile)) {
+ Log.i(TAG, "PeopleSpaceTile is not valid");
+ return null;
+ }
+
+ return tile;
+ }
+
/** Returns the last interaction time with the user specified by {@code PeopleSpaceTile}. */
private static Long getLastInteraction(IPeopleManager peopleManager,
PeopleSpaceTile tile) {
try {
- int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+ int userId = getUserId(tile);
String pkg = tile.getPackageName();
return peopleManager.getLastInteraction(pkg, userId, tile.getId());
} catch (Exception e) {
@@ -618,20 +817,35 @@ 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) {
- return context.getString(includeLastChatted ? R.string.last_interaction_status_less_than
- : R.string.less_than_timestamp,
- formatter.formatMeasures(new Measure(MIN_HOUR, MeasureUnit.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(
+ new Measure(durationSinceLastInteraction.toMinutes(), MeasureUnit.MINUTE)));
} else if (durationSinceLastInteraction.toDays() < ONE_DAY) {
- return context.getString(
- includeLastChatted ? R.string.last_interaction_status : R.string.timestamp,
- formatter.formatMeasures(
- new Measure(durationSinceLastInteraction.toHours(), MeasureUnit.HOUR)));
+ 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(
+ new Measure(durationSinceLastInteraction.toHours(),
+ MeasureUnit.HOUR)));
} else if (durationSinceLastInteraction.toDays() < DAYS_IN_A_WEEK) {
- return context.getString(
- includeLastChatted ? R.string.last_interaction_status : R.string.timestamp,
- formatter.formatMeasures(
- new Measure(durationSinceLastInteraction.toDays(), MeasureUnit.DAY)));
+ 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(
+ new Measure(durationSinceLastInteraction.toHours(),
+ MeasureUnit.DAY)));
} else {
return context.getString(durationSinceLastInteraction.toDays() == DAYS_IN_A_WEEK
? (includeLastChatted ? R.string.last_interaction_status :
@@ -655,7 +869,7 @@ public class PeopleSpaceUtils {
* </li>
*/
public static boolean shouldKeepConversation(PeopleSpaceTile tile) {
- return tile != null && tile.getUserName().length() != 0;
+ return tile != null && !TextUtils.isEmpty(tile.getUserName());
}
private static boolean hasBirthdayStatus(PeopleSpaceTile tile, Context context) {
@@ -746,8 +960,7 @@ public class PeopleSpaceUtils {
private static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager,
Context context, int appWidgetId, PeopleSpaceTile tile) {
updateAppWidgetOptions(appWidgetManager, appWidgetId, tile);
- RemoteViews views = createRemoteViews(context,
- tile, appWidgetId);
+ RemoteViews views = createRemoteViews(context, tile, appWidgetId);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
@@ -792,6 +1005,16 @@ 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.
*
@@ -810,4 +1033,14 @@ public class PeopleSpaceUtils {
public static String getKey(String shortcutId, String packageName, int userId) {
return shortcutId + "/" + userId + "/" + packageName;
}
+
+ /** Returns the userId associated with a {@link PeopleSpaceTile} */
+ public static int getUserId(PeopleSpaceTile tile) {
+ return getUserHandle(tile).getIdentifier();
+ }
+
+ /** Returns the {@link UserHandle} associated with a {@link PeopleSpaceTile} */
+ public static UserHandle getUserHandle(PeopleSpaceTile tile) {
+ return UserHandle.getUserHandleForUid(tile.getUid());
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java
new file mode 100644
index 000000000000..b188acbf30f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java
@@ -0,0 +1,61 @@
+/*
+ * 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 android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.FeatureFlags;
+
+import javax.inject.Inject;
+
+/**
+ * Enables People Space widgets.
+ */
+@SysUISingleton
+public class PeopleSpaceWidgetEnabler extends SystemUI {
+ private static final String TAG = "PeopleSpaceWdgtEnabler";
+ private Context mContext;
+ private FeatureFlags mFeatureFlags;
+
+ @Inject
+ public PeopleSpaceWidgetEnabler(Context context, FeatureFlags featureFlags) {
+ super(context);
+ mContext = context;
+ mFeatureFlags = featureFlags;
+ }
+
+ @Override
+ public void start() {
+ Log.d(TAG, "Starting service");
+ try {
+ boolean showPeopleSpace = mFeatureFlags.isPeopleTileEnabled();
+ mContext.getPackageManager().setComponentEnabledSetting(
+ new ComponentName(mContext, PeopleSpaceWidgetProvider.class),
+ showPeopleSpace
+ ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+ } catch (Exception e) {
+ Log.w(TAG, "Error enabling People Space widget:", e);
+ }
+ }
+}
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 bee54e4dca0a..bee9889eaa4e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -135,12 +135,14 @@ public class PeopleSpaceWidgetManager {
try {
String sbnShortcutId = sbn.getShortcutId();
if (sbnShortcutId == null) {
+ if (DEBUG) Log.d(TAG, "Sbn shortcut id is null");
return;
}
int[] widgetIds = mAppWidgetService.getAppWidgetIds(
new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
);
if (widgetIds.length == 0) {
+ Log.d(TAG, "No app widget ids returned");
return;
}
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
@@ -148,6 +150,7 @@ public class PeopleSpaceWidgetManager {
String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId);
Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
if (storedWidgetIds.isEmpty()) {
+ Log.d(TAG, "No stored widget ids");
return;
}
for (String widgetIdString : storedWidgetIds) {
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 f5577d30c75c..3d1055fdece2 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -16,13 +16,20 @@
package com.android.systemui.people.widget;
+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.PendingIntent;
import android.app.people.IPeopleManager;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.LauncherApps;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import android.widget.RemoteViews;
@@ -32,6 +39,8 @@ import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
import com.android.systemui.people.PeopleSpaceUtils;
+import java.util.Collections;
+
/** People Space Widget Provider class. */
public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
private static final String TAG = "PeopleSpaceWidgetPvd";
@@ -88,11 +97,31 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
+ LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+
for (int widgetId : appWidgetIds) {
if (DEBUG) Log.d(TAG, "Widget removed");
mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED);
+ if (launcherApps != null) {
+ SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
+ Context.MODE_PRIVATE);
+ String packageName = widgetSp.getString(PACKAGE_NAME, null);
+ String shortcutId = widgetSp.getString(SHORTCUT_ID, null);
+ int userId = widgetSp.getInt(USER_ID, -1);
+
+ if (packageName != null && shortcutId != null && userId != -1) {
+ try {
+ if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + shortcutId);
+ launcherApps.uncacheShortcuts(packageName,
+ Collections.singletonList(shortcutId),
+ UserHandle.of(userId),
+ LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
+ } catch (Exception e) {
+ Log.d(TAG, "Exception uncaching shortcut:" + e);
+ }
+ }
+ }
PeopleSpaceUtils.removeStorageForTile(context, widgetId);
}
}
-
}
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 fb33affcbac5..80794cb64883 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
@@ -28,9 +28,11 @@ import android.util.Log;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.people.PeopleSpaceTileView;
import com.android.systemui.people.PeopleSpaceUtils;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import java.util.ArrayList;
import java.util.List;
@@ -42,6 +44,7 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R
private IPeopleManager mPeopleManager;
private INotificationManager mNotificationManager;
+ private NotificationEntryManager mNotificationEntryManager;
private PackageManager mPackageManager;
private LauncherApps mLauncherApps;
private List<PeopleSpaceTile> mTiles = new ArrayList<>();
@@ -56,6 +59,7 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R
if (DEBUG) Log.d(TAG, "onCreate called");
mNotificationManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
mPackageManager = mContext.getPackageManager();
mPeopleManager = IPeopleManager.Stub.asInterface(
ServiceManager.getService(Context.PEOPLE_SERVICE));
@@ -70,7 +74,7 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R
private void setTileViewsWithPriorityConversations() {
try {
mTiles = PeopleSpaceUtils.getTiles(mContext, mNotificationManager,
- mPeopleManager, mLauncherApps);
+ mPeopleManager, mLauncherApps, mNotificationEntryManager);
} catch (Exception e) {
Log.e(TAG, "Couldn't retrieve conversations", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index 870e714ee24c..bdd37fc521fa 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -16,11 +16,11 @@ package com.android.systemui.privacy
import android.content.Context
import android.util.AttributeSet
-import android.view.Gravity
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
+import com.android.settingslib.Utils
import com.android.systemui.R
class OngoingPrivacyChip @JvmOverloads constructor(
@@ -30,26 +30,13 @@ class OngoingPrivacyChip @JvmOverloads constructor(
defStyleRes: Int = 0
) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes) {
- private val iconMarginExpanded = context.resources.getDimensionPixelSize(
- R.dimen.ongoing_appops_chip_icon_margin_expanded)
- private val iconMarginCollapsed = context.resources.getDimensionPixelSize(
- R.dimen.ongoing_appops_chip_icon_margin_collapsed)
- private val iconSize =
- context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
- private val iconColor = context.resources.getColor(
- R.color.status_bar_clock_color, context.theme)
- private val sidePadding =
- context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
- private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg)
+ private var iconMargin = 0
+ private var iconSize = 0
+ private var iconColor = 0
+ private var defaultBackgroundColor = 0
+ private var cameraBackgroundColor = 0
+
private lateinit var iconsContainer: LinearLayout
- private lateinit var back: FrameLayout
- var expanded = false
- set(value) {
- if (value != field) {
- field = value
- updateView(PrivacyChipBuilder(context, privacyList))
- }
- }
var privacyList = emptyList<PrivacyItem>()
set(value) {
@@ -60,15 +47,13 @@ class OngoingPrivacyChip @JvmOverloads constructor(
override fun onFinishInflate() {
super.onFinishInflate()
- back = requireViewById(R.id.background)
iconsContainer = requireViewById(R.id.icons_container)
+
+ updateResources()
}
// Should only be called if the builder icons or app changed
private fun updateView(builder: PrivacyChipBuilder) {
- back.background = if (expanded) backgroundDrawable else null
- val padding = if (expanded) sidePadding else 0
- back.setPaddingRelative(padding, 0, padding, 0)
fun setIcons(chipBuilder: PrivacyChipBuilder, iconsContainer: ViewGroup) {
iconsContainer.removeAllViews()
chipBuilder.generateIcons().forEachIndexed { i, it ->
@@ -81,7 +66,7 @@ class OngoingPrivacyChip @JvmOverloads constructor(
iconsContainer.addView(image, iconSize, iconSize)
if (i != 0) {
val lp = image.layoutParams as MarginLayoutParams
- lp.marginStart = if (expanded) iconMarginExpanded else iconMarginCollapsed
+ lp.marginStart = iconMargin
image.layoutParams = lp
}
}
@@ -90,10 +75,11 @@ class OngoingPrivacyChip @JvmOverloads constructor(
if (!privacyList.isEmpty()) {
generateContentDescription(builder)
setIcons(builder, iconsContainer)
- val lp = iconsContainer.layoutParams as FrameLayout.LayoutParams
- lp.gravity = Gravity.CENTER_VERTICAL or
- (if (expanded) Gravity.CENTER_HORIZONTAL else Gravity.END)
- iconsContainer.layoutParams = lp
+ if (builder.types.contains(PrivacyType.TYPE_CAMERA)) {
+ iconsContainer.background.setTint(cameraBackgroundColor)
+ } else {
+ iconsContainer.background.setTint(defaultBackgroundColor)
+ }
} else {
iconsContainer.removeAllViews()
}
@@ -105,4 +91,20 @@ class OngoingPrivacyChip @JvmOverloads constructor(
contentDescription = context.getString(
R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
}
+
+ private fun updateResources() {
+ iconMargin = context.resources
+ .getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin)
+ iconSize = context.resources
+ .getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
+ iconColor =
+ Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
+ defaultBackgroundColor = context.getColor(R.color.privacy_circle_microphone_location)
+ cameraBackgroundColor = context.getColor(R.color.privacy_circle_camera)
+
+ val padding = context.resources
+ .getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
+ iconsContainer.setPaddingRelative(padding, 0, padding, 0)
+ iconsContainer.background = context.getDrawable(R.drawable.privacy_chip_bg)
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
index 3f79be8fcdd4..680a617e4d26 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
@@ -41,13 +41,12 @@ import java.util.concurrent.atomic.AtomicBoolean
* @param context A context to create the dialog
* @param list list of elements to show in the dialog. The elements will show in the same order they
* appear in the list
- * @param activityStarter a callback to start an activity for a given permission group name (as
- * given by [PrivacyType.permGroupName])
+ * @param activityStarter a callback to start an activity for a given package name and user id
*/
class PrivacyDialog(
context: Context,
private val list: List<PrivacyElement>,
- activityStarter: (String) -> Unit
+ activityStarter: (String, Int) -> Unit
) : SystemUIDialog(context, R.style.ScreenRecord) {
private val dismissListeners = mutableListOf<WeakReference<OnDialogDismissed>>()
@@ -129,7 +128,7 @@ class PrivacyDialog(
} ?: firstLine
newView.requireViewById<TextView>(R.id.text).text = finalText
newView.apply {
- tag = element.type.permGroupName
+ setTag(element)
setOnClickListener(clickListener)
}
return newView
@@ -152,12 +151,17 @@ class PrivacyDialog(
}
private val clickListener = View.OnClickListener { v ->
- v.tag?.let { activityStarter(it as String) }
+ v.tag?.let {
+ val element = it as PrivacyElement
+ activityStarter(element.packageName, element.userId)
+ }
}
/** */
data class PrivacyElement(
val type: PrivacyType,
+ val packageName: String,
+ val userId: Int,
val applicationName: CharSequence,
val attribution: CharSequence?,
val lastActiveTimestamp: Long,
@@ -169,6 +173,8 @@ class PrivacyDialog(
init {
builder.append("type=${type.logName}")
+ builder.append(", packageName=$packageName")
+ builder.append(", userId=$userId")
builder.append(", appName=$applicationName")
if (attribution != null) {
builder.append(", attribution=$attribution")
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
index 8b6c04fb3b01..03c184336364 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
@@ -43,7 +43,7 @@ private val defaultDialogProvider = object : PrivacyDialogController.DialogProvi
override fun makeDialog(
context: Context,
list: List<PrivacyDialog.PrivacyElement>,
- starter: (String) -> Unit
+ starter: (String, Int) -> Unit
): PrivacyDialog {
return PrivacyDialog(context, list, starter)
}
@@ -107,10 +107,11 @@ class PrivacyDialogController(
}
@MainThread
- private fun startActivity(permGroupName: String) {
- val intent = Intent(Intent.ACTION_MANAGE_PERMISSION_APPS)
- intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, permGroupName)
- privacyLogger.logStartSettingsActivityFromDialog(permGroupName)
+ private fun startActivity(packageName: String, userId: Int) {
+ val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS)
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
+ intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
+ privacyLogger.logStartSettingsActivityFromDialog(packageName, userId)
if (!keyguardStateController.isUnlocked) {
// If we are locked, hide the dialog so the user can unlock
dialog?.hide()
@@ -159,6 +160,8 @@ class PrivacyDialogController(
}
PrivacyDialog.PrivacyElement(
t,
+ it.packageName,
+ UserHandle.getUserId(it.uid),
appName,
it.attribution,
it.lastAccess,
@@ -257,7 +260,7 @@ class PrivacyDialogController(
fun makeDialog(
context: Context,
list: List<PrivacyDialog.PrivacyElement>,
- starter: (String) -> Unit
+ starter: (String, Int) -> Unit
): PrivacyDialog
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
index 8059475dd832..ebf6ae9be22f 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
@@ -121,11 +121,12 @@ class PrivacyLogger @Inject constructor(
})
}
- fun logStartSettingsActivityFromDialog(permGroupName: String) {
+ fun logStartSettingsActivityFromDialog(packageName: String, userId: Int) {
log(LogLevel.INFO, {
- str1 = permGroupName
+ str1 = packageName
+ int1 = userId
}, {
- "Start settings activity from dialog for perm group: $str1"
+ "Start settings activity from dialog for packageName=$str1, userId=$int1 "
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index d248ab544656..c8edaec98ee4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -362,7 +362,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
if(view == parent || view == null) return;
// Ignore tile pages as they can have some offset we don't want to take into account in
// RTL.
- if (!(view instanceof PagedTileLayout.TilePage)) {
+ if (!(view instanceof PagedTileLayout.TilePage || view instanceof SideLabelTileLayout)) {
loc1[0] += view.getLeft();
loc1[1] += view.getTop();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 619729e55314..9967936ac1bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -72,6 +72,7 @@ public class QSDetail extends LinearLayout {
private boolean mFullyExpanded;
private QuickStatusBarHeader mHeader;
private boolean mTriggeredExpand;
+ private boolean mShouldAnimate;
private int mOpenX;
private int mOpenY;
private boolean mAnimatingOpen;
@@ -108,16 +109,6 @@ public class QSDetail extends LinearLayout {
updateDetailText();
mClipper = new QSDetailClipper(this);
-
- final OnClickListener doneListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- announceForAccessibility(
- mContext.getString(R.string.accessibility_desc_quick_settings));
- mQsPanelController.closeDetail();
- }
- };
- mDetailDoneButton.setOnClickListener(doneListener);
}
/** */
@@ -169,6 +160,7 @@ public class QSDetail extends LinearLayout {
public void handleShowingDetail(final DetailAdapter adapter, int x, int y,
boolean toggleQs) {
final boolean showingDetail = adapter != null;
+ final boolean wasShowingDetail = mDetailAdapter != null;
setClickable(showingDetail);
if (showingDetail) {
setupDetailHeader(adapter);
@@ -178,6 +170,7 @@ public class QSDetail extends LinearLayout {
} else {
mTriggeredExpand = false;
}
+ mShouldAnimate = adapter.shouldAnimate();
mOpenX = x;
mOpenY = y;
} else {
@@ -190,10 +183,10 @@ public class QSDetail extends LinearLayout {
}
}
- boolean visibleDiff = (mDetailAdapter != null) != (adapter != null);
- if (!visibleDiff && mDetailAdapter == adapter) return; // already in right state
- AnimatorListener listener = null;
- if (adapter != null) {
+ boolean visibleDiff = wasShowingDetail != showingDetail;
+ if (!visibleDiff && !wasShowingDetail) return; // already in right state
+ AnimatorListener listener;
+ if (showingDetail) {
int viewCacheIndex = adapter.getMetricsCategory();
View detailView = adapter.createDetailView(mContext, mDetailViews.get(viewCacheIndex),
mDetailContent);
@@ -213,7 +206,7 @@ public class QSDetail extends LinearLayout {
listener = mHideGridContentWhenDone;
setVisibility(View.VISIBLE);
} else {
- if (mDetailAdapter != null) {
+ if (wasShowingDetail) {
Dependency.get(MetricsLogger.class).hidden(mDetailAdapter.getMetricsCategory());
mUiEventLogger.log(mDetailAdapter.closeDetailEvent());
}
@@ -227,7 +220,15 @@ public class QSDetail extends LinearLayout {
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- animateDetailVisibleDiff(x, y, visibleDiff, listener);
+ if (mShouldAnimate) {
+ animateDetailVisibleDiff(x, y, visibleDiff, listener);
+ } else {
+ if (showingDetail) {
+ showImmediately();
+ } else {
+ hideImmediately();
+ }
+ }
}
protected void animateDetailVisibleDiff(int x, int y, boolean visibleDiff, AnimatorListener listener) {
@@ -245,6 +246,17 @@ public class QSDetail extends LinearLayout {
}
}
+ void showImmediately() {
+ setVisibility(VISIBLE);
+ mClipper.cancelAnimator();
+ mClipper.showBackground();
+ }
+
+ public void hideImmediately() {
+ mClipper.cancelAnimator();
+ setVisibility(View.GONE);
+ }
+
protected void setupDetailFooter(DetailAdapter adapter) {
final Intent settingsIntent = adapter.getSettingsIntent();
mDetailSettingsButton.setVisibility(settingsIntent != null ? VISIBLE : GONE);
@@ -255,6 +267,13 @@ public class QSDetail extends LinearLayout {
Dependency.get(ActivityStarter.class)
.postStartActivityDismissingKeyguard(settingsIntent, 0);
});
+ mDetailDoneButton.setOnClickListener(v -> {
+ announceForAccessibility(
+ mContext.getString(R.string.accessibility_desc_quick_settings));
+ if (!adapter.onDoneButtonClicked()) {
+ mQsPanelController.closeDetail();
+ }
+ });
}
protected void setupDetailHeader(final DetailAdapter adapter) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
index 7d87e174d95d..b50af004aff9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
@@ -39,7 +39,7 @@ public class QSDetailDisplayer {
/** Show the supplied DetailAdapter in the Quick Settings. */
public void showDetailAdapter(DetailAdapter detailAdapter, int x, int y) {
if (mQsPanelController != null) {
- mQsPanelController.showDetailDapater(detailAdapter, x, y);
+ mQsPanelController.showDetailAdapter(detailAdapter, x, y);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index f56a890c54d4..fcb35e2040ea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -40,6 +40,7 @@ import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.brightness.BrightnessController;
import com.android.systemui.settings.brightness.BrightnessSlider;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.tuner.TunerService;
@@ -92,9 +93,10 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
BrightnessSlider.Factory brightnessSliderFactory,
- @Named(QS_LABELS_FLAG) boolean qsLabelsFlag) {
+ @Named(QS_LABELS_FLAG) boolean qsLabelsFlag,
+ FeatureFlags featureFlags) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
- metricsLogger, uiEventLogger, qsLogger, dumpManager);
+ metricsLogger, uiEventLogger, qsLogger, dumpManager, featureFlags);
mQsSecurityFooter = qsSecurityFooter;
mTunerService = tunerService;
mQsCustomizerController = qsCustomizerController;
@@ -309,7 +311,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
}
/** */
- public void showDetailDapater(DetailAdapter detailAdapter, int x, int y) {
+ public void showDetailAdapter(DetailAdapter detailAdapter, int x, int y) {
mView.showDetailAdapter(true, detailAdapter, new int[]{x, y});
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index b02799f49660..9426e7122c1c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -34,6 +34,8 @@ import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.util.Utils;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.animation.DisappearParameters;
@@ -63,6 +65,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
private final UiEventLogger mUiEventLogger;
private final QSLogger mQSLogger;
private final DumpManager mDumpManager;
+ private final FeatureFlags mFeatureFlags;
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
private int mLastOrientation;
@@ -93,11 +96,18 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
private boolean mUsingHorizontalLayout;
- protected QSPanelControllerBase(T view, QSTileHost host,
+ protected QSPanelControllerBase(
+ T view,
+ QSTileHost host,
QSCustomizerController qsCustomizerController,
- @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer, MediaHost mediaHost,
- MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager) {
+ @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
+ MediaHost mediaHost,
+ MetricsLogger metricsLogger,
+ UiEventLogger uiEventLogger,
+ QSLogger qsLogger,
+ DumpManager dumpManager,
+ FeatureFlags featureFlags
+ ) {
super(view);
mHost = host;
mQsCustomizerController = qsCustomizerController;
@@ -107,6 +117,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
mUiEventLogger = uiEventLogger;
mQSLogger = qsLogger;
mDumpManager = dumpManager;
+ mFeatureFlags = featureFlags;
}
@Override
@@ -334,9 +345,12 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
}
boolean shouldUseHorizontalLayout() {
+ if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, getResources())) {
+ return false;
+ }
return mUsingMediaPlayer && mMediaHost.getVisible()
- && getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
+ && getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
}
private void logTiles() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 386638217110..1411fa1ea21f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -170,7 +170,8 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
// Update visibility of footer
mIsVisible = (isDeviceManaged && !isDemoDevice) || hasCACerts || hasCACertsInWorkProfile
|| vpnName != null || vpnNameWorkProfile != null
- || isProfileOwnerOfOrganizationOwnedDevice || isParentalControlsEnabled;
+ || isProfileOwnerOfOrganizationOwnedDevice || isParentalControlsEnabled
+ || (hasWorkProfile && isNetworkLoggingEnabled);
// Update the string
mFooterTextContent = getFooterText(isDeviceManaged, hasWorkProfile,
hasCACerts, hasCACertsInWorkProfile, isNetworkLoggingEnabled, vpnName,
@@ -275,12 +276,30 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
vpnName);
}
if (isProfileOwnerOfOrganizationOwnedDevice) {
+ if (isNetworkLoggingEnabled) {
+ if (organizationName == null) {
+ return mContext.getString(
+ R.string.quick_settings_disclosure_management_monitoring);
+ }
+ return mContext.getString(
+ R.string.quick_settings_disclosure_named_management_monitoring,
+ organizationName);
+ }
if (workProfileOrganizationName == null) {
return mContext.getString(R.string.quick_settings_disclosure_management);
}
return mContext.getString(R.string.quick_settings_disclosure_named_management,
workProfileOrganizationName);
}
+ if (hasWorkProfile && isNetworkLoggingEnabled) {
+ if (workProfileOrganizationName == null) {
+ return mContext.getString(
+ R.string.quick_settings_disclosure_managed_profile_monitoring);
+ }
+ return mContext.getString(
+ R.string.quick_settings_disclosure_named_managed_profile_monitoring,
+ workProfileOrganizationName);
+ }
return null;
}
@@ -367,7 +386,8 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
}
// network logging section
- CharSequence networkLoggingMessage = getNetworkLoggingMessage(isNetworkLoggingEnabled);
+ CharSequence networkLoggingMessage = getNetworkLoggingMessage(isDeviceManaged,
+ isNetworkLoggingEnabled);
if (networkLoggingMessage == null) {
dialogView.findViewById(R.id.network_logging_disclosures).setVisibility(View.GONE);
} else {
@@ -492,9 +512,15 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
return mContext.getString(R.string.monitoring_description_ca_certificate);
}
- protected CharSequence getNetworkLoggingMessage(boolean isNetworkLoggingEnabled) {
+ protected CharSequence getNetworkLoggingMessage(boolean isDeviceManaged,
+ boolean isNetworkLoggingEnabled) {
if (!isNetworkLoggingEnabled) return null;
- return mContext.getString(R.string.monitoring_description_management_network_logging);
+ if (isDeviceManaged) {
+ return mContext.getString(R.string.monitoring_description_management_network_logging);
+ } else {
+ return mContext.getString(
+ R.string.monitoring_description_managed_profile_network_logging);
+ }
}
protected CharSequence getVpnMessage(boolean isDeviceManaged, boolean hasWorkProfile,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index a0db2000cb4d..383e932a6955 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -30,6 +30,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.statusbar.FeatureFlags;
import java.util.ArrayList;
import java.util.List;
@@ -57,9 +58,11 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
@Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
@Named(QUICK_QS_PANEL) MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager, @Named(QS_LABELS_FLAG) boolean qsLabelsFlag) {
+ DumpManager dumpManager, @Named(QS_LABELS_FLAG) boolean qsLabelsFlag,
+ FeatureFlags featureFlags
+ ) {
super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
- uiEventLogger, qsLogger, dumpManager);
+ uiEventLogger, qsLogger, dumpManager, featureFlags);
mUseSideLabels = qsLabelsFlag;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 4248cf2efcd0..a0bf5846ef53 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -29,7 +29,6 @@ import android.media.AudioManager;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.util.Pair;
-import android.view.ContextThemeWrapper;
import android.view.DisplayCutout;
import android.view.View;
import android.view.ViewGroup;
@@ -48,7 +47,6 @@ import androidx.lifecycle.LifecycleRegistry;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
-import com.android.systemui.DualToneHandler;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.privacy.OngoingPrivacyChip;
@@ -75,8 +73,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn
protected QuickQSPanel mHeaderQsPanel;
private TouchAnimator mStatusIconsAlphaAnimator;
private TouchAnimator mHeaderTextContainerAlphaAnimator;
- private TouchAnimator mPrivacyChipAlphaAnimator;
- private DualToneHandler mDualToneHandler;
private View mSystemIconsView;
private View mQuickQsStatusIcons;
@@ -94,6 +90,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn
private OngoingPrivacyChip mPrivacyChip;
private Space mSpace;
private BatteryMeterView mBatteryRemainingIcon;
+ private TintedIconManager mTintedIconManager;
// Used for RingerModeTracker
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
@@ -111,8 +108,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
- mDualToneHandler = new DualToneHandler(
- new ContextThemeWrapper(context, R.style.QSHeaderTheme));
}
@Override
@@ -150,10 +145,9 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn
}
void onAttach(TintedIconManager iconManager) {
- int colorForeground = Utils.getColorAttrDefaultColor(getContext(),
- android.R.attr.colorForeground);
- float intensity = getColorIntensity(colorForeground);
- int fillColor = mDualToneHandler.getSingleColor(intensity);
+ mTintedIconManager = iconManager;
+ int fillColor = Utils.getColorAttrDefaultColor(getContext(),
+ android.R.attr.textColorPrimary);
// Set the correct tint for the status icons so they contrast
iconManager.setTint(fillColor);
@@ -272,19 +266,19 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn
int textColor = Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary);
if (textColor != mTextColorPrimary) {
+ int textColorSecondary = Utils.getColorAttrDefaultColor(mContext,
+ android.R.attr.textColorSecondary);
mTextColorPrimary = textColor;
mClockView.setTextColor(textColor);
-
- float intensity = getColorIntensity(textColor);
- int fillColor = mDualToneHandler.getSingleColor(intensity);
-
- Rect tintArea = new Rect(0, 0, 0, 0);
- mBatteryRemainingIcon.onDarkChanged(tintArea, intensity, fillColor);
+ if (mTintedIconManager != null) {
+ mTintedIconManager.setTint(textColor);
+ }
+ mBatteryRemainingIcon.updateColors(mTextColorPrimary, textColorSecondary,
+ mTextColorPrimary);
}
updateStatusIconAlphaAnimator();
updateHeaderTextContainerAlphaAnimator();
- updatePrivacyChipAlphaAnimator();
}
private void updateStatusIconAlphaAnimator() {
@@ -299,12 +293,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn
.build();
}
- private void updatePrivacyChipAlphaAnimator() {
- mPrivacyChipAlphaAnimator = new TouchAnimator.Builder()
- .addFloat(mPrivacyChip, "alpha", 1, 0, 1)
- .build();
- }
-
/** */
public void setExpanded(boolean expanded, QuickQSPanelController quickQSPanelController) {
if (mExpanded == expanded) return;
@@ -344,10 +332,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn
mHeaderTextContainerView.setVisibility(INVISIBLE);
}
}
- if (mPrivacyChipAlphaAnimator != null) {
- mPrivacyChip.setExpanded(expansionFraction > 0.5);
- mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction);
- }
mKeyguardExpansionFraction = keyguardExpansionFraction;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 7d8d86fe691e..ae0b5d11db13 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -28,9 +28,7 @@ import android.widget.TextView;
import com.android.settingslib.Utils;
import com.android.settingslib.graph.SignalDrawable;
-import com.android.systemui.DualToneHandler;
import com.android.systemui.R;
-import com.android.systemui.qs.QuickStatusBarHeader;
import java.util.Objects;
@@ -40,10 +38,8 @@ public class QSCarrier extends LinearLayout {
private TextView mCarrierText;
private ImageView mMobileSignal;
private ImageView mMobileRoaming;
- private DualToneHandler mDualToneHandler;
- private ColorStateList mColorForegroundStateList;
- private float mColorForegroundIntensity;
private CellSignalState mLastSignalState;
+ private boolean mProviderModel;
public QSCarrier(Context context) {
super(context);
@@ -64,21 +60,20 @@ public class QSCarrier extends LinearLayout {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mDualToneHandler = new DualToneHandler(getContext());
- mMobileGroup = findViewById(R.id.mobile_combo);
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
- mMobileRoaming = findViewById(R.id.mobile_roaming_large);
+ mProviderModel = true;
} else {
- mMobileRoaming = findViewById(R.id.mobile_roaming);
+ mProviderModel = false;
}
+ mMobileGroup = findViewById(R.id.mobile_combo);
+ mMobileRoaming = findViewById(R.id.mobile_roaming);
mMobileSignal = findViewById(R.id.mobile_signal);
mCarrierText = findViewById(R.id.qs_carrier_text);
- mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
-
- int colorForeground = Utils.getColorAttrDefaultColor(mContext,
- android.R.attr.colorForeground);
- mColorForegroundStateList = ColorStateList.valueOf(colorForeground);
- mColorForegroundIntensity = QuickStatusBarHeader.getColorIntensity(colorForeground);
+ if (mProviderModel) {
+ mMobileSignal.setImageDrawable(mContext.getDrawable(R.drawable.ic_qs_no_calling_sms));
+ } else {
+ mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
+ }
}
/**
@@ -92,26 +87,31 @@ public class QSCarrier extends LinearLayout {
mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
if (state.visible) {
mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
- ColorStateList colorStateList = ColorStateList.valueOf(
- mDualToneHandler.getSingleColor(mColorForegroundIntensity));
+ ColorStateList colorStateList = Utils.getColorAttr(mContext,
+ android.R.attr.textColorPrimary);
mMobileRoaming.setImageTintList(colorStateList);
mMobileSignal.setImageTintList(colorStateList);
- mMobileSignal.setImageLevel(state.mobileSignalIconId);
- StringBuilder contentDescription = new StringBuilder();
- if (state.contentDescription != null) {
- contentDescription.append(state.contentDescription).append(", ");
- }
- if (state.roaming) {
- contentDescription
- .append(mContext.getString(R.string.data_connection_roaming))
- .append(", ");
- }
- // TODO: show mobile data off/no internet text for 5 seconds before carrier text
- if (hasValidTypeContentDescription(state.typeContentDescription)) {
- contentDescription.append(state.typeContentDescription);
+ if (mProviderModel) {
+ mMobileSignal.setImageDrawable(mContext.getDrawable(state.mobileSignalIconId));
+ mMobileSignal.setContentDescription(state.contentDescription);
+ } else {
+ mMobileSignal.setImageLevel(state.mobileSignalIconId);
+ StringBuilder contentDescription = new StringBuilder();
+ if (state.contentDescription != null) {
+ contentDescription.append(state.contentDescription).append(", ");
+ }
+ if (state.roaming) {
+ contentDescription
+ .append(mContext.getString(R.string.data_connection_roaming))
+ .append(", ");
+ }
+ // TODO: show mobile data off/no internet text for 5 seconds before carrier text
+ if (hasValidTypeContentDescription(state.typeContentDescription)) {
+ contentDescription.append(state.typeContentDescription);
+ }
+ mMobileSignal.setContentDescription(contentDescription);
}
- mMobileSignal.setContentDescription(contentDescription);
}
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index 091aba64a1fa..beacf88cf354 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -19,6 +19,7 @@ package com.android.systemui.qs.carrier;
import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
import android.annotation.MainThread;
+import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
@@ -26,6 +27,7 @@ import android.os.Message;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
@@ -33,6 +35,9 @@ import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
import com.android.keyguard.CarrierTextController;
+import com.android.settingslib.AccessibilityContentDescriptions;
+import com.android.settingslib.mobile.TelephonyIcons;
+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;
@@ -62,6 +67,9 @@ public class QSCarrierGroupController {
new CellSignalState[SIM_SLOTS];
private View[] mCarrierDividers = new View[SIM_SLOTS - 1];
private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS];
+ private int[] mLastSignalLevel = new int[SIM_SLOTS];
+ private String[] mLastSignalLevelDescription = new String[SIM_SLOTS];
+ private final boolean mProviderModel;
private final NetworkController.SignalCallback mSignalCallback =
new NetworkController.SignalCallback() {
@@ -72,6 +80,9 @@ public class QSCarrierGroupController {
CharSequence typeContentDescription,
CharSequence typeContentDescriptionHtml, CharSequence description,
boolean isWide, int subId, boolean roaming, boolean showTriangle) {
+ if (mProviderModel) {
+ return;
+ }
int slotIndex = getSlotIndex(subId);
if (slotIndex >= SIM_SLOTS) {
Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
@@ -92,6 +103,46 @@ public class QSCarrierGroupController {
}
@Override
+ public void setCallIndicator(NetworkController.IconState statusIcon, int subId) {
+ if (!mProviderModel) {
+ return;
+ }
+ int slotIndex = getSlotIndex(subId);
+ if (slotIndex >= SIM_SLOTS) {
+ Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
+ return;
+ }
+ if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
+ return;
+ }
+ if (statusIcon.icon == R.drawable.ic_qs_no_calling_sms) {
+ if (statusIcon.visible) {
+ mInfos[slotIndex] = new CellSignalState(true,
+ statusIcon.icon, statusIcon.contentDescription, "", false);
+ } else {
+ // Whenever the no Calling & SMS state is cleared, switched to the last
+ // known call strength icon.
+ mInfos[slotIndex] = new CellSignalState(
+ true, mLastSignalLevel[slotIndex],
+ mLastSignalLevelDescription[slotIndex], "", false);
+ }
+ mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
+ } else {
+ mLastSignalLevel[slotIndex] = statusIcon.icon;
+ mLastSignalLevelDescription[slotIndex] = statusIcon.contentDescription;
+ // Only Shows the call strength icon when the no Calling & SMS icon is not
+ // shown.
+ if (mInfos[slotIndex].mobileSignalIconId
+ != R.drawable.ic_qs_no_calling_sms) {
+ mInfos[slotIndex] = new CellSignalState(true, statusIcon.icon,
+ statusIcon.contentDescription, "", false);
+ mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
+ }
+ }
+ }
+
+ @Override
public void setNoSims(boolean hasNoSims, boolean simDetected) {
if (hasNoSims) {
for (int i = 0; i < SIM_SLOTS; i++) {
@@ -118,7 +169,12 @@ public class QSCarrierGroupController {
private QSCarrierGroupController(QSCarrierGroup view, ActivityStarter activityStarter,
@Background Handler bgHandler, @Main Looper mainLooper,
NetworkController networkController,
- CarrierTextController.Builder carrierTextControllerBuilder) {
+ CarrierTextController.Builder carrierTextControllerBuilder, Context context) {
+ if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ mProviderModel = true;
+ } else {
+ mProviderModel = false;
+ }
mActivityStarter = activityStarter;
mBgHandler = bgHandler;
mNetworkController = networkController;
@@ -149,7 +205,13 @@ public class QSCarrierGroupController {
mCarrierDividers[1] = view.getCarrierDivider2();
for (int i = 0; i < SIM_SLOTS; i++) {
- mInfos[i] = new CellSignalState();
+ mInfos[i] = new CellSignalState(true, R.drawable.ic_qs_no_calling_sms,
+ context.getText(AccessibilityContentDescriptions.NO_CALLING).toString(),
+ "", false);
+ mLastSignalLevel[i] = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
+ mLastSignalLevelDescription[i] =
+ context.getText(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0])
+ .toString();
mCarrierGroups[i].setOnClickListener(onClickListener);
}
view.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
@@ -305,16 +367,18 @@ public class QSCarrierGroupController {
private final Looper mLooper;
private final NetworkController mNetworkController;
private final CarrierTextController.Builder mCarrierTextControllerBuilder;
+ private final Context mContext;
@Inject
public Builder(ActivityStarter activityStarter, @Background Handler handler,
@Main Looper looper, NetworkController networkController,
- CarrierTextController.Builder carrierTextControllerBuilder) {
+ CarrierTextController.Builder carrierTextControllerBuilder, Context context) {
mActivityStarter = activityStarter;
mHandler = handler;
mLooper = looper;
mNetworkController = networkController;
mCarrierTextControllerBuilder = carrierTextControllerBuilder;
+ mContext = context;
}
public Builder setQSCarrierGroup(QSCarrierGroup view) {
@@ -324,7 +388,7 @@ public class QSCarrierGroupController {
public QSCarrierGroupController build() {
return new QSCarrierGroupController(mView, mActivityStarter, mHandler, mLooper,
- mNetworkController, mCarrierTextControllerBuilder);
+ mNetworkController, mCarrierTextControllerBuilder, mContext);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index abf230e31e93..d4bab2197249 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -102,6 +102,12 @@ public class QSCustomizerController extends ViewController<QSCustomizer> {
public void onConfigChanged(Configuration newConfig) {
mView.updateNavBackDrop(newConfig, mLightBarController);
mView.updateResources();
+ if (mTileAdapter.updateNumColumns()) {
+ RecyclerView.LayoutManager lm = mView.getRecyclerView().getLayoutManager();
+ if (lm instanceof GridLayoutManager) {
+ ((GridLayoutManager) lm).setSpanCount(mTileAdapter.getNumColumns());
+ }
+ }
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 507048c62186..048fdc3a0e5a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -98,7 +98,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
private final UiEventLogger mUiEventLogger;
private final AccessibilityDelegateCompat mAccessibilityDelegate;
private RecyclerView mRecyclerView;
- private final int mNumColumns;
+ private int mNumColumns;
@Inject
public TileAdapter(Context context, QSTileHost qsHost, UiEventLogger uiEventLogger) {
@@ -123,6 +123,21 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
mRecyclerView = null;
}
+ /**
+ * Update the number of columns to show, from resources.
+ *
+ * @return {@code true} if the number of columns changed, {@code false} otherwise
+ */
+ public boolean updateNumColumns() {
+ int numColumns = mContext.getResources().getInteger(R.integer.quick_settings_num_columns);
+ if (numColumns != mNumColumns) {
+ mNumColumns = numColumns;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
public int getNumColumns() {
return mNumColumns;
}
@@ -441,15 +456,17 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
return position > mEditIndex;
}
- private void addFromPosition(int position) {
- if (!canAddFromPosition(position)) return;
+ private boolean addFromPosition(int position) {
+ if (!canAddFromPosition(position)) return false;
move(position, mEditIndex);
+ return true;
}
- private void removeFromPosition(int position) {
- if (!canRemoveFromPosition(position)) return;
+ private boolean removeFromPosition(int position) {
+ if (!canRemoveFromPosition(position)) return false;
TileInfo info = mTiles.get(position);
move(position, info.isSystem ? mEditIndex : mTileDividerIndex);
+ return true;
}
public SpanSizeLookup getSizeLookup() {
@@ -578,11 +595,17 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
}
private void add() {
- addFromPosition(getLayoutPosition());
+ if (addFromPosition(getLayoutPosition())) {
+ itemView.announceForAccessibility(
+ itemView.getContext().getText(R.string.accessibility_qs_edit_tile_added));
+ }
}
private void remove() {
- removeFromPosition(getLayoutPosition());
+ if (removeFromPosition(getLayoutPosition())) {
+ itemView.announceForAccessibility(
+ itemView.getContext().getText(R.string.accessibility_qs_edit_tile_removed));
+ }
}
boolean isCurrentTile() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
index 9ab2d7370ed8..35a8257bd5a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
@@ -16,6 +16,9 @@
package com.android.systemui.qs.dagger;
+import android.content.Context;
+import android.hardware.display.ColorDisplayManager;
+
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.FeatureFlags;
@@ -27,6 +30,7 @@ import dagger.Provides;
@Module
public interface QSFlagsModule {
String QS_LABELS_FLAG = "qs_labels_flag";
+ String RBC_AVAILABLE = "rbc_available";
@Provides
@SysUISingleton
@@ -34,4 +38,12 @@ public interface QSFlagsModule {
static boolean provideQSFlag(FeatureFlags featureFlags) {
return featureFlags.isQSLabelsEnabled();
}
+
+ /** */
+ @Provides
+ @SysUISingleton
+ @Named(RBC_AVAILABLE)
+ static boolean isReduceBrightColorsAvailable(Context context) {
+ return ColorDisplayManager.isReduceBrightColorsAvailable(context);
+ }
}
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 11e6330d37f3..6983b38489f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -37,6 +37,7 @@ import com.android.systemui.qs.tiles.CastTile;
import com.android.systemui.qs.tiles.CellularTile;
import com.android.systemui.qs.tiles.ColorInversionTile;
import com.android.systemui.qs.tiles.DataSaverTile;
+import com.android.systemui.qs.tiles.DeviceControlsTile;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.FlashlightTile;
import com.android.systemui.qs.tiles.HotspotTile;
@@ -89,6 +90,7 @@ public class QSFactoryImpl implements QSFactory {
private final Provider<ReduceBrightColorsTile> mReduceBrightColorsTileProvider;
private final Provider<CameraToggleTile> mCameraToggleTileProvider;
private final Provider<MicrophoneToggleTile> mMicrophoneToggleTileProvider;
+ private final Provider<DeviceControlsTile> mDeviceControlsTileProvider;
private final Lazy<QSHost> mQsHostLazy;
private final Provider<CustomTile.Builder> mCustomTileBuilderProvider;
@@ -123,7 +125,8 @@ public class QSFactoryImpl implements QSFactory {
Provider<ScreenRecordTile> screenRecordTileProvider,
Provider<ReduceBrightColorsTile> reduceBrightColorsTileProvider,
Provider<CameraToggleTile> cameraToggleTileProvider,
- Provider<MicrophoneToggleTile> microphoneToggleTileProvider) {
+ Provider<MicrophoneToggleTile> microphoneToggleTileProvider,
+ Provider<DeviceControlsTile> deviceControlsTileProvider) {
mQsHostLazy = qsHostLazy;
mCustomTileBuilderProvider = customTileBuilderProvider;
@@ -153,6 +156,7 @@ public class QSFactoryImpl implements QSFactory {
mReduceBrightColorsTileProvider = reduceBrightColorsTileProvider;
mCameraToggleTileProvider = cameraToggleTileProvider;
mMicrophoneToggleTileProvider = microphoneToggleTileProvider;
+ mDeviceControlsTileProvider = deviceControlsTileProvider;
}
public QSTile createTile(String tileSpec) {
@@ -212,6 +216,8 @@ public class QSFactoryImpl implements QSFactory {
return mCameraToggleTileProvider.get();
case "mictoggle":
return mMicrophoneToggleTileProvider.get();
+ case "controls":
+ return mDeviceControlsTileProvider.get();
}
// Custom tiles
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
new file mode 100644
index 000000000000..41445917a011
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.content.Intent
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.service.quicksettings.Tile
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.R
+import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.controls.dagger.ControlsComponent
+import com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE
+import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.controls.ui.ControlsDialog
+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.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.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,
+ 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,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger
+) {
+
+ companion object {
+ const val SETTINGS_FLAG = "controls_lockscreen"
+ }
+
+ private val controlsLockscreen = globalSettings.getInt(SETTINGS_FLAG, 0) != 0
+ 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 {
+ override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
+ if (hasControlsApps.compareAndSet(serviceInfos.isEmpty(), serviceInfos.isNotEmpty())) {
+ refreshState()
+ }
+ }
+ }
+
+ init {
+ controlsComponent.getControlsListingController().ifPresent {
+ it.observe(this, listingCallback)
+ }
+ }
+
+ override fun isAvailable(): Boolean {
+ return featureFlags.isKeyguardLayoutEnabled &&
+ controlsLockscreen &&
+ controlsComponent.getVisibility() != UNAVAILABLE
+ }
+
+ override fun newTileState(): QSTile.State {
+ return QSTile.State().also {
+ it.state = Tile.STATE_UNAVAILABLE // Start unavailable matching `hasControlsApps`
+ }
+ }
+
+ 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_UNAVAILABLE) {
+ mUiHandler.post {
+ createDialog()
+ controlsDialog?.show(controlsComponent.getControlsUiController().get())
+ }
+ }
+ }
+
+ override fun handleUpdateState(state: QSTile.State, arg: Any?) {
+ state.label = tileLabel
+ state.secondaryLabel = ""
+ state.stateDescription = ""
+ state.contentDescription = state.label
+ state.icon = icon
+ if (hasControlsApps.get()) {
+ state.state = Tile.STATE_ACTIVE
+ if (controlsDialog == null) {
+ mUiHandler.post(this::createDialog)
+ }
+ } else {
+ state.state = Tile.STATE_UNAVAILABLE
+ dismissDialog()
+ }
+ }
+
+ override fun getMetricsCategory(): Int {
+ return 0
+ }
+
+ override fun getLongClickIntent(): Intent {
+ return intent
+ }
+
+ override fun getTileLabel(): CharSequence {
+ return mContext.getText(R.string.quick_controls_title)
+ }
+}
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 3f8f7464cf49..49dd84165979 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -248,15 +248,13 @@ public class InternetTile extends QSTileImpl<SignalState> {
+ "isTransient = " + isTransient + ","
+ "statusLabel = " + statusLabel);
}
- // When airplane mode is enabled, we need to refresh the Internet Tile even if the WiFi
- // is not the default network.
+ mWifiInfo.mEnabled = enabled;
if (qsIcon == null) {
return;
}
mWifiInfo.mConnected = qsIcon.visible;
mWifiInfo.mWifiSignalIconId = qsIcon.icon;
mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription;
- mWifiInfo.mEnabled = enabled;
mWifiInfo.mSsid = description;
mWifiInfo.mActivityIn = activityIn;
mWifiInfo.mActivityOut = activityOut;
@@ -465,15 +463,13 @@ public class InternetTile extends QSTileImpl<SignalState> {
}
minimalContentDescription.append(
mContext.getString(R.string.quick_settings_internet_label)).append(",");
- if (state.value) {
- if (wifiConnected) {
- minimalStateDescription.append(cb.mWifiSignalContentDescription);
- minimalContentDescription.append(removeDoubleQuotes(cb.mSsid));
- if (!TextUtils.isEmpty(state.secondaryLabel)) {
- minimalContentDescription.append(",").append(state.secondaryLabel);
- }
- }
+ if (state.value && wifiConnected) {
+ minimalStateDescription.append(cb.mWifiSignalContentDescription);
+ minimalContentDescription.append(removeDoubleQuotes(cb.mSsid));
+ } else if (!TextUtils.isEmpty(state.secondaryLabel)) {
+ minimalContentDescription.append(",").append(state.secondaryLabel);
}
+
state.stateDescription = minimalStateDescription.toString();
state.contentDescription = minimalContentDescription.toString();
state.dualLabelContentDescription = r.getString(
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 84c7611478cd..f94cabcee297 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -16,12 +16,13 @@
package com.android.systemui.qs.tiles;
+import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
+
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
-import android.text.TextUtils;
import android.widget.Switch;
import com.android.internal.logging.MetricsLogger;
@@ -39,6 +40,7 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
+import javax.inject.Named;
/** Quick settings tile: Reduce Bright Colors **/
public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> {
@@ -46,9 +48,11 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> {
//TODO(b/170973645): get icon drawable
private final Icon mIcon = null;
private final SecureSetting mActivatedSetting;
+ private final boolean mIsAvailable;
@Inject
public ReduceBrightColorsTile(
+ @Named(RBC_AVAILABLE) boolean isAvailable,
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
@@ -69,11 +73,12 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> {
refreshState();
}
};
+ mIsAvailable = isAvailable;
+
}
@Override
public boolean isAvailable() {
- // TODO(b/170970675): Call into ColorDisplayService to get availability/config status
- return true;
+ return mIsAvailable;
}
@Override
@@ -121,15 +126,6 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> {
state.label = mContext.getString(R.string.quick_settings_reduce_bright_colors_label);
state.expandedAccessibilityClassName = Switch.class.getName();
state.contentDescription = state.label;
-
- final int intensity = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mActivatedSetting.getCurrentUser());
- state.secondaryLabel = state.value ? mContext.getString(
- R.string.quick_settings_reduce_bright_colors_secondary_label, intensity) : "";
-
- state.contentDescription = TextUtils.isEmpty(state.secondaryLabel)
- ? state.label
- : TextUtils.concat(state.label, ", ", state.secondaryLabel);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index 6a8c61491709..6ca550c9ddb2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -41,7 +41,7 @@ public class UserDetailItemView extends LinearLayout {
protected static int layoutResId = R.layout.qs_user_detail_item;
private UserAvatarView mAvatar;
- private TextView mName;
+ protected TextView mName;
private int mActivatedStyle;
private int mRegularStyle;
private View mRestrictedPadlock;
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 26adfdcd8539..a6cddd3367d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -82,7 +82,7 @@ public class UserTile extends QSTileImpl<State> implements UserInfoController.On
@Override
public DetailAdapter getDetailAdapter() {
- return mUserSwitcherController.userDetailAdapter;
+ return mUserSwitcherController.mUserDetailAdapter;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index a9f76f61c537..5b2a7e7ff617 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -101,7 +101,7 @@ import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -149,7 +149,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
private final ScreenshotHelper mScreenshotHelper;
private final Optional<OneHanded> mOneHandedOptional;
private final CommandQueue mCommandQueue;
- private final Transitions mShellTransitions;
+ private final RemoteTransitions mShellTransitions;
private Region mActiveNavBarRegion;
@@ -620,6 +620,19 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
@Override
+ public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+ if (!verifyCaller("exitSplitScreenOnHide")) {
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSplitScreenOptional.ifPresent(s -> s.exitSplitScreenOnHide(exitSplitScreenOnHide));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void startTask(int taskId, int stage, int position, Bundle options) {
if (!verifyCaller("startTask")) {
return;
@@ -799,7 +812,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
Optional<Lazy<StatusBar>> statusBarOptionalLazy,
Optional<OneHanded> oneHandedOptional,
BroadcastDispatcher broadcastDispatcher,
- Transitions shellTransitions) {
+ RemoteTransitions shellTransitions) {
super(broadcastDispatcher);
mContext = context;
mPipOptional = pipOptional;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 543874325254..26781f4ccf09 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -277,12 +277,12 @@ public class ScreenMediaRecorder {
*/
void end() {
mMediaRecorder.stop();
- mMediaProjection.stop();
mMediaRecorder.release();
- mMediaRecorder = null;
- mMediaProjection = null;
mInputSurface.release();
mVirtualDisplay.release();
+ mMediaProjection.stop();
+ mMediaRecorder = null;
+ mMediaProjection = null;
stopInternalAudioRecording();
Log.d(TAG, "end recording");
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index c8afd0b6cfe9..1386ddfa7692 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -16,18 +16,29 @@
package com.android.systemui.screenshot;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Bundle;
import android.util.AttributeSet;
+import android.util.IntArray;
+import android.util.Log;
import android.util.MathUtils;
import android.view.MotionEvent;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import androidx.annotation.Nullable;
+import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
+import com.android.internal.widget.ExploreByTouchHelper;
import com.android.systemui.R;
/**
@@ -35,6 +46,7 @@ import com.android.systemui.R;
* cropped out.
*/
public class CropView extends View {
+ private static final String TAG = "CropView";
public enum CropBoundary {
NONE, TOP, BOTTOM
}
@@ -74,6 +86,8 @@ public class CropView extends View {
t.recycle();
// 48 dp touchable region around each handle.
mCropTouchMargin = 24 * getResources().getDisplayMetrics().density;
+
+ setAccessibilityDelegate(new AccessibilityHelper());
}
@Override
@@ -118,10 +132,7 @@ public class CropView extends View {
case MotionEvent.ACTION_UP:
if (mCurrentDraggingBoundary != CropBoundary.NONE) {
// Commit the delta to the stored crop values.
- mTopCrop += mTopDelta;
- mBottomCrop += mBottomDelta;
- mTopDelta = 0;
- mBottomDelta = 0;
+ commitDeltas();
updateListener(event);
}
}
@@ -129,6 +140,42 @@ public class CropView extends View {
}
/**
+ * Animate the given boundary to the given value.
+ */
+ public void animateBoundaryTo(CropBoundary boundary, float value) {
+ if (boundary == CropBoundary.NONE) {
+ Log.w(TAG, "No boundary selected for animation");
+ return;
+ }
+ float totalDelta = (boundary == CropBoundary.TOP) ? (value - mTopCrop)
+ : (value - mBottomCrop);
+ ValueAnimator animator = new ValueAnimator();
+ animator.addUpdateListener(animation -> {
+ if (boundary == CropBoundary.TOP) {
+ mTopDelta = animation.getAnimatedFraction() * totalDelta;
+ } else {
+ mBottomDelta = animation.getAnimatedFraction() * totalDelta;
+ }
+ invalidate();
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ commitDeltas();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ commitDeltas();
+ }
+ });
+ animator.setFloatValues(0f, 1f);
+ animator.setDuration(750);
+ animator.setInterpolator(new FastOutSlowInInterpolator());
+ animator.start();
+ }
+
+ /**
* @return value [0,1] representing the position of the top crop boundary. Does not reflect
* changes from any in-progress touch input.
*/
@@ -148,6 +195,13 @@ public class CropView extends View {
mCropInteractionListener = listener;
}
+ private void commitDeltas() {
+ mTopCrop += mTopDelta;
+ mBottomCrop += mBottomDelta;
+ mTopDelta = 0;
+ mBottomDelta = 0;
+ }
+
private void updateListener(MotionEvent event) {
if (mCropInteractionListener != null) {
float boundaryPosition = (mCurrentDraggingBoundary == CropBoundary.TOP)
@@ -185,6 +239,87 @@ public class CropView extends View {
return CropBoundary.NONE;
}
+ private class AccessibilityHelper extends ExploreByTouchHelper {
+
+ private static final int TOP_HANDLE_ID = 1;
+ private static final int BOTTOM_HANDLE_ID = 2;
+
+ AccessibilityHelper() {
+ super(CropView.this);
+ }
+
+ @Override
+ protected int getVirtualViewAt(float x, float y) {
+ if (Math.abs(y - fractionToPixels(mTopCrop)) < mCropTouchMargin) {
+ return TOP_HANDLE_ID;
+ }
+ if (Math.abs(y - fractionToPixels(mBottomCrop)) < mCropTouchMargin) {
+ return BOTTOM_HANDLE_ID;
+ }
+ return ExploreByTouchHelper.INVALID_ID;
+ }
+
+ @Override
+ protected void getVisibleVirtualViews(IntArray virtualViewIds) {
+ virtualViewIds.add(TOP_HANDLE_ID);
+ virtualViewIds.add(BOTTOM_HANDLE_ID);
+ }
+
+ @Override
+ protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
+ switch(virtualViewId) {
+ case TOP_HANDLE_ID:
+ event.setContentDescription(
+ getResources().getString(R.string.screenshot_top_boundary));
+ break;
+ case BOTTOM_HANDLE_ID:
+ event.setContentDescription(
+ getResources().getString(R.string.screenshot_bottom_boundary));
+ break;
+ }
+ }
+
+ @Override
+ protected void onPopulateNodeForVirtualView(int virtualViewId,
+ AccessibilityNodeInfo node) {
+ switch(virtualViewId) {
+ case TOP_HANDLE_ID:
+ node.setContentDescription(
+ getResources().getString(R.string.screenshot_top_boundary));
+ setNodePositions(mTopCrop, node);
+ break;
+ case BOTTOM_HANDLE_ID:
+ node.setContentDescription(
+ getResources().getString(R.string.screenshot_bottom_boundary));
+ setNodePositions(mBottomCrop, node);
+ break;
+ }
+
+ // TODO: need to figure out the full set of actions to support here.
+ node.addAction(
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
+ node.setClickable(true);
+ node.setFocusable(true);
+ }
+
+ @Override
+ protected boolean onPerformActionForVirtualView(
+ int virtualViewId, int action, Bundle arguments) {
+ return false;
+ }
+
+ private void setNodePositions(float fraction, AccessibilityNodeInfo node) {
+ int pixels = fractionToPixels(fraction);
+ Rect rect = new Rect(0, (int) (pixels - mCropTouchMargin),
+ getWidth(), (int) (pixels + mCropTouchMargin));
+ node.setBoundsInParent(rect);
+ int[] pos = new int[2];
+ getLocationOnScreen(pos);
+ rect.offset(pos[0], pos[1]);
+ node.setBoundsInScreen(rect);
+ }
+ }
+
/**
* Listen for crop motion events and state.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java
index 212e6c86e9da..a95c91bfeceb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java
@@ -70,7 +70,7 @@ class ImageTile implements AutoCloseable {
RecordingCanvas canvas = mNode.beginRecording(w, h);
canvas.save();
- canvas.clipRect(0, 0, mLocation.right, mLocation.bottom);
+ canvas.clipRect(0, 0, mLocation.width(), mLocation.height());
canvas.drawBitmap(Bitmap.wrapHardwareBuffer(mImage.getHardwareBuffer(), COLOR_SPACE),
0, 0, null);
canvas.restore();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
index 20f845103723..ae3cd9996f04 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
@@ -21,6 +21,8 @@ import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.util.Log;
import androidx.annotation.UiThread;
@@ -32,11 +34,14 @@ import java.util.List;
* <p>
* To display on-screen, use {@link #getDrawable()}.
*/
-@UiThread
class ImageTileSet {
private static final String TAG = "ImageTileSet";
+ ImageTileSet(@UiThread Handler handler) {
+ mHandler = handler;
+ }
+
interface OnBoundsChangedListener {
/**
* Reports an update to the bounding box that contains all active tiles. These are virtual
@@ -54,6 +59,7 @@ class ImageTileSet {
private final List<ImageTile> mTiles = new ArrayList<>();
private final Rect mBounds = new Rect();
+ private final Handler mHandler;
private OnContentChangedListener mOnContentChangedListener;
private OnBoundsChangedListener mOnBoundsChangedListener;
@@ -73,13 +79,32 @@ class ImageTileSet {
newBounds.union(newRect);
if (!newBounds.equals(mBounds)) {
mBounds.set(newBounds);
- if (mOnBoundsChangedListener != null) {
- mOnBoundsChangedListener.onBoundsChanged(
- newBounds.left, newBounds.top, newBounds.right, newBounds.bottom);
- }
+ notifyBoundsChanged(mBounds);
}
- if (mOnContentChangedListener != null) {
+ notifyContentChanged();
+ }
+
+ void notifyContentChanged() {
+ if (mOnContentChangedListener == null) {
+ return;
+ }
+ if (mHandler.getLooper().isCurrentThread()) {
mOnContentChangedListener.onContentChanged();
+ } else {
+ mHandler.post(() -> mOnContentChangedListener.onContentChanged());
+ }
+ }
+
+ void notifyBoundsChanged(Rect bounds) {
+ if (mOnBoundsChangedListener == null) {
+ return;
+ }
+ if (mHandler.getLooper().isCurrentThread()) {
+ mOnBoundsChangedListener.onBoundsChanged(
+ bounds.left, bounds.top, bounds.right, bounds.bottom);
+ } else {
+ mHandler.post(() -> mOnBoundsChangedListener.onBoundsChanged(
+ bounds.left, bounds.top, bounds.right, bounds.bottom));
}
}
@@ -117,22 +142,16 @@ class ImageTileSet {
* getHeight()).
*/
Bitmap toBitmap(Rect bounds) {
+ Log.d(TAG, "exporting with bounds: " + bounds);
if (mTiles.isEmpty()) {
return null;
}
final RenderNode output = new RenderNode("Bitmap Export");
- output.setPosition(0, 0, getWidth(), getHeight());
+ output.setPosition(0, 0, bounds.width(), bounds.height());
RecordingCanvas canvas = output.beginRecording();
- canvas.translate(-getLeft(), -getTop());
- // Additional translation to account for the requested bounds
- canvas.translate(-bounds.left, -bounds.top);
- canvas.clipRect(bounds);
- for (ImageTile tile : mTiles) {
- canvas.save();
- canvas.translate(tile.getLeft(), tile.getTop());
- canvas.drawRenderNode(tile.getDisplayList());
- canvas.restore();
- }
+ Drawable drawable = getDrawable();
+ drawable.setBounds(bounds);
+ drawable.draw(canvas);
output.endRecording();
return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height());
}
@@ -162,14 +181,13 @@ class ImageTileSet {
}
void clear() {
- mBounds.set(0, 0, 0, 0);
+ if (mBounds.isEmpty()) {
+ return;
+ }
+ mBounds.setEmpty();
mTiles.forEach(ImageTile::close);
mTiles.clear();
- if (mOnBoundsChangedListener != null) {
- mOnBoundsChangedListener.onBoundsChanged(0, 0, 0, 0);
- }
- if (mOnContentChangedListener != null) {
- mOnContentChangedListener.onContentChanged();
- }
+ notifyBoundsChanged(mBounds);
+ notifyContentChanged();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
index f88715164bc7..f8f1d3ac9a5b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
@@ -144,7 +144,9 @@ public class MagnifierView extends View implements CropView.CropInteractionListe
setAlpha(0f);
setTranslationX((getParentWidth() - getWidth()) / 2);
setVisibility(View.VISIBLE);
- animate().alpha(1f).translationX(0).scaleX(1f).scaleY(1f).start();
+ boolean touchOnRight = event.getX() > getParentWidth() / 2;
+ float translateXTarget = touchOnRight ? 0 : getParentWidth() - getWidth();
+ animate().alpha(1f).translationX(translateXTarget).scaleX(1f).scaleY(1f).start();
break;
case MotionEvent.ACTION_MOVE:
mLastCropPosition = cropPosition;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
index bb07012f2355..dc639dce4951 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
@@ -50,8 +50,6 @@ import javax.inject.Inject;
public class ScrollCaptureClient {
private static final int TILE_SIZE_PX_MAX = 4 * (1024 * 1024);
private static final int TILES_PER_PAGE = 2; // increase once b/174571735 is addressed
- private static final int MAX_PAGES = 5;
- private static final int MAX_IMAGE_COUNT = MAX_PAGES * TILES_PER_PAGE;
@VisibleForTesting
static final int MATCH_ANY_TASK = ActivityTaskManager.INVALID_TASK_ID;
@@ -66,10 +64,11 @@ public class ScrollCaptureClient {
/**
* Session start should be deferred until UI is active because of resource allocation and
* potential visible side effects in the target window.
-
+ *
* @param sessionConsumer listener to receive the session once active
+ * @param maxPages the capture buffer size expressed as a multiple of the content height
*/
- void start(Consumer<Session> sessionConsumer);
+ void start(Consumer<Session> sessionConsumer, float maxPages);
/**
* Close the connection.
@@ -96,6 +95,17 @@ public class ScrollCaptureClient {
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
+ + '}';
+ }
}
/**
@@ -196,6 +206,7 @@ public class ScrollCaptureClient {
private int mTileWidth;
private Rect mRequestRect;
private boolean mStarted;
+ private int mMaxTiles;
private ControllerCallbacks(Consumer<Connection> connectionConsumer) {
mConnectionConsumer = connectionConsumer;
@@ -285,12 +296,15 @@ public class ScrollCaptureClient {
// ScrollCaptureController.Connection
@Override
- public void start(Consumer<Session> sessionConsumer) {
+ public void start(Consumer<Session> sessionConsumer, float maxPages) {
if (DEBUG_SCROLL) {
- Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + ")");
+ Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + ","
+ + " maxPages=" + maxPages + ")"
+ + " [maxHeight: " + (mMaxTiles * mTileHeight) + "px]");
}
+ mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE);
mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
- MAX_IMAGE_COUNT, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
+ mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
mSessionConsumer = sessionConsumer;
try {
mConnection.startCapture(mReader.getSurface());
@@ -345,7 +359,7 @@ public class ScrollCaptureClient {
@Override
public int getMaxTiles() {
- return MAX_IMAGE_COUNT;
+ return mMaxTiles;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 25438a6f57ba..ad5e637b189e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -24,6 +24,7 @@ import android.content.Intent;
import android.graphics.Rect;
import android.net.Uri;
import android.os.UserHandle;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -34,6 +35,7 @@ import android.widget.ImageView;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
+import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
import com.android.systemui.screenshot.ScrollCaptureClient.Connection;
import com.android.systemui.screenshot.ScrollCaptureClient.Session;
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
@@ -44,13 +46,23 @@ import java.time.ZonedDateTime;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
-import java.util.function.Consumer;
/**
* Interaction controller between the UI and ScrollCaptureClient.
*/
public class ScrollCaptureController implements OnComputeInternalInsetsListener {
private static final String TAG = "ScrollCaptureController";
+ private static final float MAX_PAGES_DEFAULT = 3f;
+
+ private static final String SETTING_KEY_MAX_PAGES = "screenshot.scroll_max_pages";
+
+ private static final int UP = -1;
+ private static final int DOWN = 1;
+
+ private int mDirection = DOWN;
+ private boolean mAtBottomEdge;
+ private boolean mAtTopEdge;
+ private Session mSession;
// TODO: Support saving without additional action.
private enum PendingAction {
@@ -59,7 +71,6 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
SAVE
}
- public static final int MAX_PAGES = 5;
public static final int MAX_HEIGHT = 12000;
private final Connection mConnection;
@@ -91,7 +102,7 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
mBgExecutor = bgExecutor;
mImageExporter = exporter;
mUiEventLogger = uiEventLogger;
- mImageTileSet = new ImageTileSet();
+ mImageTileSet = new ImageTileSet(context.getMainThreadHandler());
}
/**
@@ -129,7 +140,9 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
mEdit.setOnClickListener(this::onClicked);
mShare.setOnClickListener(this::onClicked);
- mConnection.start(this::startCapture);
+ float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(),
+ SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT);
+ mConnection.start(this::startCapture, maxPages);
}
@@ -232,41 +245,82 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
return mWindow.findViewById(res);
}
+
+ private void onCaptureResult(CaptureResult result) {
+ Log.d(TAG, "onCaptureResult: " + result);
+ boolean emptyResult = result.captured.height() == 0;
+ boolean partialResult = !emptyResult
+ && result.captured.height() < result.requested.height();
+ boolean finish = false;
+
+ if (partialResult || emptyResult) {
+ // Potentially reached a vertical boundary. Extend in the other direction.
+ switch (mDirection) {
+ case DOWN:
+ Log.d(TAG, "Reached bottom edge.");
+ mAtBottomEdge = true;
+ mDirection = UP;
+ break;
+ case UP:
+ Log.d(TAG, "Reached top edge.");
+ mAtTopEdge = true;
+ mDirection = DOWN;
+ break;
+ }
+
+ if (mAtTopEdge && mAtBottomEdge) {
+ Log.d(TAG, "Reached both top and bottom edge, ending.");
+ finish = true;
+ } else {
+ // only reverse if the edge was relatively close to the starting point
+ if (mImageTileSet.getHeight() < mSession.getPageHeight() * 3) {
+ Log.d(TAG, "Restarting in reverse direction.");
+
+ // Because of temporary limitations, we cannot just jump to the opposite edge
+ // and continue there. Instead, clear the results and start over capturing from
+ // here in the other direction.
+ mImageTileSet.clear();
+ } else {
+ Log.d(TAG, "Capture is tall enough, stopping here.");
+ finish = true;
+ }
+ }
+ }
+
+ if (!emptyResult) {
+ mImageTileSet.addTile(new ImageTile(result.image, result.captured));
+ }
+
+ Log.d(TAG, "bounds: " + mImageTileSet.getLeft() + "," + mImageTileSet.getTop()
+ + " - " + mImageTileSet.getRight() + "," + mImageTileSet.getBottom()
+ + " (" + mImageTileSet.getWidth() + "x" + mImageTileSet.getHeight() + ")");
+
+
+ // Stop when "too tall"
+ if (mImageTileSet.size() >= mSession.getMaxTiles()
+ || mImageTileSet.getHeight() > MAX_HEIGHT) {
+ Log.d(TAG, "Max height and/or tile count reached.");
+ finish = true;
+ }
+
+ if (finish) {
+ Session session = mSession;
+ mSession = null;
+ Log.d(TAG, "Stop.");
+ mUiExecutor.execute(() -> afterCaptureComplete(session));
+ return;
+ }
+
+ int nextTop = (mDirection == DOWN) ? result.captured.bottom
+ : result.captured.top - mSession.getTileHeight();
+ Log.d(TAG, "requestTile: " + nextTop);
+ mSession.requestTile(nextTop, /* consumer */ this::onCaptureResult);
+ }
+
private void startCapture(Session session) {
- Log.d(TAG, "startCapture");
- Consumer<ScrollCaptureClient.CaptureResult> consumer =
- new Consumer<ScrollCaptureClient.CaptureResult>() {
-
- int mFrameCount = 0;
- int mTop = 0;
-
- @Override
- public void accept(ScrollCaptureClient.CaptureResult result) {
- mFrameCount++;
-
- boolean emptyFrame = result.captured.height() == 0;
- if (!emptyFrame) {
- ImageTile tile = new ImageTile(result.image, result.captured);
- Log.d(TAG, "Adding tile: " + tile);
- mImageTileSet.addTile(tile);
- Log.d(TAG, "New dimens: w=" + mImageTileSet.getWidth() + ", "
- + "h=" + mImageTileSet.getHeight());
- }
-
- if (emptyFrame || mFrameCount >= MAX_PAGES
- || mTop + session.getTileHeight() > MAX_HEIGHT) {
-
- mUiExecutor.execute(() -> afterCaptureComplete(session));
- return;
- }
- mTop += result.captured.height();
- session.requestTile(mTop, /* consumer */ this);
- }
- };
-
- // fire it up!
- session.requestTile(0, consumer);
- };
+ mSession = session;
+ session.requestTile(0, this::onCaptureResult);
+ }
@UiThread
void afterCaptureComplete(Session session) {
@@ -277,6 +331,7 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
} else {
mPreview.setImageDrawable(mImageTileSet.getDrawable());
mMagnifierView.setImageTileset(mImageTileSet);
+ mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java b/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java
index 72f489bdd398..4ec8eb22c67a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java
@@ -56,7 +56,7 @@ public class TiledImageDrawable extends Drawable {
mNode = new RenderNode("TiledImageDrawable");
}
mNode.setPosition(0, 0, mTiles.getWidth(), mTiles.getHeight());
- Canvas canvas = mNode.beginRecording(mTiles.getWidth(), mTiles.getHeight());
+ Canvas canvas = mNode.beginRecording();
// Align content (virtual) top/left with 0,0, within the render node
canvas.translate(-mTiles.getLeft(), -mTiles.getTop());
for (int i = 0; i < mTiles.size(); i++) {
@@ -79,8 +79,8 @@ public class TiledImageDrawable extends Drawable {
if (canvas.isHardwareAccelerated()) {
Rect bounds = getBounds();
canvas.save();
- canvas.clipRect(bounds);
- canvas.translate(bounds.left, bounds.top);
+ canvas.clipRect(0, 0, bounds.width(), bounds.height());
+ canvas.translate(-bounds.left, -bounds.top);
canvas.drawRenderNode(mNode);
canvas.restore();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index d707bca0383e..9f182e19efaf 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -28,6 +28,7 @@ import android.hardware.SensorPrivacyManager.EXTRA_SENSOR
import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA
import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE
import android.os.Bundle
+import android.os.Handler
import android.text.Html
import android.util.Log
import com.android.internal.app.AlertActivity
@@ -43,10 +44,13 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene
companion object {
private val LOG_TAG = SensorUseStartedActivity::class.java.simpleName
+
+ private const val SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS = 2000L
}
private var sensor = -1
private lateinit var sensorUsePackageName: String
+ private var unsuppressImmediately = false
private lateinit var sensorPrivacyManager: SensorPrivacyManager
private lateinit var appOpsManager: AppOpsManager
@@ -118,6 +122,7 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene
super.onStart()
sensorPrivacyManager.suppressIndividualSensorPrivacyReminders(sensorUsePackageName, true)
+ unsuppressImmediately = false
}
override fun onClick(dialog: DialogInterface?, which: Int) {
@@ -131,16 +136,16 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene
}
override fun onDismissSucceeded() {
- sensorPrivacyManager
- .setIndividualSensorPrivacyForProfileGroup(sensor, false)
- setResult(RESULT_OK)
+ disableSensorPrivacy()
}
})
} else {
- sensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, false)
- setResult(RESULT_OK)
+ disableSensorPrivacy()
}
}
+ BUTTON_NEGATIVE -> {
+ unsuppressImmediately = false
+ }
}
dismiss()
@@ -149,10 +154,24 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene
override fun onStop() {
super.onDestroy()
- sensorPrivacyManager.suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false)
+ if (unsuppressImmediately) {
+ sensorPrivacyManager
+ .suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false)
+ } else {
+ Handler(mainLooper).postDelayed({
+ sensorPrivacyManager
+ .suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false)
+ }, SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS)
+ }
}
override fun onBackPressed() {
// do not allow backing out
}
+
+ private fun disableSensorPrivacy() {
+ sensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, false)
+ unsuppressImmediately = true
+ setResult(RESULT_OK)
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index a3b5f27933be..0bfc8e5d554b 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -41,7 +41,7 @@ import android.service.vr.IVrStateCallbacks;
import android.util.Log;
import android.util.MathUtils;
-import com.android.internal.BrightnessSynchronizer;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.RestrictedLockUtilsInternal;
@@ -79,6 +79,7 @@ public class BrightnessController implements ToggleSlider.Listener {
private final float mMaximumBacklightForVr;
private final float mDefaultBacklightForVr;
+ private final int mDisplayId;
private final Context mContext;
private final ToggleSlider mControl;
private final boolean mAutomaticAvailable;
@@ -311,6 +312,7 @@ public class BrightnessController implements ToggleSlider.Listener {
};
mBrightnessObserver = new BrightnessObserver(mHandler);
+ mDisplayId = mContext.getDisplayId();
PowerManager pm = context.getSystemService(PowerManager.class);
mMinimumBacklight = pm.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
@@ -420,7 +422,7 @@ public class BrightnessController implements ToggleSlider.Listener {
}
private void setBrightness(float brightness) {
- mDisplayManager.setTemporaryBrightness(brightness);
+ mDisplayManager.setTemporaryBrightness(mDisplayId, brightness);
}
private void updateVrMode(boolean isEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index e7b60c3a0d67..862c27907e0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -67,7 +67,19 @@ public class FeatureFlags {
return mFlagReader.isEnabled(R.bool.flag_brightness_slider);
}
+ public boolean useNewLockscreenAnimations() {
+ return mFlagReader.isEnabled(R.bool.flag_lockscreen_animations);
+ }
+
public boolean isPeopleTileEnabled() {
return mFlagReader.isEnabled(R.bool.flag_conversations);
}
+
+ public boolean isToastStyleEnabled() {
+ return mFlagReader.isEnabled(R.bool.flag_toast_style);
+ }
+
+ public boolean isMonetEnabled() {
+ return mFlagReader.isEnabled(R.bool.flag_monet);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index c816784a22f2..7e2d27ac728a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -16,10 +16,24 @@
package com.android.systemui.statusbar;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+import static com.android.keyguard.KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_LOGOUT;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_RESTING;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -47,6 +61,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.ViewClippingUtil;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -56,13 +71,16 @@ import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.keyguard.KeyguardIndication;
+import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -76,8 +94,7 @@ import javax.inject.Inject;
* Controls the indications and error messages shown on the Keyguard
*/
@SysUISingleton
-public class KeyguardIndicationController implements StateListener,
- KeyguardStateController.Callback {
+public class KeyguardIndicationController implements KeyguardStateController.Callback {
private static final String TAG = "KeyguardIndication";
private static final boolean DEBUG_CHARGING_SPEED = false;
@@ -94,14 +111,17 @@ public class KeyguardIndicationController implements StateListener,
private final StatusBarStateController mStatusBarStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private ViewGroup mIndicationArea;
- private KeyguardIndicationTextView mTextView;
- private KeyguardIndicationTextView mDisclosure;
+ private KeyguardIndicationTextView mTopIndicationView;
private final IBatteryStats mBatteryInfo;
private final SettableWakeLock mWakeLock;
private final DockManager mDockManager;
private final DevicePolicyManager mDevicePolicyManager;
private final UserManager mUserManager;
+ private final @Main DelayableExecutor mExecutor;
+ private final LockPatternUtils mLockPatternUtils;
+ private final IActivityManager mIActivityManager;
+ protected KeyguardIndicationRotateTextViewController mRotateTextViewController;
private BroadcastReceiver mBroadcastReceiver;
private LockscreenLockIconController mLockIconController;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -110,7 +130,7 @@ public class KeyguardIndicationController implements StateListener,
private String mAlignmentIndication;
private CharSequence mTransientIndication;
private boolean mTransientTextIsError;
- private ColorStateList mInitialTextColorState;
+ protected ColorStateList mInitialTextColorState;
private boolean mVisible;
private boolean mHideTransientMessageOnScreenOff;
@@ -124,8 +144,8 @@ public class KeyguardIndicationController implements StateListener,
private int mBatteryLevel;
private boolean mBatteryPresent = true;
private long mChargingTimeRemaining;
- private float mDisclosureMaxAlpha;
private String mMessageToShowOnScreenOn;
+ protected int mLockScreenMode;
private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
@@ -151,7 +171,8 @@ public class KeyguardIndicationController implements StateListener,
BroadcastDispatcher broadcastDispatcher,
DevicePolicyManager devicePolicyManager,
IBatteryStats iBatteryStats,
- UserManager userManager) {
+ UserManager userManager,
+ @Main DelayableExecutor executor) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mDevicePolicyManager = devicePolicyManager;
@@ -165,23 +186,28 @@ public class KeyguardIndicationController implements StateListener,
wakeLockBuilder.setTag("Doze:KeyguardIndication").build(), TAG);
mBatteryInfo = iBatteryStats;
mUserManager = userManager;
+ mExecutor = executor;
+ mLockPatternUtils = new LockPatternUtils(context);
+ mIActivityManager = ActivityManager.getService();
mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback());
mKeyguardUpdateMonitor.registerCallback(mTickReceiver);
- mStatusBarStateController.addCallback(this);
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
mKeyguardStateController.addCallback(this);
}
public void setIndicationArea(ViewGroup indicationArea) {
mIndicationArea = indicationArea;
- mTextView = indicationArea.findViewById(R.id.keyguard_indication_text);
- mInitialTextColorState = mTextView != null ?
- mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
- mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure);
- mDisclosureMaxAlpha = mDisclosure.getAlpha();
+ mTopIndicationView = indicationArea.findViewById(R.id.keyguard_indication_text);
+ mInitialTextColorState = mTopIndicationView != null
+ ? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
+ mRotateTextViewController = new KeyguardIndicationRotateTextViewController(
+ indicationArea.findViewById(R.id.keyguard_indication_text_bottom),
+ mExecutor,
+ mStatusBarStateController,
+ mLockScreenMode);
updateIndication(false /* animate */);
updateDisclosure();
-
if (mBroadcastReceiver == null) {
// Update the disclosure proactively to avoid IPC on the critical path.
mBroadcastReceiver = new BroadcastReceiver() {
@@ -233,19 +259,204 @@ public class KeyguardIndicationController implements StateListener,
return mUpdateMonitorCallback;
}
+ /**
+ * Doesn't include disclosure which gets triggered separately.
+ */
+ private void updateIndications(boolean animate, int userId) {
+ updateOwnerInfo();
+ updateBattery(animate);
+ updateUserLocked(userId);
+ updateTransient();
+ updateTrust(userId, getTrustGrantedIndication(), getTrustManagedIndication());
+ updateAlignment();
+ updateLogoutView();
+ updateResting();
+ }
+
private void updateDisclosure() {
- // NOTE: Because this uses IPC, avoid calling updateDisclosure() on a critical path.
+ // avoid calling this method since it has an IPC
if (whitelistIpcs(this::isOrganizationOwnedDevice)) {
- CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName();
- if (organizationName != null) {
- mDisclosure.switchIndication(mContext.getResources().getString(
- R.string.do_disclosure_with_name, organizationName));
- } else {
- mDisclosure.switchIndication(R.string.do_disclosure_generic);
+ final CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName();
+ final CharSequence disclosure = organizationName != null
+ ? mContext.getResources().getString(R.string.do_disclosure_with_name,
+ organizationName)
+ : mContext.getResources().getText(R.string.do_disclosure_generic);
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_DISCLOSURE,
+ new KeyguardIndication.Builder()
+ .setMessage(disclosure)
+ .setTextColor(mInitialTextColorState)
+ .build(),
+ /* updateImmediately */ false);
+ } else {
+ mRotateTextViewController.hideIndication(INDICATION_TYPE_DISCLOSURE);
+ }
+
+ if (isKeyguardLayoutEnabled()) {
+ updateResting();
+ }
+ }
+
+ private void updateOwnerInfo() {
+ if (!isKeyguardLayoutEnabled()) {
+ mRotateTextViewController.hideIndication(INDICATION_TYPE_OWNER_INFO);
+ return;
+ }
+ String info = mLockPatternUtils.getDeviceOwnerInfo();
+ if (info == null) {
+ // Use the current user owner information if enabled.
+ final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (ownerInfoEnabled) {
+ info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser());
}
- mDisclosure.setVisibility(View.VISIBLE);
+ }
+ if (info != null) {
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_OWNER_INFO,
+ new KeyguardIndication.Builder()
+ .setMessage(info)
+ .setTextColor(mInitialTextColorState)
+ .build(),
+ false);
+ } else {
+ mRotateTextViewController.hideIndication(INDICATION_TYPE_OWNER_INFO);
+ }
+ }
+
+ private void updateBattery(boolean animate) {
+ if (mPowerPluggedIn || mEnableBatteryDefender) {
+ String powerIndication = computePowerIndication();
+ if (DEBUG_CHARGING_SPEED) {
+ powerIndication += ", " + (mChargingWattage / 1000) + " mW";
+ }
+
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_BATTERY,
+ new KeyguardIndication.Builder()
+ .setMessage(powerIndication)
+ .setTextColor(mInitialTextColorState)
+ .build(),
+ animate);
+ } else {
+ // don't show the charging information if device isn't plugged in
+ mRotateTextViewController.hideIndication(INDICATION_TYPE_BATTERY);
+ }
+ }
+
+ private void updateUserLocked(int userId) {
+ if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) {
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_USER_LOCKED,
+ new KeyguardIndication.Builder()
+ .setMessage(mContext.getResources().getText(
+ com.android.internal.R.string.lockscreen_storage_locked))
+ .setTextColor(mInitialTextColorState)
+ .build(),
+ false);
+ } else {
+ mRotateTextViewController.hideIndication(INDICATION_TYPE_USER_LOCKED);
+ }
+ }
+
+ private void updateTransient() {
+ if (!TextUtils.isEmpty(mTransientIndication)) {
+ mRotateTextViewController.showTransient(mTransientIndication,
+ mTransientTextIsError);
+ } else {
+ mRotateTextViewController.hideTransient();
+ }
+ }
+
+ private void updateTrust(int userId, CharSequence trustGrantedIndication,
+ CharSequence trustManagedIndication) {
+ if (!TextUtils.isEmpty(trustGrantedIndication)
+ && mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_TRUST,
+ new KeyguardIndication.Builder()
+ .setMessage(trustGrantedIndication)
+ .setTextColor(mInitialTextColorState)
+ .build(),
+ false);
+ } else if (!TextUtils.isEmpty(trustManagedIndication)
+ && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId)
+ && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_TRUST,
+ new KeyguardIndication.Builder()
+ .setMessage(trustManagedIndication)
+ .setTextColor(mInitialTextColorState)
+ .build(),
+ false);
+ } else {
+ mRotateTextViewController.hideIndication(INDICATION_TYPE_TRUST);
+ }
+ }
+
+ private void updateAlignment() {
+ if (!TextUtils.isEmpty(mAlignmentIndication)) {
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_ALIGNMENT,
+ new KeyguardIndication.Builder()
+ .setMessage(mAlignmentIndication)
+ .setTextColor(Utils.getColorError(mContext))
+ .build(),
+ true);
+ } else {
+ mRotateTextViewController.hideIndication(INDICATION_TYPE_ALIGNMENT);
+ }
+ }
+
+ private void updateResting() {
+ if (mRestingIndication != null
+ && !mRotateTextViewController.hasIndications()) {
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_RESTING,
+ new KeyguardIndication.Builder()
+ .setMessage(mRestingIndication)
+ .setTextColor(mInitialTextColorState)
+ .build(),
+ false);
} else {
- mDisclosure.setVisibility(View.GONE);
+ mRotateTextViewController.hideIndication(INDICATION_TYPE_RESTING);
+ }
+ }
+
+ protected boolean isKeyguardLayoutEnabled() {
+ return mLockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1;
+ }
+
+ private void updateLogoutView() {
+ if (!isKeyguardLayoutEnabled()) {
+ mRotateTextViewController.hideIndication(INDICATION_TYPE_LOGOUT);
+ return;
+ }
+ final boolean shouldShowLogout = mKeyguardUpdateMonitor.isLogoutEnabled()
+ && KeyguardUpdateMonitor.getCurrentUser() != UserHandle.USER_SYSTEM;
+ if (shouldShowLogout) {
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_LOGOUT,
+ new KeyguardIndication.Builder()
+ .setMessage(mContext.getResources().getString(
+ com.android.internal.R.string.global_action_logout))
+ .setTextColor(mInitialTextColorState)
+ .setBackground(mContext.getDrawable(
+ com.android.systemui.R.drawable.logout_button_background))
+ .setClickListener((view) -> {
+ int currentUserId = KeyguardUpdateMonitor.getCurrentUser();
+ try {
+ mIActivityManager.switchUser(UserHandle.USER_SYSTEM);
+ mIActivityManager.stopUser(currentUserId, true /* force */,
+ null);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Failed to logout user", re);
+ }
+ })
+ .build(),
+ false);
+ } else {
+ mRotateTextViewController.hideIndication(INDICATION_TYPE_LOGOUT);
}
}
@@ -281,9 +492,10 @@ public class KeyguardIndicationController implements StateListener,
return UserHandle.USER_NULL;
}
- public void setVisible(boolean visible) {
+ @VisibleForTesting
+ protected void setVisible(boolean visible) {
mVisible = visible;
- mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
+ mIndicationArea.setVisibility(visible ? VISIBLE : GONE);
if (visible) {
// If this is called after an error message was already shown, we should not clear it.
// Otherwise the error message won't be shown
@@ -382,6 +594,7 @@ public class KeyguardIndicationController implements StateListener,
mTransientIndication = null;
mHideTransientMessageOnScreenOff = false;
mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+ mRotateTextViewController.hideTransient();
updateIndication(false);
}
}
@@ -396,98 +609,109 @@ public class KeyguardIndicationController implements StateListener,
}
// A few places might need to hide the indication, so always start by making it visible
- mIndicationArea.setVisibility(View.VISIBLE);
+ mIndicationArea.setVisibility(VISIBLE);
// Walk down a precedence-ordered list of what indication
// should be shown based on user or device state
+ // AoD
if (mDozing) {
+ mTopIndicationView.setVisibility(VISIBLE);
// When dozing we ignore any text color and use white instead, because
// colors can be hard to read in low brightness.
- mTextView.setTextColor(Color.WHITE);
+ mTopIndicationView.setTextColor(Color.WHITE);
if (!TextUtils.isEmpty(mTransientIndication)) {
- mTextView.switchIndication(mTransientIndication);
+ mTopIndicationView.switchIndication(mTransientIndication, null);
} else if (!mBatteryPresent) {
// If there is no battery detected, hide the indication and bail
- mIndicationArea.setVisibility(View.GONE);
+ mIndicationArea.setVisibility(GONE);
} else if (!TextUtils.isEmpty(mAlignmentIndication)) {
- mTextView.switchIndication(mAlignmentIndication);
- mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color));
+ mTopIndicationView.switchIndication(mAlignmentIndication, null);
+ mTopIndicationView.setTextColor(mContext.getColor(R.color.misalignment_text_color));
} else if (mPowerPluggedIn || mEnableBatteryDefender) {
String indication = computePowerIndication();
if (animate) {
- animateText(mTextView, indication);
+ animateText(mTopIndicationView, indication);
} else {
- mTextView.switchIndication(indication);
+ mTopIndicationView.switchIndication(indication, null);
}
} else {
String percentage = NumberFormat.getPercentInstance()
.format(mBatteryLevel / 100f);
- mTextView.switchIndication(percentage);
+ mTopIndicationView.switchIndication(percentage, null);
}
return;
}
+ // LOCK SCREEN
+ // Some cases here might need to hide the indication (if the battery is not present)
int userId = KeyguardUpdateMonitor.getCurrentUser();
- String trustGrantedIndication = getTrustGrantedIndication();
- String trustManagedIndication = getTrustManagedIndication();
-
- String powerIndication = null;
- if (mPowerPluggedIn || mEnableBatteryDefender) {
- powerIndication = computePowerIndication();
- }
- // Some cases here might need to hide the indication (if the battery is not present)
- boolean hideIndication = false;
- boolean isError = false;
- if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) {
- mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
- } else if (!TextUtils.isEmpty(mTransientIndication)) {
- if (powerIndication != null && !mTransientIndication.equals(powerIndication)) {
- String indication = mContext.getResources().getString(
- R.string.keyguard_indication_trust_unlocked_plugged_in,
- mTransientIndication, powerIndication);
- mTextView.switchIndication(indication);
- hideIndication = !mBatteryPresent;
- } else {
- mTextView.switchIndication(mTransientIndication);
+ if (mLockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1) {
+ mTopIndicationView.setVisibility(GONE);
+ updateIndications(animate, userId);
+ } else {
+ boolean hideIndication = false;
+ boolean isError = false;
+ String trustGrantedIndication = getTrustGrantedIndication();
+ String trustManagedIndication = getTrustManagedIndication();
+ String powerIndication = null;
+
+ if (mPowerPluggedIn || mEnableBatteryDefender) {
+ powerIndication = computePowerIndication();
}
- isError = mTransientTextIsError;
- } else if (!TextUtils.isEmpty(trustGrantedIndication)
- && mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
- if (powerIndication != null) {
- String indication = mContext.getResources().getString(
- R.string.keyguard_indication_trust_unlocked_plugged_in,
- trustGrantedIndication, powerIndication);
- mTextView.switchIndication(indication);
+ if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) {
+ mTopIndicationView.switchIndication(
+ com.android.internal.R.string.lockscreen_storage_locked);
+ } else if (!TextUtils.isEmpty(mTransientIndication)) {
+ if (powerIndication != null && !mTransientIndication.equals(powerIndication)) {
+ String indication = mContext.getResources().getString(
+ R.string.keyguard_indication_trust_unlocked_plugged_in,
+ mTransientIndication, powerIndication);
+ mTopIndicationView.switchIndication(indication, null);
+ hideIndication = !mBatteryPresent;
+ } else {
+ mTopIndicationView.switchIndication(mTransientIndication, null);
+ }
+ isError = mTransientTextIsError;
+ } else if (!TextUtils.isEmpty(trustGrantedIndication)
+ && mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
+ if (powerIndication != null) {
+ String indication = mContext.getResources().getString(
+ R.string.keyguard_indication_trust_unlocked_plugged_in,
+ trustGrantedIndication, powerIndication);
+ mTopIndicationView.switchIndication(indication, null);
+ hideIndication = !mBatteryPresent;
+ } else {
+ mTopIndicationView.switchIndication(trustGrantedIndication, null);
+ }
+ } else if (!TextUtils.isEmpty(mAlignmentIndication)) {
+ mTopIndicationView.switchIndication(mAlignmentIndication, null);
+ isError = true;
hideIndication = !mBatteryPresent;
+ } else if (mPowerPluggedIn || mEnableBatteryDefender) {
+ if (DEBUG_CHARGING_SPEED) {
+ powerIndication += ", " + (mChargingWattage / 1000) + " mW";
+ }
+ if (animate) {
+ animateText(mTopIndicationView, powerIndication);
+ } else {
+ mTopIndicationView.switchIndication(powerIndication, null);
+ }
+ hideIndication = !mBatteryPresent;
+ } else if (!TextUtils.isEmpty(trustManagedIndication)
+ && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId)
+ && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
+ mTopIndicationView.switchIndication(trustManagedIndication, null);
} else {
- mTextView.switchIndication(trustGrantedIndication);
- }
- } else if (!TextUtils.isEmpty(mAlignmentIndication)) {
- mTextView.switchIndication(mAlignmentIndication);
- isError = true;
- hideIndication = !mBatteryPresent;
- } else if (mPowerPluggedIn || mEnableBatteryDefender) {
- if (DEBUG_CHARGING_SPEED) {
- powerIndication += ", " + (mChargingWattage / 1000) + " mW";
+ mTopIndicationView.switchIndication(mRestingIndication, null);
}
- if (animate) {
- animateText(mTextView, powerIndication);
- } else {
- mTextView.switchIndication(powerIndication);
+
+ mTopIndicationView.setTextColor(
+ isError ? Utils.getColorError(mContext) : mInitialTextColorState);
+
+ if (hideIndication) {
+ mIndicationArea.setVisibility(GONE);
}
- hideIndication = !mBatteryPresent;
- } else if (!TextUtils.isEmpty(trustManagedIndication)
- && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId)
- && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
- mTextView.switchIndication(trustManagedIndication);
- } else {
- mTextView.switchIndication(mRestingIndication);
- }
- mTextView.setTextColor(isError ? Utils.getColorError(mContext)
- : mInitialTextColorState);
- if (hideIndication) {
- mIndicationArea.setVisibility(View.GONE);
}
}
@@ -510,7 +734,7 @@ public class KeyguardIndicationController implements StateListener,
@Override
public void onAnimationStart(Animator animation) {
- textView.switchIndication(indication);
+ textView.switchIndication(indication, null);
}
@Override
@@ -634,18 +858,6 @@ public class KeyguardIndicationController implements StateListener,
}
}
- public void setDozing(boolean dozing) {
- if (mDozing == dozing) {
- return;
- }
- mDozing = dozing;
- if (mHideTransientMessageOnScreenOff && mDozing) {
- hideTransientIndication();
- } else {
- updateIndication(false);
- }
- }
-
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("KeyguardIndicationController:");
pw.println(" mTransientTextIsError: " + mTransientTextIsError);
@@ -659,23 +871,10 @@ public class KeyguardIndicationController implements StateListener,
pw.println(" mDozing: " + mDozing);
pw.println(" mBatteryLevel: " + mBatteryLevel);
pw.println(" mBatteryPresent: " + mBatteryPresent);
- pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText()));
+ pw.println(" mTextView.getText(): " + (
+ mTopIndicationView == null ? null : mTopIndicationView.getText()));
pw.println(" computePowerIndication(): " + computePowerIndication());
- }
-
- @Override
- public void onStateChanged(int newState) {
- // don't care
- }
-
- @Override
- public void onDozingChanged(boolean isDozing) {
- setDozing(isDozing);
- }
-
- @Override
- public void onDozeAmountChanged(float linear, float eased) {
- mDisclosure.setAlpha((1 - linear) * mDisclosureMaxAlpha);
+ mRotateTextViewController.dump(fd, pw, args);
}
@Override
@@ -687,6 +886,11 @@ public class KeyguardIndicationController implements StateListener,
public static final int HIDE_DELAY_MS = 5000;
@Override
+ public void onLockScreenModeChanged(int mode) {
+ mLockScreenMode = mode;
+ }
+
+ @Override
public void onRefreshBatteryInfo(BatteryStatus status) {
boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
|| status.status == BatteryManager.BATTERY_STATUS_FULL;
@@ -857,11 +1061,39 @@ public class KeyguardIndicationController implements StateListener,
}
@Override
+ public void onLogoutEnabledChanged() {
+ if (mVisible) {
+ updateIndication(false);
+ }
+ }
+
+ @Override
public void onRequireUnlockForNfc() {
showTransientIndication(mContext.getString(R.string.require_unlock_for_nfc),
false /* isError */, false /* hideOnScreenOff */);
hideTransientIndicationDelayed(HIDE_DELAY_MS);
}
-
}
+
+ private StatusBarStateController.StateListener mStatusBarStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onStateChanged(int newState) {
+ setVisible(newState == StatusBarState.KEYGUARD);
+ }
+
+ @Override
+ public void onDozingChanged(boolean dozing) {
+ if (mDozing == dozing) {
+ return;
+ }
+ mDozing = dozing;
+
+ if (mHideTransientMessageOnScreenOff && mDozing) {
+ hideTransientIndication();
+ } else {
+ updateIndication(false);
+ }
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 2f0f90d318eb..c1feacaba440 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -11,14 +11,10 @@ import android.graphics.PorterDuffColorFilter
import android.graphics.PorterDuffXfermode
import android.graphics.RadialGradient
import android.graphics.Shader
-import android.os.SystemProperties
import android.util.AttributeSet
import android.view.View
import com.android.systemui.Interpolators
-val enableLightReveal =
- SystemProperties.getBoolean("persist.sysui.show_new_screen_on_transitions", false)
-
/**
* Provides methods to modify the various properties of a [LightRevealScrim] to reveal between 0% to
* 100% of the view(s) underneath the scrim.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index a816eccebe33..34b29ca9721b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -133,10 +133,11 @@ public class NotificationRemoteInputManager implements Dumpable {
protected Callback mCallback;
protected final ArrayList<NotificationLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
- private final RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
+ private final RemoteViews.InteractionHandler
+ mInteractionHandler = new RemoteViews.InteractionHandler() {
@Override
- public boolean onClickHandler(
+ public boolean onInteraction(
View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) {
mStatusBarLazy.get().wakeUpIfDozing(SystemClock.uptimeMillis(), view,
"NOTIFICATION_CLICK");
@@ -164,11 +165,11 @@ public class NotificationRemoteInputManager implements Dumpable {
Notification.Action action = getActionFromView(view, entry, pendingIntent);
return mCallback.handleRemoteViewClick(view, pendingIntent,
action == null ? false : action.isAuthenticationRequired(), () -> {
- Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
- options.second.setLaunchWindowingMode(
- WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
- mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent);
- return RemoteViews.startPendingIntent(view, pendingIntent, options);
+ Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
+ options.second.setLaunchWindowingMode(
+ WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+ mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent);
+ return RemoteViews.startPendingIntent(view, pendingIntent, options);
});
}
@@ -388,7 +389,28 @@ public class NotificationRemoteInputManager implements Dumpable {
*/
public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input,
PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo) {
+ return activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo,
+ null /* userMessageContent */, null /* authBypassCheck */);
+ }
+ /**
+ * Activates a given {@link RemoteInput}
+ *
+ * @param view The view of the action button or suggestion chip that was tapped.
+ * @param inputs The remote inputs that need to be sent to the app.
+ * @param input The remote input that needs to be activated.
+ * @param pendingIntent The pending intent to be sent to the app.
+ * @param editedSuggestionInfo The smart reply that should be inserted in the remote input, or
+ * {@code null} if the user is not editing a smart reply.
+ * @param userMessageContent User-entered text with which to initialize the remote input view.
+ * @param authBypassCheck Optional auth bypass check associated with this remote input
+ * activation. If {@code null}, we never bypass.
+ * @return Whether the {@link RemoteInput} was activated.
+ */
+ public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input,
+ PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo,
+ @Nullable String userMessageContent,
+ @Nullable AuthBypassPredicate authBypassCheck) {
ViewParent p = view.getParent();
RemoteInputView riv = null;
ExpandableNotificationRow row = null;
@@ -410,40 +432,9 @@ public class NotificationRemoteInputManager implements Dumpable {
row.setUserExpanded(true);
- if (!mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
- final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
-
- final boolean isLockedManagedProfile =
- mUserManager.getUserInfo(userId).isManagedProfile()
- && mKeyguardManager.isDeviceLocked(userId);
-
- final boolean isParentUserLocked;
- if (isLockedManagedProfile) {
- final UserInfo profileParent = mUserManager.getProfileParent(userId);
- isParentUserLocked = (profileParent != null)
- && mKeyguardManager.isDeviceLocked(profileParent.id);
- } else {
- isParentUserLocked = false;
- }
-
- if (mLockscreenUserManager.isLockscreenPublicMode(userId)
- || mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
- // If the parent user is no longer locked, and the user to which the remote input
- // is destined is a locked, managed profile, then onLockedWorkRemoteInput should be
- // called to unlock it.
- if (isLockedManagedProfile && !isParentUserLocked) {
- mCallback.onLockedWorkRemoteInput(userId, row, view);
- } else {
- // Even if we don't have security we should go through this flow, otherwise
- // we won't go to the shade.
- mCallback.onLockedRemoteInput(row, view);
- }
- return true;
- }
- if (isLockedManagedProfile) {
- mCallback.onLockedWorkRemoteInput(userId, row, view);
- return true;
- }
+ final boolean deferBouncer = authBypassCheck != null;
+ if (!deferBouncer && showBouncerForRemoteInput(view, pendingIntent, row)) {
+ return true;
}
if (riv != null && !riv.isAttachedToWindow()) {
@@ -461,7 +452,10 @@ public class NotificationRemoteInputManager implements Dumpable {
&& !row.getPrivateLayout().getExpandedChild().isShown()) {
// The expanded layout is selected, but it's not shown yet, let's wait on it to
// show before we do the animation.
- mCallback.onMakeExpandedVisibleForRemoteInput(row, view);
+ mCallback.onMakeExpandedVisibleForRemoteInput(row, view, deferBouncer, () -> {
+ activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo,
+ userMessageContent, authBypassCheck);
+ });
return true;
}
@@ -491,10 +485,62 @@ public class NotificationRemoteInputManager implements Dumpable {
riv.setPendingIntent(pendingIntent);
riv.setRemoteInput(inputs, input, editedSuggestionInfo);
riv.focusAnimated();
+ if (userMessageContent != null) {
+ riv.setEditTextContent(userMessageContent);
+ }
+ if (deferBouncer) {
+ final ExpandableNotificationRow finalRow = row;
+ riv.setBouncerChecker(() -> !authBypassCheck.canSendRemoteInputWithoutBouncer()
+ && showBouncerForRemoteInput(view, pendingIntent, finalRow));
+ }
return true;
}
+ private boolean showBouncerForRemoteInput(View view, PendingIntent pendingIntent,
+ ExpandableNotificationRow row) {
+ if (mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
+ return false;
+ }
+
+ final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
+
+ final boolean isLockedManagedProfile =
+ mUserManager.getUserInfo(userId).isManagedProfile()
+ && mKeyguardManager.isDeviceLocked(userId);
+
+ final boolean isParentUserLocked;
+ if (isLockedManagedProfile) {
+ final UserInfo profileParent = mUserManager.getProfileParent(userId);
+ isParentUserLocked = (profileParent != null)
+ && mKeyguardManager.isDeviceLocked(profileParent.id);
+ } else {
+ isParentUserLocked = false;
+ }
+
+ if ((mLockscreenUserManager.isLockscreenPublicMode(userId)
+ || mStatusBarStateController.getState() == StatusBarState.KEYGUARD)) {
+ // If the parent user is no longer locked, and the user to which the remote
+ // input
+ // is destined is a locked, managed profile, then onLockedWorkRemoteInput
+ // should be
+ // called to unlock it.
+ if (isLockedManagedProfile && !isParentUserLocked) {
+ mCallback.onLockedWorkRemoteInput(userId, row, view);
+ } else {
+ // Even if we don't have security we should go through this flow, otherwise
+ // we won't go to the shade.
+ mCallback.onLockedRemoteInput(row, view);
+ }
+ return true;
+ }
+ if (isLockedManagedProfile) {
+ mCallback.onLockedWorkRemoteInput(userId, row, view);
+ return true;
+ }
+ return false;
+ }
+
private RemoteInputView findRemoteInputView(View v) {
if (v == null) {
return null;
@@ -645,8 +691,8 @@ public class NotificationRemoteInputManager implements Dumpable {
*
* @return on-click handler
*/
- public RemoteViews.OnClickHandler getRemoteViewsOnClickHandler() {
- return mOnClickHandler;
+ public RemoteViews.InteractionHandler getRemoteViewsOnClickHandler() {
+ return mInteractionHandler;
}
@VisibleForTesting
@@ -807,8 +853,11 @@ public class NotificationRemoteInputManager implements Dumpable {
*
* @param row
* @param clickedView
+ * @param deferBouncer
+ * @param runnable
*/
- void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView);
+ void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView,
+ boolean deferBouncer, Runnable runnable);
/**
* Return whether or not remote input should be handled for this view.
@@ -845,4 +894,28 @@ public class NotificationRemoteInputManager implements Dumpable {
*/
boolean handleClick();
}
+
+ /**
+ * Predicate that is associated with a specific {@link #activateRemoteInput(View, RemoteInput[],
+ * RemoteInput, PendingIntent, EditedSuggestionInfo, String, AuthBypassPredicate)}
+ * invocation that determines whether or not the bouncer can be bypassed when sending the
+ * RemoteInput.
+ */
+ public interface AuthBypassPredicate {
+ /**
+ * Determines if the RemoteInput can be sent without the bouncer. Should be checked the
+ * same frame that the RemoteInput is to be sent.
+ */
+ boolean canSendRemoteInputWithoutBouncer();
+ }
+
+ /** Shows the bouncer if necessary */
+ public interface BouncerChecker {
+ /**
+ * Shows the bouncer if necessary in order to send a RemoteInput.
+ *
+ * @return {@code true} if the bouncer was shown, {@code false} otherwise
+ */
+ boolean showBouncerIfNecessary();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index e5c11bc692ba..3c7c4ec3fc82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -24,7 +24,6 @@ import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.graphics.Color;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.FeatureFlagUtils;
@@ -278,11 +277,7 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver,
@Override
public void setStaticDrawableColor(int color) {
ColorStateList list = ColorStateList.valueOf(color);
- float intensity = color == Color.WHITE ? 0 : 1;
- // We want the ability to change the theme from the one set by SignalDrawable in certain
- // surfaces. In this way, we can pass a theme to the view.
- mMobileDrawable.setTintList(
- ColorStateList.valueOf(mDualToneHandler.getSingleColor(intensity)));
+ mMobileDrawable.setTintList(list);
mIn.setImageTintList(list);
mOut.setImageTintList(list);
mMobileType.setImageTintList(list);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
index 0465ebff9aeb..4b4e51383b7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
@@ -18,23 +18,19 @@ package com.android.systemui.statusbar.notification;
import static android.service.notification.NotificationListenerService.Ranking;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.ENABLE_NAS_FEEDBACK;
+
import android.app.NotificationManager;
-import android.content.ContentResolver;
import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
import android.util.Pair;
-import androidx.annotation.Nullable;
-
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.util.DeviceConfigProxy;
import javax.inject.Inject;
@@ -45,10 +41,10 @@ import javax.inject.Inject;
* should show an indicator.
*/
@SysUISingleton
-public class AssistantFeedbackController extends ContentObserver {
- private final Uri FEEDBACK_URI
- = Settings.Global.getUriFor(Settings.Global.NOTIFICATION_FEEDBACK_ENABLED);
- private ContentResolver mResolver;
+public class AssistantFeedbackController {
+ private final Context mContext;
+ private final Handler mHandler;
+ private final DeviceConfigProxy mDeviceConfigProxy;
public static final int STATUS_UNCHANGED = 0;
public static final int STATUS_ALERTED = 1;
@@ -56,34 +52,39 @@ public class AssistantFeedbackController extends ContentObserver {
public static final int STATUS_PROMOTED = 3;
public static final int STATUS_DEMOTED = 4;
- private boolean mFeedbackEnabled;
+ private volatile boolean mFeedbackEnabled;
+
+ private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ if (properties.getKeyset().contains(ENABLE_NAS_FEEDBACK)) {
+ mFeedbackEnabled = properties.getBoolean(
+ ENABLE_NAS_FEEDBACK, false);
+ }
+ }
+ };
/** Injected constructor */
@Inject
- public AssistantFeedbackController(Context context) {
- super(new Handler(Looper.getMainLooper()));
- mResolver = context.getContentResolver();
- mResolver.registerContentObserver(FEEDBACK_URI, false, this, UserHandle.USER_ALL);
- update(null);
+ public AssistantFeedbackController(@Main Handler handler,
+ Context context, DeviceConfigProxy proxy) {
+ mHandler = handler;
+ mContext = context;
+ mDeviceConfigProxy = proxy;
+ mFeedbackEnabled = mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ ENABLE_NAS_FEEDBACK, false);
+ mDeviceConfigProxy.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ this::postToHandler, mPropertiesChangedListener);
}
- @Override
- public void onChange(boolean selfChange, @Nullable Uri uri, int flags) {
- update(uri);
- }
-
- @VisibleForTesting
- public void update(@Nullable Uri uri) {
- if (uri == null || FEEDBACK_URI.equals(uri)) {
- mFeedbackEnabled = Settings.Global.getInt(mResolver,
- Settings.Global.NOTIFICATION_FEEDBACK_ENABLED, 0)
- != 0;
- }
+ private void postToHandler(Runnable r) {
+ this.mHandler.post(r);
}
/**
- * Determines whether to show any user controls related to the assistant. This is based on the
- * settings flag {@link Settings.Global.NOTIFICATION_FEEDBACK_ENABLED}
+ * Determines whether to show any user controls related to the assistant based on the
+ * DeviceConfig flag value
*/
public boolean isFeedbackEnabled() {
return mFeedbackEnabled;
@@ -108,10 +109,10 @@ public class AssistantFeedbackController extends ContentObserver {
&& newImportance < NotificationManager.IMPORTANCE_DEFAULT) {
return STATUS_SILENCED;
} else if (oldImportance < newImportance
- || ranking.getRankingAdjustment() == ranking.RANKING_PROMOTED) {
+ || ranking.getRankingAdjustment() == Ranking.RANKING_PROMOTED) {
return STATUS_PROMOTED;
} else if (oldImportance > newImportance
- || ranking.getRankingAdjustment() == ranking.RANKING_DEMOTED) {
+ || ranking.getRankingAdjustment() == Ranking.RANKING_DEMOTED) {
return STATUS_DEMOTED;
} else {
return STATUS_UNCHANGED;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index e391250dc8fd..50cbbd5d4852 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -264,6 +264,20 @@ class NotificationWakeUpCoordinator @Inject constructor(
}
override fun onStateChanged(newState: Int) {
+ if (dozeParameters.shouldControlUnlockedScreenOff()) {
+ if (animatingScreenOff &&
+ state == StatusBarState.KEYGUARD &&
+ newState == StatusBarState.SHADE) {
+ // If we're animating the screen off and going from KEYGUARD back to SHADE, the
+ // animation was cancelled and we are unlocking. Override the doze amount to 0f (not
+ // dozing) so that the notifications are no longer hidden.
+ setDozeAmount(0f, 0f)
+ }
+
+ animatingScreenOff =
+ state == StatusBarState.SHADE && newState == StatusBarState.KEYGUARD
+ }
+
overrideDozeAmountIfBypass()
if (bypassController.bypassEnabled &&
newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED &&
@@ -273,13 +287,6 @@ class NotificationWakeUpCoordinator @Inject constructor(
setNotificationsVisible(visible = false, increaseSpeed = false, animate = true)
}
- // If we want to control the screen off animation, check whether we are going from SHADE to
- // KEYGUARD.
- if (dozeParameters.shouldControlUnlockedScreenOff()) {
- animatingScreenOff =
- state == StatusBarState.SHADE && newState == StatusBarState.KEYGUARD
- }
-
this.state = newState
}
@@ -386,8 +393,6 @@ class NotificationWakeUpCoordinator @Inject constructor(
override fun onDozingChanged(isDozing: Boolean) {
if (isDozing) {
setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
- } else {
- animatingScreenOff = false
}
}
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 a03fc136da61..1251b58171da 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
@@ -71,6 +71,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ContrastColorUtil;
import com.android.internal.widget.CachingIconView;
+import com.android.internal.widget.CallLayout;
import com.android.internal.widget.MessagingLayout;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
@@ -103,7 +104,7 @@ import com.android.systemui.statusbar.notification.stack.SwipeableView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions;
+import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.wmshell.BubblesManager;
import java.io.FileDescriptor;
@@ -165,6 +166,7 @@ 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;
@@ -645,8 +647,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
private void updateLimitsForView(NotificationContentView layout) {
- boolean customView = layout.getContractedChild() != null
- && layout.getContractedChild().getId()
+ View contractedView = layout.getContractedChild();
+ boolean customView = contractedView != null
+ && contractedView.getId()
!= com.android.internal.R.id.status_bar_latest_event_content;
boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
boolean beforeP = mEntry.targetSdk < Build.VERSION_CODES.P;
@@ -661,7 +664,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
View expandedView = layout.getExpandedChild();
boolean isMediaLayout = expandedView != null
&& expandedView.findViewById(com.android.internal.R.id.media_actions) != null;
- boolean isMessagingLayout = layout.getContractedChild() instanceof MessagingLayout;
+ boolean isMessagingLayout = contractedView instanceof MessagingLayout;
+ boolean isCallLayout = contractedView instanceof CallLayout;
boolean showCompactMediaSeekbar = mMediaManager.getShowCompactMediaSeekbar();
if (customView && beforeS && !mIsSummaryWithChildren) {
@@ -684,6 +688,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
// make sure we don't crop them terribly. We actually need to revisit this and give
// them a headerless design, then remove this hack.
smallHeight = mMaxSmallHeightLarge;
+ } else if (isCallLayout) {
+ smallHeight = mMaxCallHeight;
} else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
smallHeight = mMaxSmallHeightLarge;
} else {
@@ -1645,6 +1651,8 @@ 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,
@@ -3188,8 +3196,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
/**
* Returns the Smart Suggestions backing the smart suggestion buttons in the notification.
*/
- public SmartRepliesAndActions getExistingSmartRepliesAndActions() {
- return mPrivateLayout.getCurrentSmartRepliesAndActions();
+ public InflatedSmartReplyState getExistingSmartReplyState() {
+ return mPrivateLayout.getCurrentSmartReplyState();
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
index 35f35615e1c1..14683eca5080 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
@@ -16,8 +16,14 @@
package com.android.systemui.statusbar.notification.row;
+import static android.service.notification.NotificationAssistantService.FEEDBACK_RATING;
+
+import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_ALERTED;
+import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_DEMOTED;
+import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_PROMOTED;
+import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_SILENCED;
+
import android.annotation.SuppressLint;
-import android.app.Notification;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -36,20 +42,17 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsContent {
private static final String TAG = "FeedbackInfo";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final String FEEDBACK_KEY = "feedback_key";
private NotificationGuts mGutsContainer;
private NotificationListenerService.Ranking mRanking;
@@ -146,15 +149,15 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC
sb.append(String.format(
"[DEBUG]: oldImportance=%d, newImportance=%d, ranking=%d\n\n",
mRanking.getChannel().getImportance(), mRanking.getImportance(),
- mRanking.getRankingAdjustment()));
+ mRanking.getRankingScore()));
}
- if (status == mFeedbackController.STATUS_ALERTED) {
+ if (status == STATUS_ALERTED) {
sb.append(mContext.getText(R.string.feedback_alerted));
- } else if (status == mFeedbackController.STATUS_SILENCED) {
+ } else if (status == STATUS_SILENCED) {
sb.append(mContext.getText(R.string.feedback_silenced));
- } else if (status == mFeedbackController.STATUS_PROMOTED) {
+ } else if (status == STATUS_PROMOTED) {
sb.append(mContext.getText(R.string.feedback_promoted));
- } else if (status == mFeedbackController.STATUS_DEMOTED) {
+ } else if (status == STATUS_DEMOTED) {
sb.append(mContext.getText(R.string.feedback_demoted));
}
sb.append(" ");
@@ -182,7 +185,8 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC
private void handleFeedback(boolean positive) {
Bundle feedback = new Bundle();
- feedback.putBoolean(FEEDBACK_KEY, positive);
+ feedback.putInt(FEEDBACK_RATING, positive ? 1 : -1);
+
sendFeedbackToAssistant(feedback);
}
@@ -191,19 +195,8 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC
return;
}
- //TODO(b/154257994): remove this when feedback apis are in place
- final int count = mNotificationEntryManager.getActiveNotificationsCount();
- final int rank = mEntry.getRanking().getRank();
- NotificationVisibility.NotificationLocation location =
- NotificationLogger.getNotificationLocation(mEntry);
- final NotificationVisibility nv = NotificationVisibility.obtain(
- mEntry.getKey(), rank, count, true, location);
- Notification.Action action = new Notification.Action.Builder(null, null,
- null)
- .addExtras(feedback)
- .build();
try {
- mStatusBarService.onNotificationActionClick(mRanking.getKey(), -1, action, nv, true);
+ mStatusBarService.onNotificationFeedbackReceived(mRanking.getKey(), feedback);
} catch (RemoteException e) {
if (DEBUG) {
Log.e(TAG, "Failed to send feedback to assistant", e);
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 c2c4590fa6cb..fdd8f347c248 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
@@ -48,9 +48,9 @@ import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.InflatedSmartReplies;
-import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions;
-import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflater;
+import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
+import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
+import com.android.systemui.statusbar.policy.SmartReplyStateInflater;
import com.android.systemui.util.Assert;
import java.util.HashMap;
@@ -74,7 +74,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final NotifRemoteViewCache mRemoteViewCache;
private final ConversationNotificationProcessor mConversationProcessor;
private final Executor mBgExecutor;
- private final SmartRepliesAndActionsInflater mSmartRepliesAndActionsInflater;
+ private final SmartReplyStateInflater mSmartReplyStateInflater;
@Inject
NotificationContentInflater(
@@ -83,13 +83,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder
ConversationNotificationProcessor conversationProcessor,
MediaFeatureFlag mediaFeatureFlag,
@Background Executor bgExecutor,
- SmartRepliesAndActionsInflater smartRepliesInflater) {
+ SmartReplyStateInflater smartRepliesInflater) {
mRemoteViewCache = remoteViewCache;
mRemoteInputManager = remoteInputManager;
mConversationProcessor = conversationProcessor;
mIsMediaInQS = mediaFeatureFlag.getEnabled();
mBgExecutor = bgExecutor;
- mSmartRepliesAndActionsInflater = smartRepliesInflater;
+ mSmartReplyStateInflater = smartRepliesInflater;
}
@Override
@@ -133,7 +133,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
callback,
mRemoteInputManager.getRemoteViewsOnClickHandler(),
mIsMediaInQS,
- mSmartRepliesAndActionsInflater);
+ mSmartReplyStateInflater);
if (mInflateSynchronously) {
task.onPostExecute(task.doInBackground());
} else {
@@ -150,7 +150,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
@InflationFlag int reInflateFlags,
Notification.Builder builder,
Context packageContext,
- SmartRepliesAndActionsInflater smartRepliesInflater) {
+ SmartReplyStateInflater smartRepliesInflater) {
InflationProgress result = createRemoteViews(reInflateFlags,
builder,
bindParams.isLowPriority,
@@ -160,7 +160,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
result = inflateSmartReplyViews(result, reInflateFlags, entry,
row.getContext(), packageContext,
- row.getExistingSmartRepliesAndActions(),
+ row.getExistingSmartReplyState(),
smartRepliesInflater);
apply(
@@ -268,15 +268,26 @@ public class NotificationContentInflater implements NotificationRowContentBinder
NotificationEntry entry,
Context context,
Context packageContext,
- SmartRepliesAndActions previousSmartRepliesAndActions,
- SmartRepliesAndActionsInflater inflater) {
- if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0 && result.newExpandedView != null) {
- result.expandedInflatedSmartReplies = inflater.inflateSmartReplies(
- context, packageContext, entry, previousSmartRepliesAndActions);
- }
- if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0 && result.newHeadsUpView != null) {
- result.headsUpInflatedSmartReplies = inflater.inflateSmartReplies(
- context, packageContext, entry, previousSmartRepliesAndActions);
+ InflatedSmartReplyState previousSmartReplyState,
+ SmartReplyStateInflater inflater) {
+ boolean inflateContracted = (reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0
+ && result.newContentView != null;
+ boolean inflateExpanded = (reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0
+ && result.newExpandedView != null;
+ boolean inflateHeadsUp = (reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0
+ && result.newHeadsUpView != null;
+ if (inflateContracted || inflateExpanded || inflateHeadsUp) {
+ result.inflatedSmartReplyState = inflater.inflateSmartReplyState(entry);
+ }
+ if (inflateExpanded) {
+ result.expandedInflatedSmartReplies = inflater.inflateSmartReplyViewHolder(
+ context, packageContext, entry, previousSmartReplyState,
+ result.inflatedSmartReplyState);
+ }
+ if (inflateHeadsUp) {
+ result.headsUpInflatedSmartReplies = inflater.inflateSmartReplyViewHolder(
+ context, packageContext, entry, previousSmartReplyState,
+ result.inflatedSmartReplyState);
}
return result;
}
@@ -317,7 +328,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
NotifRemoteViewCache remoteViewCache,
NotificationEntry entry,
ExpandableNotificationRow row,
- RemoteViews.OnClickHandler remoteViewClickHandler,
+ RemoteViews.InteractionHandler remoteViewClickHandler,
@Nullable InflationCallback callback) {
NotificationContentView privateLayout = row.getPrivateLayout();
NotificationContentView publicLayout = row.getPublicLayout();
@@ -442,7 +453,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
final NotificationEntry entry,
final ExpandableNotificationRow row,
boolean isNewView,
- RemoteViews.OnClickHandler remoteViewClickHandler,
+ RemoteViews.InteractionHandler remoteViewClickHandler,
@Nullable final InflationCallback callback,
NotificationContentView parentLayout,
View existingView,
@@ -566,6 +577,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
NotificationContentView privateLayout = row.getPrivateLayout();
NotificationContentView publicLayout = row.getPublicLayout();
if (runningInflations.isEmpty()) {
+ boolean setRepliesAndActions = true;
if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
if (result.inflatedContentView != null) {
// New view case
@@ -578,6 +590,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED,
result.newContentView);
}
+ setRepliesAndActions = true;
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
@@ -599,6 +612,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
privateLayout.setExpandedInflatedSmartReplies(null);
}
row.setExpandable(result.newExpandedView != null);
+ setRepliesAndActions = true;
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
@@ -619,6 +633,10 @@ public class NotificationContentInflater implements NotificationRowContentBinder
} else {
privateLayout.setHeadsUpInflatedSmartReplies(null);
}
+ setRepliesAndActions = true;
+ }
+ if (setRepliesAndActions) {
+ privateLayout.setInflatedSmartReplyState(result.inflatedSmartReplyState);
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
@@ -705,11 +723,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final Executor mBgExecutor;
private ExpandableNotificationRow mRow;
private Exception mError;
- private RemoteViews.OnClickHandler mRemoteViewClickHandler;
+ private RemoteViews.InteractionHandler mRemoteViewClickHandler;
private CancellationSignal mCancellationSignal;
private final ConversationNotificationProcessor mConversationProcessor;
private final boolean mIsMediaInQS;
- private final SmartRepliesAndActionsInflater mSmartRepliesInflater;
+ private final SmartReplyStateInflater mSmartRepliesInflater;
private AsyncInflationTask(
Executor bgExecutor,
@@ -723,9 +741,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder
boolean usesIncreasedHeight,
boolean usesIncreasedHeadsUpHeight,
InflationCallback callback,
- RemoteViews.OnClickHandler remoteViewClickHandler,
+ RemoteViews.InteractionHandler remoteViewClickHandler,
boolean isMediaFlagEnabled,
- SmartRepliesAndActionsInflater smartRepliesInflater) {
+ SmartReplyStateInflater smartRepliesInflater) {
mEntry = entry;
mRow = row;
mBgExecutor = bgExecutor;
@@ -776,15 +794,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder
InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight,
mUsesIncreasedHeadsUpHeight, packageContext);
- SmartRepliesAndActions repliesAndActions =
- mRow.getExistingSmartRepliesAndActions();
+ InflatedSmartReplyState previousSmartReplyState = mRow.getExistingSmartReplyState();
return inflateSmartReplyViews(
inflationProgress,
mReInflateFlags,
mEntry,
mContext,
packageContext,
- repliesAndActions,
+ previousSmartReplyState,
mSmartRepliesInflater);
} catch (Exception e) {
mError = e;
@@ -879,8 +896,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private CharSequence headsUpStatusBarText;
private CharSequence headsUpStatusBarTextPublic;
- private InflatedSmartReplies expandedInflatedSmartReplies;
- private InflatedSmartReplies headsUpInflatedSmartReplies;
+ private InflatedSmartReplyState inflatedSmartReplyState;
+ private InflatedSmartReplyViewHolder expandedInflatedSmartReplies;
+ private InflatedSmartReplyViewHolder headsUpInflatedSmartReplies;
}
@VisibleForTesting
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 d2774df330da..f427ba958b8f 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
@@ -58,16 +58,18 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
-import com.android.systemui.statusbar.policy.InflatedSmartReplies;
-import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions;
+import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
+import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
import com.android.systemui.statusbar.policy.RemoteInputView;
-import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflaterKt;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.statusbar.policy.SmartReplyStateInflaterKt;
import com.android.systemui.statusbar.policy.SmartReplyView;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* A frame layout containing the actual payload of the notification, including the contracted,
@@ -106,9 +108,9 @@ public class NotificationContentView extends FrameLayout {
private SmartReplyView mExpandedSmartReplyView;
private SmartReplyView mHeadsUpSmartReplyView;
private SmartReplyController mSmartReplyController;
- private InflatedSmartReplies mExpandedInflatedSmartReplies;
- private InflatedSmartReplies mHeadsUpInflatedSmartReplies;
- private SmartRepliesAndActions mCurrentSmartRepliesAndActions;
+ private InflatedSmartReplyViewHolder mExpandedInflatedSmartReplies;
+ private InflatedSmartReplyViewHolder mHeadsUpInflatedSmartReplies;
+ private InflatedSmartReplyState mCurrentSmartReplyState;
private NotificationViewWrapper mContractedWrapper;
private NotificationViewWrapper mExpandedWrapper;
@@ -1189,29 +1191,19 @@ public class NotificationContentView extends FrameLayout {
applyRemoteInput(entry, hasFreeformRemoteInput(entry));
- if (mExpandedInflatedSmartReplies == null && mHeadsUpInflatedSmartReplies == null) {
+ if (mCurrentSmartReplyState == null) {
if (DEBUG) {
- Log.d(TAG, "Both expanded, and heads-up InflatedSmartReplies are null, "
- + "don't add smart replies.");
+ Log.d(TAG, "InflatedSmartReplies are null, don't add smart replies.");
}
return;
}
- // The inflated smart-reply objects for the expanded view and the heads-up view both contain
- // the same SmartRepliesAndActions to avoid discrepancies between the two views. We here
- // reuse that object for our local SmartRepliesAndActions to avoid discrepancies between
- // this class and the InflatedSmartReplies classes.
- mCurrentSmartRepliesAndActions = mExpandedInflatedSmartReplies != null
- ? mExpandedInflatedSmartReplies.getSmartRepliesAndActions()
- : mHeadsUpInflatedSmartReplies.getSmartRepliesAndActions();
if (DEBUG) {
Log.d(TAG, String.format("Adding suggestions for %s, %d actions, and %d replies.",
entry.getSbn().getKey(),
- mCurrentSmartRepliesAndActions.smartActions == null ? 0 :
- mCurrentSmartRepliesAndActions.smartActions.actions.size(),
- mCurrentSmartRepliesAndActions.smartReplies == null ? 0 :
- mCurrentSmartRepliesAndActions.smartReplies.choices.size()));
+ mCurrentSmartReplyState.getSmartActionsList().size(),
+ mCurrentSmartReplyState.getSmartRepliesList().size()));
}
- applySmartReplyView(mCurrentSmartRepliesAndActions, entry);
+ applySmartReplyView(mCurrentSmartReplyState, entry);
}
private void applyRemoteInput(NotificationEntry entry, boolean hasFreeformRemoteInput) {
@@ -1415,41 +1407,74 @@ public class NotificationContentView extends FrameLayout {
}
private void applySmartReplyView(
- SmartRepliesAndActions smartRepliesAndActions,
+ InflatedSmartReplyState state,
NotificationEntry entry) {
+ if (mContractedChild != null) {
+ applyExternalSmartReplyState(mContractedChild, state);
+ }
if (mExpandedChild != null) {
- mExpandedSmartReplyView = applySmartReplyView(mExpandedChild, smartRepliesAndActions,
+ applyExternalSmartReplyState(mExpandedChild, state);
+ mExpandedSmartReplyView = applySmartReplyView(mExpandedChild, state,
entry, mExpandedInflatedSmartReplies);
if (mExpandedSmartReplyView != null) {
- if (smartRepliesAndActions.smartReplies != null
- || smartRepliesAndActions.smartActions != null) {
- int numSmartReplies = smartRepliesAndActions.smartReplies == null
- ? 0 : smartRepliesAndActions.smartReplies.choices.size();
- int numSmartActions = smartRepliesAndActions.smartActions == null
- ? 0 : smartRepliesAndActions.smartActions.actions.size();
- boolean fromAssistant = smartRepliesAndActions.smartReplies == null
- ? smartRepliesAndActions.smartActions.fromAssistant
- : smartRepliesAndActions.smartReplies.fromAssistant;
- boolean editBeforeSending = smartRepliesAndActions.smartReplies != null
+ SmartReplyView.SmartReplies smartReplies = state.getSmartReplies();
+ SmartReplyView.SmartActions smartActions = state.getSmartActions();
+ if (smartReplies != null || smartActions != null) {
+ int numSmartReplies = smartReplies == null ? 0 : smartReplies.choices.size();
+ int numSmartActions = smartActions == null ? 0 : smartActions.actions.size();
+ boolean fromAssistant = smartReplies == null
+ ? smartActions.fromAssistant
+ : smartReplies.fromAssistant;
+ boolean editBeforeSending = smartReplies != null
&& mSmartReplyConstants.getEffectiveEditChoicesBeforeSending(
- smartRepliesAndActions.smartReplies.remoteInput
- .getEditChoicesBeforeSending());
+ smartReplies.remoteInput.getEditChoicesBeforeSending());
mSmartReplyController.smartSuggestionsAdded(entry, numSmartReplies,
numSmartActions, fromAssistant, editBeforeSending);
}
}
}
- if (mHeadsUpChild != null && mSmartReplyConstants.getShowInHeadsUp()) {
- mHeadsUpSmartReplyView = applySmartReplyView(mHeadsUpChild, smartRepliesAndActions,
- entry, mHeadsUpInflatedSmartReplies);
+ if (mHeadsUpChild != null) {
+ applyExternalSmartReplyState(mHeadsUpChild, state);
+ if (mSmartReplyConstants.getShowInHeadsUp()) {
+ mHeadsUpSmartReplyView = applySmartReplyView(mHeadsUpChild, state,
+ entry, mHeadsUpInflatedSmartReplies);
+ }
+ }
+ }
+
+ private void applyExternalSmartReplyState(View view, InflatedSmartReplyState state) {
+ boolean hasPhishingAlert = state != null && state.getHasPhishingAction();
+ View phishingAlertIcon = view.findViewById(com.android.internal.R.id.phishing_alert);
+ if (phishingAlertIcon != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Setting 'phishing_alert' view visible=" + hasPhishingAlert + ".");
+ }
+ phishingAlertIcon.setVisibility(hasPhishingAlert ? View.VISIBLE : View.GONE);
+ }
+ List<Integer> suppressedActionIndices = state != null
+ ? state.getSuppressedActionIndices()
+ : Collections.emptyList();
+ ViewGroup actionsList = view.findViewById(com.android.internal.R.id.actions);
+ if (actionsList != null) {
+ if (DEBUG && !suppressedActionIndices.isEmpty()) {
+ Log.d(TAG, "Suppressing actions with indices: " + suppressedActionIndices);
+ }
+ for (int i = 0; i < actionsList.getChildCount(); i++) {
+ View actionBtn = actionsList.getChildAt(i);
+ Object actionIndex =
+ actionBtn.getTag(com.android.internal.R.id.notification_action_index_tag);
+ boolean suppressAction = actionIndex instanceof Integer
+ && suppressedActionIndices.contains(actionIndex);
+ actionBtn.setVisibility(suppressAction ? View.GONE : View.VISIBLE);
+ }
}
}
@Nullable
private SmartReplyView applySmartReplyView(View view,
- SmartRepliesAndActions smartRepliesAndActions,
- NotificationEntry entry, InflatedSmartReplies inflatedSmartReplyView) {
+ InflatedSmartReplyState smartReplyState,
+ NotificationEntry entry, InflatedSmartReplyViewHolder inflatedSmartReplyViewHolder) {
View smartReplyContainerCandidate = view.findViewById(
com.android.internal.R.id.smart_reply_container);
if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
@@ -1457,8 +1482,7 @@ public class NotificationContentView extends FrameLayout {
}
LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
- if (!SmartRepliesAndActionsInflaterKt
- .shouldShowSmartReplyView(entry, smartRepliesAndActions)) {
+ if (!SmartReplyStateInflaterKt.shouldShowSmartReplyView(entry, smartReplyState)) {
smartReplyContainer.setVisibility(View.GONE);
return null;
}
@@ -1471,15 +1495,15 @@ public class NotificationContentView extends FrameLayout {
smartReplyContainer.removeAllViews();
}
if (smartReplyContainer.getChildCount() == 0
- && inflatedSmartReplyView != null
- && inflatedSmartReplyView.getSmartReplyView() != null) {
- smartReplyView = inflatedSmartReplyView.getSmartReplyView();
+ && inflatedSmartReplyViewHolder != null
+ && inflatedSmartReplyViewHolder.getSmartReplyView() != null) {
+ smartReplyView = inflatedSmartReplyViewHolder.getSmartReplyView();
smartReplyContainer.addView(smartReplyView);
}
if (smartReplyView != null) {
smartReplyView.resetSmartSuggestions(smartReplyContainer);
smartReplyView.addPreInflatedButtons(
- inflatedSmartReplyView.getSmartSuggestionButtons());
+ inflatedSmartReplyViewHolder.getSmartSuggestionButtons());
// Ensure the colors of the smart suggestion buttons are up-to-date.
smartReplyView.setBackgroundTintColor(entry.getRow().getCurrentBackgroundTint());
smartReplyContainer.setVisibility(View.VISIBLE);
@@ -1495,7 +1519,7 @@ public class NotificationContentView extends FrameLayout {
* {@link SmartReplyView} related to the expanded notification state is cleared.
*/
public void setExpandedInflatedSmartReplies(
- @Nullable InflatedSmartReplies inflatedSmartReplies) {
+ @Nullable InflatedSmartReplyViewHolder inflatedSmartReplies) {
mExpandedInflatedSmartReplies = inflatedSmartReplies;
if (inflatedSmartReplies == null) {
mExpandedSmartReplyView = null;
@@ -1510,7 +1534,7 @@ public class NotificationContentView extends FrameLayout {
* {@link SmartReplyView} related to the heads-up notification state is cleared.
*/
public void setHeadsUpInflatedSmartReplies(
- @Nullable InflatedSmartReplies inflatedSmartReplies) {
+ @Nullable InflatedSmartReplyViewHolder inflatedSmartReplies) {
mHeadsUpInflatedSmartReplies = inflatedSmartReplies;
if (inflatedSmartReplies == null) {
mHeadsUpSmartReplyView = null;
@@ -1518,10 +1542,21 @@ public class NotificationContentView extends FrameLayout {
}
/**
+ * Set pre-inflated replies and actions for the notification.
+ * This can be relevant to any state of the notification, even contracted, because smart actions
+ * may cause a phishing alert to be made visible.
+ * @param smartReplyState the pre-inflated list of replies and actions
+ */
+ public void setInflatedSmartReplyState(
+ @NonNull InflatedSmartReplyState smartReplyState) {
+ mCurrentSmartReplyState = smartReplyState;
+ }
+
+ /**
* Returns the smart replies and actions currently shown in the notification.
*/
- @Nullable public SmartRepliesAndActions getCurrentSmartRepliesAndActions() {
- return mCurrentSmartRepliesAndActions;
+ @Nullable public InflatedSmartReplyState getCurrentSmartReplyState() {
+ return mCurrentSmartReplyState;
}
public void closeRemoteInput() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index 1d36ad3f13d5..7248bcef621c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -40,8 +40,6 @@ public class NotificationBigPictureTemplateViewWrapper extends NotificationTempl
public void onContentUpdated(ExpandableNotificationRow row) {
super.onContentUpdated(row);
updateImageTag(row.getEntry().getSbn());
- // Round the corners of the big picture content
- mView.findViewById(com.android.internal.R.id.big_picture).setClipToOutline(true);
}
private void updateImageTag(StatusBarNotification notification) {
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
new file mode 100644
index 000000000000..4541ebf4c4f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.statusbar.notification.row.wrapper
+
+import android.content.Context
+import android.view.View
+import com.android.internal.widget.CachingIconView
+import com.android.internal.widget.CallLayout
+import com.android.systemui.R
+import com.android.systemui.statusbar.notification.NotificationUtils
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+
+/**
+ * Wraps a notification containing a call template
+ */
+class NotificationCallTemplateViewWrapper constructor(
+ ctx: Context,
+ view: View,
+ row: ExpandableNotificationRow
+) : NotificationTemplateViewWrapper(ctx, view, row) {
+
+ private val minHeightWithActions: Int =
+ NotificationUtils.getFontScaledHeight(ctx, R.dimen.call_notification_full_height)
+ private val callLayout: CallLayout = view as CallLayout
+
+ private lateinit var conversationIconView: CachingIconView
+ private lateinit var conversationBadgeBg: View
+ private lateinit var expandBtn: View
+ private lateinit var appName: View
+ private lateinit var conversationTitleView: View
+
+ private fun resolveViews() {
+ with(callLayout) {
+ conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
+ conversationBadgeBg =
+ requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
+ expandBtn = requireViewById(com.android.internal.R.id.expand_button)
+ appName = requireViewById(com.android.internal.R.id.app_name_text)
+ conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text)
+ }
+ }
+
+ override fun onContentUpdated(row: ExpandableNotificationRow) {
+ // Reinspect the notification. Before the super call, because the super call also updates
+ // the transformation types and we need to have our values set by then.
+ resolveViews()
+ super.onContentUpdated(row)
+ }
+
+ override fun updateTransformedTypes() {
+ // This also clears the existing types
+ super.updateTransformedTypes()
+ addTransformedViews(
+ appName,
+ conversationTitleView
+ )
+ addViewsTransformingToSimilar(
+ conversationIconView,
+ conversationBadgeBg,
+ expandBtn
+ )
+ }
+
+ override fun disallowSingleClick(x: Float, y: Float): Boolean {
+ val isOnExpandButton = expandBtn.visibility == View.VISIBLE &&
+ isOnView(expandBtn, x, y)
+ return isOnExpandButton || super.disallowSingleClick(x, y)
+ }
+
+ override fun getMinLayoutHeight(): Int = minHeightWithActions
+}
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 c49f6cbda8ac..905bccfa6cdf 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
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.row.wrapper
import android.content.Context
import android.view.View
-import android.view.View.GONE
import android.view.ViewGroup
import com.android.internal.widget.CachingIconView
import com.android.internal.widget.ConversationLayout
@@ -48,8 +47,8 @@ class NotificationConversationTemplateViewWrapper constructor(
private lateinit var conversationIconView: CachingIconView
private lateinit var conversationBadgeBg: View
- private lateinit var expandButton: View
- private lateinit var expandButtonContainer: View
+ private lateinit var expandBtn: View
+ private lateinit var expandBtnContainer: View
private lateinit var imageMessageContainer: ViewGroup
private lateinit var messagingLinearLayout: MessagingLinearLayout
private lateinit var conversationTitleView: View
@@ -66,9 +65,8 @@ class NotificationConversationTemplateViewWrapper constructor(
conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
conversationBadgeBg =
requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
- expandButton = requireViewById(com.android.internal.R.id.expand_button)
- expandButtonContainer =
- requireViewById(com.android.internal.R.id.expand_button_container)
+ expandBtn = requireViewById(com.android.internal.R.id.expand_button)
+ expandBtnContainer = requireViewById(com.android.internal.R.id.expand_button_container)
importanceRing = requireViewById(com.android.internal.R.id.conversation_icon_badge_ring)
appName = requireViewById(com.android.internal.R.id.app_name_text)
conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text)
@@ -126,7 +124,7 @@ class NotificationConversationTemplateViewWrapper constructor(
addViewsTransformingToSimilar(
conversationIconView,
conversationBadgeBg,
- expandButton,
+ expandBtn,
importanceRing,
facePileTop,
facePileBottom,
@@ -134,11 +132,9 @@ class NotificationConversationTemplateViewWrapper constructor(
)
}
- override fun getExpandButton() = super.getExpandButton()
-
override fun setShelfIconVisible(visible: Boolean) {
if (conversationLayout.isImportantConversation) {
- if (conversationIconView.visibility != GONE) {
+ if (conversationIconView.visibility != View.GONE) {
conversationIconView.isForceHidden = visible
// We don't want the small icon to be hidden by the extended wrapper, as force
// hiding the conversationIcon will already do that via its listener.
@@ -152,7 +148,7 @@ class NotificationConversationTemplateViewWrapper constructor(
override fun getShelfTransformationTarget(): View? =
if (conversationLayout.isImportantConversation)
- if (conversationIconView.visibility != GONE)
+ if (conversationIconView.visibility != View.GONE)
conversationIconView
else
// A notification with a fallback icon was set to important. Currently
@@ -169,8 +165,8 @@ class NotificationConversationTemplateViewWrapper constructor(
conversationLayout.updateExpandability(expandable, onClickListener)
override fun disallowSingleClick(x: Float, y: Float): Boolean {
- val isOnExpandButton = expandButtonContainer.visibility == View.VISIBLE &&
- isOnView(expandButtonContainer, x, y)
+ val isOnExpandButton = expandBtnContainer.visibility == View.VISIBLE &&
+ isOnView(expandBtnContainer, x, y)
return isOnExpandButton || super.disallowSingleClick(x, y)
}
@@ -179,10 +175,4 @@ class NotificationConversationTemplateViewWrapper constructor(
minHeightWithActions
else
super.getMinLayoutHeight()
-
- private fun addTransformedViews(vararg vs: View?) =
- vs.forEach { view -> view?.let(mTransformationHelper::addTransformedView) }
-
- private fun addViewsTransformingToSimilar(vararg vs: View?) =
- vs.forEach { view -> view?.let(mTransformationHelper::addViewTransformingToSimilar) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
index 79648457c521..301c3726793a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
@@ -29,6 +29,30 @@ public class NotificationDecoratedCustomViewWrapper extends NotificationTemplate
private View mWrappedView = null;
+ /**
+ * Determines if the standard template contains a custom view, injected by Notification.Builder
+ */
+ public static boolean hasCustomView(View v) {
+ return getWrappedCustomView(v) != null;
+ }
+
+ private static View getWrappedCustomView(View view) {
+ if (view == null) {
+ return null;
+ }
+ ViewGroup container = view.findViewById(
+ com.android.internal.R.id.notification_main_column);
+ if (container == null) {
+ return null;
+ }
+ Integer childIndex = (Integer) container.getTag(
+ com.android.internal.R.id.notification_custom_view_index_tag);
+ if (childIndex == null || childIndex == -1) {
+ return null;
+ }
+ return container.getChildAt(childIndex);
+ }
+
protected NotificationDecoratedCustomViewWrapper(Context ctx, View view,
ExpandableNotificationRow row) {
super(ctx, view, row);
@@ -36,13 +60,7 @@ public class NotificationDecoratedCustomViewWrapper extends NotificationTemplate
@Override
public void onContentUpdated(ExpandableNotificationRow row) {
- ViewGroup container = mView.findViewById(
- com.android.internal.R.id.notification_main_column);
- Integer childIndex = (Integer) container.getTag(
- com.android.internal.R.id.notification_custom_view_index_tag);
- if (childIndex != null && childIndex != -1) {
- mWrappedView = container.getChildAt(childIndex);
- }
+ mWrappedView = getWrappedCustomView(mView);
// Custom views will most likely use just white or black as their text color.
// We need to scan through and replace these colors by Material NEXT colors.
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 97201f5c9a34..eb79e3c8a69a 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
@@ -36,7 +36,6 @@ import android.widget.TextView;
import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.NotificationExpandButton;
-import com.android.settingslib.Utils;
import com.android.systemui.Interpolators;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
@@ -60,6 +59,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
private CachingIconView mIcon;
private NotificationExpandButton mExpandButton;
private View mAltExpandTarget;
+ private View mIconContainer;
protected NotificationHeaderView mNotificationHeader;
protected NotificationTopLineView mNotificationTopLine;
private TextView mHeaderText;
@@ -67,8 +67,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
private ImageView mWorkProfileImage;
private View mAudiblyAlertedIcon;
private View mFeedbackIcon;
- private View mLeftIcon;
- private View mRightIcon;
private boolean mIsLowPriority;
private boolean mTransformLowPriorityTitle;
@@ -112,8 +110,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
mAppNameText = mView.findViewById(com.android.internal.R.id.app_name_text);
mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
mAltExpandTarget = mView.findViewById(com.android.internal.R.id.alternate_expand_target);
- mLeftIcon = mView.findViewById(com.android.internal.R.id.left_icon);
- mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon);
+ mIconContainer = mView.findViewById(com.android.internal.R.id.conversation_icon_container);
mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
mNotificationTopLine = mView.findViewById(com.android.internal.R.id.notification_top_line);
@@ -159,12 +156,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
updateCropToPaddingForImageViews();
Notification notification = row.getEntry().getSbn().getNotification();
mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
- if (mLeftIcon != null) {
- mLeftIcon.setClipToOutline(true);
- }
- if (mRightIcon != null) {
- mRightIcon.setClipToOutline(true);
- }
// We need to reset all views that are no longer transforming in case a view was previously
// transformed, but now we decided to transform its container instead.
@@ -203,11 +194,8 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
public void clearConversationSkin() {
if (mAppNameText != null) {
final ColorStateList colors = mAppNameText.getTextColors();
- final int textAppearance = Utils.getThemeAttr(
- mAppNameText.getContext(),
- com.android.internal.R.attr.notificationHeaderTextAppearance,
+ mAppNameText.setTextAppearance(
com.android.internal.R.style.TextAppearance_DeviceDefault_Notification_Info);
- mAppNameText.setTextAppearance(textAppearance);
mAppNameText.setTextColor(colors);
MarginLayoutParams layoutParams = (MarginLayoutParams) mAppNameText.getLayoutParams();
final int marginStart = mAppNameText.getResources().getDimensionPixelSize(
@@ -265,19 +253,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON, mIcon);
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_EXPANDER,
mExpandButton);
- if (mWorkProfileImage != null) {
- mTransformationHelper.addViewTransformingToSimilar(mWorkProfileImage);
- }
if (mIsLowPriority && mHeaderText != null) {
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
mHeaderText);
}
- if (mAudiblyAlertedIcon != null) {
- mTransformationHelper.addViewTransformingToSimilar(mAudiblyAlertedIcon);
- }
- if (mFeedbackIcon != null) {
- mTransformationHelper.addViewTransformingToSimilar(mFeedbackIcon);
- }
+ addViewsTransformingToSimilar(mWorkProfileImage, mAudiblyAlertedIcon, mFeedbackIcon);
}
@Override
@@ -287,6 +267,9 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
if (mAltExpandTarget != null) {
mAltExpandTarget.setOnClickListener(expandable ? onClickListener : null);
}
+ if (mIconContainer != null) {
+ mIconContainer.setOnClickListener(expandable ? onClickListener : null);
+ }
if (mNotificationHeader != null) {
mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
}
@@ -371,4 +354,20 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
super.setVisible(visible);
mTransformationHelper.setVisible(visible);
}
+
+ protected void addTransformedViews(View... views) {
+ for (View view : views) {
+ if (view != null) {
+ mTransformationHelper.addTransformedView(view);
+ }
+ }
+ }
+
+ protected void addViewsTransformingToSimilar(View... views) {
+ for (View view : views) {
+ if (view != null) {
+ mTransformationHelper.addViewTransformingToSimilar(view);
+ }
+ }
+ }
}
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 7b5c5f6dcf8f..5fff8c83048f 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
@@ -74,12 +74,17 @@ public abstract class NotificationViewWrapper implements TransformableView {
return new NotificationMessagingTemplateViewWrapper(ctx, v, row);
} else if ("conversation".equals(v.getTag())) {
return new NotificationConversationTemplateViewWrapper(ctx, v, row);
+ } else if ("call".equals(v.getTag())) {
+ return new NotificationCallTemplateViewWrapper(ctx, v, row);
}
Class<? extends Notification.Style> style =
row.getEntry().getSbn().getNotification().getNotificationStyle();
if (Notification.DecoratedCustomViewStyle.class.equals(style)) {
return new NotificationDecoratedCustomViewWrapper(ctx, v, row);
}
+ if (NotificationDecoratedCustomViewWrapper.hasCustomView(v)) {
+ return new NotificationDecoratedCustomViewWrapper(ctx, v, row);
+ }
return new NotificationTemplateViewWrapper(ctx, v, row);
} else if (v instanceof NotificationHeaderView) {
return new NotificationHeaderViewWrapper(ctx, v, row);
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 8c2fa3349e4a..85d8df8e6057 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -29,6 +29,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.tuner.TunerService;
@@ -54,6 +55,7 @@ public class DozeParameters implements TunerService.Tunable,
private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
private final Resources mResources;
private final BatteryController mBatteryController;
+ private final FeatureFlags mFeatureFlags;
private boolean mDozeAlwaysOn;
private boolean mControlScreenOffAnimation;
@@ -65,7 +67,8 @@ public class DozeParameters implements TunerService.Tunable,
AlwaysOnDisplayPolicy alwaysOnDisplayPolicy,
PowerManager powerManager,
BatteryController batteryController,
- TunerService tunerService) {
+ TunerService tunerService,
+ FeatureFlags featureFlags) {
mResources = resources;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mAlwaysOnPolicy = alwaysOnDisplayPolicy;
@@ -74,6 +77,7 @@ public class DozeParameters implements TunerService.Tunable,
mControlScreenOffAnimation = !getDisplayNeedsBlanking();
mPowerManager = powerManager;
mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation);
+ mFeatureFlags = featureFlags;
tunerService.addTunable(
this,
@@ -200,8 +204,7 @@ public class DozeParameters implements TunerService.Tunable,
* then abruptly showing AOD.
*/
public boolean shouldControlUnlockedScreenOff() {
- return getAlwaysOn() && SystemProperties.getBoolean(
- "persist.sysui.show_new_screen_on_transitions", false);
+ return getAlwaysOn() && mFeatureFlags.useNewLockscreenAnimations();
}
private boolean getBoolean(String propName, int resId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 6df6e2f31416..b6c713cadeb5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.phone;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON;
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK;
@@ -134,8 +135,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private KeyguardAffordanceView mLeftAffordanceView;
private ImageView mAltLeftButton;
private ViewGroup mIndicationArea;
- private TextView mEnterpriseDisclosure;
private TextView mIndicationText;
+ private TextView mIndicationTextBottom;
private ViewGroup mPreviewContainer;
private ViewGroup mOverlayContainer;
@@ -186,6 +187,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private ControlsComponent mControlsComponent;
private int mLockScreenMode;
private BroadcastDispatcher mBroadcastDispatcher;
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
public KeyguardBottomAreaView(Context context) {
this(context, null);
@@ -255,9 +257,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mLeftAffordanceView = findViewById(R.id.left_button);
mAltLeftButton = findViewById(R.id.alt_left_button);
mIndicationArea = findViewById(R.id.keyguard_indication_area);
- mEnterpriseDisclosure = findViewById(
- R.id.keyguard_indication_enterprise_disclosure);
mIndicationText = findViewById(R.id.keyguard_indication_text);
+ mIndicationTextBottom = findViewById(R.id.keyguard_indication_text_bottom);
mIndicationBottomMargin = getResources().getDimensionPixelSize(
R.dimen.keyguard_indication_margin_bottom);
mBurnInYOffset = getResources().getDimensionPixelSize(
@@ -300,7 +301,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
getContext().registerReceiverAsUser(mDevicePolicyReceiver,
UserHandle.ALL, filter, null, null);
- Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback);
+ mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
+ mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
mKeyguardStateController.addCallback(this);
}
@@ -312,7 +314,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mRightExtension.destroy();
mLeftExtension.destroy();
getContext().unregisterReceiver(mDevicePolicyReceiver);
- Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback);
+ mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
}
private void initAccessibility() {
@@ -334,7 +336,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
// Respect font size setting.
- mEnterpriseDisclosure.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ mIndicationTextBottom.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimensionPixelSize(
com.android.internal.R.dimen.text_size_small_material));
mIndicationText.setTextSize(TypedValue.COMPLEX_UNIT_PX,
@@ -415,12 +417,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
private void updateLeftAffordanceIcon() {
- if (mDozing) {
- mAltLeftButton.setVisibility(GONE);
- } else if (mAltLeftButton.getDrawable() != null) {
- mAltLeftButton.setVisibility(VISIBLE);
- }
-
if (!mShowLeftAffordance || mDozing) {
mLeftAffordanceView.setVisibility(GONE);
return;
@@ -435,6 +431,14 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mLeftAffordanceView.setContentDescription(state.contentDescription);
}
+ private void updateControlsVisibility() {
+ if (mDozing || mControlsComponent.getVisibility() != AVAILABLE) {
+ mAltLeftButton.setVisibility(GONE);
+ } else {
+ mAltLeftButton.setVisibility(VISIBLE);
+ }
+ }
+
public boolean isLeftVoiceAssist() {
return mLeftIsVoiceAssist;
}
@@ -774,6 +778,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
updateCameraVisibility();
updateLeftAffordanceIcon();
+ updateControlsVisibility();
if (dozing) {
mOverlayContainer.setVisibility(INVISIBLE);
@@ -897,35 +902,27 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
private void setupControls() {
- if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+ boolean inNewLayout = mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
+ boolean settingEnabled = Settings.Global.getInt(mContext.getContentResolver(),
+ "controls_lockscreen", 0) == 1;
+ if (!inNewLayout || !settingEnabled || !mControlsComponent.isEnabled()) {
mAltLeftButton.setVisibility(View.GONE);
- mAltLeftButton.setOnClickListener(null);
- return;
- }
-
- if (Settings.Global.getInt(mContext.getContentResolver(), "controls_lockscreen", 0) == 0) {
return;
}
- if (mControlsComponent.getControlsListingController().isPresent()) {
- mControlsComponent.getControlsListingController().get()
- .addCallback(list -> {
- if (!list.isEmpty()) {
- mAltLeftButton.setImageDrawable(list.get(0).loadIcon());
- mAltLeftButton.setVisibility(View.VISIBLE);
- mAltLeftButton.setOnClickListener((v) -> {
- ControlsUiController ui = mControlsComponent
- .getControlsUiController().get();
- mControlsDialog = new ControlsDialog(mContext, mBroadcastDispatcher)
- .show(ui);
- });
-
- } else {
- mAltLeftButton.setVisibility(View.GONE);
- mAltLeftButton.setOnClickListener(null);
- }
- });
- }
+ mControlsComponent.getControlsListingController().get()
+ .addCallback(list -> {
+ if (!list.isEmpty()) {
+ mAltLeftButton.setImageDrawable(list.get(0).loadIcon());
+ mAltLeftButton.setOnClickListener((v) -> {
+ ControlsUiController ui = mControlsComponent
+ .getControlsUiController().get();
+ mControlsDialog = new ControlsDialog(mContext, mBroadcastDispatcher)
+ .show(ui);
+ });
+ }
+ updateControlsVisibility();
+ });
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 57a64e440bf6..a6daed5a0850 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -27,6 +27,7 @@ import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView;
/**
* Utility class to calculate the clock position and top padding of notifications on Keyguard.
@@ -55,11 +56,24 @@ public class KeyguardClockPositionAlgorithm {
private int mKeyguardStatusHeight;
/**
+ * Height of user avatar used by the multi-user switcher. This could either be the
+ * {@link KeyguardUserSwitcherListView} when it is closed and only the current user's icon is
+ * visible, or it could be height of the avatar used by the
+ * {@link com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController}.
+ */
+ private int mUserSwitchHeight;
+
+ /**
* Preferred Y position of clock.
*/
private int mClockPreferredY;
/**
+ * Preferred Y position of user avatar used by the multi-user switcher.
+ */
+ private int mUserSwitchPreferredY;
+
+ /**
* Whether or not there is a custom clock face on keyguard.
*/
private boolean mHasCustomClock;
@@ -173,18 +187,22 @@ public class KeyguardClockPositionAlgorithm {
* Sets up algorithm values.
*/
public void setup(int statusBarMinHeight, int maxShadeBottom, int notificationStackHeight,
- float panelExpansion, int parentHeight, int keyguardStatusHeight, int clockPreferredY,
+ float panelExpansion, int parentHeight, int keyguardStatusHeight,
+ int userSwitchHeight, int clockPreferredY, int userSwitchPreferredY,
boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount,
boolean bypassEnabled, int unlockedStackScrollerPadding, boolean showLockIcon,
float qsExpansion, int cutoutTopInset) {
mMinTopMargin = statusBarMinHeight + (showLockIcon
- ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon);
+ ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon)
+ + userSwitchHeight;
mMaxShadeBottom = maxShadeBottom;
mNotificationStackHeight = notificationStackHeight;
mPanelExpansion = panelExpansion;
mHeight = parentHeight;
mKeyguardStatusHeight = keyguardStatusHeight;
+ mUserSwitchHeight = userSwitchHeight;
mClockPreferredY = clockPreferredY;
+ mUserSwitchPreferredY = userSwitchPreferredY;
mHasCustomClock = hasCustomClock;
mHasVisibleNotifs = hasVisibleNotifs;
mDarkAmount = dark;
@@ -198,6 +216,7 @@ public class KeyguardClockPositionAlgorithm {
public void run(Result result) {
final int y = getClockY(mPanelExpansion, mDarkAmount);
result.clockY = y;
+ result.userSwitchY = getUserSwitcherY(mPanelExpansion);
result.clockYFullyDozing = getClockY(
1.0f /* panelExpansion */, 1.0f /* darkAmount */);
result.clockAlpha = getClockAlpha(y);
@@ -231,7 +250,7 @@ public class KeyguardClockPositionAlgorithm {
private int getExpandedPreferredClockY() {
if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
- return mMinTopMargin;
+ return mMinTopMargin + mUserSwitchHeight;
}
return (mHasCustomClock && (!mHasVisibleNotifs || mBypassEnabled)) ? getPreferredClockY()
: getExpandedClockPosition();
@@ -246,7 +265,8 @@ public class KeyguardClockPositionAlgorithm {
final int availableHeight = mMaxShadeBottom - mMinTopMargin;
final int containerCenter = mMinTopMargin + availableHeight / 2;
- float y = containerCenter - mKeyguardStatusHeight * CLOCK_HEIGHT_WEIGHT
+ float y = containerCenter
+ - (mKeyguardStatusHeight + mUserSwitchHeight) * CLOCK_HEIGHT_WEIGHT
- mClockNotificationsMargin - mNotificationStackHeight / 2;
if (y < mMinTopMargin) {
y = mMinTopMargin;
@@ -288,6 +308,17 @@ public class KeyguardClockPositionAlgorithm {
return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mEmptyDragAmount);
}
+ private int getUserSwitcherY(float panelExpansion) {
+ float userSwitchYRegular = mUserSwitchPreferredY;
+ float userSwitchYBouncer = -mKeyguardStatusHeight - mUserSwitchHeight;
+
+ // Move user-switch up while collapsing the shade
+ float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(panelExpansion);
+ float userSwitchY = MathUtils.lerp(userSwitchYBouncer, userSwitchYRegular, shadeExpansion);
+
+ return (int) (userSwitchY + mEmptyDragAmount);
+ }
+
/**
* We might want to fade out the clock when the user is swiping up.
* One exception is when the bouncer will become visible, in this cause the clock
@@ -330,6 +361,11 @@ public class KeyguardClockPositionAlgorithm {
public int clockY;
/**
+ * The y translation of the multi-user switch.
+ */
+ public int userSwitchY;
+
+ /**
* The y translation of the clock when we're fully dozing.
*/
public int clockYFullyDozing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 30c951ac518c..8970a9a20931 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -16,11 +16,15 @@
package com.android.systemui.statusbar.phone;
+import static com.android.keyguard.KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
@@ -28,6 +32,7 @@ import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Interpolators;
+import com.android.systemui.keyguard.KeyguardIndication;
import java.util.LinkedList;
@@ -35,13 +40,15 @@ import java.util.LinkedList;
* A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open").
*/
public class KeyguardIndicationTextView extends TextView {
-
private static final int FADE_OUT_MILLIS = 200;
private static final int FADE_IN_MILLIS = 250;
private static final long MSG_DURATION_MILLIS = 600;
private long mNextAnimationTime = 0;
private boolean mAnimationsEnabled = true;
private LinkedList<CharSequence> mMessages = new LinkedList<>();
+ private LinkedList<KeyguardIndication> mKeyguardIndicationInfo = new LinkedList<>();
+
+ private boolean mUseNewAnimations = false;
public KeyguardIndicationTextView(Context context) {
super(context);
@@ -60,12 +67,33 @@ public class KeyguardIndicationTextView extends TextView {
super(context, attrs, defStyleAttr, defStyleRes);
}
+ public void setLockScreenMode(int lockScreenMode) {
+ mUseNewAnimations = lockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1;
+ }
+
+ /**
+ * Changes the text with an animation and makes sure a single indication is shown long enough.
+ */
+ public void switchIndication(int textResId) {
+ switchIndication(getResources().getText(textResId), null);
+ }
+
+ /**
+ * Changes the text with an animation and makes sure a single indication is shown long enough.
+ *
+ * @param indication The text to show.
+ */
+ public void switchIndication(KeyguardIndication indication) {
+ switchIndication(indication == null ? null : indication.getMessage(), indication);
+ }
+
/**
* Changes the text with an animation and makes sure a single indication is shown long enough.
*
* @param text The text to show.
+ * @param indication optional display information for the text
*/
- public void switchIndication(CharSequence text) {
+ public void switchIndication(CharSequence text, KeyguardIndication indication) {
if (text == null) text = "";
CharSequence lastPendingMessage = mMessages.peekLast();
@@ -74,55 +102,119 @@ public class KeyguardIndicationTextView extends TextView {
return;
}
mMessages.add(text);
+ mKeyguardIndicationInfo.add(indication);
- Animator fadeOut = ObjectAnimator.ofFloat(this, View.ALPHA, 0f);
- fadeOut.setDuration(getFadeOutMillis());
- fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
-
- final CharSequence nextText = text;
- fadeOut.addListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- setText(mMessages.poll());
- }
- });
-
+ final boolean hasIcon = indication != null && indication.getIcon() != null;
final AnimatorSet animSet = new AnimatorSet();
- final AnimatorSet.Builder animSetBuilder = animSet.play(fadeOut);
+ final AnimatorSet.Builder animSetBuilder = animSet.play(getOutAnimator());
// Make sure each animation is visible for a minimum amount of time, while not worrying
// about fading in blank text
long timeInMillis = System.currentTimeMillis();
long delay = Math.max(0, mNextAnimationTime - timeInMillis);
- setNextAnimationTime(timeInMillis + delay + getFadeOutMillis());
+ setNextAnimationTime(timeInMillis + delay + getFadeOutDuration());
- if (!text.equals("")) {
+ if (!text.equals("") || hasIcon) {
setNextAnimationTime(mNextAnimationTime + MSG_DURATION_MILLIS);
-
- ObjectAnimator fadeIn = ObjectAnimator.ofFloat(this, View.ALPHA, 1f);
- fadeIn.setDuration(getFadeInMillis());
- fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- animSetBuilder.before(fadeIn);
+ animSetBuilder.before(getInAnimator());
}
animSet.setStartDelay(delay);
animSet.start();
}
+ private AnimatorSet getOutAnimator() {
+ AnimatorSet animatorSet = new AnimatorSet();
+ Animator fadeOut = ObjectAnimator.ofFloat(this, View.ALPHA, 0f);
+ fadeOut.setDuration(getFadeOutDuration());
+ fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+ fadeOut.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ KeyguardIndication info = mKeyguardIndicationInfo.poll();
+ if (info != null) {
+ setTextColor(info.getTextColor());
+ setOnClickListener(info.getClickListener());
+ final Drawable icon = info.getIcon();
+ if (icon != null) {
+ icon.setTint(getCurrentTextColor());
+ if (icon instanceof AnimatedVectorDrawable) {
+ ((AnimatedVectorDrawable) icon).start();
+ }
+ }
+ setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null);
+ }
+ setText(mMessages.poll());
+ }
+ });
+
+ if (mUseNewAnimations) {
+ Animator yTranslate =
+ ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, 0, -getYTranslationPixels());
+ yTranslate.setDuration(getFadeOutDuration());
+ fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+ animatorSet.playTogether(fadeOut, yTranslate);
+ } else {
+ animatorSet.play(fadeOut);
+ }
+
+ return animatorSet;
+ }
+
+ private AnimatorSet getInAnimator() {
+ AnimatorSet animatorSet = new AnimatorSet();
+ ObjectAnimator fadeIn = ObjectAnimator.ofFloat(this, View.ALPHA, 1f);
+ fadeIn.setStartDelay(getFadeInDelay());
+ fadeIn.setDuration(getFadeInDuration());
+ fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+
+ if (mUseNewAnimations) {
+ Animator yTranslate =
+ ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, getYTranslationPixels(), 0);
+ yTranslate.setDuration(getYInDuration());
+ yTranslate.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ setTranslationY(0);
+ }
+ });
+ animatorSet.playTogether(yTranslate, fadeIn);
+ } else {
+ animatorSet.play(fadeIn);
+ }
+
+ return animatorSet;
+ }
+
@VisibleForTesting
public void setAnimationsEnabled(boolean enabled) {
mAnimationsEnabled = enabled;
}
- private long getFadeInMillis() {
- if (mAnimationsEnabled) return FADE_IN_MILLIS;
+ private long getFadeInDelay() {
+ if (!mAnimationsEnabled) return 0L;
+ if (mUseNewAnimations) return 150L;
return 0L;
}
- private long getFadeOutMillis() {
- if (mAnimationsEnabled) return FADE_OUT_MILLIS;
+ private long getFadeInDuration() {
+ if (!mAnimationsEnabled) return 0L;
+ if (mUseNewAnimations) return 317L;
+ return FADE_IN_MILLIS;
+ }
+
+ private long getYInDuration() {
+ if (!mAnimationsEnabled) return 0L;
+ if (mUseNewAnimations) return 600L;
return 0L;
}
+ private long getFadeOutDuration() {
+ if (!mAnimationsEnabled) return 0L;
+ if (mUseNewAnimations) return 167L;
+ return FADE_OUT_MILLIS;
+ }
+
private void setNextAnimationTime(long time) {
if (mAnimationsEnabled) {
mNextAnimationTime = time;
@@ -131,10 +223,8 @@ public class KeyguardIndicationTextView extends TextView {
}
}
- /**
- * See {@link #switchIndication}.
- */
- public void switchIndication(int textResId) {
- switchIndication(getResources().getText(textResId));
+ private int getYTranslationPixels() {
+ return mContext.getResources().getDimensionPixelSize(
+ com.android.systemui.R.dimen.keyguard_indication_y_translation);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 5f547b5df671..33798d680d05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
import android.annotation.ColorInt;
@@ -25,6 +26,7 @@ import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.UserManager;
import android.util.AttributeSet;
import android.util.Pair;
import android.util.TypedValue;
@@ -45,18 +47,15 @@ import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -75,18 +74,16 @@ public class KeyguardStatusBarView extends RelativeLayout
private boolean mShowPercentAvailable;
private boolean mBatteryCharging;
- private boolean mKeyguardUserSwitcherShowing;
private boolean mBatteryListening;
private TextView mCarrierLabel;
- private MultiUserSwitch mMultiUserSwitch;
private ImageView mMultiUserAvatar;
private BatteryMeterView mBatteryView;
private StatusIconContainer mStatusIconContainer;
private BatteryController mBatteryController;
- private KeyguardUserSwitcher mKeyguardUserSwitcher;
- private UserSwitcherController mUserSwitcherController;
+ private boolean mKeyguardUserSwitcherEnabled;
+ private final UserManager mUserManager;
private int mSystemIconsSwitcherHiddenExpandedMargin;
private int mSystemIconsBaseMargin;
@@ -109,13 +106,13 @@ public class KeyguardStatusBarView extends RelativeLayout
public KeyguardStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mUserManager = UserManager.get(getContext());
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mSystemIconsContainer = findViewById(R.id.system_icons_container);
- mMultiUserSwitch = findViewById(R.id.multi_user_switch);
mMultiUserAvatar = findViewById(R.id.multi_user_avatar);
mCarrierLabel = findViewById(R.id.keyguard_carrier_text);
mBatteryView = mSystemIconsContainer.findViewById(R.id.battery);
@@ -124,7 +121,6 @@ public class KeyguardStatusBarView extends RelativeLayout
mStatusIconContainer = findViewById(R.id.statusIcons);
loadDimens();
- updateUserSwitcher();
mBatteryController = Dependency.get(BatteryController.class);
}
@@ -137,14 +133,6 @@ public class KeyguardStatusBarView extends RelativeLayout
R.dimen.multi_user_avatar_keyguard_size);
mMultiUserAvatar.setLayoutParams(lp);
- // Multi-user switch
- lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams();
- lp.width = getResources().getDimensionPixelSize(
- R.dimen.multi_user_switch_width_keyguard);
- lp.setMarginEnd(getResources().getDimensionPixelSize(
- R.dimen.multi_user_switch_keyguard_margin));
- mMultiUserSwitch.setLayoutParams(lp);
-
// System icons
lp = (MarginLayoutParams) mSystemIconsContainer.getLayoutParams();
lp.setMarginStart(getResources().getDimensionPixelSize(
@@ -194,22 +182,28 @@ public class KeyguardStatusBarView extends RelativeLayout
}
private void updateVisibilities() {
- if (mMultiUserSwitch.getParent() != mStatusIconArea && !mKeyguardUserSwitcherShowing) {
- if (mMultiUserSwitch.getParent() != null) {
- getOverlay().remove(mMultiUserSwitch);
+ if (mMultiUserAvatar.getParent() != mStatusIconArea
+ && !mKeyguardUserSwitcherEnabled) {
+ if (mMultiUserAvatar.getParent() != null) {
+ getOverlay().remove(mMultiUserAvatar);
}
- mStatusIconArea.addView(mMultiUserSwitch, 0);
- } else if (mMultiUserSwitch.getParent() == mStatusIconArea && mKeyguardUserSwitcherShowing) {
- mStatusIconArea.removeView(mMultiUserSwitch);
+ mStatusIconArea.addView(mMultiUserAvatar, 0);
+ } else if (mMultiUserAvatar.getParent() == mStatusIconArea
+ && mKeyguardUserSwitcherEnabled) {
+ mStatusIconArea.removeView(mMultiUserAvatar);
}
- if (mKeyguardUserSwitcher == null) {
+ if (!mKeyguardUserSwitcherEnabled) {
// If we have no keyguard switcher, the screen width is under 600dp. In this case,
// we only show the multi-user switch if it's enabled through UserManager as well as
// by the user.
- if (mMultiUserSwitch.isMultiUserEnabled()) {
- mMultiUserSwitch.setVisibility(View.VISIBLE);
+ // TODO(b/138661450) Move IPC calls to background
+ boolean isMultiUserEnabled = whitelistIpcs(() -> mUserManager.isUserSwitcherEnabled(
+ mContext.getResources().getBoolean(
+ R.bool.qs_show_user_switcher_for_single_user)));
+ if (isMultiUserEnabled) {
+ mMultiUserAvatar.setVisibility(View.VISIBLE);
} else {
- mMultiUserSwitch.setVisibility(View.GONE);
+ mMultiUserAvatar.setVisibility(View.GONE);
}
}
mBatteryView.setForceShowPercent(mBatteryCharging && mShowPercentAvailable);
@@ -220,11 +214,12 @@ public class KeyguardStatusBarView extends RelativeLayout
(LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams();
// If the avatar icon is gone, we need to have some end margin to display the system icons
// correctly.
- int baseMarginEnd = mMultiUserSwitch.getVisibility() == View.GONE
+ int baseMarginEnd = mMultiUserAvatar.getVisibility() == View.GONE
? mSystemIconsBaseMargin
: 0;
- int marginEnd = mKeyguardUserSwitcherShowing ? mSystemIconsSwitcherHiddenExpandedMargin :
- baseMarginEnd;
+ int marginEnd =
+ mKeyguardUserSwitcherEnabled ? mSystemIconsSwitcherHiddenExpandedMargin
+ : baseMarginEnd;
marginEnd = calculateMargin(marginEnd, mPadding.second);
if (marginEnd != lp.getMarginEnd()) {
lp.setMarginEnd(marginEnd);
@@ -334,20 +329,11 @@ public class KeyguardStatusBarView extends RelativeLayout
}
}
- private void updateUserSwitcher() {
- boolean keyguardSwitcherAvailable = mKeyguardUserSwitcher != null;
- mMultiUserSwitch.setClickable(keyguardSwitcherAvailable);
- mMultiUserSwitch.setFocusable(keyguardSwitcherAvailable);
- mMultiUserSwitch.setKeyguardMode(keyguardSwitcherAvailable);
- }
-
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
UserInfoController userInfoController = Dependency.get(UserInfoController.class);
userInfoController.addCallback(this);
- mUserSwitcherController = Dependency.get(UserSwitcherController.class);
- mMultiUserSwitch.setUserSwitcherController(mUserSwitcherController);
userInfoController.reloadUserInfo();
Dependency.get(ConfigurationController.class).addCallback(this);
mIconManager = new TintedIconManager(findViewById(R.id.statusIcons),
@@ -369,11 +355,6 @@ public class KeyguardStatusBarView extends RelativeLayout
mMultiUserAvatar.setImageDrawable(picture);
}
- /** */
- public void setQSDetailDisplayer(QSDetailDisplayer detailDisplayer) {
- mMultiUserSwitch.setQSDetailDisplayer(detailDisplayer);
- }
-
@Override
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
if (mBatteryCharging != charging) {
@@ -387,54 +368,42 @@ public class KeyguardStatusBarView extends RelativeLayout
// could not care less
}
- public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
- mKeyguardUserSwitcher = keyguardUserSwitcher;
- mMultiUserSwitch.setKeyguardUserSwitcher(keyguardUserSwitcher);
- updateUserSwitcher();
- }
-
- public void setKeyguardUserSwitcherShowing(boolean showing, boolean animate) {
- mKeyguardUserSwitcherShowing = showing;
- if (animate) {
- animateNextLayoutChange();
- }
- updateVisibilities();
- updateLayoutConsideringCutout();
- updateSystemIconsLayoutParams();
+ public void setKeyguardUserSwitcherEnabled(boolean enabled) {
+ mKeyguardUserSwitcherEnabled = enabled;
}
private void animateNextLayoutChange() {
final int systemIconsCurrentX = mSystemIconsContainer.getLeft();
- final boolean userSwitcherVisible = mMultiUserSwitch.getParent() == mStatusIconArea;
+ final boolean userAvatarVisible = mMultiUserAvatar.getParent() == mStatusIconArea;
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
- boolean userSwitcherHiding = userSwitcherVisible
- && mMultiUserSwitch.getParent() != mStatusIconArea;
+ boolean userAvatarHiding = userAvatarVisible
+ && mMultiUserAvatar.getParent() != mStatusIconArea;
mSystemIconsContainer.setX(systemIconsCurrentX);
mSystemIconsContainer.animate()
.translationX(0)
.setDuration(400)
- .setStartDelay(userSwitcherHiding ? 300 : 0)
+ .setStartDelay(userAvatarHiding ? 300 : 0)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.start();
- if (userSwitcherHiding) {
- getOverlay().add(mMultiUserSwitch);
- mMultiUserSwitch.animate()
+ if (userAvatarHiding) {
+ getOverlay().add(mMultiUserAvatar);
+ mMultiUserAvatar.animate()
.alpha(0f)
.setDuration(300)
.setStartDelay(0)
.setInterpolator(Interpolators.ALPHA_OUT)
.withEndAction(() -> {
- mMultiUserSwitch.setAlpha(1f);
- getOverlay().remove(mMultiUserSwitch);
+ mMultiUserAvatar.setAlpha(1f);
+ getOverlay().remove(mMultiUserAvatar);
})
.start();
} else {
- mMultiUserSwitch.setAlpha(0f);
- mMultiUserSwitch.animate()
+ mMultiUserAvatar.setAlpha(0f);
+ mMultiUserAvatar.animate()
.alpha(1f)
.setDuration(300)
.setStartDelay(200)
@@ -452,8 +421,8 @@ public class KeyguardStatusBarView extends RelativeLayout
if (visibility != View.VISIBLE) {
mSystemIconsContainer.animate().cancel();
mSystemIconsContainer.setTranslationX(0);
- mMultiUserSwitch.animate().cancel();
- mMultiUserSwitch.setAlpha(1f);
+ mMultiUserAvatar.animate().cancel();
+ mMultiUserAvatar.setAlpha(1f);
} else {
updateVisibilities();
updateSystemIconsLayoutParams();
@@ -523,9 +492,9 @@ public class KeyguardStatusBarView extends RelativeLayout
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("KeyguardStatusBarView:");
pw.println(" mBatteryCharging: " + mBatteryCharging);
- pw.println(" mKeyguardUserSwitcherShowing: " + mKeyguardUserSwitcherShowing);
pw.println(" mBatteryListening: " + mBatteryListening);
pw.println(" mLayoutState: " + mLayoutState);
+ pw.println(" mKeyguardUserSwitcherEnabled: " + mKeyguardUserSwitcherEnabled);
if (mBatteryView != null) {
mBatteryView.dump(fd, pw, args);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index 480d3f42ae77..16f36b7b6b7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -35,7 +35,6 @@ import com.android.systemui.Prefs.Key;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.qs.QSDetailDisplayer;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.UserSwitcherController;
/**
@@ -44,8 +43,6 @@ import com.android.systemui.statusbar.policy.UserSwitcherController;
public class MultiUserSwitch extends FrameLayout implements View.OnClickListener {
protected QSDetailDisplayer mQSDetailDisplayer;
- private KeyguardUserSwitcher mKeyguardUserSwitcher;
- private boolean mKeyguardMode;
private UserSwitcherController.BaseUserAdapter mUserListener;
final UserManager mUserManager;
@@ -85,15 +82,6 @@ public class MultiUserSwitch extends FrameLayout implements View.OnClickListener
refreshContentDescription();
}
- public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
- mKeyguardUserSwitcher = keyguardUserSwitcher;
- }
-
- public void setKeyguardMode(boolean keyguardShowing) {
- mKeyguardMode = keyguardShowing;
- registerListener();
- }
-
public boolean isMultiUserEnabled() {
// TODO(b/138661450) Move IPC calls to background
return whitelistIpcs(() -> mUserManager.isUserSwitcherEnabled(
@@ -123,11 +111,7 @@ public class MultiUserSwitch extends FrameLayout implements View.OnClickListener
@Override
public void onClick(View v) {
- if (mKeyguardMode) {
- if (mKeyguardUserSwitcher != null) {
- mKeyguardUserSwitcher.show(true /* animate */);
- }
- } else if (mQSDetailDisplayer != null && mUserSwitcherController != null) {
+ if (mQSDetailDisplayer != null && mUserSwitcherController != null) {
View center = getChildCount() > 0 ? getChildAt(0) : this;
int[] tmpInt = new int[2];
@@ -184,6 +168,6 @@ public class MultiUserSwitch extends FrameLayout implements View.OnClickListener
}
protected DetailAdapter getUserDetailAdapter() {
- return mUserSwitcherController.userDetailAdapter;
+ return mUserSwitcherController.mUserDetailAdapter;
}
}
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 e0ef3b6483a5..0b3fd161d865 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -18,6 +18,10 @@ package com.android.systemui.statusbar.phone;
import static android.view.View.GONE;
+import static androidx.constraintlayout.widget.ConstraintSet.END;
+import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
+import static androidx.constraintlayout.widget.ConstraintSet.START;
+
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
@@ -48,6 +52,7 @@ import android.hardware.biometrics.BiometricSourceType;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.UserManager;
import android.util.Log;
import android.util.MathUtils;
import android.view.DisplayCutout;
@@ -57,12 +62,14 @@ import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
+import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
-import android.widget.TextView;
+
+import androidx.constraintlayout.widget.ConstraintSet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
@@ -75,7 +82,9 @@ import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardStatusViewController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
+import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.systemui.DejankUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -92,6 +101,7 @@ import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
@@ -131,9 +141,12 @@ import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.util.Utils;
import com.android.wm.shell.animation.FlingAnimationUtils;
import java.io.FileDescriptor;
@@ -283,6 +296,21 @@ public class NotificationPanelViewController extends PanelViewController {
}
};
+ final KeyguardUserSwitcherController.KeyguardUserSwitcherListener
+ mKeyguardUserSwitcherListener =
+ new KeyguardUserSwitcherController.KeyguardUserSwitcherListener() {
+ @Override
+ public void onKeyguardUserSwitcherChanged(boolean open) {
+ if (mKeyguardUserSwitcherController == null) {
+ updateUserSwitcherVisibility(false);
+ } else if (!mKeyguardUserSwitcherController.isSimpleUserSwitcher()) {
+ updateUserSwitcherVisibility(open
+ && mKeyguardStateController.isShowing()
+ && !mKeyguardStateController.isKeyguardFadingAway());
+ }
+ }
+ };
+
private final LayoutInflater mLayoutInflater;
private final PowerManager mPowerManager;
private final AccessibilityManager mAccessibilityManager;
@@ -295,6 +323,8 @@ public class NotificationPanelViewController extends PanelViewController {
private final MediaHierarchyManager mMediaHierarchyManager;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
+ private final KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory;
+ private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
private final QSDetailDisplayer mQSDetailDisplayer;
private final FeatureFlags mFeatureFlags;
private final ScrimController mScrimController;
@@ -307,7 +337,9 @@ public class NotificationPanelViewController extends PanelViewController {
private int mMaxAllowedKeyguardNotifications;
private KeyguardAffordanceHelper mAffordanceHelper;
- private KeyguardUserSwitcher mKeyguardUserSwitcher;
+ private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
+ private boolean mKeyguardUserSwitcherIsShowing;
+ private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
private KeyguardStatusBarView mKeyguardStatusBar;
private ViewGroup mBigClockContainer;
private QS mQs;
@@ -332,6 +364,8 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean mQsExpandedWhenExpandingStarted;
private boolean mQsFullyExpanded;
private boolean mKeyguardShowing;
+ private boolean mKeyguardQsUserSwitchEnabled;
+ private boolean mKeyguardUserSwitcherEnabled;
private boolean mDozing;
private boolean mDozingOnDown;
private int mBarState;
@@ -367,6 +401,7 @@ public class NotificationPanelViewController extends PanelViewController {
// Used for two finger gesture as well as accessibility shortcut to QS.
private boolean mQsExpandImmediate;
private boolean mTwoFingerQsExpandPossible;
+ private String mHeaderDebugInfo;
/**
* If we are in a panel collapsing motion, we reset scrollY of our scroll view but still
@@ -464,6 +499,7 @@ public class NotificationPanelViewController extends PanelViewController {
private final CommandQueue mCommandQueue;
private final NotificationLockscreenUserManager mLockscreenUserManager;
+ private final UserManager mUserManager;
private final ShadeController mShadeController;
private final MediaDataManager mMediaDataManager;
private int mDisplayId;
@@ -556,11 +592,14 @@ public class NotificationPanelViewController extends PanelViewController {
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
+ KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory,
+ KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory,
+ QSDetailDisplayer qsDetailDisplayer,
NotificationGroupManagerLegacy groupManager,
NotificationIconAreaController notificationIconAreaController,
AuthController authController,
- QSDetailDisplayer qsDetailDisplayer,
ScrimController scrimController,
+ UserManager userManager,
MediaDataManager mediaDataManager,
AmbientState ambientState,
FeatureFlags featureFlags,
@@ -581,8 +620,15 @@ public class NotificationPanelViewController extends PanelViewController {
mGroupManager = groupManager;
mNotificationIconAreaController = notificationIconAreaController;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
- mQSDetailDisplayer = qsDetailDisplayer;
mFeatureFlags = featureFlags;
+ mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
+ mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
+ mQSDetailDisplayer = qsDetailDisplayer;
+ mKeyguardUserSwitcherEnabled = mResources.getBoolean(
+ com.android.internal.R.bool.config_keyguardUserSwitcher);
+ mKeyguardQsUserSwitchEnabled =
+ mKeyguardUserSwitcherEnabled && mResources.getBoolean(
+ R.bool.config_keyguard_user_switch_opens_qs_details);
mView.setWillNotDraw(!DEBUG);
mLayoutInflater = layoutInflater;
mFalsingManager = falsingManager;
@@ -598,6 +644,7 @@ public class NotificationPanelViewController extends PanelViewController {
mDozeParameters = dozeParameters;
mBiometricUnlockController = biometricUnlockController;
mScrimController = scrimController;
+ mUserManager = userManager;
mMediaDataManager = mediaDataManager;
mControlsComponent = controlsComponent;
pulseExpansionHandler.setPulseExpandAbortListener(() -> {
@@ -660,9 +707,23 @@ public class NotificationPanelViewController extends PanelViewController {
private void onFinishInflate() {
loadDimens();
mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
- mKeyguardStatusBar.setQSDetailDisplayer(mQSDetailDisplayer);
mBigClockContainer = mView.findViewById(R.id.big_clock_container);
- updateViewControllers(mView.findViewById(R.id.keyguard_status_view));
+
+ UserAvatarView userAvatarView = null;
+ KeyguardUserSwitcherView keyguardUserSwitcherView = null;
+
+ if (mKeyguardUserSwitcherEnabled && mUserManager.isUserSwitcherEnabled()) {
+ if (mKeyguardQsUserSwitchEnabled) {
+ ViewStub stub = mView.findViewById(R.id.keyguard_qs_user_switch_stub);
+ userAvatarView = (UserAvatarView) stub.inflate();
+ } else {
+ ViewStub stub = mView.findViewById(R.id.keyguard_user_switcher_stub);
+ keyguardUserSwitcherView = (KeyguardUserSwitcherView) stub.inflate();
+ }
+ }
+
+ updateViewControllers(mView.findViewById(R.id.keyguard_status_view),
+ userAvatarView, keyguardUserSwitcherView);
mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
R.id.notification_stack_scroller);
@@ -707,6 +768,10 @@ public class NotificationPanelViewController extends PanelViewController {
});
mView.setAccessibilityDelegate(mAccessibilityDelegate);
+ // dynamically apply the split shade value overrides.
+ if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) {
+ updateResources();
+ }
}
@Override
@@ -735,7 +800,8 @@ public class NotificationPanelViewController extends PanelViewController {
R.dimen.heads_up_status_bar_padding);
}
- private void updateViewControllers(KeyguardStatusView keyguardStatusView) {
+ private void updateViewControllers(KeyguardStatusView keyguardStatusView,
+ UserAvatarView userAvatarView, KeyguardUserSwitcherView keyguardUserSwitcherView) {
// Re-associate the KeyguardStatusViewController
KeyguardStatusViewComponent statusViewComponent =
mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
@@ -746,6 +812,37 @@ public class NotificationPanelViewController extends PanelViewController {
KeyguardClockSwitchController keyguardClockSwitchController =
statusViewComponent.getKeyguardClockSwitchController();
keyguardClockSwitchController.setBigClockContainer(mBigClockContainer);
+
+ if (mKeyguardUserSwitcherController != null) {
+ // Try to close the switcher so that callbacks are triggered if necessary.
+ // Otherwise, NPV can get into a state where some of the views are still hidden
+ mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(false);
+ mKeyguardUserSwitcherController.removeCallback();
+ }
+
+ mKeyguardQsUserSwitchController = null;
+ mKeyguardUserSwitcherController = null;
+
+ // Re-associate the KeyguardUserSwitcherController
+ if (userAvatarView != null) {
+ KeyguardQsUserSwitchComponent userSwitcherComponent =
+ mKeyguardQsUserSwitchComponentFactory.build(userAvatarView);
+ mKeyguardQsUserSwitchController =
+ userSwitcherComponent.getKeyguardQsUserSwitchController();
+ mKeyguardQsUserSwitchController.setNotificationPanelViewController(this);
+ mKeyguardQsUserSwitchController.init();
+ mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true);
+ } else if (keyguardUserSwitcherView != null) {
+ KeyguardUserSwitcherComponent userSwitcherComponent =
+ mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView);
+ mKeyguardUserSwitcherController =
+ userSwitcherComponent.getKeyguardUserSwitcherController();
+ mKeyguardUserSwitcherController.setCallback(mKeyguardUserSwitcherListener);
+ mKeyguardUserSwitcherController.init();
+ mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true);
+ } else {
+ mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(false);
+ }
}
/**
@@ -783,16 +880,21 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationStackScrollLayoutController.setLayoutParams(lp);
}
- if (shouldUseSplitNotificationShade()) {
- // In order to change the constraints at runtime, all children of the Constraint Layout
- // must have ids.
- ensureAllViewsHaveIds(mNotificationContainerParent);
+ // In order to change the constraints at runtime, all children of the Constraint Layout
+ // must have ids.
+ ensureAllViewsHaveIds(mNotificationContainerParent);
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.clone(mNotificationContainerParent);
+ if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) {
+ constraintSet.connect(R.id.qs_frame, END, R.id.qs_edge_guideline, END);
+ constraintSet.connect(
+ R.id.notification_stack_scroller, START,
+ R.id.qs_edge_guideline, START);
+ } else {
+ constraintSet.connect(R.id.qs_frame, END, PARENT_ID, END);
+ constraintSet.connect(R.id.notification_stack_scroller, START, PARENT_ID, START);
}
- }
-
- private boolean shouldUseSplitNotificationShade() {
- return mFeatureFlags.isTwoColumnNotificationShadeEnabled()
- && mResources.getBoolean(R.bool.config_use_split_notification_shade);
+ constraintSet.applyTo(mNotificationContainerParent);
}
private static void ensureAllViewsHaveIds(ViewGroup parentView) {
@@ -804,7 +906,27 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
+ private View reInflateStub(int viewId, int stubId, int layoutId, boolean enabled) {
+ View view = mView.findViewById(viewId);
+ if (view != null) {
+ int index = mView.indexOfChild(view);
+ mView.removeView(view);
+ if (enabled) {
+ view = mLayoutInflater.inflate(layoutId, mView, false);
+ mView.addView(view, index);
+ } else {
+ view = null;
+ }
+ } else if (enabled) {
+ // It's possible the stub was never inflated if the configuration changed
+ ViewStub stub = mView.findViewById(stubId);
+ view = stub.inflate();
+ }
+ return view;
+ }
+
private void reInflateViews() {
+ if (DEBUG) Log.d(TAG, "reInflateViews");
// Re-inflate the status view group.
KeyguardStatusView keyguardStatusView = mView.findViewById(R.id.keyguard_status_view);
int index = mView.indexOfChild(keyguardStatusView);
@@ -813,8 +935,27 @@ public class NotificationPanelViewController extends PanelViewController {
R.layout.keyguard_status_view, mView, false);
mView.addView(keyguardStatusView, index);
+ // Re-inflate the keyguard user switcher group.
+ boolean isUserSwitcherEnabled = mUserManager.isUserSwitcherEnabled();
+ boolean showQsUserSwitch = mKeyguardQsUserSwitchEnabled && isUserSwitcherEnabled;
+ boolean showKeyguardUserSwitcher =
+ !mKeyguardQsUserSwitchEnabled
+ && mKeyguardUserSwitcherEnabled
+ && isUserSwitcherEnabled;
+ UserAvatarView userAvatarView = (UserAvatarView) reInflateStub(
+ R.id.keyguard_qs_user_switch_view /* viewId */,
+ R.id.keyguard_qs_user_switch_stub /* stubId */,
+ R.layout.keyguard_qs_user_switch /* layoutId */,
+ showQsUserSwitch /* enabled */);
+ KeyguardUserSwitcherView keyguardUserSwitcherView =
+ (KeyguardUserSwitcherView) reInflateStub(
+ R.id.keyguard_user_switcher_view /* viewId */,
+ R.id.keyguard_user_switcher_stub /* stubId */,
+ R.layout.keyguard_user_switcher /* layoutId */,
+ showKeyguardUserSwitcher /* enabled */);
+
mBigClockContainer.removeAllViews();
- updateViewControllers(keyguardStatusView);
+ updateViewControllers(keyguardStatusView, userAvatarView, keyguardUserSwitcherView);
// Update keyguard bottom area
index = mView.indexOfChild(mKeyguardBottomArea);
@@ -838,6 +979,20 @@ public class NotificationPanelViewController extends PanelViewController {
false,
false,
mBarState);
+ if (mKeyguardQsUserSwitchController != null) {
+ mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility(
+ mBarState,
+ false,
+ false,
+ mBarState);
+ }
+ if (mKeyguardUserSwitcherController != null) {
+ mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility(
+ mBarState,
+ false,
+ false,
+ mBarState);
+ }
setKeyguardBottomAreaVisibility(mBarState, false);
}
@@ -930,10 +1085,15 @@ public class NotificationPanelViewController extends PanelViewController {
int totalHeight = mView.getHeight();
int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight);
+ int userSwitcherPreferredY = mStatusBarMinHeight;
boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
.getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications);
+ int userIconHeight = mKeyguardQsUserSwitchController != null
+ ? mKeyguardQsUserSwitchController.getUserIconHeight()
+ : (mKeyguardUserSwitcherController != null
+ ? mKeyguardUserSwitcherController.getUserIconHeight() : 0);
mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding,
mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
getExpandedFraction(),
@@ -942,7 +1102,8 @@ public class NotificationPanelViewController extends PanelViewController {
? mKeyguardStatusViewController.getHeight()
: (int) (mKeyguardStatusViewController.getHeight()
- mShelfHeight / 2.0f - mDarkIconSize / 2.0f),
- clockPreferredY, hasCustomClock(),
+ userIconHeight,
+ clockPreferredY, userSwitcherPreferredY, hasCustomClock(),
hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
bypassEnabled, getUnlockedStackScrollerPadding(),
mUpdateMonitor.canShowLockIcon(),
@@ -952,6 +1113,18 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardStatusViewController.updatePosition(
mClockPositionResult.clockX, mClockPositionResult.clockY,
mClockPositionResult.clockScale, animateClock);
+ if (mKeyguardQsUserSwitchController != null) {
+ mKeyguardQsUserSwitchController.updatePosition(
+ mClockPositionResult.clockX,
+ mClockPositionResult.userSwitchY,
+ animateClock);
+ }
+ if (mKeyguardUserSwitcherController != null) {
+ mKeyguardUserSwitcherController.updatePosition(
+ mClockPositionResult.clockX,
+ mClockPositionResult.userSwitchY,
+ animateClock);
+ }
updateNotificationTranslucency();
updateClock();
stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
@@ -1092,6 +1265,12 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateClock() {
mKeyguardStatusViewController.setAlpha(mClockPositionResult.clockAlpha);
+ if (mKeyguardQsUserSwitchController != null) {
+ mKeyguardQsUserSwitchController.setAlpha(mClockPositionResult.clockAlpha);
+ }
+ if (mKeyguardUserSwitcherController != null) {
+ mKeyguardUserSwitcherController.setAlpha(mClockPositionResult.clockAlpha);
+ }
}
public void animateToFullShade(long delay) {
@@ -1180,6 +1359,12 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
+ public void expandWithQsDetail(DetailAdapter qsDetailAdapter) {
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
+ flingSettings(0 /* velocity */, FLING_EXPAND);
+ mQSDetailDisplayer.showDetailAdapter(qsDetailAdapter, 0, 0);
+ }
+
public void expandWithoutQs() {
if (isQsExpanded()) {
flingSettings(0 /* velocity */, FLING_COLLAPSE);
@@ -1773,8 +1958,9 @@ public class NotificationPanelViewController extends PanelViewController {
mBarState != KEYGUARD && (!mQsExpanded
|| mQsExpansionFromOverscroll));
- if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) {
- mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
+ if (mKeyguardUserSwitcherController != null && mQsExpanded
+ && !mStackScrollerOverscrolling) {
+ mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(true);
}
if (mQs == null) return;
mQs.setExpanded(mQsExpanded);
@@ -1841,6 +2027,10 @@ public class NotificationPanelViewController extends PanelViewController {
}
private float calculateQsTopPadding() {
+ // in split shade mode we want notifications to be directly below status bar
+ if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources) && !mKeyguardShowing) {
+ return 0f;
+ }
if (mKeyguardShowing && (mQsExpandImmediate
|| mIsExpanding && mQsExpandedWhenExpandingStarted)) {
@@ -2480,7 +2670,9 @@ public class NotificationPanelViewController extends PanelViewController {
super.onTrackingStarted();
if (mQsFullyExpanded) {
mQsExpandImmediate = true;
- mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
+ if (!Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) {
+ mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
+ }
}
if (mBarState == KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
mAffordanceHelper.animateHideLeftRightIcon();
@@ -2608,10 +2800,6 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
- public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
- mKeyguardUserSwitcher = keyguardUserSwitcher;
- }
-
public void onScreenTurningOn() {
mKeyguardStatusViewController.dozeTimeTick();
}
@@ -2726,12 +2914,12 @@ public class NotificationPanelViewController extends PanelViewController {
}
/**
- * Updates the vertical position of the panel so it is positioned closer to the touch
+ * Updates the horizontal position of the panel so it is positioned closer to the touch
* responsible for opening the panel.
*
* @param x the x-coordinate the touch event
*/
- protected void updateVerticalPanelPosition(float x) {
+ protected void updateHorizontalPanelPosition(float x) {
if (mNotificationStackScrollLayoutController.getWidth() * 1.75f > mView.getWidth()) {
resetHorizontalPanelPosition();
return;
@@ -3078,6 +3266,20 @@ public class NotificationPanelViewController extends PanelViewController {
true /* keyguardFadingAway */,
false /* goingToFullShade */,
mBarState);
+ if (mKeyguardQsUserSwitchController != null) {
+ mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility(
+ mBarState,
+ true /* keyguardFadingAway */,
+ false /* goingToFullShade */,
+ mBarState);
+ }
+ if (mKeyguardUserSwitcherController != null) {
+ mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility(
+ mBarState,
+ true /* keyguardFadingAway */,
+ false /* goingToFullShade */,
+ mBarState);
+ }
}
/**
@@ -3205,8 +3407,8 @@ public class NotificationPanelViewController extends PanelViewController {
return mView.getHeight();
}
- public TextView getHeaderDebugInfo() {
- return mView.findViewById(R.id.header_debug_info);
+ public void setHeaderDebugInfo(String text) {
+ if (DEBUG) mHeaderDebugInfo = text;
}
public void onThemeChanged() {
@@ -3301,7 +3503,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
- updateVerticalPanelPosition(event.getX());
+ updateHorizontalPanelPosition(event.getX());
handled = true;
}
handled |= super.onTouch(v, event);
@@ -3320,6 +3522,50 @@ public class NotificationPanelViewController extends PanelViewController {
return mNotificationStackScrollLayoutController;
}
+ /**
+ * Close the keyguard user switcher if it is open and capable of closing.
+ *
+ * Has no effect if user switcher isn't supported, if the user switcher is already closed, or
+ * if the user switcher uses "simple" mode. The simple user switcher cannot be closed.
+ *
+ * @return true if the keyguard user switcher was open, and is now closed
+ */
+ public boolean closeUserSwitcherIfOpen() {
+ if (mKeyguardUserSwitcherController != null) {
+ return mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(
+ true /* animate */);
+ }
+ return false;
+ }
+
+ private void updateUserSwitcherVisibility(boolean open) {
+ // Do not update if previously called with the same state.
+ if (mKeyguardUserSwitcherIsShowing == open) {
+ return;
+ }
+ mKeyguardUserSwitcherIsShowing = open;
+
+ if (open) {
+ animateKeyguardStatusBarOut();
+ mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
+ mBarState,
+ true /* keyguardFadingAway */,
+ true /* goingToFullShade */,
+ mBarState);
+ setKeyguardBottomAreaVisibility(mBarState, true);
+ mNotificationContainerParent.setVisibility(View.GONE);
+ } else {
+ animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
+ StatusBarState.KEYGUARD,
+ false,
+ false,
+ StatusBarState.SHADE_LOCKED);
+ setKeyguardBottomAreaVisibility(mBarState, false);
+ mNotificationContainerParent.setVisibility(View.VISIBLE);
+ }
+ }
+
private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener {
@Override
public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
@@ -3620,6 +3866,7 @@ public class NotificationPanelViewController extends PanelViewController {
private class ConfigurationListener implements ConfigurationController.ConfigurationListener {
@Override
public void onThemeChanged() {
+ if (DEBUG) Log.d(TAG, "onThemeChanged");
final int themeResId = mView.getContext().getThemeResId();
if (mThemeResId == themeResId) {
return;
@@ -3631,11 +3878,15 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public void onOverlayChanged() {
+ if (DEBUG) Log.d(TAG, "onOverlayChanged");
reInflateViews();
}
@Override
- public void onUiModeChanged() {}
+ public void onDensityOrFontScaleChanged() {
+ if (DEBUG) Log.d(TAG, "onDensityOrFontScaleChanged");
+ reInflateViews();
+ }
}
private class StatusBarStateListener implements StateListener {
@@ -3820,6 +4071,8 @@ public class NotificationPanelViewController extends PanelViewController {
p.setStrokeWidth(2);
p.setStyle(Paint.Style.STROKE);
canvas.drawLine(0, getMaxPanelHeight(), mView.getWidth(), getMaxPanelHeight(), p);
+ p.setTextSize(24);
+ if (mHeaderDebugInfo != null) canvas.drawText(mHeaderDebugInfo, 50, 100, p);
p.setColor(Color.BLUE);
canvas.drawLine(0, getExpandedHeight(), mView.getWidth(), getExpandedHeight(), p);
p.setColor(Color.GREEN);
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 b36740620d08..e394ebc65a6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -22,8 +22,6 @@ import android.content.res.Configuration;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
-import android.view.ViewStub;
-import android.view.ViewStub.OnInflateListener;
import android.view.WindowInsets;
import android.widget.FrameLayout;
@@ -44,14 +42,11 @@ import java.util.Comparator;
* The container with notification stack scroller and quick settings inside.
*/
public class NotificationsQuickSettingsContainer extends ConstraintLayout
- implements OnInflateListener, FragmentListener,
- AboveShelfObserver.HasViewAboveShelfChangedListener {
+ implements FragmentListener, AboveShelfObserver.HasViewAboveShelfChangedListener {
private FrameLayout mQsFrame;
- private View mUserSwitcher;
private NotificationStackScrollLayout mStackScroller;
private View mKeyguardStatusBar;
- private boolean mInflated;
private boolean mQsExpanded;
private boolean mCustomizerAnimating;
@@ -73,9 +68,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
mStackScroller = findViewById(R.id.notification_stack_scroller);
mStackScrollerMargin = ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin;
mKeyguardStatusBar = findViewById(R.id.keyguard_header);
- ViewStub userSwitcher = findViewById(R.id.keyguard_user_switcher);
- userSwitcher.setOnInflateListener(this);
- mUserSwitcher = userSwitcher;
}
@Override
@@ -119,10 +111,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
// touches first but the panel gets drawn above.
mDrawingOrderedChildren.clear();
mLayoutDrawingOrder.clear();
- if (mInflated && mUserSwitcher.getVisibility() == View.VISIBLE) {
- mDrawingOrderedChildren.add(mUserSwitcher);
- mLayoutDrawingOrder.add(mUserSwitcher);
- }
if (mKeyguardStatusBar.getVisibility() == View.VISIBLE) {
mDrawingOrderedChildren.add(mKeyguardStatusBar);
mLayoutDrawingOrder.add(mKeyguardStatusBar);
@@ -158,14 +146,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
}
@Override
- public void onInflate(ViewStub stub, View inflated) {
- if (stub == mUserSwitcher) {
- mUserSwitcher = inflated;
- mInflated = true;
- }
- }
-
- @Override
public void onFragmentViewCreated(String tag, Fragment fragment) {
QS container = (QS) fragment;
container.setContainer(this);
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 33bdfd02b239..d88309cd0197 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -35,7 +35,6 @@ import static com.android.systemui.charging.WirelessChargingLayout.UNKNOWN_BATTE
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
-import static com.android.systemui.statusbar.LightRevealScrimKt.getEnableLightReveal;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -90,7 +89,6 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
-import android.os.UserManager;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings;
@@ -181,6 +179,7 @@ import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyboardShortcuts;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -226,7 +225,6 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
@@ -442,6 +440,7 @@ public class StatusBar extends SystemUI implements DemoMode,
private final KeyguardViewMediator mKeyguardViewMediator;
protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final BrightnessSlider.Factory mBrightnessSliderFactory;
+ private final FeatureFlags mFeatureFlags;
private final List<ExpansionChangedListener> mExpansionChangedListeners;
@@ -622,7 +621,6 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
- private KeyguardUserSwitcher mKeyguardUserSwitcher;
private final UserSwitcherController mUserSwitcherController;
private final NetworkController mNetworkController;
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
@@ -765,7 +763,8 @@ public class StatusBar extends SystemUI implements DemoMode,
Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
NotificationIconAreaController notificationIconAreaController,
- BrightnessSlider.Factory brightnessSliderFactory) {
+ BrightnessSlider.Factory brightnessSliderFactory,
+ FeatureFlags featureFlags) {
super(context);
mNotificationsController = notificationsController;
mLightBarController = lightBarController;
@@ -843,6 +842,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mDemoModeController = demoModeController;
mNotificationIconAreaController = notificationIconAreaController;
mBrightnessSliderFactory = brightnessSliderFactory;
+ mFeatureFlags = featureFlags;
mExpansionChangedListeners = new ArrayList<>();
@@ -1143,8 +1143,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mLockscreenWallpaper = mLockscreenWallpaperLazy.get();
}
- mKeyguardIndicationController.setIndicationArea(
- mNotificationShadeWindowView.findViewById(R.id.keyguard_indication_area));
mNotificationPanelViewController.setKeyguardIndicationController(
mKeyguardIndicationController);
@@ -1186,9 +1184,11 @@ public class StatusBar extends SystemUI implements DemoMode,
mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
- if (getEnableLightReveal()) {
+ if (mFeatureFlags.useNewLockscreenAnimations() && mDozeParameters.getAlwaysOn()) {
mLightRevealScrim.setVisibility(View.VISIBLE);
mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
+ } else {
+ mLightRevealScrim.setVisibility(View.GONE);
}
mNotificationPanelViewController.initDependencies(
@@ -1209,9 +1209,6 @@ public class StatusBar extends SystemUI implements DemoMode,
});
mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
- if (UserManager.get(mContext).isUserSwitcherEnabled()) {
- createUserSwitcher();
- }
// Set up the quick settings tile panel
final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame);
@@ -1438,9 +1435,6 @@ public class StatusBar extends SystemUI implements DemoMode,
// TODO: Bring these out of StatusBar.
mUserInfoControllerImpl.onDensityOrFontScaleChanged();
mUserSwitcherController.onDensityOrFontScaleChanged();
- if (mKeyguardUserSwitcher != null) {
- mKeyguardUserSwitcher.onDensityOrFontScaleChanged();
- }
mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
mHeadsUpManager.onDensityOrFontScaleChanged();
}
@@ -1474,13 +1468,6 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
- protected void createUserSwitcher() {
- mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
- mNotificationShadeWindowView.findViewById(R.id.keyguard_user_switcher),
- mNotificationShadeWindowView.findViewById(R.id.keyguard_header),
- mNotificationPanelViewController);
- }
-
private void inflateStatusBarWindow() {
mNotificationShadeWindowView = mSuperStatusBarViewFactory.getNotificationShadeWindowView();
StatusBarComponent statusBarComponent = mStatusBarComponentBuilder.get()
@@ -3263,7 +3250,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
if (mUserSwitcherController != null && mUserSwitcherController.useFullscreenUserSwitcher()) {
mStatusBarStateController.setState(StatusBarState.FULLSCREEN_USER_SWITCHER);
- } else if (!mPulseExpansionHandler.isWakingToShadeLocked()){
+ } else if (!mPulseExpansionHandler.isWakingToShadeLocked()) {
mStatusBarStateController.setState(StatusBarState.KEYGUARD);
}
updatePanelExpansionForKeyguard();
@@ -3562,15 +3549,15 @@ public class StatusBar extends SystemUI implements DemoMode,
}
return true;
}
+ if (mNotificationPanelViewController.closeUserSwitcherIfOpen()) {
+ return true;
+ }
if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
if (mNotificationPanelViewController.canPanelBeCollapsed()) {
mShadeController.animateCollapsePanels();
}
return true;
}
- if (mKeyguardUserSwitcher != null && mKeyguardUserSwitcher.hideIfNotSimple(true)) {
- return true;
- }
return false;
}
@@ -3619,27 +3606,8 @@ public class StatusBar extends SystemUI implements DemoMode,
updateTheme();
mNavigationBarController.touchAutoDim(mDisplayId);
Trace.beginSection("StatusBar#updateKeyguardState");
- if (mState == StatusBarState.KEYGUARD) {
- mKeyguardIndicationController.setVisible(true);
- if (mKeyguardUserSwitcher != null) {
- mKeyguardUserSwitcher.setKeyguard(true,
- mStatusBarStateController.fromShadeLocked());
- }
- if (mStatusBarView != null) mStatusBarView.removePendingHideExpandedRunnables();
- if (mAmbientIndicationContainer != null) {
- mAmbientIndicationContainer.setVisibility(View.VISIBLE);
- }
- } else {
- mKeyguardIndicationController.setVisible(false);
- if (mKeyguardUserSwitcher != null) {
- mKeyguardUserSwitcher.setKeyguard(false,
- mStatusBarStateController.goingToFullShade() ||
- mState == StatusBarState.SHADE_LOCKED ||
- mStatusBarStateController.fromShadeLocked());
- }
- if (mAmbientIndicationContainer != null) {
- mAmbientIndicationContainer.setVisibility(View.INVISIBLE);
- }
+ if (mState == StatusBarState.KEYGUARD && mStatusBarView != null) {
+ mStatusBarView.removePendingHideExpandedRunnables();
}
updateDozingState();
checkBarModes();
@@ -3651,7 +3619,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onDozeAmountChanged(float linear, float eased) {
- if (getEnableLightReveal()) {
+ if (mFeatureFlags.useNewLockscreenAnimations()) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 00acd7bb6707..862037617374 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -42,8 +42,8 @@ import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusBarWifiView;
import com.android.systemui.statusbar.StatusIconDisplayable;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.NoCallingIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
import java.util.List;
@@ -66,7 +66,7 @@ public interface StatusBarIconController {
/**
* Display the no calling & SMS icons.
*/
- void setNoCallingIcons(String slot, List<NoCallingIconState> states);
+ void setCallIndicatorIcons(String slot, List<CallIndicatorIconState> states);
public void setIconVisibility(String slot, boolean b);
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 5e8d59041fab..f0c8527bcb7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -34,8 +34,8 @@ import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusIconDisplayable;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.NoCallingIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -206,6 +206,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
Collections.reverse(iconStates);
for (MobileIconState state : iconStates) {
+
StatusBarIconHolder holder = mobileSlot.getHolderForTag(state.subId);
if (holder == null) {
holder = StatusBarIconHolder.fromMobileIconState(state);
@@ -218,23 +219,25 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
}
/**
- * Accept a list of NoCallingIconStates, and show them in the same slot
+ * Accept a list of CallIndicatorIconStates, and show them in the same slot
* @param slot StatusBar slot
* @param states All of the no Calling & SMS icon states
*/
@Override
- public void setNoCallingIcons(String slot, List<NoCallingIconState> states) {
+ public void setCallIndicatorIcons(String slot, List<CallIndicatorIconState> states) {
Slot noCallingSlot = getSlot(slot);
int slotIndex = getSlotIndex(slot);
-
- for (NoCallingIconState state : states) {
+ for (CallIndicatorIconState state : states) {
StatusBarIconHolder holder = noCallingSlot.getHolderForTag(state.subId);
if (holder == null) {
- holder = StatusBarIconHolder.fromNoCallingState(mContext, state);
- holder.setVisible(state.visible);
+ holder = StatusBarIconHolder.fromCallIndicatorState(mContext, state);
setIcon(slotIndex, holder);
} else {
- holder.setVisible(state.visible);
+ int resId = state.isNoCalling ? state.noCallingResId : state.callStrengthResId;
+ String contentDescription = state.isNoCalling
+ ? state.noCallingDescription : state.callStrengthDescription;
+ holder.setIcon(new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
+ Icon.createWithResource(mContext, resId), 0, 0, contentDescription));
setIcon(slotIndex, holder);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
index 36a0e63db19f..a1a2d30e9b00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
@@ -22,8 +22,8 @@ import android.graphics.drawable.Icon;
import android.os.UserHandle;
import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.NoCallingIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
/**
@@ -72,14 +72,18 @@ public class StatusBarIconHolder {
}
/**
- * Creates a new StatusBarIconHolder from a NoCallingIconState.
+ * Creates a new StatusBarIconHolder from a CallIndicatorIconState.
*/
- public static StatusBarIconHolder fromNoCallingState(
- Context context, NoCallingIconState state) {
+ public static StatusBarIconHolder fromCallIndicatorState(
+ Context context, CallIndicatorIconState state) {
StatusBarIconHolder holder = new StatusBarIconHolder();
+ int resId = state.isNoCalling ? state.noCallingResId : state.callStrengthResId;
+ String contentDescription = state.isNoCalling
+ ? state.noCallingDescription : state.callStrengthDescription;
holder.mIcon = new StatusBarIcon(UserHandle.SYSTEM, context.getPackageName(),
- Icon.createWithResource(context, state.resId), 0, 0, null);
+ Icon.createWithResource(context, resId), 0, 0, contentDescription);
holder.mTag = state.subId;
+ holder.setVisible(true);
return holder;
}
@@ -92,6 +96,10 @@ public class StatusBarIconHolder {
return mIcon;
}
+ public void setIcon(StatusBarIcon icon) {
+ mIcon = icon;
+ }
+
@Nullable
public WifiIconState getWifiState() {
return mWifiState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 024a0b17b5cc..525f2205f784 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -29,7 +29,6 @@ import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.util.Log;
import android.util.Slog;
-import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
@@ -162,11 +161,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- if (MULTIUSER_DEBUG) {
- mNotificationPanelDebugText = mNotificationPanel.getHeaderDebugInfo();
- mNotificationPanelDebugText.setVisibility(View.VISIBLE);
- }
-
IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
Context.VR_SERVICE));
if (vrManager != null) {
@@ -332,7 +326,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
// Begin old BaseStatusBar.userSwitched
mHeadsUpManager.setUser(newUserId);
// End old BaseStatusBar.userSwitched
- if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
+ if (MULTIUSER_DEBUG) mNotificationPanel.setHeaderDebugInfo("USER " + newUserId);
mCommandQueue.animateCollapsePanels();
if (mReinflateNotificationsOnUserSwitched) {
updateNotificationsOnDensityOrFontScaleChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 36519ac0d808..983b296e006b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -180,8 +180,8 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
@Override
public void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row,
- View clickedView) {
- if (mKeyguardStateController.isShowing()) {
+ View clickedView, boolean deferBouncer, Runnable runnable) {
+ if (!deferBouncer && mKeyguardStateController.isShowing()) {
onLockedRemoteInput(row, clickedView);
} else {
if (row.isChildInGroup() && !row.areChildrenExpanded()) {
@@ -189,7 +189,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
}
row.setUserExpanded(true);
- row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick);
+ row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 027902be7929..ccda7903bdd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -22,6 +22,7 @@ import android.telephony.SubscriptionInfo;
import android.util.ArraySet;
import android.util.Log;
+import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.NetworkController;
@@ -67,7 +68,8 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
private boolean mWifiVisible = false;
private ArrayList<MobileIconState> mMobileStates = new ArrayList<MobileIconState>();
- private ArrayList<NoCallingIconState> mNoCallingStates = new ArrayList<NoCallingIconState>();
+ private ArrayList<CallIndicatorIconState> mCallIndicatorStates =
+ new ArrayList<CallIndicatorIconState>();
private WifiIconState mWifiIconState = new WifiIconState();
public StatusBarSignalPolicy(Context context, StatusBarIconController iconController) {
@@ -201,19 +203,25 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
}
@Override
- public void setNoCallingStatus(boolean noCalling, int subId) {
+ public void setCallIndicator(IconState statusIcon, int subId) {
if (DEBUG) {
- Log.d(TAG, "setNoCallingStatus: "
- + "noCalling = " + noCalling + ","
+ Log.d(TAG, "setCallIndicator: "
+ + "statusIcon = " + statusIcon + ","
+ "subId = " + subId);
}
- NoCallingIconState state = getNoCallingState(subId);
+ CallIndicatorIconState state = getNoCallingState(subId);
if (state == null) {
return;
}
- state.visible = noCalling;
- mIconController.setNoCallingIcons(
- mSlotNoCalling, NoCallingIconState.copyStates(mNoCallingStates));
+ if (statusIcon.icon == R.drawable.ic_qs_no_calling_sms) {
+ state.isNoCalling = statusIcon.visible;
+ state.noCallingDescription = statusIcon.contentDescription;
+ } else {
+ state.callStrengthResId = statusIcon.icon;
+ state.callStrengthDescription = statusIcon.contentDescription;
+ }
+ mIconController.setCallIndicatorIcons(
+ mSlotNoCalling, CallIndicatorIconState.copyStates(mCallIndicatorStates));
}
@Override
@@ -274,8 +282,8 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
}
}
- private NoCallingIconState getNoCallingState(int subId) {
- for (NoCallingIconState state : mNoCallingStates) {
+ private CallIndicatorIconState getNoCallingState(int subId) {
+ for (CallIndicatorIconState state : mCallIndicatorStates) {
if (state.subId == subId) {
return state;
}
@@ -316,23 +324,25 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
}
mIconController.removeAllIconsForSlot(mSlotMobile);
+ mIconController.removeAllIconsForSlot(mSlotNoCalling);
mMobileStates.clear();
- List<NoCallingIconState> noCallingStates = new ArrayList<NoCallingIconState>();
- noCallingStates.addAll(mNoCallingStates);
- mNoCallingStates.clear();
+ List<CallIndicatorIconState> noCallingStates = new ArrayList<CallIndicatorIconState>();
+ noCallingStates.addAll(mCallIndicatorStates);
+ mCallIndicatorStates.clear();
final int n = subs.size();
for (int i = 0; i < n; i++) {
mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId()));
boolean isNewSub = true;
- for (NoCallingIconState state : noCallingStates) {
+ for (CallIndicatorIconState state : noCallingStates) {
if (state.subId == subs.get(i).getSubscriptionId()) {
- mNoCallingStates.add(state);
+ mCallIndicatorStates.add(state);
isNewSub = false;
break;
}
}
if (isNewSub) {
- mNoCallingStates.add(new NoCallingIconState(subs.get(i).getSubscriptionId()));
+ mCallIndicatorStates.add(
+ new CallIndicatorIconState(subs.get(i).getSubscriptionId()));
}
}
}
@@ -426,14 +436,18 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
/**
* Stores the StatusBar state for no Calling & SMS.
*/
- public static class NoCallingIconState {
- public boolean visible;
- public int resId;
+ public static class CallIndicatorIconState {
+ public boolean isNoCalling;
+ public int noCallingResId;
+ public int callStrengthResId;
public int subId;
+ public String noCallingDescription;
+ public String callStrengthDescription;
- private NoCallingIconState(int subId) {
+ private CallIndicatorIconState(int subId) {
this.subId = subId;
- this.resId = R.drawable.ic_qs_no_calling_sms;
+ this.noCallingResId = R.drawable.ic_qs_no_calling_sms;
+ this.callStrengthResId = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
}
@Override
@@ -442,27 +456,36 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
if (o == null || getClass() != o.getClass()) {
return false;
}
- NoCallingIconState that = (NoCallingIconState) o;
- return visible == that.visible
- && resId == that.resId
- && subId == that.subId;
+ CallIndicatorIconState that = (CallIndicatorIconState) o;
+ return isNoCalling == that.isNoCalling
+ && noCallingResId == that.noCallingResId
+ && callStrengthResId == that.callStrengthResId
+ && subId == that.subId
+ && noCallingDescription == that.noCallingDescription
+ && callStrengthDescription == that.callStrengthDescription;
+
}
@Override
public int hashCode() {
- return Objects.hash(visible, resId, subId);
+ return Objects.hash(isNoCalling, noCallingResId,
+ callStrengthResId, subId, noCallingDescription, callStrengthDescription);
}
- private void copyTo(NoCallingIconState other) {
- other.visible = visible;
- other.resId = resId;
+ private void copyTo(CallIndicatorIconState other) {
+ other.isNoCalling = isNoCalling;
+ other.noCallingResId = noCallingResId;
+ other.callStrengthResId = callStrengthResId;
other.subId = subId;
+ other.noCallingDescription = noCallingDescription;
+ other.callStrengthDescription = callStrengthDescription;
}
- private static List<NoCallingIconState> copyStates(List<NoCallingIconState> inStates) {
- ArrayList<NoCallingIconState> outStates = new ArrayList<>();
- for (NoCallingIconState state : inStates) {
- NoCallingIconState copy = new NoCallingIconState(state.subId);
+ private static List<CallIndicatorIconState> copyStates(
+ List<CallIndicatorIconState> inStates) {
+ ArrayList<CallIndicatorIconState> outStates = new ArrayList<>();
+ for (CallIndicatorIconState state : inStates) {
+ CallIndicatorIconState copy = new CallIndicatorIconState(state.subId);
state.copyTo(copy);
outStates.add(copy);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 9e9533d0e199..b572c57590ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -47,6 +47,7 @@ import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.settings.brightness.BrightnessSlider;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -200,7 +201,8 @@ public interface StatusBarPhoneModule {
DismissCallbackRegistry dismissCallbackRegistry,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
NotificationIconAreaController notificationIconAreaController,
- BrightnessSlider.Factory brightnessSliderFactory) {
+ BrightnessSlider.Factory brightnessSliderFactory,
+ FeatureFlags featureFlags) {
return new StatusBar(
context,
notificationsController,
@@ -279,6 +281,7 @@ public interface StatusBarPhoneModule {
notificationShadeDepthController,
statusBarTouchableRegionManager,
notificationIconAreaController,
- brightnessSliderFactory);
+ brightnessSliderFactory,
+ featureFlags);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index 5d61da57d418..c1031a2b3c7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -25,6 +25,8 @@ import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
@@ -34,6 +36,7 @@ import java.util.List;
* the current or specified Looper.
*/
public class CallbackHandler extends Handler implements EmergencyListener, SignalCallback {
+ private static final String TAG = "CallbackHandler";
private static final int MSG_EMERGENCE_CHANGED = 0;
private static final int MSG_SUBS_CHANGED = 1;
private static final int MSG_NO_SIM_VISIBLE_CHANGED = 2;
@@ -42,11 +45,19 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa
private static final int MSG_MOBILE_DATA_ENABLED_CHANGED = 5;
private static final int MSG_ADD_REMOVE_EMERGENCY = 6;
private static final int MSG_ADD_REMOVE_SIGNAL = 7;
+ private static final int HISTORY_SIZE = 64;
+ private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
// All the callbacks.
private final ArrayList<EmergencyListener> mEmergencyListeners = new ArrayList<>();
private final ArrayList<SignalCallback> mSignalCallbacks = new ArrayList<>();
+ // Save the previous HISTORY_SIZE states for logging.
+ private final String[] mHistory = new String[HISTORY_SIZE];
+ // Where to copy the next state into.
+ private int mHistoryIndex;
+ private String mLastCallback;
+
public CallbackHandler() {
super(Looper.getMainLooper());
}
@@ -111,12 +122,27 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa
public void setWifiIndicators(final boolean enabled, final IconState statusIcon,
final IconState qsIcon, final boolean activityIn, final boolean activityOut,
final String description, boolean isTransient, String secondaryLabel) {
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("setWifiIndicators: ")
+ .append("enabled=").append(enabled).append(",")
+ .append("statusIcon=").append(statusIcon).append(",")
+ .append("qsIcon=").append(qsIcon).append(",")
+ .append("activityIn=").append(activityIn).append(",")
+ .append("activityOut=").append(activityOut).append(",")
+ .append("description=").append(description).append(",")
+ .append("isTransient=").append(isTransient).append(",")
+ .append("secondaryLabel=").append(secondaryLabel)
+ .toString();
+ recordLastCallback(log);
post(() -> {
for (SignalCallback callback : mSignalCallbacks) {
callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut,
description, isTransient, secondaryLabel);
}
});
+
+
}
@Override
@@ -125,6 +151,25 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa
final boolean activityOut, final int volteIcon, final CharSequence typeContentDescription,
CharSequence typeContentDescriptionHtml, final CharSequence description,
final boolean isWide, final int subId, boolean roaming, boolean showTriangle) {
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("setMobileDataIndicators: ")
+ .append("statusIcon=").append(statusIcon).append(",")
+ .append("qsIcon=").append(qsIcon).append(",")
+ .append("statusType=").append(statusType).append(",")
+ .append("qsType=").append(qsType).append(",")
+ .append("activityIn=").append(activityIn).append(",")
+ .append("activityOut=").append(activityOut).append(",")
+ .append("typeContentDescription=").append(typeContentDescription).append(",")
+ .append("typeContentDescriptionHtml=").append(typeContentDescriptionHtml)
+ .append(",")
+ .append("description=").append(description).append(",")
+ .append("isWide=").append(isWide).append(",")
+ .append("subId=").append(subId).append(",")
+ .append("roaming=").append(roaming).append(",")
+ .append("showTriangle=").append(showTriangle)
+ .toString();
+ recordLastCallback(log);
post(() -> {
for (SignalCallback signalCluster : mSignalCallbacks) {
signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType,
@@ -138,6 +183,20 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa
@Override
public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
boolean noNetworksAvailable) {
+ String currentCallback = new StringBuilder()
+ .append("setConnectivityStatus: ")
+ .append("noDefaultNetwork=").append(noDefaultNetwork).append(",")
+ .append("noValidatedNetwork=").append(noValidatedNetwork).append(",")
+ .append("noNetworksAvailable=").append(noNetworksAvailable)
+ .toString();
+ if (!currentCallback.equals(mLastCallback)) {
+ mLastCallback = currentCallback;
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append(currentCallback).append(",")
+ .toString();
+ recordLastCallback(log);
+ }
post(() -> {
for (SignalCallback signalCluster : mSignalCallbacks) {
signalCluster.setConnectivityStatus(
@@ -147,16 +206,41 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa
}
@Override
- public void setNoCallingStatus(boolean noCalling, int subId) {
+ public void setCallIndicator(IconState statusIcon, int subId) {
+ String currentCallback = new StringBuilder()
+ .append("setCallIndicator: ")
+ .append("statusIcon=").append(statusIcon).append(",")
+ .append("subId=").append(subId)
+ .toString();
+ if (!currentCallback.equals(mLastCallback)) {
+ mLastCallback = currentCallback;
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append(currentCallback).append(",")
+ .toString();
+ recordLastCallback(log);
+ }
post(() -> {
for (SignalCallback signalCluster : mSignalCallbacks) {
- signalCluster.setNoCallingStatus(noCalling, subId);
+ signalCluster.setCallIndicator(statusIcon, subId);
}
});
}
@Override
public void setSubs(List<SubscriptionInfo> subs) {
+ String currentCallback = new StringBuilder()
+ .append("setSubs: ")
+ .append("subs=").append(subs == null ? "" : subs.toString())
+ .toString();
+ if (!currentCallback.equals(mLastCallback)) {
+ mLastCallback = currentCallback;
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append(currentCallback).append(",")
+ .toString();
+ recordLastCallback(log);
+ }
obtainMessage(MSG_SUBS_CHANGED, subs).sendToTarget();
}
@@ -177,11 +261,29 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa
@Override
public void setEthernetIndicators(IconState icon) {
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("setEthernetIndicators: ")
+ .append("icon=").append(icon)
+ .toString();
+ recordLastCallback(log);
obtainMessage(MSG_ETHERNET_CHANGED, icon).sendToTarget();;
}
@Override
public void setIsAirplaneMode(IconState icon) {
+ String currentCallback = new StringBuilder()
+ .append("setIsAirplaneMode: ")
+ .append("icon=").append(icon)
+ .toString();
+ if (!currentCallback.equals(mLastCallback)) {
+ mLastCallback = currentCallback;
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append(currentCallback).append(",")
+ .toString();
+ recordLastCallback(log);
+ }
obtainMessage(MSG_AIRPLANE_MODE_CHANGED, icon).sendToTarget();;
}
@@ -193,4 +295,28 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa
obtainMessage(MSG_ADD_REMOVE_SIGNAL, listening ? 1 : 0, 0, listener).sendToTarget();
}
+ protected void recordLastCallback(String callback) {
+ mHistory[mHistoryIndex] = callback;
+ mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
+ }
+
+ /**
+ * Dump the Callback logs
+ */
+ public void dump(PrintWriter pw) {
+ pw.println(" - CallbackHandler -----");
+ int size = 0;
+ for (int i = 0; i < HISTORY_SIZE; i++) {
+ if (mHistory[i] != null) {
+ size++;
+ }
+ }
+ // Print out the previous states in ordered number.
+ for (int i = mHistoryIndex + HISTORY_SIZE - 1;
+ i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
+ pw.println(" Previous Callback(" + (mHistoryIndex + HISTORY_SIZE - i) + "): "
+ + mHistory[i & (HISTORY_SIZE - 1)]);
+ }
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
deleted file mode 100644
index cbc8405cc057..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Notification;
-import android.widget.Button;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Holder for inflated smart replies and actions. These objects should be inflated on a background
- * thread, to later be accessed and modified on the (performance critical) UI thread.
- */
-public class InflatedSmartReplies {
- @Nullable private final SmartReplyView mSmartReplyView;
- @Nullable private final List<Button> mSmartSuggestionButtons;
- @NonNull private final SmartRepliesAndActions mSmartRepliesAndActions;
-
- public InflatedSmartReplies(
- @Nullable SmartReplyView smartReplyView,
- @Nullable List<Button> smartSuggestionButtons,
- @NonNull SmartRepliesAndActions smartRepliesAndActions) {
- mSmartReplyView = smartReplyView;
- mSmartSuggestionButtons = smartSuggestionButtons;
- mSmartRepliesAndActions = smartRepliesAndActions;
- }
-
- @Nullable public SmartReplyView getSmartReplyView() {
- return mSmartReplyView;
- }
-
- @Nullable public List<Button> getSmartSuggestionButtons() {
- return mSmartSuggestionButtons;
- }
-
- @NonNull public SmartRepliesAndActions getSmartRepliesAndActions() {
- return mSmartRepliesAndActions;
- }
-
- /**
- * A storage for smart replies and smart action.
- */
- public static class SmartRepliesAndActions {
- @Nullable public final SmartReplyView.SmartReplies smartReplies;
- @Nullable public final SmartReplyView.SmartActions smartActions;
-
- SmartRepliesAndActions(
- @Nullable SmartReplyView.SmartReplies smartReplies,
- @Nullable SmartReplyView.SmartActions smartActions) {
- this.smartReplies = smartReplies;
- this.smartActions = smartActions;
- }
-
- @NonNull public List<CharSequence> getSmartReplies() {
- return smartReplies == null ? Collections.emptyList() : smartReplies.choices;
- }
-
- @NonNull public List<Notification.Action> getSmartActions() {
- return smartActions == null ? Collections.emptyList() : smartActions.actions;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyState.kt
new file mode 100644
index 000000000000..1f8b84ff1ec5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyState.kt
@@ -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.systemui.statusbar.policy
+
+import android.app.Notification
+import com.android.systemui.statusbar.policy.SmartReplyView.SmartActions
+import com.android.systemui.statusbar.policy.SmartReplyView.SmartReplies
+
+/**
+ * A storage for smart replies, smart actions, and related state
+ */
+class InflatedSmartReplyState internal constructor(
+ val smartReplies: SmartReplies?,
+ val smartActions: SmartActions?,
+ val suppressedActions: SuppressedActions?,
+ val hasPhishingAction: Boolean
+) {
+ val smartRepliesList: List<CharSequence>
+ get() = smartReplies?.choices ?: emptyList()
+ val smartActionsList: List<Notification.Action>
+ get() = smartActions?.actions ?: emptyList()
+ val suppressedActionIndices: List<Int>
+ get() = suppressedActions?.suppressedActionIndices ?: emptyList()
+
+ /**
+ * Data class for standard actions suppressed by the smart actions.
+ */
+ class SuppressedActions(val suppressedActionIndices: List<Int>)
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyViewHolder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyViewHolder.kt
new file mode 100644
index 000000000000..4f69cd6a1367
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyViewHolder.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.policy
+
+import android.widget.Button
+
+/**
+ * Holder for inflated smart replies and actions. These objects should be inflated on a background
+ * thread, to later be accessed and modified on the (performance critical) UI thread.
+ */
+class InflatedSmartReplyViewHolder(
+ val smartReplyView: SmartReplyView?,
+ val smartSuggestionButtons: List<Button>?
+) \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
new file mode 100644
index 000000000000..38f3bc891394
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -0,0 +1,345 @@
+/*
+ * 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.statusbar.policy;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.DataSetObserver;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardConstants;
+import com.android.keyguard.KeyguardVisibilityHelper;
+import com.android.keyguard.dagger.KeyguardUserSwitcherScope;
+import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.UserAvatarView;
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+
+/**
+ * Manages the user switch on the Keyguard that is used for opening the QS user panel.
+ */
+@KeyguardUserSwitcherScope
+public class KeyguardQsUserSwitchController extends ViewController<UserAvatarView> {
+
+ private static final String TAG = "KeyguardQsUserSwitchController";
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+
+ private static final AnimationProperties ANIMATION_PROPERTIES =
+ new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+
+ private final Context mContext;
+ private Resources mResources;
+ private final UserSwitcherController mUserSwitcherController;
+ private final ScreenLifecycle mScreenLifecycle;
+ private UserSwitcherController.BaseUserAdapter mAdapter;
+ private final KeyguardStateController mKeyguardStateController;
+ protected final SysuiStatusBarStateController mStatusBarStateController;
+ private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
+ private final KeyguardUserDetailAdapter mUserDetailAdapter;
+ private NotificationPanelViewController mNotificationPanelViewController;
+ private UserManager mUserManager;
+ UserSwitcherController.UserRecord mCurrentUser;
+
+ // State info for the user switch and keyguard
+ private int mBarState;
+ private float mDarkAmount;
+
+ private final StatusBarStateController.StateListener mStatusBarStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onStateChanged(int newState) {
+ if (DEBUG) Log.d(TAG, String.format("onStateChanged: newState=%d", newState));
+
+ boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
+ boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
+ int oldState = mBarState;
+ mBarState = newState;
+
+ setKeyguardQsUserSwitchVisibility(
+ newState,
+ keyguardFadingAway,
+ goingToFullShade,
+ oldState);
+ }
+
+ @Override
+ public void onDozeAmountChanged(float linearAmount, float amount) {
+ if (DEBUG) {
+ Log.d(TAG, String.format("onDozeAmountChanged: linearAmount=%f amount=%f",
+ linearAmount, amount));
+ }
+ setDarkAmount(amount);
+ }
+ };
+
+ @Inject
+ public KeyguardQsUserSwitchController(
+ UserAvatarView view,
+ Context context,
+ @Main Resources resources,
+ UserManager userManager,
+ ScreenLifecycle screenLifecycle,
+ UserSwitcherController userSwitcherController,
+ KeyguardStateController keyguardStateController,
+ SysuiStatusBarStateController statusBarStateController,
+ DozeParameters dozeParameters,
+ UiEventLogger uiEventLogger) {
+ super(view);
+ if (DEBUG) Log.d(TAG, "New KeyguardQsUserSwitchController");
+ mContext = context;
+ mResources = resources;
+ mUserManager = userManager;
+ mScreenLifecycle = screenLifecycle;
+ mUserSwitcherController = userSwitcherController;
+ mKeyguardStateController = keyguardStateController;
+ mStatusBarStateController = statusBarStateController;
+ mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView,
+ keyguardStateController, dozeParameters);
+ mUserDetailAdapter = new KeyguardUserDetailAdapter(mUserSwitcherController, mContext,
+ uiEventLogger);
+ }
+
+ @Override
+ protected void onInit() {
+ super.onInit();
+ if (DEBUG) Log.d(TAG, "onInit");
+ mAdapter = new UserSwitcherController.BaseUserAdapter(mUserSwitcherController) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return null;
+ }
+ };
+
+ mView.setOnClickListener(v -> {
+ if (isListAnimating()) {
+ return;
+ }
+
+ // Tapping anywhere in the view will open QS user panel
+ openQsUserPanel();
+ });
+
+ updateView(true /* forceUpdate */);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ if (DEBUG) Log.d(TAG, "onViewAttached");
+ mAdapter.registerDataSetObserver(mDataSetObserver);
+ mDataSetObserver.onChanged();
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ if (DEBUG) Log.d(TAG, "onViewDetached");
+
+ mAdapter.unregisterDataSetObserver(mDataSetObserver);
+ mStatusBarStateController.removeCallback(mStatusBarStateListener);
+ }
+
+ public final DataSetObserver mDataSetObserver = new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ updateView(false /* forceUpdate */);
+ }
+ };
+
+ /**
+ * @return true if the current user has changed
+ */
+ private boolean updateCurrentUser() {
+ UserSwitcherController.UserRecord previousUser = mCurrentUser;
+ mCurrentUser = null;
+ for (int i = 0; i < mAdapter.getCount(); i++) {
+ UserSwitcherController.UserRecord r = mAdapter.getItem(i);
+ if (r.isCurrent) {
+ mCurrentUser = r;
+ return !mCurrentUser.equals(previousUser);
+ }
+ }
+ return mCurrentUser == null && previousUser != null;
+ }
+
+ /**
+ * @param forceUpdate whether to update view even if current user did not change
+ */
+ private void updateView(boolean forceUpdate) {
+ if (!updateCurrentUser() && !forceUpdate) {
+ return;
+ }
+
+ if (mCurrentUser == null) {
+ mView.setVisibility(View.GONE);
+ return;
+ }
+
+ mView.setVisibility(View.VISIBLE);
+
+ String currentUserName = mCurrentUser.info.name;
+ String contentDescription = null;
+
+ if (!TextUtils.isEmpty(currentUserName)) {
+ contentDescription = mContext.getString(
+ R.string.accessibility_quick_settings_user,
+ currentUserName);
+ }
+
+ if (!TextUtils.equals(mView.getContentDescription(), contentDescription)) {
+ mView.setContentDescription(contentDescription);
+ }
+
+ if (mCurrentUser.picture == null) {
+ mView.setDrawableWithBadge(getDrawable(mCurrentUser).mutate(),
+ mCurrentUser.resolveId());
+ } else {
+ int avatarSize =
+ (int) mResources.getDimension(R.dimen.kg_framed_avatar_size);
+ Drawable drawable = new CircleFramedDrawable(mCurrentUser.picture, avatarSize);
+ drawable.setColorFilter(
+ mCurrentUser.isSwitchToEnabled ? null
+ : mAdapter.getDisabledUserAvatarColorFilter());
+ mView.setDrawableWithBadge(drawable, mCurrentUser.info.id);
+ }
+ }
+
+ Drawable getDrawable(UserSwitcherController.UserRecord item) {
+ Drawable drawable;
+ if (item.isCurrent && item.isGuest) {
+ drawable = mContext.getDrawable(R.drawable.ic_avatar_guest_user);
+ } else {
+ drawable = mAdapter.getIconDrawable(mContext, item);
+ }
+
+ int iconColorRes;
+ if (item.isSwitchToEnabled) {
+ iconColorRes = R.color.kg_user_switcher_avatar_icon_color;
+ } else {
+ iconColorRes = R.color.kg_user_switcher_restricted_avatar_icon_color;
+ }
+ drawable.setTint(mResources.getColor(iconColorRes, mContext.getTheme()));
+
+ Drawable bg = mContext.getDrawable(R.drawable.kg_bg_avatar);
+ drawable = new LayerDrawable(new Drawable[]{bg, drawable});
+ return drawable;
+ }
+
+ /**
+ * Get the height of the keyguard user switcher view when closed.
+ */
+ public int getUserIconHeight() {
+ return mView.getHeight();
+ }
+
+ /**
+ * Set the visibility of the user avatar view based on some new state.
+ */
+ public void setKeyguardQsUserSwitchVisibility(
+ int statusBarState,
+ boolean keyguardFadingAway,
+ boolean goingToFullShade,
+ int oldStatusBarState) {
+ mKeyguardVisibilityHelper.setViewVisibility(
+ statusBarState, keyguardFadingAway, goingToFullShade, oldStatusBarState);
+ }
+
+ /**
+ * Update position of the view with an optional animation
+ */
+ public void updatePosition(int x, int y, boolean animate) {
+ PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, ANIMATION_PROPERTIES, animate);
+ PropertyAnimator.setProperty(mView, AnimatableProperty.TRANSLATION_X, -Math.abs(x),
+ ANIMATION_PROPERTIES, animate);
+ }
+
+ /**
+ * Set keyguard user avatar view alpha.
+ */
+ public void setAlpha(float alpha) {
+ if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
+ mView.setAlpha(alpha);
+ }
+ }
+
+ /**
+ * Set the amount (ratio) that the device has transitioned to doze.
+ *
+ * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
+ */
+ private void setDarkAmount(float darkAmount) {
+ boolean isAwake = darkAmount != 0;
+ if (darkAmount == mDarkAmount) {
+ return;
+ }
+ mDarkAmount = darkAmount;
+ mView.setVisibility(isAwake ? View.VISIBLE : View.GONE);
+ }
+
+ private boolean isListAnimating() {
+ return mKeyguardVisibilityHelper.isVisibilityAnimating();
+ }
+
+ private void openQsUserPanel() {
+ mNotificationPanelViewController.expandWithQsDetail(mUserDetailAdapter);
+ }
+
+ public void setNotificationPanelViewController(
+ NotificationPanelViewController notificationPanelViewController) {
+ mNotificationPanelViewController = notificationPanelViewController;
+ }
+
+ class KeyguardUserDetailAdapter extends UserSwitcherController.UserDetailAdapter {
+ KeyguardUserDetailAdapter(UserSwitcherController userSwitcherController, Context context,
+ UiEventLogger uiEventLogger) {
+ super(userSwitcherController, context, uiEventLogger);
+ }
+
+ @Override
+ public boolean shouldAnimate() {
+ return false;
+ }
+
+ @Override
+ public boolean onDoneButtonClicked() {
+ if (mNotificationPanelViewController != null) {
+ mNotificationPanelViewController.animateCloseQs(true /* animateAway */);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
index 07433e13104c..0649478a42aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
@@ -17,8 +17,15 @@
package com.android.systemui.statusbar.policy;
import android.content.Context;
+import android.graphics.Color;
import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import androidx.core.graphics.ColorUtils;
+
+import com.android.keyguard.KeyguardConstants;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.qs.tiles.UserDetailItemView;
@@ -27,6 +34,14 @@ import com.android.systemui.qs.tiles.UserDetailItemView;
*/
public class KeyguardUserDetailItemView extends UserDetailItemView {
+ private static final String TAG = "KeyguardUserDetailItemView";
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+
+ private static final int ANIMATION_DURATION_FADE_NAME = 240;
+
+ private float mDarkAmount;
+ private int mTextColor;
+
public KeyguardUserDetailItemView(Context context) {
this(context, null);
}
@@ -48,4 +63,89 @@ public class KeyguardUserDetailItemView extends UserDetailItemView {
protected int getFontSizeDimen() {
return R.dimen.kg_user_switcher_text_size;
}
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mTextColor = mName.getCurrentTextColor();
+ updateDark();
+ }
+
+ /**
+ * Update visibility of this view.
+ *
+ * @param showItem If true, this item is visible on the screen to the user. Generally this
+ * means that the item would be clickable. If false, item visibility will be
+ * set to GONE and hidden entirely.
+ * @param showTextName Whether or not the name should be shown next to the icon. If false,
+ * only the icon is shown.
+ * @param animate Whether the transition should be animated. Note, this only applies to
+ * animating the text name. The item itself will not animate (i.e. fade in/out).
+ * Instead, we delegate that to the parent view.
+ */
+ void updateVisibilities(boolean showItem, boolean showTextName, boolean animate) {
+ if (DEBUG) {
+ Log.d(TAG, String.format("updateVisibilities itemIsShown=%b nameIsShown=%b animate=%b",
+ showItem, showTextName, animate));
+ }
+
+ getBackground().setAlpha((showItem && showTextName) ? 255 : 0);
+
+ if (showItem) {
+ if (showTextName) {
+ mName.setVisibility(View.VISIBLE);
+ if (animate) {
+ mName.setAlpha(0f);
+ mName.animate()
+ .alpha(1f)
+ .setDuration(ANIMATION_DURATION_FADE_NAME)
+ .setInterpolator(Interpolators.ALPHA_IN);
+ } else {
+ mName.setAlpha(1f);
+ }
+ } else {
+ if (animate) {
+ mName.setVisibility(View.VISIBLE);
+ mName.setAlpha(1f);
+ mName.animate()
+ .alpha(0f)
+ .setDuration(ANIMATION_DURATION_FADE_NAME)
+ .setInterpolator(Interpolators.ALPHA_OUT)
+ .withEndAction(() -> {
+ mName.setVisibility(View.GONE);
+ mName.setAlpha(1f);
+ });
+ } else {
+ mName.setVisibility(View.GONE);
+ mName.setAlpha(1f);
+ }
+ }
+ setVisibility(View.VISIBLE);
+ setAlpha(1f);
+ } else {
+ // If item isn't shown, don't animate. The parent class will animate the view instead
+ setVisibility(View.GONE);
+ setAlpha(1f);
+ mName.setVisibility(showTextName ? View.VISIBLE : View.GONE);
+ mName.setAlpha(1f);
+ }
+ }
+
+ /**
+ * Set the amount (ratio) that the device has transitioned to doze.
+ *
+ * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
+ */
+ public void setDarkAmount(float darkAmount) {
+ if (mDarkAmount == darkAmount) {
+ return;
+ }
+ mDarkAmount = darkAmount;
+ updateDark();
+ }
+
+ private void updateDark() {
+ final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
+ mName.setTextColor(blendedTextColor);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
deleted file mode 100644
index 90f557753132..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.policy;
-
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA;
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.database.DataSetObserver;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.widget.FrameLayout;
-
-import com.android.settingslib.animation.AppearAnimationUtils;
-import com.android.settingslib.drawable.CircleFramedDrawable;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.qs.tiles.UserDetailItemView;
-import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-
-import java.util.ArrayList;
-
-/**
- * Manages the user switcher on the Keyguard.
- */
-public class KeyguardUserSwitcher {
-
- private static final String TAG = "KeyguardUserSwitcher";
- private static final boolean ALWAYS_ON = false;
-
- private final Container mUserSwitcherContainer;
- private final KeyguardStatusBarView mStatusBarView;
- private final KeyguardUserAdapter mAdapter;
- private final AppearAnimationUtils mAppearAnimationUtils;
- private final KeyguardUserSwitcherScrim mBackground;
-
- private ViewGroup mUserSwitcher;
- private ObjectAnimator mBgAnimator;
- private UserSwitcherController mUserSwitcherController;
- private boolean mAnimating;
-
- public KeyguardUserSwitcher(Context context, ViewStub userSwitcher,
- KeyguardStatusBarView statusBarView,
- NotificationPanelViewController panelViewController) {
- boolean keyguardUserSwitcherEnabled =
- context.getResources().getBoolean(
- com.android.internal.R.bool.config_keyguardUserSwitcher) || ALWAYS_ON;
- UserSwitcherController userSwitcherController = Dependency.get(UserSwitcherController.class);
- if (userSwitcherController != null && keyguardUserSwitcherEnabled) {
- mUserSwitcherContainer = (Container) userSwitcher.inflate();
- mBackground = new KeyguardUserSwitcherScrim(context);
- reinflateViews();
- mStatusBarView = statusBarView;
- mStatusBarView.setKeyguardUserSwitcher(this);
- panelViewController.setKeyguardUserSwitcher(this);
- mAdapter = new KeyguardUserAdapter(context, userSwitcherController, this);
- mAdapter.registerDataSetObserver(mDataSetObserver);
- mUserSwitcherController = userSwitcherController;
- mAppearAnimationUtils = new AppearAnimationUtils(context, 400, -0.5f, 0.5f,
- Interpolators.FAST_OUT_SLOW_IN);
- mUserSwitcherContainer.setKeyguardUserSwitcher(this);
- } else {
- mUserSwitcherContainer = null;
- mStatusBarView = null;
- mAdapter = null;
- mAppearAnimationUtils = null;
- mBackground = null;
- }
- }
-
- private void reinflateViews() {
- if (mUserSwitcher != null) {
- mUserSwitcher.setBackground(null);
- mUserSwitcher.removeOnLayoutChangeListener(mBackground);
- }
- mUserSwitcherContainer.removeAllViews();
-
- LayoutInflater.from(mUserSwitcherContainer.getContext())
- .inflate(R.layout.keyguard_user_switcher_inner, mUserSwitcherContainer);
-
- mUserSwitcher = (ViewGroup) mUserSwitcherContainer.findViewById(
- R.id.keyguard_user_switcher_inner);
- mUserSwitcher.addOnLayoutChangeListener(mBackground);
- mUserSwitcher.setBackground(mBackground);
- }
-
- public void setKeyguard(boolean keyguard, boolean animate) {
- if (mUserSwitcher != null) {
- if (keyguard && shouldExpandByDefault()) {
- show(animate);
- } else {
- hide(animate);
- }
- }
- }
-
- /**
- * @return true if the user switcher should be expanded by default on the lock screen.
- * @see android.os.UserManager#isUserSwitcherEnabled()
- */
- private boolean shouldExpandByDefault() {
- return (mUserSwitcherController != null) && mUserSwitcherController.isSimpleUserSwitcher();
- }
-
- public void show(boolean animate) {
- if (mUserSwitcher != null && mUserSwitcherContainer.getVisibility() != View.VISIBLE) {
- cancelAnimations();
- mAdapter.refresh();
- mUserSwitcherContainer.setVisibility(View.VISIBLE);
- mStatusBarView.setKeyguardUserSwitcherShowing(true, animate);
- if (animate) {
- startAppearAnimation();
- }
- }
- }
-
- private boolean hide(boolean animate) {
- if (mUserSwitcher != null && mUserSwitcherContainer.getVisibility() == View.VISIBLE) {
- cancelAnimations();
- if (animate) {
- startDisappearAnimation();
- } else {
- mUserSwitcherContainer.setVisibility(View.GONE);
- }
- mStatusBarView.setKeyguardUserSwitcherShowing(false, animate);
- return true;
- }
- return false;
- }
-
- private void cancelAnimations() {
- int count = mUserSwitcher.getChildCount();
- for (int i = 0; i < count; i++) {
- mUserSwitcher.getChildAt(i).animate().cancel();
- }
- if (mBgAnimator != null) {
- mBgAnimator.cancel();
- }
- mUserSwitcher.animate().cancel();
- mAnimating = false;
- }
-
- private void startAppearAnimation() {
- int count = mUserSwitcher.getChildCount();
- View[] objects = new View[count];
- for (int i = 0; i < count; i++) {
- objects[i] = mUserSwitcher.getChildAt(i);
- }
- mUserSwitcher.setClipChildren(false);
- mUserSwitcher.setClipToPadding(false);
- mAppearAnimationUtils.startAnimation(objects, new Runnable() {
- @Override
- public void run() {
- mUserSwitcher.setClipChildren(true);
- mUserSwitcher.setClipToPadding(true);
- }
- });
- mAnimating = true;
- mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255);
- mBgAnimator.setDuration(400);
- mBgAnimator.setInterpolator(Interpolators.ALPHA_IN);
- mBgAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mBgAnimator = null;
- mAnimating = false;
- }
- });
- mBgAnimator.start();
- }
-
- private void startDisappearAnimation() {
- mAnimating = true;
- mUserSwitcher.animate()
- .alpha(0f)
- .setDuration(300)
- .setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mUserSwitcherContainer.setVisibility(View.GONE);
- mUserSwitcher.setAlpha(1f);
- mAnimating = false;
- }
- });
- }
-
- private void refresh() {
- final int childCount = mUserSwitcher.getChildCount();
- final int adapterCount = mAdapter.getCount();
- final int N = Math.max(childCount, adapterCount);
- for (int i = 0; i < N; i++) {
- if (i < adapterCount) {
- View oldView = null;
- if (i < childCount) {
- oldView = mUserSwitcher.getChildAt(i);
- }
- View newView = mAdapter.getView(i, oldView, mUserSwitcher);
- if (oldView == null) {
- // We ran out of existing views. Add it at the end.
- mUserSwitcher.addView(newView);
- } else if (oldView != newView) {
- // We couldn't rebind the view. Replace it.
- mUserSwitcher.removeViewAt(i);
- mUserSwitcher.addView(newView, i);
- }
- } else {
- int lastIndex = mUserSwitcher.getChildCount() - 1;
- mUserSwitcher.removeViewAt(lastIndex);
- }
- }
- }
-
- public boolean hideIfNotSimple(boolean animate) {
- if (mUserSwitcherContainer != null && !mUserSwitcherController.isSimpleUserSwitcher()) {
- return hide(animate);
- }
- return false;
- }
-
- boolean isAnimating() {
- return mAnimating;
- }
-
- public final DataSetObserver mDataSetObserver = new DataSetObserver() {
- @Override
- public void onChanged() {
- refresh();
- }
- };
-
- public void onDensityOrFontScaleChanged() {
- if (mUserSwitcherContainer != null) {
- reinflateViews();
- refresh();
- }
- }
-
- static class KeyguardUserAdapter extends
- UserSwitcherController.BaseUserAdapter implements View.OnClickListener {
-
- private Context mContext;
- private KeyguardUserSwitcher mKeyguardUserSwitcher;
- private View mCurrentUserView;
- // List of users where the first entry is always the current user
- private ArrayList<UserSwitcherController.UserRecord> mUsersOrdered = new ArrayList<>();
-
- KeyguardUserAdapter(Context context, UserSwitcherController controller,
- KeyguardUserSwitcher kgu) {
- super(controller);
- mContext = context;
- mKeyguardUserSwitcher = kgu;
- }
-
- @Override
- public void notifyDataSetChanged() {
- refreshUserOrder();
- super.notifyDataSetChanged();
- }
-
- void refreshUserOrder() {
- ArrayList<UserSwitcherController.UserRecord> users = super.getUsers();
- mUsersOrdered = new ArrayList<>(users.size());
- for (int i = 0; i < users.size(); i++) {
- UserSwitcherController.UserRecord record = users.get(i);
- if (record.isCurrent) {
- mUsersOrdered.add(0, record);
- } else {
- mUsersOrdered.add(record);
- }
- }
- }
-
- @Override
- protected ArrayList<UserSwitcherController.UserRecord> getUsers() {
- return mUsersOrdered;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- UserSwitcherController.UserRecord item = getItem(position);
- return createUserDetailItemView(convertView, parent, item);
- }
-
- KeyguardUserDetailItemView convertOrInflate(View convertView, ViewGroup parent) {
- if (!(convertView instanceof KeyguardUserDetailItemView)
- || !(convertView.getTag() instanceof UserSwitcherController.UserRecord)) {
- convertView = LayoutInflater.from(mContext).inflate(
- R.layout.keyguard_user_switcher_item, parent, false);
- }
- return (KeyguardUserDetailItemView) convertView;
- }
-
- UserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent,
- UserSwitcherController.UserRecord item) {
- KeyguardUserDetailItemView v = convertOrInflate(convertView, parent);
- if (!item.isCurrent || item.isGuest) {
- v.setOnClickListener(this);
- } else {
- v.setOnClickListener(null);
- v.setClickable(false);
- }
-
- String name = getName(mContext, item);
- if (item.picture == null) {
- v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId());
- } else {
- int avatarSize =
- (int) mContext.getResources().getDimension(R.dimen.kg_framed_avatar_size);
- Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize);
- drawable.setColorFilter(
- item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter());
- v.bind(name, drawable, item.info.id);
- }
- v.setActivated(item.isCurrent);
- v.setDisabledByAdmin(item.isDisabledByAdmin);
- v.setEnabled(item.isSwitchToEnabled);
- v.setAlpha(v.isEnabled() ? USER_SWITCH_ENABLED_ALPHA : USER_SWITCH_DISABLED_ALPHA);
-
- if (item.isCurrent) {
- mCurrentUserView = v;
- }
- v.setTag(item);
- return v;
- }
-
- private static Drawable getDrawable(Context context,
- UserSwitcherController.UserRecord item) {
- Drawable drawable = getIconDrawable(context, item);
- int iconColorRes;
- if (item.isCurrent) {
- iconColorRes = R.color.kg_user_switcher_selected_avatar_icon_color;
- } else if (!item.isSwitchToEnabled) {
- iconColorRes = R.color.GM2_grey_600;
- } else {
- iconColorRes = R.color.kg_user_switcher_avatar_icon_color;
- }
- drawable.setTint(context.getResources().getColor(iconColorRes, context.getTheme()));
-
- if (item.isCurrent) {
- Drawable bg = context.getDrawable(R.drawable.bg_avatar_selected);
- drawable = new LayerDrawable(new Drawable[]{bg, drawable});
- }
-
- return drawable;
- }
-
- @Override
- public void onClick(View v) {
- UserSwitcherController.UserRecord user = (UserSwitcherController.UserRecord) v.getTag();
- if (user.isCurrent && !user.isGuest) {
- // Close the switcher if tapping the current user. Guest is excluded because
- // tapping the guest user while it's current clears the session.
- mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
- } else if (user.isSwitchToEnabled) {
- if (!user.isAddUser && !user.isRestricted && !user.isDisabledByAdmin) {
- if (mCurrentUserView != null) {
- mCurrentUserView.setActivated(false);
- }
- v.setActivated(true);
- }
- onUserListItemClicked(user);
- }
- }
- }
-
- public static class Container extends FrameLayout {
-
- private KeyguardUserSwitcher mKeyguardUserSwitcher;
-
- public Container(Context context, AttributeSet attrs) {
- super(context, attrs);
- setClipChildren(false);
- }
-
- public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
- mKeyguardUserSwitcher = keyguardUserSwitcher;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- // Hide switcher if it didn't handle the touch event (and let the event go through).
- if (mKeyguardUserSwitcher != null && !mKeyguardUserSwitcher.isAnimating()) {
- mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
- }
- return false;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
new file mode 100644
index 000000000000..b76e451cb681
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -0,0 +1,639 @@
+/*
+ * 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.statusbar.policy;
+
+import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA;
+import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.DataSetObserver;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import com.android.keyguard.KeyguardConstants;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.KeyguardVisibilityHelper;
+import com.android.keyguard.dagger.KeyguardUserSwitcherScope;
+import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.ViewController;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+import javax.inject.Inject;
+
+/**
+ * Manages the user switcher on the Keyguard.
+ */
+@KeyguardUserSwitcherScope
+public class KeyguardUserSwitcherController extends ViewController<KeyguardUserSwitcherView> {
+
+ private static final String TAG = "KeyguardUserSwitcherController";
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+
+ private static final AnimationProperties ANIMATION_PROPERTIES =
+ new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+
+ private final Context mContext;
+ private final UserSwitcherController mUserSwitcherController;
+ private final ScreenLifecycle mScreenLifecycle;
+ private final KeyguardUserAdapter mAdapter;
+ private final KeyguardStateController mKeyguardStateController;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private WeakReference<KeyguardUserSwitcherListener> mKeyguardUserSwitcherCallback;
+ protected final SysuiStatusBarStateController mStatusBarStateController;
+ private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
+
+ // Child views of KeyguardUserSwitcherView
+ private KeyguardUserSwitcherListView mListView;
+ private LinearLayout mEndGuestButton;
+
+ // State info for the user switcher
+ private boolean mUserSwitcherOpen;
+ private int mCurrentUserId = UserHandle.USER_NULL;
+ private boolean mCurrentUserIsGuest;
+ private int mBarState;
+ private float mDarkAmount;
+
+ private final KeyguardUpdateMonitorCallback mInfoCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ if (DEBUG) Log.d(TAG, String.format("onKeyguardVisibilityChanged %b", showing));
+ // Any time the keyguard is hidden, try to close the user switcher menu to
+ // restore keyguard to the default state
+ if (!showing) {
+ closeSwitcherIfOpenAndNotSimple(false);
+ }
+ }
+
+ @Override
+ public void onUserSwitching(int userId) {
+ closeSwitcherIfOpenAndNotSimple(false);
+ }
+ };
+
+ private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
+ @Override
+ public void onScreenTurnedOff() {
+ if (DEBUG) Log.d(TAG, "onScreenTurnedOff");
+ closeSwitcherIfOpenAndNotSimple(false);
+ }
+ };
+
+ private final StatusBarStateController.StateListener mStatusBarStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onStateChanged(int newState) {
+ if (DEBUG) Log.d(TAG, String.format("onStateChanged: newState=%d", newState));
+
+ boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
+ boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
+ int oldState = mBarState;
+ mBarState = newState;
+
+ if (mStatusBarStateController.goingToFullShade()
+ || mKeyguardStateController.isKeyguardFadingAway()) {
+ closeSwitcherIfOpenAndNotSimple(true);
+ }
+
+ setKeyguardUserSwitcherVisibility(
+ newState,
+ keyguardFadingAway,
+ goingToFullShade,
+ oldState);
+ }
+
+ @Override
+ public void onDozeAmountChanged(float linearAmount, float amount) {
+ if (DEBUG) {
+ Log.d(TAG, String.format("onDozeAmountChanged: linearAmount=%f amount=%f",
+ linearAmount, amount));
+ }
+ setDarkAmount(amount);
+ }
+ };
+
+ @Inject
+ public KeyguardUserSwitcherController(
+ KeyguardUserSwitcherView keyguardUserSwitcherView,
+ Context context,
+ @Main Resources resources,
+ LayoutInflater layoutInflater,
+ ScreenLifecycle screenLifecycle,
+ UserSwitcherController userSwitcherController,
+ KeyguardStateController keyguardStateController,
+ SysuiStatusBarStateController statusBarStateController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ DozeParameters dozeParameters) {
+ super(keyguardUserSwitcherView);
+ if (DEBUG) Log.d(TAG, "New KeyguardUserSwitcherController");
+ mContext = context;
+ mScreenLifecycle = screenLifecycle;
+ mUserSwitcherController = userSwitcherController;
+ mKeyguardStateController = keyguardStateController;
+ mStatusBarStateController = statusBarStateController;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mAdapter = new KeyguardUserAdapter(mContext, resources, layoutInflater,
+ mUserSwitcherController, this);
+ mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView,
+ keyguardStateController, dozeParameters);
+ }
+
+ @Override
+ protected void onInit() {
+ super.onInit();
+
+ if (DEBUG) Log.d(TAG, "onInit");
+
+ mListView = mView.findViewById(R.id.keyguard_user_switcher_list);
+ mEndGuestButton = mView.findViewById(R.id.end_guest_button);
+
+ mEndGuestButton.setOnClickListener(v -> {
+ mUserSwitcherController.showExitGuestDialog(mCurrentUserId);
+ });
+
+ mView.setOnTouchListener((v, event) -> {
+ if (!isListAnimating()) {
+ // Hide switcher if it didn't handle the touch event (and block the event from
+ // going through).
+ return closeSwitcherIfOpenAndNotSimple(true);
+ }
+ return false;
+ });
+ }
+
+ @Override
+ protected void onViewAttached() {
+ if (DEBUG) Log.d(TAG, "onViewAttached");
+ mAdapter.registerDataSetObserver(mDataSetObserver);
+ mDataSetObserver.onChanged();
+ mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
+ mScreenLifecycle.addObserver(mScreenObserver);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ if (DEBUG) Log.d(TAG, "onViewDetached");
+
+ // Detaching the view will always close the switcher
+ closeSwitcherIfOpenAndNotSimple(false);
+
+ mAdapter.unregisterDataSetObserver(mDataSetObserver);
+ mKeyguardUpdateMonitor.removeCallback(mInfoCallback);
+ mStatusBarStateController.removeCallback(mStatusBarStateListener);
+ mScreenLifecycle.removeObserver(mScreenObserver);
+ }
+
+ /**
+ * See:
+ *
+ * <ul>
+ * <li>{@link com.android.internal.R.bool.config_expandLockScreenUserSwitcher}</li>
+ * <li>{@link UserSwitcherController.SIMPLE_USER_SWITCHER_GLOBAL_SETTING}</li>
+ * </ul>
+ *
+ * @return true if the user switcher should be open by default on the lock screen.
+ * @see android.os.UserManager#isUserSwitcherEnabled()
+ */
+ public boolean isSimpleUserSwitcher() {
+ return mUserSwitcherController.isSimpleUserSwitcher();
+ }
+
+ /**
+ * @param animate if the transition should be animated
+ * @return true if the switcher state changed
+ */
+ public boolean closeSwitcherIfOpenAndNotSimple(boolean animate) {
+ if (isUserSwitcherOpen() && !isSimpleUserSwitcher()) {
+ setUserSwitcherOpened(false /* open */, animate);
+ return true;
+ }
+ return false;
+ }
+
+ public final DataSetObserver mDataSetObserver = new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ refreshUserList();
+ }
+ };
+
+ void refreshUserList() {
+ final int childCount = mListView.getChildCount();
+ final int adapterCount = mAdapter.getCount();
+ final int count = Math.max(childCount, adapterCount);
+
+ if (DEBUG) {
+ Log.d(TAG, String.format("refreshUserList childCount=%d adapterCount=%d", childCount,
+ adapterCount));
+ }
+
+ boolean foundCurrentUser = false;
+ for (int i = 0; i < count; i++) {
+ if (i < adapterCount) {
+ View oldView = null;
+ if (i < childCount) {
+ oldView = mListView.getChildAt(i);
+ }
+ KeyguardUserDetailItemView newView = (KeyguardUserDetailItemView)
+ mAdapter.getView(i, oldView, mListView);
+ UserSwitcherController.UserRecord userTag =
+ (UserSwitcherController.UserRecord) newView.getTag();
+ if (userTag.isCurrent) {
+ if (i != 0) {
+ Log.w(TAG, "Current user is not the first view in the list");
+ }
+ foundCurrentUser = true;
+ mCurrentUserId = userTag.info.id;
+ mCurrentUserIsGuest = userTag.isGuest;
+ // Current user is always visible
+ newView.updateVisibilities(true /* showItem */,
+ mUserSwitcherOpen /* showTextName */, false /* animate */);
+ } else {
+ // Views for non-current users are always expanded (e.g. they should the name
+ // next to the user icon). However, they could be hidden entirely if the list
+ // is closed.
+ newView.updateVisibilities(mUserSwitcherOpen /* showItem */,
+ true /* showTextName */, false /* animate */);
+ }
+ newView.setDarkAmount(mDarkAmount);
+ if (oldView == null) {
+ // We ran out of existing views. Add it at the end.
+ mListView.addView(newView);
+ } else if (oldView != newView) {
+ // We couldn't rebind the view. Replace it.
+ mListView.replaceView(newView, i);
+ }
+ } else {
+ mListView.removeLastView();
+ }
+ }
+ if (!foundCurrentUser) {
+ Log.w(TAG, "Current user is not listed");
+ mCurrentUserId = UserHandle.USER_NULL;
+ mCurrentUserIsGuest = false;
+ }
+ }
+
+ /**
+ * Get the height of the keyguard user switcher view when closed.
+ */
+ public int getUserIconHeight() {
+ View firstChild = mListView.getChildAt(0);
+ return firstChild == null ? 0 : firstChild.getHeight();
+ }
+
+ /**
+ * Set the visibility of the keyguard user switcher view based on some new state.
+ */
+ public void setKeyguardUserSwitcherVisibility(
+ int statusBarState,
+ boolean keyguardFadingAway,
+ boolean goingToFullShade,
+ int oldStatusBarState) {
+ mKeyguardVisibilityHelper.setViewVisibility(
+ statusBarState, keyguardFadingAway, goingToFullShade, oldStatusBarState);
+ }
+
+ /**
+ * Update position of the view with an optional animation
+ */
+ public void updatePosition(int x, int y, boolean animate) {
+ PropertyAnimator.setProperty(mListView, AnimatableProperty.Y, y, ANIMATION_PROPERTIES,
+ animate);
+ PropertyAnimator.setProperty(mListView, AnimatableProperty.TRANSLATION_X, -Math.abs(x),
+ ANIMATION_PROPERTIES, animate);
+ }
+
+ /**
+ * Set keyguard user switcher view alpha.
+ */
+ public void setAlpha(float alpha) {
+ if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
+ mView.setAlpha(alpha);
+ }
+ }
+
+ /**
+ * Set the amount (ratio) that the device has transitioned to doze.
+ *
+ * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
+ */
+ private void setDarkAmount(float darkAmount) {
+ boolean isAwake = darkAmount != 0;
+ if (darkAmount == mDarkAmount) {
+ return;
+ }
+ mDarkAmount = darkAmount;
+ mListView.setDarkAmount(darkAmount);
+ mView.setVisibility(isAwake ? View.VISIBLE : View.GONE);
+ if (!isAwake) {
+ closeSwitcherIfOpenAndNotSimple(false);
+ }
+ }
+
+ private boolean isListAnimating() {
+ return mKeyguardVisibilityHelper.isVisibilityAnimating() || mListView.isAnimating();
+ }
+
+ /**
+ * Remove the callback if it exists.
+ */
+ public void removeCallback() {
+ if (DEBUG) Log.d(TAG, "removeCallback");
+ mKeyguardUserSwitcherCallback = null;
+ }
+
+ /**
+ * Register to receive notifications about keyguard user switcher state
+ * (see {@link KeyguardUserSwitcherListener}.
+ *
+ * Only one callback can be used at a time.
+ *
+ * @param callback The callback to register
+ */
+ public void setCallback(KeyguardUserSwitcherListener callback) {
+ if (DEBUG) Log.d(TAG, "setCallback");
+ mKeyguardUserSwitcherCallback = new WeakReference<>(callback);
+ }
+
+ /**
+ * If user switcher state changes, notifies all {@link KeyguardUserSwitcherListener}.
+ * Switcher state is updatd before animations finish.
+ *
+ * @param animate true to animate transition. The user switcher state (i.e.
+ * {@link #isUserSwitcherOpen()}) is updated before animation is finished.
+ */
+ private void setUserSwitcherOpened(boolean open, boolean animate) {
+ boolean wasOpen = mUserSwitcherOpen;
+ if (DEBUG) {
+ Log.d(TAG, String.format("setUserSwitcherOpened: %b -> %b (animate=%b)", wasOpen,
+ open, animate));
+ }
+ mUserSwitcherOpen = open;
+ if (mUserSwitcherOpen != wasOpen) {
+ notifyUserSwitcherStateChanged();
+ }
+ updateVisibilities(animate);
+ }
+
+ private void updateVisibilities(boolean animate) {
+ if (DEBUG) Log.d(TAG, String.format("updateVisibilities: animate=%b", animate));
+ mEndGuestButton.animate().cancel();
+ if (mUserSwitcherOpen && mCurrentUserIsGuest) {
+ // Show the "End guest session" button
+ mEndGuestButton.setVisibility(View.VISIBLE);
+ if (animate) {
+ mEndGuestButton.setAlpha(0f);
+ mEndGuestButton.animate()
+ .alpha(1f)
+ .setDuration(360)
+ .setInterpolator(Interpolators.ALPHA_IN)
+ .withEndAction(() -> {
+ mEndGuestButton.setClickable(true);
+ });
+ } else {
+ mEndGuestButton.setClickable(true);
+ mEndGuestButton.setAlpha(1f);
+ }
+ } else {
+ // Hide the "End guest session" button. If it's already GONE, don't try to
+ // animate it or it will appear again for an instant.
+ mEndGuestButton.setClickable(false);
+ if (animate && mEndGuestButton.getVisibility() != View.GONE) {
+ mEndGuestButton.setVisibility(View.VISIBLE);
+ mEndGuestButton.setAlpha(1f);
+ mEndGuestButton.animate()
+ .alpha(0f)
+ .setDuration(360)
+ .setInterpolator(Interpolators.ALPHA_OUT)
+ .withEndAction(() -> {
+ mEndGuestButton.setVisibility(View.GONE);
+ mEndGuestButton.setAlpha(1f);
+ });
+ } else {
+ mEndGuestButton.setVisibility(View.GONE);
+ mEndGuestButton.setAlpha(1f);
+ }
+ }
+
+ mListView.updateVisibilities(mUserSwitcherOpen, animate);
+ }
+
+ private boolean isUserSwitcherOpen() {
+ return mUserSwitcherOpen;
+ }
+
+ private void notifyUserSwitcherStateChanged() {
+ if (DEBUG) {
+ Log.d(TAG, String.format("notifyUserSwitcherStateChanged: mUserSwitcherOpen=%b",
+ mUserSwitcherOpen));
+ }
+ if (mKeyguardUserSwitcherCallback != null) {
+ KeyguardUserSwitcherListener cb = mKeyguardUserSwitcherCallback.get();
+ if (cb != null) {
+ cb.onKeyguardUserSwitcherChanged(mUserSwitcherOpen);
+ }
+ }
+ }
+
+ /**
+ * Callback for keyguard user switcher state information
+ */
+ public interface KeyguardUserSwitcherListener {
+
+ /**
+ * Called when the keyguard enters or leaves user switcher mode. This will be called
+ * before the animations are finished.
+ *
+ * @param open if true, keyguard is showing the user switcher or transitioning from/to user
+ * switcher mode.
+ */
+ void onKeyguardUserSwitcherChanged(boolean open);
+ }
+
+ static class KeyguardUserAdapter extends
+ UserSwitcherController.BaseUserAdapter implements View.OnClickListener {
+
+ private final Context mContext;
+ private final Resources mResources;
+ private final LayoutInflater mLayoutInflater;
+ private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
+ private View mCurrentUserView;
+ // List of users where the first entry is always the current user
+ private ArrayList<UserSwitcherController.UserRecord> mUsersOrdered = new ArrayList<>();
+
+ KeyguardUserAdapter(Context context, Resources resources, LayoutInflater layoutInflater,
+ UserSwitcherController controller,
+ KeyguardUserSwitcherController keyguardUserSwitcherController) {
+ super(controller);
+ mContext = context;
+ mResources = resources;
+ mLayoutInflater = layoutInflater;
+ mKeyguardUserSwitcherController = keyguardUserSwitcherController;
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ // At this point, value of isSimpleUserSwitcher() may have changed in addition to the
+ // data set
+ refreshUserOrder();
+ super.notifyDataSetChanged();
+ }
+
+ void refreshUserOrder() {
+ ArrayList<UserSwitcherController.UserRecord> users = super.getUsers();
+ mUsersOrdered = new ArrayList<>(users.size());
+ for (int i = 0; i < users.size(); i++) {
+ UserSwitcherController.UserRecord record = users.get(i);
+ if (record.isCurrent) {
+ mUsersOrdered.add(0, record);
+ } else {
+ mUsersOrdered.add(record);
+ }
+ }
+ }
+
+ @Override
+ protected ArrayList<UserSwitcherController.UserRecord> getUsers() {
+ return mUsersOrdered;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ UserSwitcherController.UserRecord item = getItem(position);
+ return createUserDetailItemView(convertView, parent, item);
+ }
+
+ @Override
+ public String getName(Context context, UserSwitcherController.UserRecord item) {
+ if (item.isGuest) {
+ return context.getString(com.android.settingslib.R.string.guest_nickname);
+ } else {
+ return super.getName(context, item);
+ }
+ }
+
+ KeyguardUserDetailItemView convertOrInflate(View convertView, ViewGroup parent) {
+ if (!(convertView instanceof KeyguardUserDetailItemView)
+ || !(convertView.getTag() instanceof UserSwitcherController.UserRecord)) {
+ convertView = mLayoutInflater.inflate(
+ R.layout.keyguard_user_switcher_item, parent, false);
+ }
+ return (KeyguardUserDetailItemView) convertView;
+ }
+
+ KeyguardUserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent,
+ UserSwitcherController.UserRecord item) {
+ KeyguardUserDetailItemView v = convertOrInflate(convertView, parent);
+ v.setOnClickListener(this);
+
+ String name = getName(mContext, item);
+ if (item.picture == null) {
+ v.bind(name, getDrawable(item).mutate(), item.resolveId());
+ } else {
+ int avatarSize =
+ (int) mResources.getDimension(R.dimen.kg_framed_avatar_size);
+ Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize);
+ drawable.setColorFilter(
+ item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter());
+ v.bind(name, drawable, item.info.id);
+ }
+ v.setActivated(item.isCurrent);
+ v.setDisabledByAdmin(item.isDisabledByAdmin);
+ v.setEnabled(item.isSwitchToEnabled);
+ v.setAlpha(v.isEnabled() ? USER_SWITCH_ENABLED_ALPHA : USER_SWITCH_DISABLED_ALPHA);
+
+ if (item.isCurrent) {
+ mCurrentUserView = v;
+ }
+ v.setTag(item);
+ return v;
+ }
+
+ private Drawable getDrawable(UserSwitcherController.UserRecord item) {
+ Drawable drawable;
+ if (item.isCurrent && item.isGuest) {
+ drawable = mContext.getDrawable(R.drawable.ic_avatar_guest_user);
+ } else {
+ drawable = getIconDrawable(mContext, item);
+ }
+
+ int iconColorRes;
+ if (item.isSwitchToEnabled) {
+ iconColorRes = R.color.kg_user_switcher_avatar_icon_color;
+ } else {
+ iconColorRes = R.color.kg_user_switcher_restricted_avatar_icon_color;
+ }
+ drawable.setTint(mResources.getColor(iconColorRes, mContext.getTheme()));
+
+ Drawable bg = mContext.getDrawable(R.drawable.kg_bg_avatar);
+ drawable = new LayerDrawable(new Drawable[]{bg, drawable});
+ return drawable;
+ }
+
+ @Override
+ public void onClick(View v) {
+ UserSwitcherController.UserRecord user = (UserSwitcherController.UserRecord) v.getTag();
+
+ if (mKeyguardUserSwitcherController.isListAnimating()) {
+ return;
+ }
+
+ if (mKeyguardUserSwitcherController.isUserSwitcherOpen()) {
+ if (user.isCurrent) {
+ // Close the switcher if tapping the current user
+ mKeyguardUserSwitcherController.setUserSwitcherOpened(
+ false /* open */, true /* animate */);
+ } else if (user.isSwitchToEnabled) {
+ if (!user.isAddUser && !user.isRestricted && !user.isDisabledByAdmin) {
+ if (mCurrentUserView != null) {
+ mCurrentUserView.setActivated(false);
+ }
+ v.setActivated(true);
+ }
+ onUserListItemClicked(user);
+ }
+ } else {
+ // If switcher is closed, tapping anywhere in the view will open it
+ mKeyguardUserSwitcherController.setUserSwitcherOpened(
+ true /* open */, true /* animate */);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
new file mode 100644
index 000000000000..7c82c116eb3d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
@@ -0,0 +1,168 @@
+/*
+ * 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.statusbar.policy;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+
+import com.android.keyguard.AlphaOptimizedLinearLayout;
+import com.android.keyguard.KeyguardConstants;
+import com.android.settingslib.animation.AppearAnimationUtils;
+import com.android.settingslib.animation.DisappearAnimationUtils;
+import com.android.systemui.Interpolators;
+
+/**
+ * The container for the user switcher on Keyguard.
+ */
+public class KeyguardUserSwitcherListView extends AlphaOptimizedLinearLayout {
+
+ private static final String TAG = "KeyguardUserSwitcherListView";
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+
+ private static final int ANIMATION_DURATION_OPENING = 360;
+ private static final int ANIMATION_DURATION_CLOSING = 240;
+
+ private boolean mAnimating;
+ private final AppearAnimationUtils mAppearAnimationUtils;
+ private final DisappearAnimationUtils mDisappearAnimationUtils;
+
+ public KeyguardUserSwitcherListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setClipChildren(false);
+ mAppearAnimationUtils = new AppearAnimationUtils(context, ANIMATION_DURATION_OPENING,
+ -0.5f, 0.5f, Interpolators.FAST_OUT_SLOW_IN);
+ mDisappearAnimationUtils = new DisappearAnimationUtils(context, ANIMATION_DURATION_CLOSING,
+ 0.5f, 0.5f, Interpolators.FAST_OUT_LINEAR_IN);
+ }
+
+ /**
+ * Set the amount (ratio) that the device has transitioned to doze.
+ *
+ * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
+ */
+ void setDarkAmount(float darkAmount) {
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View v = getChildAt(i);
+ if (v instanceof KeyguardUserDetailItemView) {
+ ((KeyguardUserDetailItemView) v).setDarkAmount(darkAmount);
+ }
+ }
+ }
+
+ boolean isAnimating() {
+ return mAnimating;
+ }
+
+ /**
+ * Update visibilities of this view and child views for when the user list is open or closed.
+ * If closed, this hides everything but the first item (which is always the current user).
+ */
+ void updateVisibilities(boolean open, boolean animate) {
+ if (DEBUG) {
+ Log.d(TAG, String.format("updateVisibilities: open=%b animate=%b childCount=%d",
+ open, animate, getChildCount()));
+ }
+
+ mAnimating = false;
+
+ int userListCount = getChildCount();
+ if (userListCount > 0) {
+ // The first child is always the current user.
+ KeyguardUserDetailItemView currentUserView = ((KeyguardUserDetailItemView) getChildAt(
+ 0));
+ currentUserView.updateVisibilities(true /* showItem */, open /* showTextName */,
+ animate);
+ currentUserView.setClickable(true);
+ currentUserView.clearAnimation();
+ }
+
+ if (userListCount <= 1) {
+ return;
+ }
+
+ if (animate) {
+ // Create an array of all the remaining users (that aren't the current user).
+ KeyguardUserDetailItemView[] otherUserViews =
+ new KeyguardUserDetailItemView[userListCount - 1];
+ for (int i = 1, n = 0; i < userListCount; i++, n++) {
+ otherUserViews[n] = (KeyguardUserDetailItemView) getChildAt(i);
+
+ // Update clickable state immediately so that the menu feels more responsive
+ otherUserViews[n].setClickable(open);
+
+ // Before running the animation, ensure visibility is set correctly
+ otherUserViews[n].updateVisibilities(
+ true /* showItem */, true /* showTextName */, false /* animate */);
+ otherUserViews[n].clearAnimation();
+ }
+
+ setClipChildren(false);
+ setClipToPadding(false);
+
+ mAnimating = true;
+
+ final int nonCurrentUserCount = otherUserViews.length;
+ if (open) {
+ mAppearAnimationUtils.startAnimation(otherUserViews, () -> {
+ setClipChildren(true);
+ setClipToPadding(true);
+ mAnimating = false;
+ });
+ } else {
+ mDisappearAnimationUtils.startAnimation(otherUserViews, () -> {
+ setClipChildren(true);
+ setClipToPadding(true);
+ for (int i = 0; i < nonCurrentUserCount; i++) {
+ otherUserViews[i].updateVisibilities(
+ false /* showItem */, true /* showTextName */, false /* animate */);
+ }
+ mAnimating = false;
+ });
+ }
+ } else {
+ for (int i = 1; i < userListCount; i++) {
+ KeyguardUserDetailItemView nonCurrentUserView =
+ ((KeyguardUserDetailItemView) getChildAt(i));
+ nonCurrentUserView.clearAnimation();
+ nonCurrentUserView.updateVisibilities(
+ open /* showItem */, true /* showTextName */, false /* animate */);
+ nonCurrentUserView.setClickable(open);
+ }
+ }
+ }
+
+ /**
+ * Replaces the view at the specified position in the group.
+ *
+ * @param index the position in the group of the view to remove
+ */
+ void replaceView(KeyguardUserDetailItemView newView, int index) {
+ removeViewAt(index);
+ addView(newView, index);
+ }
+
+ /**
+ * Removes the last view in the group.
+ */
+ void removeLastView() {
+ int lastIndex = getChildCount() - 1;
+ removeViewAt(lastIndex);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherView.java
new file mode 100644
index 000000000000..3f0e23f7c72e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherView.java
@@ -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.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+/**
+ * The container for the user switcher on Keyguard.
+ */
+public class KeyguardUserSwitcherView extends FrameLayout {
+
+ public KeyguardUserSwitcherView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 72c750222022..2089472757eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -31,6 +31,7 @@ import android.net.NetworkCapabilities;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings.Global;
+import android.telephony.AccessNetworkConstants;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthCdma;
import android.telephony.CellSignalStrengthNr;
@@ -43,6 +44,11 @@ import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
+import android.telephony.ims.RegistrationManager.RegistrationCallback;
import android.text.Html;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
@@ -55,12 +61,14 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneConstants.DataState;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.settingslib.AccessibilityContentDescriptions;
import com.android.settingslib.SignalIcon.MobileIconGroup;
import com.android.settingslib.SignalIcon.MobileState;
import com.android.settingslib.Utils;
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.MobileMappings.Config;
import com.android.settingslib.mobile.MobileStatusTracker;
+import com.android.settingslib.mobile.MobileStatusTracker.MobileStatus;
import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.SignalStrengthUtil;
@@ -72,6 +80,7 @@ import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.BitSet;
import java.util.List;
import java.util.Map;
@@ -80,12 +89,20 @@ import java.util.Map;
* Monitors the mobile signal changes and update the SysUI icons.
*/
public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> {
+ private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+ private static final int STATUS_HISTORY_SIZE = 64;
+ private static final int IMS_TYPE_WWAN = 1;
+ private static final int IMS_TYPE_WLAN = 2;
+ private static final int IMS_TYPE_WLAN_CROSS_SIM = 3;
private final TelephonyManager mPhone;
+ private final ImsMmTelManager mImsMmTelManager;
private final SubscriptionDefaults mDefaults;
private final String mNetworkNameDefault;
private final String mNetworkNameSeparator;
private final ContentObserver mObserver;
private final boolean mProviderModel;
+ private final Handler mReceiverHandler;
+ private int mImsType = IMS_TYPE_WWAN;
// Save entire info for logging, we only use the id.
final SubscriptionInfo mSubscriptionInfo;
// @VisibleForDemoMode
@@ -101,14 +118,24 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
private ServiceState mServiceState;
private SignalStrength mSignalStrength;
+ private int mLastLevel;
private MobileIconGroup mDefaultIcons;
private Config mConfig;
@VisibleForTesting
boolean mInflateSignalStrengths = false;
private MobileStatusTracker.Callback mCallback;
+ private RegistrationCallback mRegistrationCallback;
+ private int mLastWwanLevel;
+ private int mLastWlanLevel;
+ private int mLastWlanCrossSimLevel;
@VisibleForTesting
MobileStatusTracker mMobileStatusTracker;
+ // Save the previous STATUS_HISTORY_SIZE states for logging.
+ private final String[] mMobileStatusHistory = new String[STATUS_HISTORY_SIZE];
+ // Where to copy the next state into.
+ private int mMobileStatusHistoryIndex;
+
private int mCallState = TelephonyManager.CALL_STATE_IDLE;
/****************************SideCar****************************/
@@ -141,6 +168,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
.toString();
mNetworkNameDefault = getTextIfExists(
com.android.internal.R.string.lockscreen_carrier_default).toString();
+ mReceiverHandler = new Handler(receiverLooper);
mNetworkToIconLookup = mapIconSets(mConfig);
mDefaultIcons = getDefaultIcons(mConfig);
@@ -178,14 +206,25 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
}
};
mCallback = new MobileStatusTracker.Callback() {
+ private String mLastStatus;
+
@Override
public void onMobileStatusChanged(boolean updateTelephony,
- MobileStatusTracker.MobileStatus mobileStatus) {
+ MobileStatus mobileStatus) {
if (Log.isLoggable(mTag, Log.DEBUG)) {
Log.d(mTag, "onMobileStatusChanged="
+ " updateTelephony=" + updateTelephony
+ " mobileStatus=" + mobileStatus.toString());
}
+ String currentStatus = mobileStatus.toString();
+ if (!currentStatus.equals(mLastStatus)) {
+ mLastStatus = currentStatus;
+ String status = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append(currentStatus)
+ .toString();
+ recordLastMobileStatus(status);
+ }
updateMobileStatus(mobileStatus);
if (updateTelephony) {
updateTelephony();
@@ -194,6 +233,53 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
}
}
};
+
+ mRegistrationCallback = new RegistrationCallback() {
+ @Override
+ public void onRegistered(ImsRegistrationAttributes attributes) {
+ Log.d(mTag, "onRegistered: " + "attributes=" + attributes);
+ int imsTransportType = attributes.getTransportType();
+ int registrationAttributes = attributes.getAttributeFlags();
+ if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+ mImsType = IMS_TYPE_WWAN;
+ IconState statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
+ getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
+ if (registrationAttributes == 0) {
+ mImsType = IMS_TYPE_WLAN;
+ IconState statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
+ getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ } else if (registrationAttributes
+ == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) {
+ mImsType = IMS_TYPE_WLAN_CROSS_SIM;
+ IconState statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
+ getCallStrengthDescription(
+ mLastWlanCrossSimLevel, /* isWifi= */false));
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ }
+ }
+ }
+
+ @Override
+ public void onUnregistered(ImsReasonInfo info) {
+ Log.d(mTag, "onDeregistered: " + "info=" + info);
+ mImsType = IMS_TYPE_WWAN;
+ IconState statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
+ getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ }
+ };
+ mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(info.getSubscriptionId());
mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper,
info, mDefaults, mCallback);
mProviderModel = FeatureFlagUtils.isEnabled(
@@ -250,14 +336,41 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
mContext.registerReceiver(mVolteSwitchObserver,
new IntentFilter("org.codeaurora.intent.action.ACTION_ENHANCE_4G_SWITCH"));
mFeatureConnector.connect();
+ if (mProviderModel) {
+ mReceiverHandler.post(mTryRegisterIms);
+ }
}
+ // There is no listener to monitor whether the IMS service is ready, so we have to retry the
+ // IMS registration.
+ private final Runnable mTryRegisterIms = new Runnable() {
+ private static final int MAX_RETRY = 12;
+ private int mRetryCount;
+
+ @Override
+ public void run() {
+ try {
+ mRetryCount++;
+ mImsMmTelManager.registerImsRegistrationCallback(
+ mReceiverHandler::post, mRegistrationCallback);
+ Log.d(mTag, "registerImsRegistrationCallback succeeded");
+ } catch (RuntimeException | ImsException e) {
+ if (mRetryCount < MAX_RETRY) {
+ Log.e(mTag, mRetryCount + " registerImsRegistrationCallback failed", e);
+ // Wait for 5 seconds to retry
+ mReceiverHandler.postDelayed(mTryRegisterIms, 5000);
+ }
+ }
+ }
+ };
+
/**
* Stop listening for phone state changes.
*/
public void unregisterListener() {
mMobileStatusTracker.setListening(false);
mContext.getContentResolver().unregisterContentObserver(mObserver);
+ mImsMmTelManager.unregisterImsRegistrationCallback(mRegistrationCallback);
mContext.unregisterReceiver(mVolteSwitchObserver);
mFeatureConnector.disconnect();
}
@@ -504,16 +617,8 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
return Utils.isInService(mServiceState);
}
- String getNonDefaultCarrierName() {
- if (!mCurrentState.networkNameData.equals(mNetworkNameDefault)) {
- return mCurrentState.networkNameData;
- } else if (mSubscriptionInfo.getCarrierName() != null) {
- return mSubscriptionInfo.getCarrierName().toString();
- } else if (mSubscriptionInfo.getDisplayName() != null) {
- return mSubscriptionInfo.getDisplayName().toString();
- } else {
- return "";
- }
+ String getNetworkNameForCarrierWiFi() {
+ return mPhone.getSimOperatorName();
}
private boolean isRoaming() {
@@ -617,21 +722,22 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
/**
* Extracts the CellSignalStrengthCdma from SignalStrength then returns the level
*/
- private final int getCdmaLevel() {
+ private int getCdmaLevel(SignalStrength signalStrength) {
List<CellSignalStrengthCdma> signalStrengthCdma =
- mSignalStrength.getCellSignalStrengths(CellSignalStrengthCdma.class);
+ signalStrength.getCellSignalStrengths(CellSignalStrengthCdma.class);
if (!signalStrengthCdma.isEmpty()) {
return signalStrengthCdma.get(0).getLevel();
}
return CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
- private void updateMobileStatus(MobileStatusTracker.MobileStatus mobileStatus) {
+ private void updateMobileStatus(MobileStatus mobileStatus) {
mCurrentState.activityIn = mobileStatus.activityIn;
mCurrentState.activityOut = mobileStatus.activityOut;
mCurrentState.dataSim = mobileStatus.dataSim;
mCurrentState.carrierNetworkChangeMode = mobileStatus.carrierNetworkChangeMode;
mDataState = mobileStatus.dataState;
+ notifyMobileLevelChangeIfNecessary(mobileStatus.signalStrength);
mSignalStrength = mobileStatus.signalStrength;
mTelephonyDisplayInfo = mobileStatus.telephonyDisplayInfo;
int lastVoiceState = mServiceState != null ? mServiceState.getState() : -1;
@@ -646,9 +752,117 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
&& (lastVoiceState == -1
|| (lastVoiceState == ServiceState.STATE_IN_SERVICE
|| currentVoiceState == ServiceState.STATE_IN_SERVICE))) {
- notifyNoCallingStatusChange(
- currentVoiceState != ServiceState.STATE_IN_SERVICE,
- mSubscriptionInfo.getSubscriptionId());
+ boolean isNoCalling = currentVoiceState != ServiceState.STATE_IN_SERVICE;
+ IconState statusIcon = new IconState(isNoCalling, R.drawable.ic_qs_no_calling_sms,
+ getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ }
+ }
+
+ private int getCallStrengthIcon(int level, boolean isWifi) {
+ return isWifi ? TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[level]
+ : TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[level];
+ }
+
+ private String getCallStrengthDescription(int level, boolean isWifi) {
+ return isWifi
+ ? getTextIfExists(AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[level])
+ .toString()
+ : getTextIfExists(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[level])
+ .toString();
+ }
+
+ void refreshCallIndicator(SignalCallback callback) {
+ boolean isNoCalling = mServiceState != null
+ && mServiceState.getState() != ServiceState.STATE_IN_SERVICE;
+ IconState statusIcon = new IconState(isNoCalling, R.drawable.ic_qs_no_calling_sms,
+ getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
+ callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
+
+ switch (mImsType) {
+ case IMS_TYPE_WWAN:
+ statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
+ getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
+ break;
+ case IMS_TYPE_WLAN:
+ statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
+ getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
+ break;
+ case IMS_TYPE_WLAN_CROSS_SIM:
+ statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
+ getCallStrengthDescription(mLastWlanCrossSimLevel, /* isWifi= */false));
+ }
+ callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ }
+
+ void notifyWifiLevelChange(int level) {
+ if (!mProviderModel) {
+ return;
+ }
+ Log.d("mTag", "notifyWifiLevelChange " + mImsType);
+ mLastWlanLevel = level;
+ if (mImsType != IMS_TYPE_WLAN) {
+ return;
+ }
+ IconState statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(level, /* isWifi= */true),
+ getCallStrengthDescription(level, /* isWifi= */true));
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ }
+
+ void notifyDefaultMobileLevelChange(int level) {
+ if (!mProviderModel) {
+ return;
+ }
+ Log.d("mTag", "notifyDefaultMobileLevelChange " + mImsType);
+ mLastWlanCrossSimLevel = level;
+ if (mImsType != IMS_TYPE_WLAN_CROSS_SIM) {
+ return;
+ }
+ IconState statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(level, /* isWifi= */false),
+ getCallStrengthDescription(level, /* isWifi= */false));
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ }
+
+ void notifyMobileLevelChangeIfNecessary(SignalStrength signalStrength) {
+ if (!mProviderModel) {
+ return;
+ }
+ int newLevel = getSignalLevel(signalStrength);
+ if (newLevel != mLastLevel) {
+ mLastLevel = newLevel;
+ Log.d("mTag", "notifyMobileLevelChangeIfNecessary " + mImsType);
+ mLastWwanLevel = newLevel;
+ if (mImsType == IMS_TYPE_WWAN) {
+ IconState statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(newLevel, /* isWifi= */false),
+ getCallStrengthDescription(newLevel, /* isWifi= */false));
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ }
+ if (mCurrentState.dataSim) {
+ mNetworkController.notifyDefaultMobileLevelChange(newLevel);
+ }
+ }
+ }
+
+ int getSignalLevel(SignalStrength signalStrength) {
+ if (signalStrength == null) {
+ return 0;
+ }
+ if (!signalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) {
+ return getCdmaLevel(signalStrength);
+ } else {
+ return signalStrength.getLevel();
}
}
@@ -666,29 +880,25 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
checkDefaultData();
mCurrentState.connected = Utils.isInService(mServiceState) && mSignalStrength != null;
if (mCurrentState.connected) {
- if (!mSignalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) {
- mCurrentState.level = getCdmaLevel();
- } else {
- mCurrentState.level = mSignalStrength.getLevel();
- if (mConfig.showRsrpSignalLevelforLTE) {
- if (DEBUG) {
- Log.d(mTag, "updateTelephony CS:" + mServiceState.getVoiceNetworkType()
- + "/" + TelephonyManager.getNetworkTypeName(
- mServiceState.getVoiceNetworkType())
- + ", PS:" + mServiceState.getDataNetworkType()
- + "/"+ TelephonyManager.getNetworkTypeName(
- mServiceState.getDataNetworkType()));
- }
- int dataType = mServiceState.getDataNetworkType();
- if (dataType == TelephonyManager.NETWORK_TYPE_LTE ||
- dataType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
+ mCurrentState.level = getSignalLevel(mSignalStrength);
+ if (mConfig.showRsrpSignalLevelforLTE) {
+ if (DEBUG) {
+ Log.d(mTag, "updateTelephony CS:" + mServiceState.getVoiceNetworkType()
+ + "/" + TelephonyManager.getNetworkTypeName(
+ mServiceState.getVoiceNetworkType())
+ + ", PS:" + mServiceState.getDataNetworkType()
+ + "/"+ TelephonyManager.getNetworkTypeName(
+ mServiceState.getDataNetworkType()));
+ }
+ int dataType = mServiceState.getDataNetworkType();
+ if (dataType == TelephonyManager.NETWORK_TYPE_LTE ||
+ dataType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
+ mCurrentState.level = getAlternateLteLevel(mSignalStrength);
+ }else if ( dataType == TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+ int voiceType = mServiceState.getVoiceNetworkType();
+ if (voiceType == TelephonyManager.NETWORK_TYPE_LTE ||
+ voiceType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
mCurrentState.level = getAlternateLteLevel(mSignalStrength);
- }else if ( dataType == TelephonyManager.NETWORK_TYPE_UNKNOWN) {
- int voiceType = mServiceState.getVoiceNetworkType();
- if (voiceType == TelephonyManager.NETWORK_TYPE_LTE ||
- voiceType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
- mCurrentState.level = getAlternateLteLevel(mSignalStrength);
- }
}
}
}
@@ -859,6 +1069,16 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
notifyListenersIfNecessary();
}
+ private void recordLastMobileStatus(String mobileStatus) {
+ mMobileStatusHistory[mMobileStatusHistoryIndex] = mobileStatus;
+ mMobileStatusHistoryIndex = (mMobileStatusHistoryIndex + 1) % STATUS_HISTORY_SIZE;
+ }
+
+ @VisibleForTesting
+ void setImsType(int imsType) {
+ mImsType = imsType;
+ }
+
public void registerFiveGStateListener(FiveGServiceClient client) {
int phoneId = mSubscriptionInfo.getSimSlotIndex();
client.registerListener(phoneId, mFiveGStateListener);
@@ -942,6 +1162,20 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
pw.println(" mDataState=" + mDataState + ",");
pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
pw.println(" isDataDisabled=" + isDataDisabled() + ",");
+ pw.println(" MobileStatusHistory");
+ int size = 0;
+ for (int i = 0; i < STATUS_HISTORY_SIZE; i++) {
+ if (mMobileStatusHistory[i] != null) {
+ size++;
+ }
+ }
+ // Print out the previous states in ordered number.
+ for (int i = mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - 1;
+ i >= mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - size; i--) {
+ pw.println(" Previous MobileStatus("
+ + (mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - i) + "): "
+ + mMobileStatusHistory[i & (STATUS_HISTORY_SIZE - 1)]);
+ }
pw.println(" mFiveGState=" + mFiveGState + ",");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 89f42cd98a8d..e93d6400dac5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -53,7 +53,7 @@ public interface NetworkController extends CallbackController<SignalCallback>, D
/**
* Callback for listeners to be able to update the state of any UI tracking connectivity
- * @param statusIcon the icon that should be shown in the status bar
+ * @param statusIcon the icon that should be shown in the status bar
* @param qsIcon the icon to show in Quick Settings
* @param statusType the resId of the data type icon (e.g. LTE) to show in the status bar
* @param qsType similar to above, the resId of the data type icon to show in Quick Settings
@@ -95,11 +95,11 @@ public interface NetworkController extends CallbackController<SignalCallback>, D
boolean noNetworksAvailable) {}
/**
- * Callback for listeners to be able to update the no calling & SMS status
- * @param noCalling whether the calling and SMS is not working.
+ * Callback for listeners to be able to update the call indicator
+ * @param statusIcon the icon for the call indicator
* @param subId subscription ID for which to update the UI
*/
- default void setNoCallingStatus(boolean noCalling, int subId) {}
+ default void setCallIndicator(IconState statusIcon, int subId) {}
}
public interface EmergencyListener {
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 fca4360ba82d..94bcc9e5be70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -78,6 +78,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceP
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
@@ -102,6 +103,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
private static final int EMERGENCY_VOICE_CONTROLLER = 200;
private static final int EMERGENCY_NO_SUB = 300;
private static final int EMERGENCY_ASSUMED_VOICE_CONTROLLER = 400;
+ private static final int HISTORY_SIZE = 16;
+ private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
private final Context mContext;
private final TelephonyManager mPhone;
@@ -152,6 +155,11 @@ public class NetworkControllerImpl extends BroadcastReceiver
// This list holds our ordering.
private List<SubscriptionInfo> mCurrentSubscriptions = new ArrayList<>();
+ // Save the previous HISTORY_SIZE states for logging.
+ private final String[] mHistory = new String[HISTORY_SIZE];
+ // Where to copy the next state into.
+ private int mHistoryIndex;
+
@VisibleForTesting
boolean mListening;
@@ -314,6 +322,12 @@ public class NetworkControllerImpl extends BroadcastReceiver
public void onLost(Network network) {
mLastNetwork = null;
mLastNetworkCapabilities = null;
+ String callback = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("onLost: ")
+ .append("network=").append(network)
+ .toString();
+ recordLastNetworkCallback(callback);
updateConnectivity();
}
@@ -334,6 +348,13 @@ public class NetworkControllerImpl extends BroadcastReceiver
}
mLastNetwork = network;
mLastNetworkCapabilities = networkCapabilities;
+ String callback = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("onCapabilitiesChanged: ")
+ .append("network=").append(network).append(",")
+ .append("networkCapabilities=").append(networkCapabilities)
+ .toString();
+ recordLastNetworkCallback(callback);
updateConnectivity();
}
};
@@ -538,9 +559,23 @@ public class NetworkControllerImpl extends BroadcastReceiver
return mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
}
- String getNonDefaultMobileDataNetworkName(int subId) {
+ String getNetworkNameForCarrierWiFi(int subId) {
MobileSignalController controller = getControllerWithSubId(subId);
- return controller != null ? controller.getNonDefaultCarrierName() : "";
+ return controller != null ? controller.getNetworkNameForCarrierWiFi() : "";
+ }
+
+ void notifyWifiLevelChange(int level) {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
+ mobileSignalController.notifyWifiLevelChange(level);
+ }
+ }
+
+ void notifyDefaultMobileLevelChange(int level) {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
+ mobileSignalController.notifyDefaultMobileLevelChange(level);
+ }
}
private void notifyControllersMobileDataChanged() {
@@ -612,6 +647,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
mobileSignalController.notifyListeners(cb);
+ if (mProviderModel) {
+ mobileSignalController.refreshCallIndicator(cb);
+ }
}
mCallbackHandler.setListening(cb, true);
}
@@ -1008,6 +1046,19 @@ public class NetworkControllerImpl extends BroadcastReceiver
pw.print(" mEmergencySource=");
pw.println(emergencyToString(mEmergencySource));
+ pw.println(" - DefaultNetworkCallback -----");
+ int size = 0;
+ for (int i = 0; i < HISTORY_SIZE; i++) {
+ if (mHistory[i] != null) {
+ size++;
+ }
+ }
+ for (int i = mHistoryIndex + HISTORY_SIZE - 1;
+ i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
+ pw.println(" Previous NetworkCallback(" + (mHistoryIndex + HISTORY_SIZE - i) + "): "
+ + mHistory[i & (HISTORY_SIZE - 1)]);
+ }
+
pw.println(" - config ------");
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
@@ -1018,6 +1069,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
mEthernetSignalController.dump(pw);
mAccessPoints.dump(pw);
+
+ mCallbackHandler.dump(pw);
}
private static final String emergencyToString(int emergencySource) {
@@ -1247,6 +1300,11 @@ public class NetworkControllerImpl extends BroadcastReceiver
return s;
}
+ private void recordLastNetworkCallback(String callback) {
+ mHistory[mHistoryIndex] = callback;
+ mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
+ }
+
private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
null, null, null, "", false, null, null);
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 9380d9110c35..320b00af0fc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -73,6 +73,7 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo;
@@ -80,6 +81,7 @@ import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewW
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.LightBarController;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@@ -99,6 +101,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
private final SendButtonTextWatcher mTextWatcher;
private final TextView.OnEditorActionListener mEditorActionHandler;
+ private final NotificationRemoteInputManager mRemoteInputManager;
+ private final List<OnFocusChangeListener> mEditTextFocusChangeListeners = new ArrayList<>();
private RemoteEditText mEditText;
private ImageButton mSendButton;
private ProgressBar mProgressBar;
@@ -121,12 +125,14 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
private boolean mResetting;
private NotificationViewWrapper mWrapper;
private Consumer<Boolean> mOnVisibilityChangedListener;
+ private NotificationRemoteInputManager.BouncerChecker mBouncerChecker;
public RemoteInputView(Context context, AttributeSet attrs) {
super(context, attrs);
mTextWatcher = new SendButtonTextWatcher();
mEditorActionHandler = new EditorActionHandler();
mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class);
+ mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
mStatusBarManagerService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
@@ -200,6 +206,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
}
private void sendRemoteInput(Intent intent) {
+ if (mBouncerChecker != null && mBouncerChecker.showBouncerIfNecessary()) {
+ mEditText.hideIme();
+ return;
+ }
+
mEditText.setEnabled(false);
mSendButton.setVisibility(INVISIBLE);
mProgressBar.setVisibility(VISIBLE);
@@ -351,6 +362,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
}
}
+ /** Populates the text field of the remote input with the given content. */
+ public void setEditTextContent(@Nullable CharSequence editTextContent) {
+ mEditText.setText(editTextContent);
+ }
+
public void focusAnimated() {
if (getVisibility() != VISIBLE) {
Animator animator = ViewAnimationUtils.createCircularReveal(
@@ -545,6 +561,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
super.onVisibilityChanged(changedView, visibility);
if (changedView == this && mOnVisibilityChangedListener != null) {
mOnVisibilityChangedListener.accept(visibility == VISIBLE);
+ // Hide soft-keyboard when the input view became invisible
+ // (i.e. The notification shade collapsed by pressing the home key)
+ if (visibility != VISIBLE && !mEditText.isVisibleToUser()) {
+ mEditText.hideIme();
+ }
}
}
@@ -552,6 +573,37 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
return getVisibility() == VISIBLE && mController.isSpinning(mEntry.getKey(), mToken);
}
+ /**
+ * Sets a {@link com.android.systemui.statusbar.NotificationRemoteInputManager.BouncerChecker}
+ * that will be used to determine if the device needs to be unlocked before sending the
+ * RemoteInput.
+ */
+ public void setBouncerChecker(
+ @Nullable NotificationRemoteInputManager.BouncerChecker bouncerChecker) {
+ mBouncerChecker = bouncerChecker;
+ }
+
+ /** Registers a listener for focus-change events on the EditText */
+ public void addOnEditTextFocusChangedListener(View.OnFocusChangeListener listener) {
+ mEditTextFocusChangeListeners.add(listener);
+ }
+
+ /** Removes a previously-added listener for focus-change events on the EditText */
+ public void removeOnEditTextFocusChangedListener(View.OnFocusChangeListener listener) {
+ mEditTextFocusChangeListeners.remove(listener);
+ }
+
+ /** Determines if the EditText has focus. */
+ public boolean editTextHasFocus() {
+ return mEditText != null && mEditText.hasFocus();
+ }
+
+ private void onEditTextFocusChanged(RemoteEditText remoteEditText, boolean focused) {
+ for (View.OnFocusChangeListener listener : mEditTextFocusChangeListeners) {
+ listener.onFocusChange(remoteEditText, focused);
+ }
+ }
+
/** Handler for button click on send action in IME. */
private class EditorActionHandler implements TextView.OnEditorActionListener {
@@ -603,6 +655,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
private RemoteInputView mRemoteInputView;
boolean mShowImeOnInputConnection;
private LightBarController mLightBarController;
+ private InputMethodManager mInputMethodManager;
UserHandle mUser;
public RemoteEditText(Context context, AttributeSet attrs) {
@@ -621,6 +674,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
setOnReceiveContentListener(types, listener);
}
+ private void hideIme() {
+ if (mInputMethodManager != null) {
+ mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
+ }
+ }
+
private void defocusIfNeeded(boolean animate) {
if (mRemoteInputView != null && mRemoteInputView.mEntry.getRow().isChangingPosition()
|| isTemporarilyDetached()) {
@@ -654,6 +713,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
+ if (mRemoteInputView != null) {
+ mRemoteInputView.onEditTextFocusChanged(this, focused);
+ }
if (!focused) {
defocusIfNeeded(true /* animate */);
}
@@ -724,17 +786,16 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
if (mShowImeOnInputConnection && ic != null) {
Context targetContext = userContext != null ? userContext : getContext();
- final InputMethodManager imm =
- targetContext.getSystemService(InputMethodManager.class);
- if (imm != null) {
+ mInputMethodManager = targetContext.getSystemService(InputMethodManager.class);
+ if (mInputMethodManager != null) {
// onCreateInputConnection is called by InputMethodManager in the middle of
// setting up the connection to the IME; wait with requesting the IME until that
// work has completed.
post(new Runnable() {
@Override
public void run() {
- imm.viewClicked(RemoteEditText.this);
- imm.showSoftInput(RemoteEditText.this, 0);
+ mInputMethodManager.viewClicked(RemoteEditText.this);
+ mInputMethodManager.showSoftInput(RemoteEditText.this, 0);
}
});
}
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 4f4a504ddc9c..738cab15431a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -31,12 +31,11 @@ import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
-import android.net.IConnectivityManager;
import android.net.Network;
import android.net.NetworkRequest;
+import android.net.VpnManager;
import android.os.Handler;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.security.KeyChain;
@@ -84,7 +83,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
private final Context mContext;
private final ConnectivityManager mConnectivityManager;
- private final IConnectivityManager mConnectivityManagerService;
+ private final VpnManager mVpnManager;
private final DevicePolicyManager mDevicePolicyManager;
private final PackageManager mPackageManager;
private final UserManager mUserManager;
@@ -116,8 +115,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
context.getSystemService(Context.DEVICE_POLICY_SERVICE);
mConnectivityManager = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
- mConnectivityManagerService = IConnectivityManager.Stub.asInterface(
- ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+ mVpnManager = context.getSystemService(VpnManager.class);
mPackageManager = context.getPackageManager();
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mBgExecutor = bgExecutor;
@@ -399,25 +397,19 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
private void updateState() {
// Find all users with an active VPN
SparseArray<VpnConfig> vpns = new SparseArray<>();
- try {
- for (UserInfo user : mUserManager.getUsers()) {
- VpnConfig cfg = mConnectivityManagerService.getVpnConfig(user.id);
- if (cfg == null) {
+ for (UserInfo user : mUserManager.getUsers()) {
+ VpnConfig cfg = mVpnManager.getVpnConfig(user.id);
+ if (cfg == null) {
+ continue;
+ } else if (cfg.legacy) {
+ // Legacy VPNs should do nothing if the network is disconnected. Third-party
+ // VPN warnings need to continue as traffic can still go to the app.
+ LegacyVpnInfo legacyVpn = mVpnManager.getLegacyVpnInfo(user.id);
+ if (legacyVpn == null || legacyVpn.state != LegacyVpnInfo.STATE_CONNECTED) {
continue;
- } else if (cfg.legacy) {
- // Legacy VPNs should do nothing if the network is disconnected. Third-party
- // VPN warnings need to continue as traffic can still go to the app.
- LegacyVpnInfo legacyVpn = mConnectivityManagerService.getLegacyVpnInfo(user.id);
- if (legacyVpn == null || legacyVpn.state != LegacyVpnInfo.STATE_CONNECTED) {
- continue;
- }
}
- vpns.put(user.id, cfg);
}
- } catch (RemoteException rme) {
- // Roll back to previous state
- Log.e(TAG, "Unable to list active VPNs", rme);
- return;
+ vpns.put(user.id, cfg);
}
mCurrentVpns = vpns;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
index 554145e9773e..4b6722c17b85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
@@ -23,6 +23,7 @@ import android.util.Log;
import com.android.settingslib.SignalIcon.IconGroup;
import com.android.settingslib.SignalIcon.State;
+import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import java.io.PrintWriter;
@@ -167,8 +168,8 @@ public abstract class SignalController<T extends State, I extends IconGroup> {
}
}
- protected final void notifyNoCallingStatusChange(boolean noCalling, int subId) {
- mCallbackHandler.setNoCallingStatus(noCalling, subId);
+ protected final void notifyCallStateChange(IconState statusIcon, int subId) {
+ mCallbackHandler.setCallIndicator(statusIcon, subId);
}
/**
@@ -187,7 +188,8 @@ public abstract class SignalController<T extends State, I extends IconGroup> {
* and last value of any state data.
*/
protected void recordLastState() {
- mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)].copyFrom(mLastState);
+ mHistory[mHistoryIndex].copyFrom(mLastState);
+ mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
}
public void dump(PrintWriter pw) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
index ea803253ea0f..0bf2d503e5a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
@@ -44,7 +44,7 @@ import com.android.systemui.statusbar.SmartReplyController
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logging.NotificationLogger
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
-import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions
+import com.android.systemui.statusbar.policy.InflatedSmartReplyState.SuppressedActions
import com.android.systemui.statusbar.policy.SmartReplyView.SmartActions
import com.android.systemui.statusbar.policy.SmartReplyView.SmartButtonType
import com.android.systemui.statusbar.policy.SmartReplyView.SmartReplies
@@ -53,10 +53,10 @@ import javax.inject.Inject
/** Returns whether we should show the smart reply view and its smart suggestions. */
fun shouldShowSmartReplyView(
entry: NotificationEntry,
- smartRepliesAndActions: SmartRepliesAndActions
+ smartReplyState: InflatedSmartReplyState
): Boolean {
- if (smartRepliesAndActions.smartReplies == null
- && smartRepliesAndActions.smartActions == null) {
+ if (smartReplyState.smartReplies == null &&
+ smartReplyState.smartActions == null) {
// There are no smart replies and no smart actions.
return false
}
@@ -71,58 +71,65 @@ fun shouldShowSmartReplyView(
.getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false)
}
-/** Determines if two [SmartRepliesAndActions] are visually similar. */
+/** Determines if two [InflatedSmartReplyState] are visually similar. */
fun areSuggestionsSimilar(
- left: SmartRepliesAndActions?,
- right: SmartRepliesAndActions?
+ left: InflatedSmartReplyState?,
+ right: InflatedSmartReplyState?
): Boolean = when {
left === right -> true
left == null || right == null -> false
- left.getSmartReplies() != right.getSmartReplies() -> false
- else -> !NotificationUiAdjustment.areDifferent(left.getSmartActions(), right.getSmartActions())
+ left.hasPhishingAction != right.hasPhishingAction -> false
+ left.smartRepliesList != right.smartRepliesList -> false
+ left.suppressedActionIndices != right.suppressedActionIndices -> false
+ else -> !NotificationUiAdjustment.areDifferent(left.smartActionsList, right.smartActionsList)
}
-interface SmartRepliesAndActionsInflater {
- fun inflateSmartReplies(
+interface SmartReplyStateInflater {
+ fun inflateSmartReplyState(entry: NotificationEntry): InflatedSmartReplyState
+
+ fun inflateSmartReplyViewHolder(
sysuiContext: Context,
notifPackageContext: Context,
entry: NotificationEntry,
- existingRepliesAndAction: SmartRepliesAndActions?
- ): InflatedSmartReplies
+ existingSmartReplyState: InflatedSmartReplyState?,
+ newSmartReplyState: InflatedSmartReplyState
+ ): InflatedSmartReplyViewHolder
}
-/*internal*/ class SmartRepliesAndActionsInflaterImpl @Inject constructor(
+/*internal*/ class SmartReplyStateInflaterImpl @Inject constructor(
private val constants: SmartReplyConstants,
private val activityManagerWrapper: ActivityManagerWrapper,
private val packageManagerWrapper: PackageManagerWrapper,
private val devicePolicyManagerWrapper: DevicePolicyManagerWrapper,
private val smartRepliesInflater: SmartReplyInflater,
private val smartActionsInflater: SmartActionInflater
-) : SmartRepliesAndActionsInflater {
+) : SmartReplyStateInflater {
+
+ override fun inflateSmartReplyState(entry: NotificationEntry): InflatedSmartReplyState =
+ chooseSmartRepliesAndActions(entry)
- override fun inflateSmartReplies(
+ override fun inflateSmartReplyViewHolder(
sysuiContext: Context,
notifPackageContext: Context,
entry: NotificationEntry,
- existingRepliesAndAction: SmartRepliesAndActions?
- ): InflatedSmartReplies {
- val newRepliesAndActions = chooseSmartRepliesAndActions(entry)
- if (!shouldShowSmartReplyView(entry, newRepliesAndActions)) {
- return InflatedSmartReplies(
+ existingSmartReplyState: InflatedSmartReplyState?,
+ newSmartReplyState: InflatedSmartReplyState
+ ): InflatedSmartReplyViewHolder {
+ if (!shouldShowSmartReplyView(entry, newSmartReplyState)) {
+ return InflatedSmartReplyViewHolder(
null /* smartReplyView */,
- null /* smartSuggestionButtons */,
- newRepliesAndActions)
+ null /* smartSuggestionButtons */)
}
// Only block clicks if the smart buttons are different from the previous set - to avoid
// scenarios where a user incorrectly cannot click smart buttons because the
// notification is updated.
val delayOnClickListener =
- !areSuggestionsSimilar(existingRepliesAndAction, newRepliesAndActions)
+ !areSuggestionsSimilar(existingSmartReplyState, newSmartReplyState)
val smartReplyView = SmartReplyView.inflate(sysuiContext, constants)
- val smartReplies = newRepliesAndActions.smartReplies
+ val smartReplies = newSmartReplyState.smartReplies
smartReplyView.setSmartRepliesGeneratedByAssistant(smartReplies?.fromAssistant ?: false)
val smartReplyButtons = smartReplies?.let {
smartReplies.choices.asSequence().mapIndexed { index, choice ->
@@ -136,7 +143,7 @@ interface SmartRepliesAndActionsInflater {
}
} ?: emptySequence()
- val smartActionButtons = newRepliesAndActions.smartActions?.let { smartActions ->
+ val smartActionButtons = newSmartReplyState.smartActions?.let { smartActions ->
val themedPackageContext =
ContextThemeWrapper(notifPackageContext, sysuiContext.theme)
smartActions.actions.asSequence()
@@ -153,10 +160,9 @@ interface SmartRepliesAndActionsInflater {
}
} ?: emptySequence()
- return InflatedSmartReplies(
+ return InflatedSmartReplyViewHolder(
smartReplyView,
- (smartReplyButtons + smartActionButtons).toList(),
- newRepliesAndActions)
+ (smartReplyButtons + smartActionButtons).toList())
}
/**
@@ -165,23 +171,23 @@ interface SmartRepliesAndActionsInflater {
* replies or actions generated by the NotificationAssistantService (NAS), and if the app
* provides any smart actions we also don't show any NAS-generated replies or actions.
*/
- fun chooseSmartRepliesAndActions(entry: NotificationEntry): SmartRepliesAndActions {
+ fun chooseSmartRepliesAndActions(entry: NotificationEntry): InflatedSmartReplyState {
val notification = entry.sbn.notification
val remoteInputActionPair = notification.findRemoteInputActionPair(false /* freeform */)
val freeformRemoteInputActionPair =
notification.findRemoteInputActionPair(true /* freeform */)
if (!constants.isEnabled) {
if (DEBUG) {
- Log.d(TAG, "Smart suggestions not enabled, not adding suggestions for "
- + entry.sbn.key)
+ Log.d(TAG, "Smart suggestions not enabled, not adding suggestions for " +
+ entry.sbn.key)
}
- return SmartRepliesAndActions(null, null)
+ return InflatedSmartReplyState(null, null, null, false)
}
// Only use smart replies from the app if they target P or above. We have this check because
// the smart reply API has been used for other things (Wearables) in the past. The API to
// add smart actions is new in Q so it doesn't require a target-sdk check.
- val enableAppGeneratedSmartReplies = (!constants.requiresTargetingP()
- || entry.targetSdk >= Build.VERSION_CODES.P)
+ val enableAppGeneratedSmartReplies = (!constants.requiresTargetingP() ||
+ entry.targetSdk >= Build.VERSION_CODES.P)
val appGeneratedSmartActions = notification.contextualActions
var smartReplies: SmartReplies? = when {
@@ -207,18 +213,18 @@ interface SmartRepliesAndActionsInflater {
if (smartReplies == null && smartActions == null) {
val entryReplies = entry.smartReplies
val entryActions = entry.smartActions
- if (entryReplies.isNotEmpty()
- && freeformRemoteInputActionPair != null
- && freeformRemoteInputActionPair.second.allowGeneratedReplies
- && freeformRemoteInputActionPair.second.actionIntent != null) {
+ if (entryReplies.isNotEmpty() &&
+ freeformRemoteInputActionPair != null &&
+ freeformRemoteInputActionPair.second.allowGeneratedReplies &&
+ freeformRemoteInputActionPair.second.actionIntent != null) {
smartReplies = SmartReplies(
entryReplies,
freeformRemoteInputActionPair.first,
freeformRemoteInputActionPair.second.actionIntent,
true /* fromAssistant */)
}
- if (entryActions.isNotEmpty()
- && notification.allowSystemGeneratedContextualActions) {
+ if (entryActions.isNotEmpty() &&
+ notification.allowSystemGeneratedContextualActions) {
val systemGeneratedActions: List<Notification.Action> = when {
activityManagerWrapper.isLockTaskKioskModeActive ->
// Filter actions if we're in kiosk-mode - we don't care about screen
@@ -229,7 +235,21 @@ interface SmartRepliesAndActionsInflater {
smartActions = SmartActions(systemGeneratedActions, true /* fromAssistant */)
}
}
- return SmartRepliesAndActions(smartReplies, smartActions)
+ val hasPhishingAction = smartActions?.actions?.any {
+ it.isContextual && it.semanticAction ==
+ Notification.Action.SEMANTIC_ACTION_CONVERSATION_IS_PHISHING
+ } ?: false
+ var suppressedActions: SuppressedActions? = null
+ if (hasPhishingAction) {
+ // If there is a phishing action, calculate the indices of the actions with RemoteInput
+ // as those need to be hidden from the view.
+ val suppressedActionIndices = notification.actions.mapIndexedNotNull { index, action ->
+ if (action.remoteInputs?.isNotEmpty() == true) index else null
+ }
+ suppressedActions = SuppressedActions(suppressedActionIndices)
+ }
+ return InflatedSmartReplyState(smartReplies, smartActions, suppressedActions,
+ hasPhishingAction)
}
/**
@@ -311,8 +331,8 @@ interface SmartActionInflater {
actionIndex: Int,
action: Notification.Action
) =
- if (smartActions.fromAssistant
- && SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY == action.semanticAction) {
+ if (smartActions.fromAssistant &&
+ SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY == action.semanticAction) {
entry.row.doSmartActionClick(entry.row.x.toInt() / 2,
entry.row.y.toInt() / 2, SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY)
smartReplyController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 34c78813d11a..ad4fa64ac905 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -794,8 +794,8 @@ public class SmartReplyView extends ViewGroup {
public final List<CharSequence> choices;
public final boolean fromAssistant;
- public SmartReplies(List<CharSequence> choices, RemoteInput remoteInput,
- PendingIntent pendingIntent, boolean fromAssistant) {
+ public SmartReplies(@NonNull List<CharSequence> choices, @NonNull RemoteInput remoteInput,
+ @NonNull PendingIntent pendingIntent, boolean fromAssistant) {
this.choices = choices;
this.remoteInput = remoteInput;
this.pendingIntent = pendingIntent;
@@ -812,7 +812,7 @@ public class SmartReplyView extends ViewGroup {
public final List<Notification.Action> actions;
public final boolean fromAssistant;
- public SmartActions(List<Notification.Action> actions, boolean fromAssistant) {
+ public SmartActions(@NonNull List<Notification.Action> actions, boolean fromAssistant) {
this.actions = actions;
this.fromAssistant = fromAssistant;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 0552396bab07..d4029e64036e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -113,12 +113,15 @@ public class UserSwitcherController implements Dumpable {
private int mLastNonGuestUser = UserHandle.USER_SYSTEM;
private boolean mResumeUserOnGuestLogout = true;
private boolean mSimpleUserSwitcher;
- private boolean mAddUsersWhenLocked;
+ // When false, there won't be any visual affordance to add a new user from the keyguard even if
+ // the user is unlocked
+ private boolean mAddUsersFromLockScreen;
private boolean mPauseRefreshUsers;
private int mSecondaryUser = UserHandle.USER_NULL;
private Intent mSecondaryUserServiceIntent;
private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
private final UiEventLogger mUiEventLogger;
+ public final DetailAdapter mUserDetailAdapter;
@Inject
public UserSwitcherController(Context context, KeyguardStateController keyguardStateController,
@@ -129,6 +132,7 @@ public class UserSwitcherController implements Dumpable {
mBroadcastDispatcher = broadcastDispatcher;
mActivityTaskManager = activityTaskManager;
mUiEventLogger = uiEventLogger;
+ mUserDetailAdapter = new UserDetailAdapter(this, mContext, mUiEventLogger);
if (!UserManager.isGuestUserEphemeral()) {
mGuestResumeSessionReceiver.register(mBroadcastDispatcher);
}
@@ -204,7 +208,7 @@ public class UserSwitcherController implements Dumpable {
}
mForcePictureLoadForUserId.clear();
- final boolean addUsersWhenLocked = mAddUsersWhenLocked;
+ final boolean addUsersWhenLocked = mAddUsersFromLockScreen;
new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() {
@SuppressWarnings("unchecked")
@Override
@@ -421,7 +425,7 @@ public class UserSwitcherController implements Dumpable {
}
}
- private void showExitGuestDialog(int id) {
+ protected void showExitGuestDialog(int id) {
int newId = UserHandle.USER_SYSTEM;
if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) {
UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
@@ -554,7 +558,7 @@ public class UserSwitcherController implements Dumpable {
private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
public void onChange(boolean selfChange) {
mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
- mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(),
+ mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
refreshUsers(UserHandle.USER_NULL);
};
@@ -610,43 +614,26 @@ public class UserSwitcherController implements Dumpable {
}
public int getUserCount() {
- boolean secureKeyguardShowing = mKeyguardStateController.isShowing()
- && mKeyguardStateController.isMethodSecure()
- && !mKeyguardStateController.canDismissLockScreen();
- if (!secureKeyguardShowing) {
- return getUsers().size();
- }
- // The lock screen is secure and showing. Filter out restricted records.
- final int userSize = getUsers().size();
- int count = 0;
- for (int i = 0; i < userSize; i++) {
- if (getUsers().get(i).isGuest) continue;
- if (getUsers().get(i).isRestricted) {
- break;
- } else {
- count++;
- }
- }
- return count;
+ return countUsers(false);
}
@Override
public int getCount() {
- boolean secureKeyguardShowing = mKeyguardStateController.isShowing()
- && mKeyguardStateController.isMethodSecure()
- && !mKeyguardStateController.canDismissLockScreen();
- if (!secureKeyguardShowing) {
- return getUsers().size();
- }
- // The lock screen is secure and showing. Filter out restricted records.
+ return countUsers(true);
+ }
+
+ private int countUsers(boolean includeGuest) {
+ boolean keyguardShowing = mKeyguardStateController.isShowing();
final int userSize = getUsers().size();
int count = 0;
for (int i = 0; i < userSize; i++) {
- if (getUsers().get(i).isRestricted) {
+ if (getUsers().get(i).isGuest && !includeGuest) {
+ continue;
+ }
+ if (getUsers().get(i).isRestricted && keyguardShowing) {
break;
- } else {
- count++;
}
+ count++;
}
return count;
}
@@ -695,11 +682,7 @@ public class UserSwitcherController implements Dumpable {
if (item.isAddUser) {
iconRes = R.drawable.ic_add_circle;
} else if (item.isGuest) {
- if (item.isCurrent) {
- iconRes = R.drawable.ic_exit_to_app;
- } else {
- iconRes = R.drawable.ic_avatar_guest_user;
- }
+ iconRes = R.drawable.ic_avatar_guest_user;
} else {
iconRes = R.drawable.ic_avatar_user;
}
@@ -800,9 +783,20 @@ public class UserSwitcherController implements Dumpable {
}
}
- public final DetailAdapter userDetailAdapter = new DetailAdapter() {
+ public static class UserDetailAdapter implements DetailAdapter {
private final Intent USER_SETTINGS_INTENT = new Intent(Settings.ACTION_USER_SETTINGS);
+ private final UserSwitcherController mUserSwitcherController;
+ private final Context mContext;
+ private final UiEventLogger mUiEventLogger;
+
+ UserDetailAdapter(UserSwitcherController userSwitcherController, Context context,
+ UiEventLogger uiEventLogger) {
+ mUserSwitcherController = userSwitcherController;
+ mContext = context;
+ mUiEventLogger = uiEventLogger;
+ }
+
@Override
public CharSequence getTitle() {
return mContext.getString(R.string.quick_settings_user_title);
@@ -813,7 +807,7 @@ public class UserSwitcherController implements Dumpable {
UserDetailView v;
if (!(convertView instanceof UserDetailView)) {
v = UserDetailView.inflate(context, parent, false);
- v.createAndSetAdapter(UserSwitcherController.this, mUiEventLogger);
+ v.createAndSetAdapter(mUserSwitcherController, mUiEventLogger);
} else {
v = (UserDetailView) convertView;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 584f461fb358..08ca016777c8 100755
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -41,6 +41,7 @@ import com.android.systemui.R;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import java.io.PrintWriter;
import java.util.Objects;
public class WifiSignalController extends
@@ -204,7 +205,7 @@ public class WifiSignalController extends
IconState qsIcon = new IconState(
mCurrentState.connected, getQsCurrentIconIdForCarrierWifi(), contentDescription);
CharSequence description =
- mNetworkController.getNonDefaultMobileDataNetworkName(mCurrentState.subId);
+ mNetworkController.getNetworkNameForCarrierWiFi(mCurrentState.subId);
callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
mCurrentState.activityIn, mCurrentState.activityOut, volteIcon, dataContentDescription,
dataContentDescriptionHtml, description, icons.isWide,
@@ -272,6 +273,7 @@ public class WifiSignalController extends
mCurrentState.connected = mWifiTracker.connected;
mCurrentState.ssid = mWifiTracker.ssid;
mCurrentState.rssi = mWifiTracker.rssi;
+ notifyWifiLevelChangeIfNecessary(mWifiTracker.level);
mCurrentState.level = mWifiTracker.level;
mCurrentState.statusLabel = mWifiTracker.statusLabel;
mCurrentState.isCarrierMerged = mWifiTracker.isCarrierMerged;
@@ -282,6 +284,12 @@ public class WifiSignalController extends
updateIconGroup();
}
+ void notifyWifiLevelChangeIfNecessary(int level) {
+ if (level != mCurrentState.level) {
+ mNetworkController.notifyWifiLevelChange(level);
+ }
+ }
+
boolean isCarrierMergedWifi(int subId) {
return mCurrentState.isDefault
&& mCurrentState.isCarrierMerged && (mCurrentState.subId == subId);
@@ -296,6 +304,12 @@ public class WifiSignalController extends
notifyListenersIfNecessary();
}
+ @Override
+ public void dump(PrintWriter pw) {
+ super.dump(pw);
+ mWifiTracker.dump(pw);
+ }
+
/**
* Handler to receive the data activity on wifi.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt
index 803d26ec3286..ce4a9270925b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt
@@ -17,10 +17,10 @@ package com.android.systemui.statusbar.policy.dagger
import com.android.systemui.statusbar.policy.SmartActionInflater
import com.android.systemui.statusbar.policy.SmartActionInflaterImpl
-import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflater
-import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflaterImpl
import com.android.systemui.statusbar.policy.SmartReplyInflater
import com.android.systemui.statusbar.policy.SmartReplyInflaterImpl
+import com.android.systemui.statusbar.policy.SmartReplyStateInflater
+import com.android.systemui.statusbar.policy.SmartReplyStateInflaterImpl
import dagger.Binds
import dagger.Module
@@ -29,6 +29,6 @@ interface SmartRepliesInflationModule {
@Binds fun bindSmartActionsInflater(impl: SmartActionInflaterImpl): SmartActionInflater
@Binds fun bindSmartReplyInflater(impl: SmartReplyInflaterImpl): SmartReplyInflater
@Binds fun bindsInflatedSmartRepliesProvider(
- impl: SmartRepliesAndActionsInflaterImpl
- ): SmartRepliesAndActionsInflater
+ impl: SmartReplyStateInflaterImpl
+ ): SmartReplyStateInflater
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index e357577f88c8..0a3e83326e01 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -15,12 +15,15 @@
*/
package com.android.systemui.theme;
+import android.content.om.FabricatedOverlay;
+import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.content.om.OverlayManager;
-import android.os.SystemProperties;
+import android.content.om.OverlayManagerTransaction;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -50,13 +53,6 @@ import java.util.stream.Collectors;
public class ThemeOverlayApplier implements Dumpable {
private static final String TAG = "ThemeOverlayApplier";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean MONET_ENABLED = SystemProperties
- .getBoolean("persist.sysui.monet", false);
-
- @VisibleForTesting
- static final String MONET_ACCENT_COLOR_PACKAGE = "com.android.theme.accentcolor.color";
- @VisibleForTesting
- static final String MONET_SYSTEM_PALETTE_PACKAGE = "com.android.theme.systemcolors.color";
@VisibleForTesting
static final String ANDROID_PACKAGE = "android";
@@ -65,12 +61,12 @@ public class ThemeOverlayApplier implements Dumpable {
@VisibleForTesting
static final String SYSUI_PACKAGE = "com.android.systemui";
- @VisibleForTesting
static final String OVERLAY_CATEGORY_ACCENT_COLOR =
"android.theme.customization.accent_color";
- @VisibleForTesting
static final String OVERLAY_CATEGORY_SYSTEM_PALETTE =
"android.theme.customization.system_palette";
+ static final String OVERLAY_CATEGORY_NEUTRAL_PALETTE =
+ "android.theme.customization.neutral_palette";
@VisibleForTesting
static final String OVERLAY_CATEGORY_FONT = "android.theme.customization.font";
@VisibleForTesting
@@ -117,16 +113,6 @@ public class ThemeOverlayApplier implements Dumpable {
OVERLAY_CATEGORY_ICON_ANDROID,
OVERLAY_CATEGORY_ICON_SYSUI);
- /**
- * List of main colors of Monet themes. These are extracted from overlays installed
- * on the system.
- */
- private final ArrayList<Integer> mMainSystemColors = new ArrayList<>();
- /**
- * Same as above, but providing accent colors instead of a system palette.
- */
- private final ArrayList<Integer> mAccentColors = new ArrayList<>();
-
/* Allowed overlay categories for each target package. */
private final Map<String, Set<String>> mTargetPackageToCategories = new ArrayMap<>();
/* Target package for each overlay category. */
@@ -162,64 +148,17 @@ public class ThemeOverlayApplier implements Dumpable {
mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_LAUNCHER, mLauncherPackage);
mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_THEME_PICKER, mThemePickerPackage);
- collectMonetSystemOverlays();
dumpManager.registerDumpable(TAG, this);
}
/**
- * List of accent colors available as Monet overlays.
- */
- List<Integer> getAvailableAccentColors() {
- return mAccentColors;
- }
-
- /**
- * List of main system colors available as Monet overlays.
- */
- List<Integer> getAvailableSystemColors() {
- return mMainSystemColors;
- }
-
- private void collectMonetSystemOverlays() {
- if (!MONET_ENABLED) {
- return;
- }
- List<OverlayInfo> androidOverlays = mOverlayManager
- .getOverlayInfosForTarget(ANDROID_PACKAGE, UserHandle.SYSTEM);
- for (OverlayInfo overlayInfo : androidOverlays) {
- String packageName = overlayInfo.packageName;
- if (DEBUG) {
- Log.d(TAG, "Processing overlay " + packageName);
- }
- if (OVERLAY_CATEGORY_SYSTEM_PALETTE.equals(overlayInfo.category)
- && packageName.startsWith(MONET_SYSTEM_PALETTE_PACKAGE)) {
- try {
- String color = packageName.replace(MONET_SYSTEM_PALETTE_PACKAGE, "");
- mMainSystemColors.add(Integer.parseInt(color, 16));
- } catch (NumberFormatException e) {
- Log.w(TAG, "Invalid package name for overlay " + packageName, e);
- }
- } else if (OVERLAY_CATEGORY_ACCENT_COLOR.equals(overlayInfo.category)
- && packageName.startsWith(MONET_ACCENT_COLOR_PACKAGE)) {
- try {
- String color = packageName.replace(MONET_ACCENT_COLOR_PACKAGE, "");
- mAccentColors.add(Integer.parseInt(color, 16));
- } catch (NumberFormatException e) {
- Log.w(TAG, "Invalid package name for overlay " + packageName, e);
- }
- } else if (DEBUG) {
- Log.d(TAG, "Unknown overlay: " + packageName + " category: "
- + overlayInfo.category);
- }
- }
- }
-
- /**
* Apply the set of overlay packages to the set of {@code UserHandle}s provided. Overlays that
* affect sysui will also be applied to the system user.
*/
void applyCurrentUserOverlays(
- Map<String, String> categoryToPackage, Set<UserHandle> userHandles) {
+ Map<String, OverlayIdentifier> categoryToPackage,
+ FabricatedOverlay[] pendingCreation,
+ Set<UserHandle> userHandles) {
// Disable all overlays that have not been specified in the user setting.
final Set<String> overlayCategoriesToDisable = new HashSet<>(THEME_CATEGORIES);
overlayCategoriesToDisable.removeAll(categoryToPackage.keySet());
@@ -229,55 +168,68 @@ public class ThemeOverlayApplier implements Dumpable {
final List<OverlayInfo> overlays = new ArrayList<>();
targetPackagesToQuery.forEach(targetPackage -> overlays.addAll(mOverlayManager
.getOverlayInfosForTarget(targetPackage, UserHandle.SYSTEM)));
- final Map<String, String> overlaysToDisable = overlays.stream()
+ final List<Pair<String, String>> overlaysToDisable = overlays.stream()
.filter(o ->
mTargetPackageToCategories.get(o.targetPackageName).contains(o.category))
.filter(o -> overlayCategoriesToDisable.contains(o.category))
.filter(o -> o.isEnabled())
- .collect(Collectors.toMap((o) -> o.category, (o) -> o.packageName));
+ .map(o -> new Pair<>(o.category, o.packageName))
+ .collect(Collectors.toList());
+
+ OverlayManagerTransaction.Builder transaction = getTransactionBuilder();
+ if (pendingCreation != null) {
+ for (FabricatedOverlay overlay : pendingCreation) {
+ transaction.registerFabricatedOverlay(overlay);
+ }
+ }
// Toggle overlays in the order of THEME_CATEGORIES.
for (String category : THEME_CATEGORIES) {
if (categoryToPackage.containsKey(category)) {
- setEnabled(categoryToPackage.get(category), category, userHandles, true);
- } else if (overlaysToDisable.containsKey(category)) {
- setEnabled(overlaysToDisable.get(category), category, userHandles, false);
+ OverlayIdentifier overlayInfo = categoryToPackage.get(category);
+ setEnabled(transaction, overlayInfo, category, userHandles, true);
}
}
- }
-
- private void setEnabled(
- String packageName, String category, Set<UserHandle> handles, boolean enabled) {
- for (UserHandle userHandle : handles) {
- setEnabledAsync(packageName, userHandle, enabled);
+ for (Pair<String, String> packageToDisable : overlaysToDisable) {
+ OverlayIdentifier overlayInfo = new OverlayIdentifier(packageToDisable.second);
+ setEnabled(transaction, overlayInfo, packageToDisable.first, userHandles, false);
}
- if (!handles.contains(UserHandle.SYSTEM) && SYSTEM_USER_CATEGORIES.contains(category)) {
- setEnabledAsync(packageName, UserHandle.SYSTEM, enabled);
- }
- }
- private void setEnabledAsync(String pkg, UserHandle userHandle, boolean enabled) {
mExecutor.execute(() -> {
- if (DEBUG) Log.d(TAG, String.format("setEnabled: %s %s %b", pkg, userHandle, enabled));
try {
- if (enabled) {
- mOverlayManager.setEnabledExclusiveInCategory(pkg, userHandle);
- } else {
- mOverlayManager.setEnabled(pkg, false, userHandle);
- }
+ mOverlayManager.commit(transaction.build());
} catch (SecurityException | IllegalStateException e) {
- Log.e(TAG,
- String.format("setEnabled failed: %s %s %b", pkg, userHandle, enabled), e);
+ Log.e(TAG, "setEnabled failed", e);
}
});
}
+ @VisibleForTesting
+ protected OverlayManagerTransaction.Builder getTransactionBuilder() {
+ return new OverlayManagerTransaction.Builder();
+ }
+
+ private void setEnabled(OverlayManagerTransaction.Builder transaction,
+ OverlayIdentifier identifier, String category, Set<UserHandle> handles,
+ boolean enabled) {
+ if (DEBUG) {
+ Log.d(TAG, "setEnabled: " + identifier.getPackageName() + " category: "
+ + category + ": " + enabled);
+ }
+ for (UserHandle userHandle : handles) {
+ transaction.setEnabled(identifier, enabled, userHandle.getIdentifier());
+ }
+ if (!handles.contains(UserHandle.SYSTEM) && SYSTEM_USER_CATEGORIES.contains(category)) {
+ transaction.setEnabled(identifier, enabled, UserHandle.SYSTEM.getIdentifier());
+ }
+ }
+
/**
* @inherit
*/
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
- pw.println("mMainSystemColors=" + mMainSystemColors.size());
- pw.println("mAccentColors=" + mAccentColors.size());
+ pw.println("mTargetPackageToCategories=" + mTargetPackageToCategories);
+ pw.println("mCategoryToTargetPackage=" + mCategoryToTargetPackage);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index d9f474480bc9..1f222d80f014 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -16,8 +16,10 @@
package com.android.systemui.theme;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR;
+import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_NEUTRAL_PALETTE;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
@@ -25,6 +27,8 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.om.FabricatedOverlay;
+import android.content.om.OverlayIdentifier;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.graphics.Color;
@@ -40,7 +44,6 @@ import android.util.Log;
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.ColorUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.SystemUI;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -48,6 +51,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.dump.DumpManager;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.settings.SecureSettings;
@@ -59,10 +63,10 @@ import org.json.JSONObject;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collection;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
import javax.inject.Inject;
@@ -77,9 +81,13 @@ import javax.inject.Inject;
*/
@SysUISingleton
public class ThemeOverlayController extends SystemUI implements Dumpable {
- private static final String TAG = "ThemeOverlayController";
+ protected static final String TAG = "ThemeOverlayController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ protected static final int PRIMARY = 0;
+ protected static final int SECONDARY = 1;
+ protected static final int NEUTRAL = 1;
+
// If lock screen wallpaper colors should also be considered when selecting the theme.
// Doing this has performance impact, given that overlays would need to be swapped when
// the device unlocks.
@@ -95,16 +103,21 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
private final Handler mBgHandler;
private final WallpaperManager mWallpaperManager;
private final KeyguardStateController mKeyguardStateController;
+ private final boolean mIsMonetEnabled;
private WallpaperColors mLockColors;
private WallpaperColors mSystemColors;
- // Color extracted from wallpaper, NOT the color used on the overlay
+ // If fabricated overlays were already created for the current theme.
+ private boolean mNeedsOverlayCreation;
+ // Dominant olor extracted from wallpaper, NOT the color used on the overlay
protected int mMainWallpaperColor = Color.TRANSPARENT;
- // Color extracted from wallpaper, NOT the color used on the overlay
+ // Accent color extracted from wallpaper, NOT the color used on the overlay
protected int mWallpaperAccentColor = Color.TRANSPARENT;
- // Main system color that maps to an overlay color
- private int mSystemOverlayColor = Color.TRANSPARENT;
- // Accent color that maps to an overlay color
- private int mAccentOverlayColor = Color.TRANSPARENT;
+ // System colors overlay
+ private FabricatedOverlay mPrimaryOverlay;
+ // Accent colors overlay
+ private FabricatedOverlay mSecondaryOverlay;
+ // Neutral system colors overlay
+ private FabricatedOverlay mNeutralOverlay;
@Inject
public ThemeOverlayController(Context context, BroadcastDispatcher broadcastDispatcher,
@@ -112,9 +125,10 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
@Background Executor bgExecutor, ThemeOverlayApplier themeOverlayApplier,
SecureSettings secureSettings, WallpaperManager wallpaperManager,
UserManager userManager, KeyguardStateController keyguardStateController,
- DumpManager dumpManager) {
+ DumpManager dumpManager, FeatureFlags featureFlags) {
super(context);
+ mIsMonetEnabled = featureFlags.isMonetEnabled();
mBroadcastDispatcher = broadcastDispatcher;
mUserManager = userManager;
mBgExecutor = bgExecutor;
@@ -221,20 +235,17 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
mMainWallpaperColor = mainColor;
mWallpaperAccentColor = accentCandidate;
- // Let's compare these colors to our finite set of overlays, and then pick an overlay.
- List<Integer> systemColors = mThemeManager.getAvailableSystemColors();
- List<Integer> accentColors = mThemeManager.getAvailableAccentColors();
-
- if (systemColors.size() == 0 || accentColors.size() == 0) {
+ if (mIsMonetEnabled) {
+ mPrimaryOverlay = getOverlay(mMainWallpaperColor, PRIMARY);
+ mSecondaryOverlay = getOverlay(mWallpaperAccentColor, SECONDARY);
+ mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL);
+ mNeedsOverlayCreation = true;
if (DEBUG) {
- Log.d(TAG, "Cannot apply system theme, palettes are unavailable");
+ Log.d(TAG, "fetched overlays. primary: " + mPrimaryOverlay + " secondary: "
+ + mSecondaryOverlay + " neutral: " + mNeutralOverlay);
}
- return;
}
- mSystemOverlayColor = getClosest(systemColors, mMainWallpaperColor);
- mAccentOverlayColor = getClosest(accentColors, mWallpaperAccentColor);
-
updateThemeOverlays();
}
@@ -257,42 +268,10 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
}
/**
- * Given a color and a list of candidates, return the candidate that's the most similar to the
- * given color.
+ * Given a color candidate, return an overlay definition.
*/
- protected int getClosest(List<Integer> candidates, int color) {
- float[] hslMain = new float[3];
- float[] hslCandidate = new float[3];
-
- ColorUtils.RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), hslMain);
- hslMain[0] /= 360f;
-
- // To close to white or black, let's use the default system theme instead of
- // applying a colorized one.
- if (hslMain[2] < 0.05 || hslMain[2] > 0.95) {
- return Color.TRANSPARENT;
- }
-
- float minDistance = Float.MAX_VALUE;
- int closestColor = Color.TRANSPARENT;
- for (int candidate: candidates) {
- ColorUtils.RGBToHSL(Color.red(candidate), Color.green(candidate), Color.blue(candidate),
- hslCandidate);
- hslCandidate[0] /= 360f;
-
- float sqDistance = squared(hslCandidate[0] - hslMain[0])
- + squared(hslCandidate[1] - hslMain[1])
- + squared(hslCandidate[2] - hslMain[2]);
- if (sqDistance < minDistance) {
- minDistance = sqDistance;
- closestColor = candidate;
- }
- }
- return closestColor;
- }
-
- private static float squared(float f) {
- return f * f;
+ protected @Nullable FabricatedOverlay getOverlay(int color, int type) {
+ return null;
}
private void updateThemeOverlays() {
@@ -301,20 +280,15 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
currentUser);
if (DEBUG) Log.d(TAG, "updateThemeOverlays. Setting: " + overlayPackageJson);
- boolean hasSystemPalette = false;
- boolean hasAccentColor = false;
- final Map<String, String> categoryToPackage = new ArrayMap<>();
+ final Map<String, OverlayIdentifier> categoryToPackage = new ArrayMap<>();
if (!TextUtils.isEmpty(overlayPackageJson)) {
try {
JSONObject object = new JSONObject(overlayPackageJson);
for (String category : ThemeOverlayApplier.THEME_CATEGORIES) {
if (object.has(category)) {
- if (category.equals(OVERLAY_CATEGORY_ACCENT_COLOR)) {
- hasAccentColor = true;
- } else if (category.equals(OVERLAY_CATEGORY_SYSTEM_PALETTE)) {
- hasSystemPalette = true;
- }
- categoryToPackage.put(category, object.getString(category));
+ OverlayIdentifier identifier =
+ new OverlayIdentifier(object.getString(category));
+ categoryToPackage.put(category, identifier);
}
}
} catch (JSONException e) {
@@ -322,17 +296,45 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
}
}
- // Let's apply the system palette, but only if it was not overridden by the style picker.
- if (!hasSystemPalette && mSystemOverlayColor != Color.TRANSPARENT) {
- categoryToPackage.put(OVERLAY_CATEGORY_SYSTEM_PALETTE,
- ThemeOverlayApplier.MONET_SYSTEM_PALETTE_PACKAGE
- + getColorString(mSystemOverlayColor));
+ // Let's generate system overlay if the style picker decided to override it.
+ OverlayIdentifier systemPalette = categoryToPackage.get(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+ if (mIsMonetEnabled && systemPalette != null && systemPalette.getPackageName() != null) {
+ try {
+ int color = Integer.parseInt(systemPalette.getPackageName().toLowerCase(), 16);
+ mPrimaryOverlay = getOverlay(color, PRIMARY);
+ // Neutral palette is always derived from primary color.
+ mNeutralOverlay = getOverlay(color, NEUTRAL);
+ mNeedsOverlayCreation = true;
+ categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Invalid color definition: " + systemPalette.getPackageName());
+ }
+ }
+
+ // Same for accent color.
+ OverlayIdentifier accentPalette = categoryToPackage.get(OVERLAY_CATEGORY_ACCENT_COLOR);
+ if (mIsMonetEnabled && accentPalette != null && accentPalette.getPackageName() != null) {
+ try {
+ int color = Integer.parseInt(accentPalette.getPackageName().toLowerCase(), 16);
+ mSecondaryOverlay = getOverlay(color, SECONDARY);
+ mNeedsOverlayCreation = true;
+ categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Invalid color definition: " + accentPalette.getPackageName());
+ }
+ }
+
+ // Compatibility with legacy themes, where full packages were defined, instead of just
+ // colors.
+ if (!categoryToPackage.containsKey(OVERLAY_CATEGORY_SYSTEM_PALETTE)
+ && mPrimaryOverlay != null) {
+ categoryToPackage.put(OVERLAY_CATEGORY_SYSTEM_PALETTE, mPrimaryOverlay.getIdentifier());
+ categoryToPackage.put(OVERLAY_CATEGORY_NEUTRAL_PALETTE,
+ mNeutralOverlay.getIdentifier());
}
- // Same for the accent color
- if (!hasAccentColor && mAccentOverlayColor != Color.TRANSPARENT) {
- categoryToPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR,
- ThemeOverlayApplier.MONET_ACCENT_COLOR_PACKAGE
- + getColorString(mAccentOverlayColor));
+ if (!categoryToPackage.containsKey(OVERLAY_CATEGORY_ACCENT_COLOR)
+ && mSecondaryOverlay != null) {
+ categoryToPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, mSecondaryOverlay.getIdentifier());
}
Set<UserHandle> userHandles = Sets.newHashSet(UserHandle.of(currentUser));
@@ -341,28 +343,32 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
userHandles.add(userInfo.getUserHandle());
}
}
- mThemeManager.applyCurrentUserOverlays(categoryToPackage, userHandles);
- }
-
- private String getColorString(int color) {
- String colorString = Integer.toHexString(color).toUpperCase();
- while (colorString.length() < 6) {
- colorString = "0" + colorString;
+ if (DEBUG) {
+ Log.d(TAG, "Applying overlays: " + categoryToPackage.keySet().stream()
+ .map(key -> key + " -> " + categoryToPackage.get(key)).collect(
+ Collectors.joining(", ")));
}
- // Remove alpha component
- if (colorString.length() > 6) {
- colorString = colorString.substring(colorString.length() - 6);
+ if (mNeedsOverlayCreation) {
+ mNeedsOverlayCreation = false;
+ mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] {
+ mPrimaryOverlay, mSecondaryOverlay, mNeutralOverlay
+ }, userHandles);
+ } else {
+ mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, userHandles);
}
- return colorString;
}
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("USE_LOCK_SCREEN_WALLPAPER=" + USE_LOCK_SCREEN_WALLPAPER);
pw.println("mLockColors=" + mLockColors);
pw.println("mSystemColors=" + mSystemColors);
pw.println("mMainWallpaperColor=" + Integer.toHexString(mMainWallpaperColor));
pw.println("mWallpaperAccentColor=" + Integer.toHexString(mWallpaperAccentColor));
- pw.println("mSystemOverlayColor=" + Integer.toHexString(mSystemOverlayColor));
- pw.println("mAccentOverlayColor=" + Integer.toHexString(mAccentOverlayColor));
+ pw.println("mPrimaryOverlay=" + mPrimaryOverlay);
+ pw.println("mSecondaryOverlay=" + mSecondaryOverlay);
+ pw.println("mNeutralOverlay=" + mNeutralOverlay);
+ pw.println("mIsMonetEnabled=" + mIsMonetEnabled);
+ pw.println("mNeedsOverlayCreation=" + mNeedsOverlayCreation);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
index e9fcf1aa9598..365cd2a5d20b 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -20,10 +20,21 @@ import android.animation.Animator;
import android.annotation.NonNull;
import android.annotation.Nullable;
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;
+import android.widget.TextView;
import android.widget.ToastPresenter;
import com.android.internal.R;
+import com.android.launcher3.icons.IconFactory;
import com.android.systemui.plugins.ToastPlugin;
/**
@@ -35,23 +46,43 @@ public class SystemUIToast implements ToastPlugin.Toast {
final CharSequence mText;
final ToastPlugin.Toast mPluginToast;
- final int mDefaultGravity;
- final int mDefaultY;
+ private final String mPackageName;
+ private final int mUserId;
+ private final LayoutInflater mLayoutInflater;
+ private final boolean mToastStyleEnabled;
+
final int mDefaultX = 0;
final int mDefaultHorizontalMargin = 0;
final int mDefaultVerticalMargin = 0;
- SystemUIToast(Context context, CharSequence text) {
- this(context, text, null);
+ private int mDefaultY;
+ private int mDefaultGravity;
+
+ @NonNull private final View mToastView;
+ @Nullable private final Animator mInAnimator;
+ @Nullable private final Animator mOutAnimator;
+
+ SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text,
+ String packageName, int userId, boolean toastStyleEnabled, int orientation) {
+ this(layoutInflater, context, text, null, packageName, userId,
+ toastStyleEnabled, orientation);
}
- SystemUIToast(Context context, CharSequence text, ToastPlugin.Toast pluginToast) {
+ SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text,
+ ToastPlugin.Toast pluginToast, String packageName, int userId,
+ boolean toastStyleEnabled, int orientation) {
+ mToastStyleEnabled = toastStyleEnabled;
+ mLayoutInflater = layoutInflater;
mContext = context;
mText = text;
mPluginToast = pluginToast;
+ mPackageName = packageName;
+ mUserId = userId;
+ mToastView = inflateToastView();
+ mInAnimator = createInAnimator();
+ mOutAnimator = createOutAnimator();
- mDefaultGravity = context.getResources().getInteger(R.integer.config_toastDefaultGravity);
- mDefaultY = context.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);
+ onOrientationChange(orientation);
}
@Override
@@ -102,28 +133,19 @@ public class SystemUIToast implements ToastPlugin.Toast {
@Override
@NonNull
public View getView() {
- if (isPluginToast() && mPluginToast.getView() != null) {
- return mPluginToast.getView();
- }
- return ToastPresenter.getTextToastView(mContext, mText);
+ return mToastView;
}
@Override
@Nullable
public Animator getInAnimation() {
- if (isPluginToast() && mPluginToast.getInAnimation() != null) {
- return mPluginToast.getInAnimation();
- }
- return null;
+ return mInAnimator;
}
@Override
@Nullable
public Animator getOutAnimation() {
- if (isPluginToast() && mPluginToast.getOutAnimation() != null) {
- return mPluginToast.getOutAnimation();
- }
- return null;
+ return mOutAnimator;
}
/**
@@ -136,4 +158,80 @@ public class SystemUIToast implements ToastPlugin.Toast {
private boolean isPluginToast() {
return mPluginToast != null;
}
+
+ private View inflateToastView() {
+ if (isPluginToast() && mPluginToast.getView() != null) {
+ return mPluginToast.getView();
+ }
+
+ View toastView;
+ if (mToastStyleEnabled) {
+ toastView = mLayoutInflater.inflate(
+ 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));
+ } else {
+ toastView = ToastPresenter.getTextToastView(mContext, mText);
+ }
+
+ return toastView;
+ }
+
+ /**
+ * Called on orientation changes to update parameters associated with the toast placement.
+ */
+ public void onOrientationChange(int orientation) {
+ if (mPluginToast != null) {
+ mPluginToast.onOrientationChange(orientation);
+ }
+
+ mDefaultY = mContext.getResources().getDimensionPixelSize(
+ mToastStyleEnabled
+ ? com.android.systemui.R.dimen.toast_y_offset
+ : R.dimen.toast_y_offset);
+ mDefaultGravity =
+ mContext.getResources().getInteger(R.integer.config_toastDefaultGravity);
+ }
+
+ private Animator createInAnimator() {
+ if (isPluginToast() && mPluginToast.getInAnimation() != null) {
+ return mPluginToast.getInAnimation();
+ }
+
+ return mToastStyleEnabled
+ ? ToastDefaultAnimation.Companion.toastIn(getView())
+ : null;
+ }
+
+ private Animator createOutAnimator() {
+ if (isPluginToast() && mPluginToast.getOutAnimation() != null) {
+ return mPluginToast.getOutAnimation();
+ }
+ return mToastStyleEnabled
+ ? ToastDefaultAnimation.Companion.toastOut(getView())
+ : null;
+ }
+
+ /**
+ * Get badged app icon if necessary, similar as used in the Settings UI.
+ * @return The icon to use
+ */
+ 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);
+ return null;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt
new file mode 100644
index 000000000000..603d69057e5c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt
@@ -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.toast
+
+import android.animation.ObjectAnimator
+import android.view.View
+import android.view.animation.LinearInterpolator
+import android.view.animation.PathInterpolator
+import android.animation.AnimatorSet
+
+class ToastDefaultAnimation {
+ /**
+ * sum of the in and out animation durations cannot exceed
+ * [com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_ANIM_BUFFER] to prevent the toast
+ * window from being removed before animations are completed
+ */
+ companion object {
+ // total duration shouldn't exceed NotificationManagerService's delay for "in" animation
+ fun toastIn(view: View): AnimatorSet? {
+ val icon: View? = view.findViewById(com.android.systemui.R.id.icon)
+ val text: View? = view.findViewById(com.android.systemui.R.id.text)
+ if (icon == null || text == null) {
+ return null
+ }
+ val linearInterp = LinearInterpolator()
+ val scaleInterp = PathInterpolator(0f, 0f, 0f, 1f)
+ val sX = ObjectAnimator.ofFloat(view, "scaleX", 0.9f, 1f).apply {
+ interpolator = scaleInterp
+ duration = 333
+ }
+ val sY = ObjectAnimator.ofFloat(view, "scaleY", 0.9f, 1f).apply {
+ interpolator = scaleInterp
+ duration = 333
+ }
+ val vA = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f).apply {
+ interpolator = linearInterp
+ duration = 66
+ }
+ text.alpha = 0f // Set now otherwise won't apply until start delay
+ val tA = ObjectAnimator.ofFloat(text, "alpha", 0f, 1f).apply {
+ interpolator = linearInterp
+ duration = 283
+ startDelay = 50
+ }
+ icon.alpha = 0f // Set now otherwise won't apply until start delay
+ val iA = ObjectAnimator.ofFloat(icon, "alpha", 0f, 1f).apply {
+ interpolator = linearInterp
+ duration = 283
+ startDelay = 50
+ }
+ return AnimatorSet().apply {
+ playTogether(sX, sY, vA, tA, iA)
+ }
+ }
+
+ fun toastOut(view: View): AnimatorSet? {
+ // total duration shouldn't exceed NotificationManagerService's delay for "out" anim
+ val icon: View? = view.findViewById(com.android.systemui.R.id.icon)
+ val text: View? = view.findViewById(com.android.systemui.R.id.text)
+ if (icon == null || text == null) {
+ return null
+ }
+ val linearInterp = LinearInterpolator()
+ val scaleInterp = PathInterpolator(0.3f, 0f, 1f, 1f)
+ val sX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.9f).apply {
+ interpolator = scaleInterp
+ duration = 250
+ }
+ val sY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.9f).apply {
+ interpolator = scaleInterp
+ duration = 250
+ }
+ val vA = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f).apply {
+ interpolator = linearInterp
+ duration = 100
+ startDelay = 150
+ }
+ val tA = ObjectAnimator.ofFloat(text, "alpha", 1f, 0f).apply {
+ interpolator = linearInterp
+ duration = 166
+ }
+ val iA = ObjectAnimator.ofFloat(icon, "alpha", 1f, 0f).apply {
+ interpolator = linearInterp
+ duration = 166
+ }
+ return AnimatorSet().apply {
+ playTogether(sX, sY, vA, tA, iA)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
index d8cb61c6b349..8b782d4b7923 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
@@ -17,6 +17,7 @@
package com.android.systemui.toast;
import android.content.Context;
+import android.view.LayoutInflater;
import androidx.annotation.NonNull;
@@ -26,6 +27,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.ToastPlugin;
import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.FeatureFlags;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -40,10 +42,18 @@ import javax.inject.Inject;
public class ToastFactory implements Dumpable {
// only one ToastPlugin can be connected at a time.
private ToastPlugin mPlugin;
+ private final LayoutInflater mLayoutInflater;
+ private final boolean mToastStyleEnabled;
@Inject
- public ToastFactory(PluginManager pluginManager, DumpManager dumpManager) {
+ public ToastFactory(
+ LayoutInflater layoutInflater,
+ PluginManager pluginManager,
+ DumpManager dumpManager,
+ FeatureFlags featureFlags) {
+ mLayoutInflater = layoutInflater;
dumpManager.registerDumpable("ToastFactory", this);
+ mToastStyleEnabled = featureFlags.isToastStyleEnabled();
pluginManager.addPluginListener(
new PluginListener<ToastPlugin>() {
@Override
@@ -64,11 +74,13 @@ public class ToastFactory implements Dumpable {
* Create a toast to be shown by ToastUI.
*/
public SystemUIToast createToast(Context context, CharSequence text, String packageName,
- int userId) {
+ int userId, int orientation) {
if (isPluginAvailable()) {
- return new SystemUIToast(context, text, mPlugin.createToast(text, packageName, userId));
+ return new SystemUIToast(mLayoutInflater, context, text, mPlugin.createToast(text,
+ packageName, userId), packageName, userId, mToastStyleEnabled, orientation);
}
- return new SystemUIToast(context, text);
+ return new SystemUIToast(mLayoutInflater, context, text, packageName, userId,
+ mToastStyleEnabled, orientation);
}
private boolean isPluginAvailable() {
@@ -79,5 +91,6 @@ public class ToastFactory implements Dumpable {
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("ToastFactory:");
pw.println(" mAttachedPlugin=" + mPlugin);
+ pw.println(" mToastStyleEnabled=" + mToastStyleEnabled);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
index 78173cf62a93..51541bd3032e 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
@@ -49,6 +49,15 @@ class ToastLogger @Inject constructor(
})
}
+ fun logOrientationChange(text: String, isPortrait: Boolean) {
+ log(DEBUG, {
+ str1 = text
+ bool1 = isPortrait
+ }, {
+ "Orientation change for toast. msg=\'$str1\' isPortrait=$bool1"
+ })
+ }
+
private inline fun log(
logLevel: LogLevel,
initializer: LogMessage.() -> Unit,
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index 409d1361223c..92ea1d0e5fbd 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -16,27 +16,28 @@
package com.android.systemui.toast;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
import android.animation.Animator;
import android.annotation.MainThread;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.INotificationManager;
import android.app.ITransientNotificationCallback;
import android.content.Context;
+import android.content.res.Configuration;
import android.os.IBinder;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Log;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
-import android.widget.Toast;
import android.widget.ToastPresenter;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.Objects;
@@ -58,18 +59,19 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
private final IAccessibilityManager mIAccessibilityManager;
private final AccessibilityManager mAccessibilityManager;
private final ToastFactory mToastFactory;
- private final DelayableExecutor mMainExecutor;
private final ToastLogger mToastLogger;
private SystemUIToast mToast;
@Nullable private ToastPresenter mPresenter;
@Nullable private ITransientNotificationCallback mCallback;
+ private ToastOutAnimatorListener mToastOutAnimatorListener;
+
+ private int mOrientation = ORIENTATION_PORTRAIT;
@Inject
public ToastUI(
Context context,
CommandQueue commandQueue,
ToastFactory toastFactory,
- @Main DelayableExecutor mainExecutor,
ToastLogger toastLogger) {
this(context, commandQueue,
INotificationManager.Stub.asInterface(
@@ -77,21 +79,19 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
IAccessibilityManager.Stub.asInterface(
ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)),
toastFactory,
- mainExecutor,
toastLogger);
}
@VisibleForTesting
ToastUI(Context context, CommandQueue commandQueue, INotificationManager notificationManager,
@Nullable IAccessibilityManager accessibilityManager,
- ToastFactory toastFactory, DelayableExecutor mainExecutor, ToastLogger toastLogger
+ ToastFactory toastFactory, ToastLogger toastLogger
) {
super(context);
mCommandQueue = commandQueue;
mNotificationManager = notificationManager;
mIAccessibilityManager = accessibilityManager;
mToastFactory = toastFactory;
- mMainExecutor = mainExecutor;
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mToastLogger = toastLogger;
}
@@ -105,36 +105,38 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
@MainThread
public void showToast(int uid, String packageName, IBinder token, CharSequence text,
IBinder windowToken, int duration, @Nullable ITransientNotificationCallback callback) {
- if (mPresenter != null) {
- hideCurrentToast();
- }
- UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
- Context context = mContext.createContextAsUser(userHandle, 0);
- mToast = mToastFactory.createToast(context, text, packageName, userHandle.getIdentifier());
+ Runnable showToastRunnable = () -> {
+ UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
+ Context context = mContext.createContextAsUser(userHandle, 0);
+ mToast = mToastFactory.createToast(mContext /* sysuiContext */, text, packageName,
+ userHandle.getIdentifier(), mOrientation);
- if (mToast.hasCustomAnimation()) {
if (mToast.getInAnimation() != null) {
mToast.getInAnimation().start();
}
- final Animator hideAnimator = mToast.getOutAnimation();
- if (hideAnimator != null) {
- final long durationMillis = duration == Toast.LENGTH_LONG
- ? TOAST_LONG_TIME : TOAST_SHORT_TIME;
- final long updatedDuration = mAccessibilityManager.getRecommendedTimeoutMillis(
- (int) durationMillis, AccessibilityManager.FLAG_CONTENT_TEXT);
- mMainExecutor.executeDelayed(() -> hideAnimator.start(),
- updatedDuration - hideAnimator.getTotalDuration());
- }
+
+ mCallback = callback;
+ mPresenter = new ToastPresenter(context, mIAccessibilityManager,
+ mNotificationManager, packageName);
+ // Set as trusted overlay so touches can pass through toasts
+ mPresenter.getLayoutParams().setTrustedOverlay();
+ mToastLogger.logOnShowToast(uid, packageName, text.toString(), token.toString());
+ mPresenter.show(mToast.getView(), token, windowToken, duration, mToast.getGravity(),
+ mToast.getXOffset(), mToast.getYOffset(), mToast.getHorizontalMargin(),
+ mToast.getVerticalMargin(), mCallback, mToast.hasCustomAnimation());
+ };
+
+ if (mToastOutAnimatorListener != null) {
+ // if we're currently animating out a toast, show new toast after prev toast is hidden
+ mToastOutAnimatorListener.setShowNextToastRunnable(showToastRunnable);
+ } else if (mPresenter != null) {
+ // if there's a toast already showing that we haven't tried hiding yet, hide it and
+ // then show the next toast after its hidden animation is done
+ hideCurrentToast(showToastRunnable);
+ } else {
+ // else, show this next toast immediately
+ showToastRunnable.run();
}
- mCallback = callback;
- mPresenter = new ToastPresenter(context, mIAccessibilityManager, mNotificationManager,
- packageName);
- // Set as trusted overlay so touches can pass through toasts
- mPresenter.getLayoutParams().setTrustedOverlay();
- mToastLogger.logOnShowToast(uid, packageName, text.toString(), token.toString());
- mPresenter.show(mToast.getView(), token, windowToken, duration, mToast.getGravity(),
- mToast.getXOffset(), mToast.getYOffset(), mToast.getHorizontalMargin(),
- mToast.getVerticalMargin(), mCallback, mToast.hasCustomAnimation());
}
@Override
@@ -146,12 +148,88 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
return;
}
mToastLogger.logOnHideToast(packageName, token.toString());
- hideCurrentToast();
+ hideCurrentToast(null);
}
@MainThread
- private void hideCurrentToast() {
- mPresenter.hide(mCallback);
+ private void hideCurrentToast(Runnable runnable) {
+ if (mToast.getOutAnimation() != null) {
+ Animator animator = mToast.getOutAnimation();
+ mToastOutAnimatorListener = new ToastOutAnimatorListener(mPresenter, mCallback,
+ runnable);
+ animator.addListener(mToastOutAnimatorListener);
+ animator.start();
+ } else {
+ mPresenter.hide(mCallback);
+ if (runnable != null) {
+ runnable.run();
+ }
+ }
+ mToast = null;
mPresenter = null;
+ mCallback = null;
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ if (newConfig.orientation != mOrientation) {
+ mOrientation = newConfig.orientation;
+ if (mToast != null) {
+ mToastLogger.logOrientationChange(mToast.mText.toString(),
+ mOrientation == ORIENTATION_PORTRAIT);
+ mToast.onOrientationChange(mOrientation);
+ mPresenter.updateLayoutParams(
+ mToast.getXOffset(),
+ mToast.getYOffset(),
+ mToast.getHorizontalMargin(),
+ mToast.getVerticalMargin(),
+ mToast.getGravity());
+ }
+ }
+ }
+
+ /**
+ * Once the out animation for a toast is finished, start showing the next toast.
+ */
+ class ToastOutAnimatorListener implements Animator.AnimatorListener {
+ final ToastPresenter mPrevPresenter;
+ final ITransientNotificationCallback mPrevCallback;
+ @Nullable Runnable mShowNextToastRunnable;
+
+ ToastOutAnimatorListener(
+ @NonNull ToastPresenter presenter,
+ @NonNull ITransientNotificationCallback callback,
+ @Nullable Runnable runnable) {
+ mPrevPresenter = presenter;
+ mPrevCallback = callback;
+ mShowNextToastRunnable = runnable;
+ }
+
+ void setShowNextToastRunnable(Runnable runnable) {
+ mShowNextToastRunnable = runnable;
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mPrevPresenter.hide(mPrevCallback);
+ if (mShowNextToastRunnable != null) {
+ mShowNextToastRunnable.run();
+ }
+ mToastOutAnimatorListener = null;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ onAnimationEnd(animation);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 09335afc059c..b67574d1c4de 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -109,7 +109,7 @@ public abstract class TunerService {
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.cancel),
(OnClickListener) null);
dialog.setButton(DialogInterface.BUTTON_POSITIVE,
- context.getString(R.string.qs_customize_remove), new OnClickListener() {
+ context.getString(R.string.guest_exit_guest_dialog_remove), new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Tell the tuner (in main SysUI process) to clear all its settings.
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 72f1f22c0ba1..fd3641cfdaa0 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -20,12 +20,15 @@ import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.provider.Settings;
import android.text.TextUtils;
import android.view.View;
+import com.android.systemui.R;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
import java.util.HashSet;
import java.util.List;
@@ -163,4 +166,15 @@ public class Utils {
}
return apps;
}
+
+ /**
+ * Returns true if the device should use the split notification shade, based on feature flags,
+ * orientation and screen width.
+ */
+ public static boolean shouldUseSplitNotificationShade(FeatureFlags featureFlags,
+ Resources resources) {
+ return featureFlags.isTwoColumnNotificationShadeEnabled()
+ && resources.getBoolean(R.bool.config_use_split_notification_shade);
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 6e7aed064159..afeda967c8c1 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -45,6 +45,7 @@ import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.ZenModeConfig;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.view.View;
import androidx.annotation.NonNull;
@@ -86,10 +87,12 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import java.util.function.IntConsumer;
import java.util.function.Supplier;
@@ -248,38 +251,19 @@ public class BubblesManager implements Dumpable {
});
mSysuiProxy = new Bubbles.SysuiProxy() {
- private <T> T executeBlockingForResult(Supplier<T> runnable, Executor executor,
- Class clazz) {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- return runnable.get();
- }
- final T[] result = (T[]) Array.newInstance(clazz, 1);
- final CountDownLatch latch = new CountDownLatch(1);
- executor.execute(() -> {
- result[0] = runnable.get();
- latch.countDown();
- });
- try {
- latch.await();
- return result[0];
- } catch (InterruptedException e) {
- return null;
- }
- }
-
@Override
- @Nullable
- public BubbleEntry getPendingOrActiveEntry(String key) {
- return executeBlockingForResult(() -> {
+ public void getPendingOrActiveEntry(String key, Consumer<BubbleEntry> callback) {
+ sysuiMainExecutor.execute(() -> {
NotificationEntry entry =
mNotificationEntryManager.getPendingOrActiveNotif(key);
- return entry == null ? null : notifToBubbleEntry(entry);
- }, sysuiMainExecutor, BubbleEntry.class);
+ callback.accept(entry == null ? null : notifToBubbleEntry(entry));
+ });
}
@Override
- public List<BubbleEntry> getShouldRestoredEntries(ArraySet<String> savedBubbleKeys) {
- return executeBlockingForResult(() -> {
+ public void getShouldRestoredEntries(ArraySet<String> savedBubbleKeys,
+ Consumer<List<BubbleEntry>> callback) {
+ sysuiMainExecutor.execute(() -> {
List<BubbleEntry> result = new ArrayList<>();
List<NotificationEntry> activeEntries =
mNotificationEntryManager.getActiveNotificationsForCurrentUser();
@@ -291,27 +275,8 @@ public class BubblesManager implements Dumpable {
result.add(notifToBubbleEntry(entry));
}
}
- return result;
- }, sysuiMainExecutor, List.class);
- }
-
- @Override
- public boolean isNotificationShadeExpand() {
- return executeBlockingForResult(() -> {
- return mNotificationShadeWindowController.getPanelExpanded();
- }, sysuiMainExecutor, Boolean.class);
- }
-
- @Override
- public boolean shouldBubbleUp(String key) {
- return executeBlockingForResult(() -> {
- final NotificationEntry entry =
- mNotificationEntryManager.getPendingOrActiveNotif(key);
- if (entry != null) {
- return mNotificationInterruptStateProvider.shouldBubbleUp(entry);
- }
- return false;
- }, sysuiMainExecutor, Boolean.class);
+ callback.accept(result);
+ });
}
@Override
@@ -587,7 +552,20 @@ public class BubblesManager implements Dumpable {
}
void onRankingUpdate(RankingMap rankingMap) {
- mBubbles.onRankingUpdated(rankingMap);
+ String[] orderedKeys = rankingMap.getOrderedKeys();
+ HashMap<String, Pair<BubbleEntry, Boolean>> pendingOrActiveNotif = new HashMap<>();
+ for (int i = 0; i < orderedKeys.length; i++) {
+ String key = orderedKeys[i];
+ NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
+ BubbleEntry bubbleEntry = entry != null
+ ? notifToBubbleEntry(entry)
+ : null;
+ boolean shouldBubbleUp = entry != null
+ ? mNotificationInterruptStateProvider.shouldBubbleUp(entry)
+ : false;
+ pendingOrActiveNotif.put(key, new Pair<>(bubbleEntry, shouldBubbleUp));
+ }
+ mBubbles.onRankingUpdated(rankingMap, pendingOrActiveNotif);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 0795d89eb0bc..ff2881953342 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -19,6 +19,7 @@ package com.android.systemui.wmshell;
import android.content.Context;
import android.os.Handler;
+import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.WMSingleton;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -28,6 +29,7 @@ import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
@@ -49,7 +51,7 @@ import dagger.Module;
import dagger.Provides;
/**
- * Dagger module for TV Pip.
+ * Provides TV specific dependencies for Pip.
*/
@Module(includes = {WMShellBaseModule.class})
public abstract class TvPipModule {
@@ -143,7 +145,8 @@ public abstract class TvPipModule {
PipAnimationController pipAnimationController,
PipTransitionController pipTransitionController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
- Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController,
+ Optional<LegacySplitScreenController> splitScreenOptional,
+ DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
index f23367b4d65b..141b9f7d410d 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -20,6 +20,7 @@ import android.animation.AnimationHandler;
import android.content.Context;
import android.view.IWindowManager;
+import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.WMSingleton;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -39,11 +40,20 @@ import dagger.Module;
import dagger.Provides;
/**
- * Provides dependencies from {@link com.android.wm.shell} which could be customized among different
- * branches of SystemUI.
+ * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only
+ * accessible from components within the WM subcomponent (can be explicitly exposed to the
+ * SysUIComponent, see {@link WMComponent}).
+ *
+ * This module only defines Shell dependencies for the TV SystemUI implementation. Common
+ * dependencies should go into {@link WMShellBaseModule}.
*/
@Module(includes = {TvPipModule.class})
public class TvWMShellModule {
+
+ //
+ // Internal common - Components used internally by multiple shell features
+ //
+
@WMSingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
@@ -53,16 +63,20 @@ public class TvWMShellModule {
transactionPool);
}
+ //
+ // Split/multiwindow
+ //
+
@WMSingleton
@Provides
- static LegacySplitScreen provideSplitScreen(Context context,
+ static LegacySplitScreenController provideSplitScreen(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController displayImeController, TransactionPool transactionPool,
ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
TaskStackListenerImpl taskStackListener, Transitions transitions,
@ShellMainThread ShellExecutor mainExecutor,
@ChoreographerSfVsync AnimationHandler sfVsyncAnimationHandler) {
- return LegacySplitScreenController.create(context, displayController, systemWindows,
+ return new LegacySplitScreenController(context, displayController, systemWindows,
displayImeController, transactionPool, shellTaskOrganizer, syncQueue,
taskStackListener, transitions, mainExecutor, sfVsyncAnimationHandler);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 81ac21c00823..4eb75ebe4553 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -43,6 +43,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.model.SysUiState;
@@ -73,7 +74,20 @@ import java.util.concurrent.Executor;
import javax.inject.Inject;
/**
- * Proxy in SysUiScope to delegate events to controllers in WM Shell library.
+ * A SystemUI service that starts with the SystemUI application and sets up any bindings between
+ * Shell and SysUI components. This service starts happens after the {@link WMComponent} has
+ * already been initialized and may only reference Shell components that are explicitly exported to
+ * SystemUI (see {@link WMComponent}.
+ *
+ * eg. SysUI application starts
+ * -> SystemUIFactory is initialized
+ * -> WMComponent is created
+ * -> WMShellBaseModule dependencies are injected
+ * -> WMShellModule (form-factory specific) dependencies are injected
+ * -> SysUIComponent is created
+ * -> WMComponents are explicitly provided to SysUIComponent for injection into SysUI code
+ * -> SysUI services are started
+ * -> WMShell starts and binds SysUI with Shell components via exported Shell interfaces
*/
@SysUISingleton
public final class WMShell extends SystemUI
@@ -142,6 +156,8 @@ public final class WMShell extends SystemUI
@Override
public void start() {
+ // TODO: Consider piping config change and other common calls to a shell component to
+ // delegate internally
mProtoTracer.add(this);
mCommandQueue.addCallback(this);
mPipOptional.ifPresent(this::initPip);
@@ -271,6 +287,13 @@ public final class WMShell extends SystemUI
@Override
public void onKeyguardVisibilityChanged(boolean showing) {
+ if (showing) {
+ // When keyguard shown, temperory lock OHM disabled to avoid mis-trigger.
+ oneHanded.setLockedDisabled(true /* locked */, false /* enabled */);
+ } else {
+ // Reset locked.
+ oneHanded.setLockedDisabled(false /* locked */, false /* enabled */);
+ }
oneHanded.stopOneHanded();
}
};
@@ -299,6 +322,13 @@ public final class WMShell extends SystemUI
}
}
});
+
+ mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ oneHanded.onConfigChanged(newConfig);
+ }
+ });
}
@VisibleForTesting
@@ -350,7 +380,7 @@ public final class WMShell extends SystemUI
switch (args[i]) {
case "enable-text": {
String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
- int result = protoLogImpl.startTextLogging(mContext, groups, pw);
+ int result = protoLogImpl.startTextLogging(groups, pw);
if (result == 0) {
pw.println("Starting logging on groups: " + Arrays.toString(groups));
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index b7aa907c6180..fba0b0079012 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -20,7 +20,6 @@ import static android.os.Process.THREAD_PRIORITY_DISPLAY;
import android.animation.AnimationHandler;
import android.app.ActivityTaskManager;
-import android.app.IActivityManager;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
@@ -32,6 +31,7 @@ import android.view.WindowManager;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.WMSingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.wm.shell.FullscreenTaskListener;
@@ -45,6 +45,7 @@ import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.TaskViewFactoryController;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.DisplayController;
@@ -63,6 +64,7 @@ import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
@@ -71,10 +73,10 @@ import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PipAppOpsListener;
import com.android.wm.shell.pip.phone.PipTouchHandler;
-import com.android.wm.shell.sizecompatui.SizeCompatUI;
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.transition.RemoteTransitions;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -84,8 +86,13 @@ import dagger.Module;
import dagger.Provides;
/**
- * Provides basic dependencies from {@link com.android.wm.shell}, the dependencies declared here
- * should be shared among different branches of SystemUI.
+ * Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only
+ * accessible from components within the WM subcomponent (can be explicitly exposed to the
+ * SysUIComponent, see {@link WMComponent}).
+ *
+ * This module only defines *common* dependencies across various SystemUI implementations,
+ * dependencies that are device/form factor SystemUI implementation specific should go into their
+ * respective modules (ie. {@link WMShellModule} for handheld, {@link TvWMShellModule} for tv, etc.)
*/
@Module
public abstract class WMShellBaseModule {
@@ -173,72 +180,65 @@ public abstract class WMShellBaseModule {
}
}
+ //
+ // Internal common - Components used internally by multiple shell features
+ //
+
@WMSingleton
@Provides
- static ShellInit provideShellInit(DisplayImeController displayImeController,
- DragAndDropController dragAndDropController,
- ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreen> splitScreenOptional,
- Optional<AppPairs> appPairsOptional,
- FullscreenTaskListener fullscreenTaskListener,
- Transitions transitions,
- @ShellMainThread ShellExecutor mainExecutor) {
- return ShellInitImpl.create(displayImeController,
- dragAndDropController,
- shellTaskOrganizer,
- legacySplitScreenOptional,
- splitScreenOptional,
- appPairsOptional,
- fullscreenTaskListener,
- transitions,
- mainExecutor);
+ static DisplayController provideDisplayController(Context context,
+ IWindowManager wmService, @ShellMainThread ShellExecutor mainExecutor) {
+ return new DisplayController(context, wmService, mainExecutor);
}
- /**
- * Note, this is only optional because we currently pass this to the SysUI component scope and
- * for non-primary users, we may inject a null-optional for that dependency.
- */
@WMSingleton
@Provides
- static Optional<ShellCommandHandler> provideShellCommandHandler(
- ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreen> splitScreenOptional,
- Optional<Pip> pipOptional,
- Optional<OneHanded> oneHandedOptional,
- Optional<HideDisplayCutout> hideDisplayCutout,
- Optional<AppPairs> appPairsOptional,
- @ShellMainThread ShellExecutor mainExecutor) {
- return Optional.of(ShellCommandHandlerImpl.create(shellTaskOrganizer,
- legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional,
- hideDisplayCutout, appPairsOptional, mainExecutor));
+ static DragAndDropController provideDragAndDropController(Context context,
+ DisplayController displayController) {
+ return new DragAndDropController(context, displayController);
}
@WMSingleton
@Provides
- static TransactionPool provideTransactionPool() {
- return new TransactionPool();
+ static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor,
+ Context context, SizeCompatUIController sizeCompatUI) {
+ return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI);
}
@WMSingleton
@Provides
- static DisplayController provideDisplayController(Context context,
- IWindowManager wmService, @ShellMainThread ShellExecutor mainExecutor) {
- return new DisplayController(context, wmService, mainExecutor);
+ static SizeCompatUIController provideSizeCompatUIController(Context context,
+ DisplayController displayController, DisplayImeController imeController,
+ SyncTransactionQueue syncQueue) {
+ return new SizeCompatUIController(context, displayController, imeController, syncQueue);
}
@WMSingleton
@Provides
- static DragAndDropController provideDragAndDropController(Context context,
- DisplayController displayController) {
- return new DragAndDropController(context, displayController);
+ static SyncTransactionQueue provideSyncTransactionQueue(TransactionPool pool,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new SyncTransactionQueue(pool, mainExecutor);
}
@WMSingleton
@Provides
- static FloatingContentCoordinator provideFloatingContentCoordinator() {
- return new FloatingContentCoordinator();
+ static SystemWindows provideSystemWindows(DisplayController displayController,
+ IWindowManager wmService) {
+ return new SystemWindows(displayController, wmService);
+ }
+
+ // We currently dedupe multiple messages, so we use the shell main handler directly
+ @WMSingleton
+ @Provides
+ static TaskStackListenerImpl providerTaskStackListenerImpl(
+ @ShellMainThread Handler mainHandler) {
+ return new TaskStackListenerImpl(mainHandler);
+ }
+
+ @WMSingleton
+ @Provides
+ static TransactionPool provideTransactionPool() {
+ return new TransactionPool();
}
@WMSingleton
@@ -248,10 +248,99 @@ public abstract class WMShellBaseModule {
return new WindowManagerShellWrapper(mainExecutor);
}
+ //
+ // Bubbles
+ //
+
+ @WMSingleton
+ @Provides
+ static Optional<Bubbles> provideBubbles(Optional<BubbleController> bubbleController) {
+ return bubbleController.map((controller) -> controller.asBubbles());
+ }
+
+ // Note: Handler needed for LauncherApps.register
+ @WMSingleton
+ @Provides
+ static Optional<BubbleController> provideBubbleController(Context context,
+ FloatingContentCoordinator floatingContentCoordinator,
+ IStatusBarService statusBarService,
+ WindowManager windowManager,
+ WindowManagerShellWrapper windowManagerShellWrapper,
+ LauncherApps launcherApps,
+ UiEventLogger uiEventLogger,
+ ShellTaskOrganizer organizer,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler) {
+ return Optional.of(BubbleController.create(context, null /* synchronizer */,
+ floatingContentCoordinator, statusBarService, windowManager,
+ windowManagerShellWrapper, launcherApps, uiEventLogger, organizer,
+ mainExecutor, mainHandler));
+ }
+
+ //
+ // Fullscreen
+ //
+
+ @WMSingleton
+ @Provides
+ static FullscreenTaskListener provideFullscreenTaskListener(SyncTransactionQueue syncQueue) {
+ return new FullscreenTaskListener(syncQueue);
+ }
+
+ //
+ // Hide display cutout
+ //
+
+ @WMSingleton
+ @Provides
+ static Optional<HideDisplayCutout> provideHideDisplayCutout(
+ Optional<HideDisplayCutoutController> hideDisplayCutoutController) {
+ return hideDisplayCutoutController.map((controller) -> controller.asHideDisplayCutout());
+ }
+
+ @WMSingleton
+ @Provides
+ static Optional<HideDisplayCutoutController> provideHideDisplayCutoutController(Context context,
+ DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor) {
+ return Optional.ofNullable(
+ HideDisplayCutoutController.create(context, displayController, mainExecutor));
+ }
+
+ //
+ // One handed mode (optional feature)
+ //
+
+ @WMSingleton
+ @Provides
+ static Optional<OneHanded> provideOneHanded(Optional<OneHandedController> oneHandedController) {
+ return oneHandedController.map((controller) -> controller.asOneHanded());
+ }
+
+ // Needs the shell main handler for ContentObserver callbacks
+ @WMSingleton
+ @Provides
+ static Optional<OneHandedController> provideOneHandedController(Context context,
+ DisplayController displayController, TaskStackListenerImpl taskStackListener,
+ UiEventLogger uiEventLogger,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler) {
+ return Optional.ofNullable(OneHandedController.create(context, displayController,
+ taskStackListener, uiEventLogger, mainExecutor, mainHandler));
+ }
+
+ //
+ // Pip (optional feature)
+ //
+
+ @WMSingleton
+ @Provides
+ static FloatingContentCoordinator provideFloatingContentCoordinator() {
+ return new FloatingContentCoordinator();
+ }
+
@WMSingleton
@Provides
static PipAppOpsListener providePipAppOpsListener(Context context,
- IActivityManager activityManager,
PipTouchHandler pipTouchHandler,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipAppOpsListener(context, pipTouchHandler.getMotionHelper(), mainExecutor);
@@ -267,38 +356,39 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
- static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger,
- PackageManager packageManager) {
- return new PipUiEventLogger(uiEventLogger, packageManager);
- }
-
- @WMSingleton
- @Provides
static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {
return new PipSurfaceTransactionHelper(context);
}
@WMSingleton
@Provides
- static SystemWindows provideSystemWindows(DisplayController displayController,
- IWindowManager wmService) {
- return new SystemWindows(displayController, wmService);
+ static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger,
+ PackageManager packageManager) {
+ return new PipUiEventLogger(uiEventLogger, packageManager);
}
+ //
+ // Shell transitions
+ //
+
@WMSingleton
@Provides
- static SyncTransactionQueue provideSyncTransactionQueue(TransactionPool pool,
- @ShellMainThread ShellExecutor mainExecutor) {
- return new SyncTransactionQueue(pool, mainExecutor);
+ static RemoteTransitions provideRemoteTransitions(Transitions transitions) {
+ return Transitions.asRemoteTransitions(transitions);
}
@WMSingleton
@Provides
- static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor,
- Context context, SizeCompatUI sizeCompatUI) {
- return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI);
+ static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellAnimationThread ShellExecutor animExecutor) {
+ return new Transitions(organizer, pool, mainExecutor, animExecutor);
}
+ //
+ // Split/multiwindow
+ //
+
@WMSingleton
@Provides
static RootTaskDisplayAreaOrganizer provideRootTaskDisplayAreaOrganizer(
@@ -306,99 +396,113 @@ public abstract class WMShellBaseModule {
return new RootTaskDisplayAreaOrganizer(mainExecutor, context);
}
- // We currently dedupe multiple messages, so we use the shell main handler directly
@WMSingleton
@Provides
- static TaskStackListenerImpl providerTaskStackListenerImpl(
- @ShellMainThread Handler mainHandler) {
- return new TaskStackListenerImpl(mainHandler);
+ static Optional<SplitScreen> provideSplitScreen(
+ Optional<SplitScreenController> splitScreenController) {
+ return splitScreenController.map((controller) -> controller.asSplitScreen());
}
- @BindsOptionalOf
- abstract LegacySplitScreen optionalLegacySplitScreen();
-
@WMSingleton
@Provides
- static Optional<SplitScreen> provideSplitScreen(ShellTaskOrganizer shellTaskOrganizer,
+ static Optional<SplitScreenController> provideSplitScreenController(
+ ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, Context context,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ @ShellMainThread ShellExecutor mainExecutor) {
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
- rootTaskDisplayAreaOrganizer));
+ rootTaskDisplayAreaOrganizer, mainExecutor));
} else {
return Optional.empty();
}
}
- @BindsOptionalOf
- abstract AppPairs optionalAppPairs();
+ // Legacy split (optional feature)
- // Note: Handler needed for LauncherApps.register
@WMSingleton
@Provides
- static Optional<Bubbles> provideBubbles(Context context,
- FloatingContentCoordinator floatingContentCoordinator,
- IStatusBarService statusBarService,
- WindowManager windowManager,
- WindowManagerShellWrapper windowManagerShellWrapper,
- LauncherApps launcherApps,
- UiEventLogger uiEventLogger,
- ShellTaskOrganizer organizer,
- @ShellMainThread ShellExecutor mainExecutor,
- @ShellMainThread Handler mainHandler) {
- return Optional.of(BubbleController.create(context, null /* synchronizer */,
- floatingContentCoordinator, statusBarService, windowManager,
- windowManagerShellWrapper, launcherApps, uiEventLogger, organizer,
- mainExecutor, mainHandler));
+ static Optional<LegacySplitScreen> provideLegacySplitScreen(
+ Optional<LegacySplitScreenController> splitScreenController) {
+ return splitScreenController.map((controller) -> controller.asLegacySplitScreen());
}
- // Needs the shell main handler for ContentObserver callbacks
+ @BindsOptionalOf
+ abstract LegacySplitScreenController optionalLegacySplitScreenController();
+
+ // App Pairs (optional feature)
+
@WMSingleton
@Provides
- static Optional<OneHanded> provideOneHandedController(Context context,
- DisplayController displayController, TaskStackListenerImpl taskStackListener,
- UiEventLogger uiEventLogger,
- @ShellMainThread ShellExecutor mainExecutor,
- @ShellMainThread Handler mainHandler) {
- return Optional.ofNullable(OneHandedController.create(context, displayController,
- taskStackListener, uiEventLogger, mainExecutor, mainHandler));
+ static Optional<AppPairs> provideAppPairs(Optional<AppPairsController> appPairsController) {
+ return appPairsController.map((controller) -> controller.asAppPairs());
}
+ @BindsOptionalOf
+ abstract AppPairsController optionalAppPairs();
+
+ //
+ // Task view factory
+ //
+
@WMSingleton
@Provides
- static Optional<HideDisplayCutout> provideHideDisplayCutoutController(Context context,
- DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor) {
- return Optional.ofNullable(
- HideDisplayCutoutController.create(context, displayController, mainExecutor));
+ static Optional<TaskViewFactory> provideTaskViewFactory(
+ TaskViewFactoryController taskViewFactoryController) {
+ return Optional.of(taskViewFactoryController.asTaskViewFactory());
}
@WMSingleton
@Provides
- static Optional<TaskViewFactory> provideTaskViewFactory(ShellTaskOrganizer shellTaskOrganizer,
+ static TaskViewFactoryController provideTaskViewFactoryController(
+ ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
- return Optional.of(new TaskViewFactoryController(shellTaskOrganizer, mainExecutor)
- .getTaskViewFactory());
+ return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor);
}
- @WMSingleton
- @Provides
- static FullscreenTaskListener provideFullscreenTaskListener(SyncTransactionQueue syncQueue) {
- return new FullscreenTaskListener(syncQueue);
- }
+ //
+ // Misc
+ //
@WMSingleton
@Provides
- static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
- @ShellMainThread ShellExecutor mainExecutor,
- @ShellAnimationThread ShellExecutor animExecutor) {
- return new Transitions(organizer, pool, mainExecutor, animExecutor);
+ static ShellInit provideShellInit(DisplayImeController displayImeController,
+ DragAndDropController dragAndDropController,
+ ShellTaskOrganizer shellTaskOrganizer,
+ Optional<LegacySplitScreenController> legacySplitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
+ Optional<AppPairsController> appPairsOptional,
+ FullscreenTaskListener fullscreenTaskListener,
+ Transitions transitions,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return ShellInitImpl.create(displayImeController,
+ dragAndDropController,
+ shellTaskOrganizer,
+ legacySplitScreenOptional,
+ splitScreenOptional,
+ appPairsOptional,
+ fullscreenTaskListener,
+ transitions,
+ mainExecutor);
}
+ /**
+ * Note, this is only optional because we currently pass this to the SysUI component scope and
+ * for non-primary users, we may inject a null-optional for that dependency.
+ */
@WMSingleton
@Provides
- static SizeCompatUI provideSizeCompatUI(Context context, DisplayController displayController,
- DisplayImeController imeController, @ShellMainThread ShellExecutor mainExecutor) {
- return SizeCompatUIController.create(context, displayController, imeController,
- mainExecutor);
+ static Optional<ShellCommandHandler> provideShellCommandHandler(
+ ShellTaskOrganizer shellTaskOrganizer,
+ Optional<LegacySplitScreenController> legacySplitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
+ Optional<Pip> pipOptional,
+ Optional<OneHandedController> oneHandedOptional,
+ Optional<HideDisplayCutoutController> hideDisplayCutout,
+ Optional<AppPairsController> appPairsOptional,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return Optional.of(ShellCommandHandlerImpl.create(shellTaskOrganizer,
+ legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional,
+ hideDisplayCutout, appPairsOptional, mainExecutor));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 2aaa0951d9d9..997b488a627f 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -21,10 +21,10 @@ import android.content.Context;
import android.os.Handler;
import android.view.IWindowManager;
+import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.WMSingleton;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
-import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
@@ -36,7 +36,6 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
@@ -60,11 +59,20 @@ import dagger.Module;
import dagger.Provides;
/**
- * Provides dependencies from {@link com.android.wm.shell} which could be customized among different
- * branches of SystemUI.
+ * Provides dependencies from {@link com.android.wm.shell}, these dependencies are only
+ * accessible from components within the WM subcomponent (can be explicitly exposed to the
+ * SysUIComponent, see {@link WMComponent}).
+ *
+ * This module only defines Shell dependencies for handheld SystemUI implementation. Common
+ * dependencies should go into {@link WMShellBaseModule}.
*/
@Module(includes = WMShellBaseModule.class)
public class WMShellModule {
+
+ //
+ // Internal common - Components used internally by multiple shell features
+ //
+
@WMSingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
@@ -74,29 +82,37 @@ public class WMShellModule {
transactionPool);
}
+ //
+ // Split/multiwindow
+ //
+
@WMSingleton
@Provides
- static LegacySplitScreen provideLegacySplitScreen(Context context,
+ static LegacySplitScreenController provideLegacySplitScreen(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController displayImeController, TransactionPool transactionPool,
ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
TaskStackListenerImpl taskStackListener, Transitions transitions,
@ShellMainThread ShellExecutor mainExecutor,
@ChoreographerSfVsync AnimationHandler sfVsyncAnimationHandler) {
- return LegacySplitScreenController.create(context, displayController, systemWindows,
+ return new LegacySplitScreenController(context, displayController, systemWindows,
displayImeController, transactionPool, shellTaskOrganizer, syncQueue,
taskStackListener, transitions, mainExecutor, sfVsyncAnimationHandler);
}
@WMSingleton
@Provides
- static AppPairs provideAppPairs(ShellTaskOrganizer shellTaskOrganizer,
+ static AppPairsController provideAppPairs(ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor) {
- return AppPairsController.create(shellTaskOrganizer, syncQueue, displayController,
+ return new AppPairsController(shellTaskOrganizer, syncQueue, displayController,
mainExecutor);
}
+ //
+ // Pip
+ //
+
@WMSingleton
@Provides
static Optional<Pip> providePip(Context context, DisplayController displayController,
@@ -161,7 +177,8 @@ public class WMShellModule {
PipAnimationController pipAnimationController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
PipTransitionController pipTransitionController,
- Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController,
+ Optional<LegacySplitScreenController> splitScreenOptional,
+ DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index a2eaea1a37c5..70a7b7a5acbc 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -34,6 +34,7 @@ import android.widget.FrameLayout;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -75,6 +76,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
NotificationIconAreaController mNotificationIconAreaController;
@Mock
ContentResolver mContentResolver;
+ @Mock
+ BroadcastDispatcher mBroadcastDispatcher;
private KeyguardClockSwitchController mController;
@@ -94,7 +97,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
mClockManager,
mKeyguardSliceViewController,
mNotificationIconAreaController,
- mContentResolver);
+ mContentResolver,
+ mBroadcastDispatcher);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 6e2398c033ba..eb95d1653e84 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -56,6 +56,7 @@ import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintManager;
import android.media.AudioManager;
+import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Handler;
import android.os.IRemoteCallback;
@@ -851,6 +852,17 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(mKeyguardUpdateMonitor.shouldListenForUdfps()).isEqualTo(false);
}
+ @Test
+ public void testRequireUnlockForNfc_Broadcast() {
+ KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+ mKeyguardUpdateMonitor.registerCallback(callback);
+ Intent intent = new Intent(NfcAdapter.ACTION_REQUIRE_UNLOCK_FOR_NFC);
+ mKeyguardUpdateMonitor.mBroadcastAllReceiver.onReceive(getContext(), intent);
+ mTestableLooper.processAllMessages();
+
+ verify(callback, atLeastOnce()).onRequireUnlockForNfc();
+ }
+
private void setKeyguardBouncerVisibility(boolean isVisible) {
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt
index 53d84dba6fb2..7b4f14dd3595 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt
@@ -34,9 +34,9 @@ import org.mockito.Mockito.verify
import kotlin.math.ceil
-private val PAINT = arrayListOf(TextPaint().apply {
+private val PAINT = TextPaint().apply {
textSize = 32f
-})
+}
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -49,10 +49,10 @@ class TextAnimatorTest : SysuiTestCase() {
@Test
fun testAnimationStarted() {
- val layout = makeLayout("Hello, World", PAINT[0])
+ val layout = makeLayout("Hello, World", PAINT)
val valueAnimator = mock(ValueAnimator::class.java)
val textInterpolator = mock(TextInterpolator::class.java)
- val paint = arrayListOf(mock(TextPaint::class.java))
+ val paint = mock(TextPaint::class.java)
`when`(textInterpolator.targetPaint).thenReturn(paint)
val textAnimator = TextAnimator(layout, {}).apply {
@@ -81,10 +81,10 @@ class TextAnimatorTest : SysuiTestCase() {
@Test
fun testAnimationNotStarted() {
- val layout = makeLayout("Hello, World", PAINT[0])
+ val layout = makeLayout("Hello, World", PAINT)
val valueAnimator = mock(ValueAnimator::class.java)
val textInterpolator = mock(TextInterpolator::class.java)
- val paint = arrayListOf(mock(TextPaint::class.java))
+ val paint = mock(TextPaint::class.java)
`when`(textInterpolator.targetPaint).thenReturn(paint)
val textAnimator = TextAnimator(layout, {}).apply {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
index 1206dab7b56d..149e179de5a6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
@@ -21,9 +21,9 @@ import android.graphics.Canvas
import android.testing.AndroidTestingRunner
import android.text.Layout
import android.text.StaticLayout
-import android.text.TextPaint
import android.text.TextDirectionHeuristic
import android.text.TextDirectionHeuristics
+import android.text.TextPaint
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -40,13 +40,13 @@ private val PAINT = TextPaint().apply {
textSize = 32f
}
-private val START_PAINT = arrayListOf(TextPaint(PAINT).apply {
+private val START_PAINT = TextPaint(PAINT).apply {
fontVariationSettings = "'wght' 400"
-})
+}
-private val END_PAINT = arrayListOf(TextPaint(PAINT).apply {
+private val END_PAINT = TextPaint(PAINT).apply {
fontVariationSettings = "'wght' 700"
-})
+}
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -67,16 +67,16 @@ class TextInterpolatorTest : SysuiTestCase() {
val layout = makeLayout(TEXT, PAINT)
val interp = TextInterpolator(layout)
- TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+ interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
- TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+ interp.targetPaint.set(END_PAINT)
interp.onTargetPaintModified()
// Just after created TextInterpolator, it should have 0 progress.
assertThat(interp.progress).isEqualTo(0f)
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- val expected = makeLayout(TEXT, START_PAINT[0]).toBitmap(BMP_WIDTH, BMP_HEIGHT)
+ val expected = makeLayout(TEXT, START_PAINT).toBitmap(BMP_WIDTH, BMP_HEIGHT)
assertThat(expected.sameAs(actual)).isTrue()
}
@@ -86,15 +86,15 @@ class TextInterpolatorTest : SysuiTestCase() {
val layout = makeLayout(TEXT, PAINT)
val interp = TextInterpolator(layout)
- TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+ interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
- TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+ interp.targetPaint.set(END_PAINT)
interp.onTargetPaintModified()
interp.progress = 1f
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- val expected = makeLayout(TEXT, END_PAINT[0]).toBitmap(BMP_WIDTH, BMP_HEIGHT)
+ val expected = makeLayout(TEXT, END_PAINT).toBitmap(BMP_WIDTH, BMP_HEIGHT)
assertThat(expected.sameAs(actual)).isTrue()
}
@@ -104,10 +104,10 @@ class TextInterpolatorTest : SysuiTestCase() {
val layout = makeLayout(TEXT, PAINT)
val interp = TextInterpolator(layout)
- TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+ interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
- TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+ interp.targetPaint.set(END_PAINT)
interp.onTargetPaintModified()
// We cannot expect exact text layout of the middle position since we don't use text shaping
@@ -115,9 +115,9 @@ class TextInterpolatorTest : SysuiTestCase() {
// end state.
interp.progress = 0.5f
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT[0])
+ assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT)
.toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse()
- assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT[0])
+ assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT)
.toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse()
}
@@ -126,10 +126,10 @@ class TextInterpolatorTest : SysuiTestCase() {
val layout = makeLayout(TEXT, PAINT)
val interp = TextInterpolator(layout)
- TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+ interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
- TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+ interp.targetPaint.set(END_PAINT)
interp.onTargetPaintModified()
interp.progress = 0.5f
@@ -148,16 +148,16 @@ class TextInterpolatorTest : SysuiTestCase() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.LTR)
val interp = TextInterpolator(layout)
- TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+ interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
- TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+ interp.targetPaint.set(END_PAINT)
interp.onTargetPaintModified()
// Just after created TextInterpolator, it should have 0 progress.
assertThat(interp.progress).isEqualTo(0f)
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- val expected = makeLayout(BIDI_TEXT, START_PAINT[0], TextDirectionHeuristics.LTR)
+ val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.LTR)
.toBitmap(BMP_WIDTH, BMP_HEIGHT)
assertThat(expected.sameAs(actual)).isTrue()
@@ -168,16 +168,16 @@ class TextInterpolatorTest : SysuiTestCase() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
val interp = TextInterpolator(layout)
- TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+ interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
- TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+ interp.targetPaint.set(END_PAINT)
interp.onTargetPaintModified()
// Just after created TextInterpolator, it should have 0 progress.
assertThat(interp.progress).isEqualTo(0f)
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- val expected = makeLayout(BIDI_TEXT, START_PAINT[0], TextDirectionHeuristics.RTL)
+ val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
.toBitmap(BMP_WIDTH, BMP_HEIGHT)
assertThat(expected.sameAs(actual)).isTrue()
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
new file mode 100644
index 000000000000..9278570714fe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.globalactions.GlobalActionsComponent
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.wm.shell.TaskViewFactory
+import dagger.Lazy
+import java.util.Optional
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Answers
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ControlActionCoordinatorImplTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var uiController: ControlsUiController
+ @Mock
+ private lateinit var lazyUiController: Lazy<ControlsUiController>
+ @Mock
+ private lateinit var keyguardStateController: KeyguardStateController
+ @Mock
+ private lateinit var bgExecutor: DelayableExecutor
+ @Mock
+ private lateinit var uiExecutor: DelayableExecutor
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var globalActionsComponent: GlobalActionsComponent
+ @Mock
+ private lateinit var taskViewFactory: Optional<TaskViewFactory>
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private lateinit var cvh: ControlViewHolder
+
+ companion object {
+ fun <T> any(): T = Mockito.any<T>()
+
+ private val ID = "id"
+ }
+
+ private lateinit var coordinator: ControlActionCoordinatorImpl
+ private lateinit var action: ControlActionCoordinatorImpl.Action
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ coordinator = spy(ControlActionCoordinatorImpl(
+ mContext,
+ bgExecutor,
+ uiExecutor,
+ activityStarter,
+ keyguardStateController,
+ globalActionsComponent,
+ taskViewFactory,
+ getFakeBroadcastDispatcher(),
+ lazyUiController
+ ))
+
+ `when`(cvh.cws.ci.controlId).thenReturn(ID)
+ action = spy(coordinator.Action(ID, {}, false))
+ doReturn(action).`when`(coordinator).createAction(any(), any(), anyBoolean())
+ }
+
+ @Test
+ fun testToggleRunsWhenUnlocked() {
+ `when`(keyguardStateController.isShowing()).thenReturn(false)
+
+ coordinator.toggle(cvh, "", true)
+ verify(coordinator).bouncerOrRun(action)
+ verify(action).invoke()
+ }
+
+ @Test
+ fun testToggleDoesNotRunWhenLockedThenRunsWhenUnlocked() {
+ `when`(keyguardStateController.isShowing()).thenReturn(true)
+ `when`(keyguardStateController.isUnlocked()).thenReturn(false)
+
+ coordinator.toggle(cvh, "", true)
+ verify(coordinator).bouncerOrRun(action)
+ verify(activityStarter).dismissKeyguardThenExecute(any(), any(), anyBoolean())
+ verify(action, never()).invoke()
+
+ // Simulate a refresh call from a Publisher, which will trigger a call to runPendingAction
+ reset(action)
+ coordinator.runPendingAction(ID)
+ verify(action, never()).invoke()
+
+ `when`(keyguardStateController.isUnlocked()).thenReturn(true)
+ reset(action)
+ coordinator.runPendingAction(ID)
+ verify(action).invoke()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
index 7fe682793152..b8f91b8d4719 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
@@ -17,11 +17,18 @@
package com.android.systemui.controls.dagger
import android.testing.AndroidTestingRunner
+import android.provider.Settings
import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.settings.SecureSettings
import dagger.Lazy
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
@@ -29,7 +36,11 @@ import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Answers
import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -42,20 +53,29 @@ class ControlsComponentTest : SysuiTestCase() {
private lateinit var uiController: ControlsUiController
@Mock
private lateinit var listingController: ControlsListingController
+ @Mock
+ private lateinit var keyguardStateController: KeyguardStateController
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private lateinit var userTracker: UserTracker
+ @Mock
+ private lateinit var lockPatternUtils: LockPatternUtils
+ @Mock
+ private lateinit var secureSettings: SecureSettings
+
+ companion object {
+ fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+ }
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+
+ `when`(userTracker.userHandle.identifier).thenReturn(0)
}
@Test
fun testFeatureEnabled() {
- val component = ControlsComponent(
- true,
- Lazy { controller },
- Lazy { uiController },
- Lazy { listingController }
- )
+ val component = setupComponent(true)
assertTrue(component.getControlsController().isPresent)
assertEquals(controller, component.getControlsController().get())
@@ -67,15 +87,80 @@ class ControlsComponentTest : SysuiTestCase() {
@Test
fun testFeatureDisabled() {
- val component = ControlsComponent(
- false,
- Lazy { controller },
- Lazy { uiController },
- Lazy { listingController }
- )
+ val component = setupComponent(false)
assertFalse(component.getControlsController().isPresent)
assertFalse(component.getControlsUiController().isPresent)
assertFalse(component.getControlsListingController().isPresent)
}
-} \ No newline at end of file
+
+ @Test
+ fun testFeatureDisabledVisibility() {
+ val component = setupComponent(false)
+
+ assertEquals(ControlsComponent.Visibility.UNAVAILABLE, component.getVisibility())
+ }
+
+ @Test
+ fun testFeatureEnabledAfterBootVisibility() {
+ `when`(controller.available).thenReturn(true)
+ `when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
+ .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT)
+ val component = setupComponent(true)
+
+ assertEquals(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK, component.getVisibility())
+ }
+
+ @Test
+ fun testFeatureEnabledAndCannotShowOnLockScreenVisibility() {
+ `when`(controller.available).thenReturn(true)
+ `when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
+ .thenReturn(STRONG_AUTH_NOT_REQUIRED)
+ `when`(keyguardStateController.isUnlocked()).thenReturn(false)
+ `when`(secureSettings.getInt(eq(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), anyInt()))
+ .thenReturn(0)
+ val component = setupComponent(true)
+
+ assertEquals(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK, component.getVisibility())
+ }
+
+ @Test
+ fun testFeatureEnabledAndCanShowOnLockScreenVisibility() {
+ `when`(controller.available).thenReturn(true)
+ `when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
+ .thenReturn(STRONG_AUTH_NOT_REQUIRED)
+ `when`(keyguardStateController.isUnlocked()).thenReturn(false)
+ `when`(secureSettings.getInt(eq(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), anyInt()))
+ .thenReturn(1)
+ val component = setupComponent(true)
+
+ assertEquals(ControlsComponent.Visibility.AVAILABLE, component.getVisibility())
+ }
+
+ @Test
+ fun testFeatureEnabledAndCanShowWhileUnlockedVisibility() {
+ `when`(secureSettings.getInt(eq(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), anyInt()))
+ .thenReturn(0)
+ `when`(controller.available).thenReturn(true)
+ `when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
+ .thenReturn(STRONG_AUTH_NOT_REQUIRED)
+ `when`(keyguardStateController.isUnlocked()).thenReturn(true)
+ val component = setupComponent(true)
+
+ assertEquals(ControlsComponent.Visibility.AVAILABLE, component.getVisibility())
+ }
+
+ private fun setupComponent(enabled: Boolean): ControlsComponent {
+ return ControlsComponent(
+ enabled,
+ mContext,
+ Lazy { controller },
+ Lazy { uiController },
+ Lazy { listingController },
+ lockPatternUtils,
+ keyguardStateController,
+ userTracker,
+ secureSettings
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index 069699c271f7..d60772730dff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -42,6 +42,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.wakelock.WakeLockFake;
import org.junit.After;
@@ -67,6 +68,8 @@ public class DozeUiTest extends SysuiTestCase {
private DozeHost mHost;
@Mock
private DozeLog mDozeLog;
+ @Mock
+ private TunerService mTunerService;
private WakeLockFake mWakeLock;
private Handler mHandler;
private HandlerThread mHandlerThread;
@@ -82,7 +85,7 @@ public class DozeUiTest extends SysuiTestCase {
mHandler = mHandlerThread.getThreadHandler();
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
- mDozeParameters, mKeyguardUpdateMonitor, mDozeLog);
+ mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService);
mDozeUi.setDozeMachine(mMachine);
}
@@ -138,7 +141,7 @@ public class DozeUiTest extends SysuiTestCase {
reset(mHost);
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
- mDozeParameters, mKeyguardUpdateMonitor, mDozeLog);
+ mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService);
mDozeUi.setDozeMachine(mMachine);
// Never animate if display doesn't support it.
@@ -173,4 +176,30 @@ public class DozeUiTest extends SysuiTestCase {
mDozeUi.transitionTo(UNINITIALIZED, DOZE);
verify(mHost).setAnimateWakeup(eq(false));
}
+
+ @Test
+ public void controlScreenOffTrueWhenKeyguardNotShowingAndControlUnlockedScreenOff() {
+ when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+ when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true);
+
+ // Tell doze that keyguard is not visible.
+ mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false /* showing */);
+
+ // Since we're controlling the unlocked screen off animation, verify that we've asked to
+ // control the screen off animation despite being unlocked.
+ verify(mDozeParameters).setControlScreenOffAnimation(true);
+ }
+
+ @Test
+ public void controlScreenOffFalseWhenKeyguardNotShowingAndControlUnlockedScreenOffFalse() {
+ when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+ when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(false);
+
+ // Tell doze that keyguard is not visible.
+ mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false /* showing */);
+
+ // Since we're not controlling the unlocked screen off animation, verify that we haven't
+ // asked to control the screen off animation since we're unlocked.
+ verify(mDozeParameters).setControlScreenOffAnimation(false);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java
index a52a598ee7ec..0457100e0294 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java
@@ -19,7 +19,7 @@ package com.android.systemui.emergency;
import android.app.Activity;
import android.os.Bundle;
-import com.android.systemui.R;
+import com.android.systemui.tests.R;
/**
* Test activity for resolving {@link EmergencyGesture#ACTION_LAUNCH_EMERGENCY} action.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java
index c79037b761aa..223714cfda30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java
@@ -18,7 +18,6 @@ package com.android.systemui.flags;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -27,59 +26,44 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.res.Resources;
-import android.provider.DeviceConfig;
import androidx.annotation.BoolRes;
import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.assist.DeviceConfigHelper;
import com.android.systemui.util.wrapper.BuildInfo;
import org.junit.Before;
import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Executor;
-
@SmallTest
public class FeatureFlagReaderTest extends SysuiTestCase {
@Mock private Resources mResources;
@Mock private BuildInfo mBuildInfo;
- @Mock private DeviceConfigHelper mDeviceConfig;
- @Mock private Executor mBackgroundExecutor;
+ @Mock private SystemPropertiesHelper mSystemPropertiesHelper;
private FeatureFlagReader mReader;
- @Captor private ArgumentCaptor<DeviceConfig.OnPropertiesChangedListener> mListenerCaptor;
- private DeviceConfig.OnPropertiesChangedListener mPropChangeListener;
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mDeviceConfig.getBoolean(anyString(), anyBoolean()))
+ when(mSystemPropertiesHelper.getBoolean(anyString(), anyBoolean()))
.thenAnswer(invocation -> invocation.getArgument(1));
defineFlag(FLAG_RESID_0, false);
defineFlag(FLAG_RESID_1, true);
initialize(true, true);
-
- verify(mDeviceConfig).addOnPropertiesChangedListener(any(), mListenerCaptor.capture());
- mPropChangeListener = mListenerCaptor.getValue();
}
private void initialize(boolean isDebuggable, boolean isOverrideable) {
when(mBuildInfo.isDebuggable()).thenReturn(isDebuggable);
when(mResources.getBoolean(R.bool.are_flags_overrideable)).thenReturn(isOverrideable);
- mReader = new FeatureFlagReader(mResources, mBuildInfo, mDeviceConfig, mBackgroundExecutor);
+ mReader = new FeatureFlagReader(mResources, mBuildInfo, mSystemPropertiesHelper);
}
@Test
@@ -136,24 +120,8 @@ public class FeatureFlagReaderTest extends SysuiTestCase {
// THEN the underlying resource and override are only queried once
verify(mResources, times(1)).getBoolean(FLAG_RESID_0);
- verify(mDeviceConfig, times(1)).getBoolean(fakeStorageKey(FLAG_RESID_0), false);
- }
-
- @Test
- public void testCachesAreClearedAfterPropsChange() {
- // GIVEN a flag whose value has already been queried
- assertFalse(mReader.isEnabled(FLAG_RESID_0));
-
- // WHEN the value of the flag changes
- overrideFlag(FLAG_RESID_0, true);
- Map<String, String> changedMap = new HashMap<>();
- changedMap.put(fakeStorageKey(FLAG_RESID_0), "true");
- DeviceConfig.Properties properties =
- new DeviceConfig.Properties("systemui", changedMap);
- mPropChangeListener.onPropertiesChanged(properties);
-
- // THEN the new value is provided
- assertTrue(mReader.isEnabled(FLAG_RESID_0));
+ verify(mSystemPropertiesHelper, times(1))
+ .getBoolean(fakeStorageKey(FLAG_RESID_0), false);
}
private void defineFlag(int resId, boolean value) {
@@ -162,12 +130,12 @@ public class FeatureFlagReaderTest extends SysuiTestCase {
}
private void overrideFlag(int resId, boolean value) {
- when(mDeviceConfig.getBoolean(eq(fakeStorageKey(resId)), anyBoolean()))
+ when(mSystemPropertiesHelper.getBoolean(eq(fakeStorageKey(resId)), anyBoolean()))
.thenReturn(value);
}
private String fakeStorageKey(@BoolRes int resId) {
- return "flag_testname_" + resId;
+ return "persist.systemui.flag_testname_" + resId;
}
private static final int FLAG_RESID_0 = 47;
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 78ee5936fe0b..1062fae52e7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -74,12 +74,14 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.RingerModeLiveData;
import com.android.systemui.util.RingerModeTracker;
+import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
import org.junit.Test;
@@ -135,6 +137,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
@Mock GlobalActionsPanelPlugin.PanelViewController mWalletController;
@Mock private Handler mHandler;
@Mock private UserContextProvider mUserContextProvider;
+ @Mock private UserTracker mUserTracker;
+ @Mock private SecureSettings mSecureSettings;
private ControlsComponent mControlsComponent;
private TestableLooper mTestableLooper;
@@ -149,9 +153,14 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
when(mUserContextProvider.getUserContext()).thenReturn(mContext);
mControlsComponent = new ControlsComponent(
true,
+ mContext,
() -> mControlsController,
() -> mControlsUiController,
- () -> mControlsListingController
+ () -> mControlsListingController,
+ mLockPatternUtils,
+ mKeyguardStateController,
+ mUserTracker,
+ mSecureSettings
);
mGlobalActionsDialog = new GlobalActionsDialog(mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
new file mode 100644
index 000000000000..b6729adeb9b3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
@@ -0,0 +1,304 @@
+/*
+ * 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.keyguard;
+
+
+import static com.android.keyguard.KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+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;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class KeyguardIndicationRotateTextViewControllerTest extends SysuiTestCase {
+
+ private static final String TEST_MESSAGE = "test message";
+ private static final String TEST_MESSAGE_2 = "test message 2";
+
+ @Mock
+ private DelayableExecutor mExecutor;
+ @Mock
+ private KeyguardIndicationTextView mView;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Captor
+ private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
+
+ private KeyguardIndicationRotateTextViewController mController;
+ private StatusBarStateController.StateListener mStatusBarStateListener;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mView.getTextColors()).thenReturn(ColorStateList.valueOf(Color.WHITE));
+ mController = new KeyguardIndicationRotateTextViewController(mView, mExecutor,
+ mStatusBarStateController, LOCK_SCREEN_MODE_LAYOUT_1);
+ mController.onViewAttached();
+
+ verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
+ mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue();
+ }
+
+ @Test
+ public void testInitialState_noIndication() {
+ assertFalse(mController.hasIndications());
+ }
+
+ @Test
+ public void testShowOneIndication() {
+ // WHEN we add our first indication
+ final KeyguardIndication indication = createIndication();
+ mController.updateIndication(
+ INDICATION_TYPE_DISCLOSURE, indication, false);
+
+ // THEN
+ // - we see controller has an indication
+ // - the indication shows immediately since it's the only one
+ // - no next indication is scheduled since there's only one indication
+ assertTrue(mController.hasIndications());
+ verify(mView).switchIndication(indication);
+ verify(mExecutor, never()).executeDelayed(any(), anyLong());
+ }
+
+ @Test
+ public void testShowTwoRotatingMessages() {
+ // GIVEN we already have an indication message
+ mController.updateIndication(
+ INDICATION_TYPE_OWNER_INFO, createIndication(), false);
+ reset(mView);
+
+ // WHEN we have a new indication type to display
+ final KeyguardIndication indication2 = createIndication();
+ mController.updateIndication(
+ INDICATION_TYPE_DISCLOSURE, indication2, false);
+
+ // THEN
+ // - we don't immediately see the new message until the delay
+ // - next indication is scheduled
+ verify(mView, never()).switchIndication(indication2);
+ verify(mExecutor).executeDelayed(any(), anyLong());
+ }
+
+ @Test
+ public void testUpdateCurrentMessage() {
+ // GIVEN we already have an indication message
+ mController.updateIndication(
+ INDICATION_TYPE_DISCLOSURE, createIndication(), false);
+ reset(mView);
+
+ // WHEN we have a new message for this indication type to display
+ final KeyguardIndication indication2 = createIndication();
+ mController.updateIndication(
+ INDICATION_TYPE_DISCLOSURE, indication2, false);
+
+ // THEN
+ // - new indication is updated immediately
+ // - we don't schedule to show anything later
+ verify(mView).switchIndication(indication2);
+ verify(mExecutor, never()).executeDelayed(any(), anyLong());
+ }
+
+ @Test
+ public void testUpdateRotatingMessageForUndisplayedIndication() {
+ // GIVEN we already have two indication messages
+ mController.updateIndication(
+ INDICATION_TYPE_OWNER_INFO, createIndication(), false);
+ mController.updateIndication(
+ INDICATION_TYPE_DISCLOSURE, createIndication(), false);
+ reset(mView);
+ reset(mExecutor);
+
+ // WHEN we have a new message for an undisplayed indication type
+ final KeyguardIndication indication3 = createIndication();
+ mController.updateIndication(
+ INDICATION_TYPE_DISCLOSURE, indication3, false);
+
+ // THEN
+ // - we don't immediately update
+ // - we don't schedule to show anything new
+ verify(mView, never()).switchIndication(indication3);
+ verify(mExecutor, never()).executeDelayed(any(), anyLong());
+ }
+
+ @Test
+ public void testUpdateImmediately() {
+ // GIVEN we already have three indication messages
+ mController.updateIndication(
+ INDICATION_TYPE_OWNER_INFO, createIndication(), false);
+ mController.updateIndication(
+ INDICATION_TYPE_DISCLOSURE, createIndication(), false);
+ mController.updateIndication(
+ INDICATION_TYPE_BATTERY, createIndication(), false);
+ reset(mView);
+ reset(mExecutor);
+
+ // WHEN we have a new message for a currently shown type that we want to show immediately
+ final KeyguardIndication indication4 = createIndication();
+ mController.updateIndication(
+ INDICATION_TYPE_BATTERY, indication4, true);
+
+ // THEN
+ // - we immediately update
+ // - we schedule a new delayable to show the next message later
+ verify(mView).switchIndication(indication4);
+ verify(mExecutor).executeDelayed(any(), anyLong());
+
+ // WHEN an already existing type is updated to show immediately
+ reset(mView);
+ reset(mExecutor);
+ final KeyguardIndication indication5 = createIndication();
+ mController.updateIndication(
+ INDICATION_TYPE_DISCLOSURE, indication5, true);
+
+ // THEN
+ // - we immediately update
+ // - we schedule a new delayable to show the next message later
+ verify(mView).switchIndication(indication5);
+ verify(mExecutor).executeDelayed(any(), anyLong());
+ }
+
+ @Test
+ public void testTransientIndication() {
+ // GIVEN we already have two indication messages
+ mController.updateIndication(
+ INDICATION_TYPE_OWNER_INFO, createIndication(), false);
+ mController.updateIndication(
+ INDICATION_TYPE_DISCLOSURE, createIndication(), false);
+ reset(mView);
+ reset(mExecutor);
+
+ // WHEN we have a transient message
+ mController.showTransient(TEST_MESSAGE_2, false);
+
+ // THEN
+ // - we immediately update
+ // - we schedule a new delayable to show the next message later
+ verify(mView).switchIndication(any(KeyguardIndication.class));
+ verify(mExecutor).executeDelayed(any(), anyLong());
+ }
+
+ @Test
+ public void testHideIndicationOneMessage() {
+ // GIVEN we have one indication message
+ mController.updateIndication(
+ INDICATION_TYPE_OWNER_INFO, createIndication(), false);
+
+ // WHEN we hide the current indication type
+ mController.hideIndication(INDICATION_TYPE_OWNER_INFO);
+
+ // THEN we immediately update the text to show no text
+ verify(mView).switchIndication(null);
+ }
+
+ @Test
+ public void testHideIndicationTwoMessages() {
+ // GIVEN we have two indication messages
+ final KeyguardIndication indication1 = createIndication();
+ final KeyguardIndication indication2 = createIndication();
+ mController.updateIndication(
+ INDICATION_TYPE_OWNER_INFO, indication1, false);
+ mController.updateIndication(
+ INDICATION_TYPE_DISCLOSURE, indication2, false);
+ assertTrue(mController.isNextIndicationScheduled());
+
+ // WHEN we hide the current indication type
+ mController.hideIndication(INDICATION_TYPE_OWNER_INFO);
+
+ // THEN we show the next indication and there's no scheduled next indication
+ verify(mView).switchIndication(indication2);
+ assertFalse(mController.isNextIndicationScheduled());
+ }
+
+ @Test
+ public void testStartDozing() {
+ // WHEN the device is dozing
+ mStatusBarStateListener.onDozingChanged(true);
+
+ // THEN the view is GONE
+ verify(mView).setVisibility(View.GONE);
+ }
+
+ @Test
+ public void testStoppedDozing() {
+ // GIVEN we're dozing & we have an indication message
+ mStatusBarStateListener.onDozingChanged(true);
+ final KeyguardIndication indication = createIndication();
+ mController.updateIndication(
+ INDICATION_TYPE_DISCLOSURE, indication, false);
+ reset(mView);
+ reset(mExecutor);
+
+ // WHEN the device is no longer dozing
+ mStatusBarStateListener.onDozingChanged(false);
+
+ // THEN show the next message
+ verify(mView).switchIndication(indication);
+ }
+
+ @Test
+ public void testIsDozing() {
+ // GIVEN the device is dozing
+ mStatusBarStateListener.onDozingChanged(true);
+ reset(mView);
+
+ // WHEN an indication is updated
+ final KeyguardIndication indication = createIndication();
+ mController.updateIndication(
+ INDICATION_TYPE_DISCLOSURE, indication, false);
+
+ // THEN no message is shown since we're dozing
+ verify(mView, never()).switchIndication(any());
+ }
+
+ private KeyguardIndication createIndication() {
+ return new KeyguardIndication.Builder()
+ .setMessage(TEST_MESSAGE)
+ .setTextColor(ColorStateList.valueOf(Color.WHITE))
+ .build();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationTest.java
new file mode 100644
index 000000000000..b44fb8e61c32
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.keyguard;
+
+import static android.graphics.Color.WHITE;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.drawable.Drawable;
+import android.testing.AndroidTestingRunner;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class KeyguardIndicationTest extends SysuiTestCase {
+
+ @Test(expected = IllegalStateException.class)
+ public void testCannotCreateIndicationWithoutMessageOrIcon() {
+ new KeyguardIndication.Builder()
+ .setTextColor(ColorStateList.valueOf(WHITE))
+ .build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testCannotCreateIndicationWithoutColor() {
+ new KeyguardIndication.Builder()
+ .setMessage("message")
+ .build();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testCannotCreateIndicationWithEmptyMessage() {
+ new KeyguardIndication.Builder()
+ .setMessage("")
+ .setTextColor(ColorStateList.valueOf(WHITE))
+ .build();
+ }
+
+ @Test
+ public void testCreateIndicationWithMessage() {
+ final String text = "regular indication";
+ final KeyguardIndication indication = new KeyguardIndication.Builder()
+ .setMessage(text)
+ .setTextColor(ColorStateList.valueOf(WHITE))
+ .build();
+ assertEquals(text, indication.getMessage());
+ }
+
+ @Test
+ public void testCreateIndicationWithIcon() {
+ final KeyguardIndication indication = new KeyguardIndication.Builder()
+ .setIcon(mDrawable)
+ .setTextColor(ColorStateList.valueOf(WHITE))
+ .build();
+ assertEquals(mDrawable, indication.getIcon());
+ }
+
+ @Test
+ public void testCreateIndicationWithMessageAndIcon() {
+ final String text = "indication with msg and icon";
+ final KeyguardIndication indication = new KeyguardIndication.Builder()
+ .setMessage(text)
+ .setIcon(mDrawable)
+ .setTextColor(ColorStateList.valueOf(WHITE))
+ .build();
+ assertEquals(text, indication.getMessage());
+ assertEquals(mDrawable, indication.getIcon());
+ }
+
+ final Drawable mDrawable = new Drawable() {
+ @Override
+ public void draw(@NonNull Canvas canvas) { }
+
+ @Override
+ public void setAlpha(int alpha) { }
+
+ @Override
+ public void setColorFilter(@Nullable ColorFilter colorFilter) { }
+
+ @Override
+ public int getOpacity() {
+ return 0;
+ }
+ };
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 00943bc53bfd..b8c37fde2ce3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -18,6 +18,8 @@ package com.android.systemui.keyguard;
import static android.view.WindowManagerPolicyConstants.OFF_BECAUSE_OF_USER;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -27,13 +29,17 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+import com.android.systemui.R;
import androidx.test.filters.SmallTest;
import com.android.internal.widget.LockPatternUtils;
@@ -45,6 +51,7 @@ import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.LightRevealScrim;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.DeviceConfigProxy;
@@ -117,4 +124,20 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mViewMediator.mViewMediatorCallback.keyguardGone();
verify(mStatusBarKeyguardViewManager).setKeyguardGoingAwayState(eq(false));
}
+
+ @Test
+ public void testIsAnimatingScreenOff() {
+ when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true);
+
+ mViewMediator.onFinishedGoingToSleep(OFF_BECAUSE_OF_USER, false);
+ mViewMediator.setDozing(true);
+
+ // Mid-doze, we should be animating the screen off animation.
+ mViewMediator.onDozeAmountChanged(0.5f, 0.5f);
+ assertTrue(mViewMediator.isAnimatingScreenOff());
+
+ // Once we're 100% dozed, the screen off animation should be completed.
+ mViewMediator.onDozeAmountChanged(1f, 1f);
+ assertFalse(mViewMediator.isAnimatingScreenOff());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 2db224f85eb0..e88c72860ed8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -315,10 +315,9 @@ class MediaDataManagerTest : SysuiTestCase() {
}
mediaDataManager.onNotificationAdded(KEY, notif)
- // THEN it loads and uses the default background color
+ // THEN it still loads
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor))
- assertThat(mediaDataCaptor.value!!.backgroundColor).isEqualTo(DEFAULT_COLOR)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java
index 0320103ceaa8..5c179d4e46b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.navigationbar.buttons;
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
@@ -25,6 +26,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.MotionEvent;
@@ -39,6 +41,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Map;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
@@ -51,6 +55,7 @@ public class NearestTouchFrameTest extends SysuiTestCase {
Configuration c = new Configuration(mContext.getResources().getConfiguration());
c.smallestScreenWidthDp = 500;
mNearestTouchFrame = new NearestTouchFrame(mContext, null, c);
+ mNearestTouchFrame.layout(0, 0, 100, 100);
}
@Test
@@ -146,7 +151,7 @@ public class NearestTouchFrameTest extends SysuiTestCase {
public void testVerticalSelection_Top() {
View top = mockViewAt(0, 0, 10, 10);
View bottom = mockViewAt(0, 20, 10, 10);
-
+ mNearestTouchFrame.setIsVertical(true);
mNearestTouchFrame.addView(top);
mNearestTouchFrame.addView(bottom);
mNearestTouchFrame.onMeasure(0, 0);
@@ -162,7 +167,7 @@ public class NearestTouchFrameTest extends SysuiTestCase {
public void testVerticalSelection_Bottom() {
View top = mockViewAt(0, 0, 10, 10);
View bottom = mockViewAt(0, 20, 10, 10);
-
+ mNearestTouchFrame.setIsVertical(true);
mNearestTouchFrame.addView(top);
mNearestTouchFrame.addView(bottom);
mNearestTouchFrame.onMeasure(0, 0);
@@ -187,6 +192,60 @@ public class NearestTouchFrameTest extends SysuiTestCase {
ev.recycle();
}
+ @Test
+ public void testViewMiddleChildNotAttachedCrash() {
+ View view1 = mockViewAt(0, 20, 10, 10);
+ View view2 = mockViewAt(11, 20, 10, 10);
+ View view3 = mockViewAt(21, 20, 10, 10);
+ when(view2.isAttachedToWindow()).thenReturn(false);
+ mNearestTouchFrame.addView(view1);
+ mNearestTouchFrame.addView(view2);
+ mNearestTouchFrame.addView(view3);
+ mNearestTouchFrame.onMeasure(0, 0);
+
+ MotionEvent ev = MotionEvent.obtain(0, 0, 0, 5 /* x */, 18 /* y */, 0);
+ mNearestTouchFrame.onTouchEvent(ev);
+ verify(view2, never()).onTouchEvent(eq(ev));
+ ev.recycle();
+ }
+
+ @Test
+ public void testCachedRegionsSplit_horizontal() {
+ View left = mockViewAt(0, 0, 5, 20);
+ View right = mockViewAt(15, 0, 5, 20);
+ mNearestTouchFrame.layout(0, 0, 20, 20);
+
+ mNearestTouchFrame.addView(left);
+ mNearestTouchFrame.addView(right);
+ mNearestTouchFrame.onMeasure(0, 0);
+
+ Map<View, Rect> childRegions = mNearestTouchFrame.getFullTouchableChildRegions();
+ assertEquals(2, childRegions.size());
+ Rect leftRegion = childRegions.get(left);
+ Rect rightRegion = childRegions.get(right);
+ assertEquals(new Rect(0, 0, 9, 20), leftRegion);
+ assertEquals(new Rect(10, 0, 20, 20), rightRegion);
+ }
+
+ @Test
+ public void testCachedRegionsSplit_vertical() {
+ View top = mockViewAt(0, 0, 20, 5);
+ View bottom = mockViewAt(0, 15, 20, 5);
+ mNearestTouchFrame.layout(0, 0, 20, 20);
+ mNearestTouchFrame.setIsVertical(true);
+
+ mNearestTouchFrame.addView(top);
+ mNearestTouchFrame.addView(bottom);
+ mNearestTouchFrame.onMeasure(0, 0);
+
+ Map<View, Rect> childRegions = mNearestTouchFrame.getFullTouchableChildRegions();
+ assertEquals(2, childRegions.size());
+ Rect topRegion = childRegions.get(top);
+ Rect bottomRegion = childRegions.get(bottom);
+ assertEquals(new Rect(0, 0, 20, 9), topRegion);
+ assertEquals(new Rect(0, 10, 20, 20), bottomRegion);
+ }
+
private View mockViewAt(int x, int y, int width, int height) {
View v = spy(new View(mContext));
doAnswer(invocation -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTest.java
new file mode 100644
index 000000000000..b3ad6ef8da6e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.app.people.ConversationChannel;
+import android.app.people.IPeopleManager;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.testing.AndroidTestingRunner;
+import android.widget.RemoteViews;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.system.PeopleProviderUtils;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class PeopleProviderTest extends SysuiTestCase {
+ private static final String TAG = "PeopleProviderTest";
+
+ private static final Uri URI = Uri.parse(PeopleProviderUtils.PEOPLE_PROVIDER_SCHEME
+ + PeopleProviderUtils.PEOPLE_PROVIDER_AUTHORITY);
+
+ private static final String SHORTCUT_ID_A = "shortcut_id_a";
+ private static final String PACKAGE_NAME_A = "package_name_a";
+ private static final UserHandle USER_HANDLE_A = UserHandle.of(1);
+ private static final String USERNAME = "username";
+
+ private final ShortcutInfo mShortcutInfo =
+ new ShortcutInfo.Builder(mContext, SHORTCUT_ID_A).setLongLabel(USERNAME).build();
+ private final ConversationChannel mConversationChannel =
+ new ConversationChannel(mShortcutInfo, USER_HANDLE_A.getIdentifier(),
+ null, null, 0L, false);
+
+ private Bundle mExtras = new Bundle();
+
+ @Mock
+ private LauncherApps mLauncherApps;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private IPeopleManager mPeopleManager;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext.setMockPackageManager(mPackageManager);
+
+ PeopleProviderTestable provider = new PeopleProviderTestable();
+ provider.initializeForTesting(
+ mContext, PeopleProviderUtils.PEOPLE_PROVIDER_AUTHORITY);
+ provider.setLauncherApps(mLauncherApps);
+ provider.setPeopleManager(mPeopleManager);
+ mContext.getContentResolver().addProvider(
+ PeopleProviderUtils.PEOPLE_PROVIDER_AUTHORITY, provider);
+
+ mContext.getTestablePermissions().setPermission(
+ PeopleProviderUtils.GET_PEOPLE_TILE_PREVIEW_PERMISSION,
+ PackageManager.PERMISSION_GRANTED);
+
+ when(mPeopleManager.getConversation(
+ eq(PACKAGE_NAME_A), eq(USER_HANDLE_A.getIdentifier()), eq(SHORTCUT_ID_A)))
+ .thenReturn(mConversationChannel);
+
+ mExtras.putString(PeopleProviderUtils.EXTRAS_KEY_SHORTCUT_ID, SHORTCUT_ID_A);
+ mExtras.putString(PeopleProviderUtils.EXTRAS_KEY_PACKAGE_NAME, PACKAGE_NAME_A);
+ mExtras.putParcelable(PeopleProviderUtils.EXTRAS_KEY_USER_HANDLE, USER_HANDLE_A);
+ }
+
+ @Test
+ public void testPermissionDeniedThrowsSecurityException() throws RemoteException {
+ mContext.getTestablePermissions().setPermission(
+ PeopleProviderUtils.GET_PEOPLE_TILE_PREVIEW_PERMISSION,
+ PackageManager.PERMISSION_DENIED);
+ try {
+ Bundle result = mContext.getContentResolver()
+ .call(URI, PeopleProviderUtils.GET_PEOPLE_TILE_PREVIEW_METHOD, null, null);
+ Assert.fail("Call should have failed with SecurityException");
+ } catch (SecurityException e) {
+ } catch (Exception e) {
+ Assert.fail("Call should have failed with SecurityException");
+ }
+ }
+
+ @Test
+ public void testPermissionGrantedNoExtraReturnsNull() throws RemoteException {
+ try {
+ Bundle result = mContext.getContentResolver()
+ .call(URI, PeopleProviderUtils.GET_PEOPLE_TILE_PREVIEW_METHOD, null, null);
+ Assert.fail("Call should have failed with IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ } catch (Exception e) {
+ Assert.fail("Call should have failed with IllegalArgumentException");
+ }
+ }
+
+ @Test
+ public void testPermissionGrantedExtrasReturnsRemoteViews() throws RemoteException {
+ try {
+ Bundle result = mContext.getContentResolver().call(
+ URI, PeopleProviderUtils.GET_PEOPLE_TILE_PREVIEW_METHOD, null, mExtras);
+ RemoteViews views = result.getParcelable(
+ PeopleProviderUtils.RESPONSE_KEY_REMOTE_VIEWS);
+ assertThat(views).isNotNull();
+ } catch (Exception e) {
+ Assert.fail("Fail " + e);
+ }
+ }
+
+ @Test
+ public void testPermissionGrantedNoConversationForShortcutReturnsNull() throws RemoteException {
+ when(mPeopleManager.getConversation(
+ eq(PACKAGE_NAME_A), eq(USER_HANDLE_A.getIdentifier()), eq(SHORTCUT_ID_A)))
+ .thenReturn(null);
+ try {
+ Bundle result = mContext.getContentResolver().call(
+ URI, PeopleProviderUtils.GET_PEOPLE_TILE_PREVIEW_METHOD, null, mExtras);
+ assertThat(result).isNull();
+ } catch (Exception e) {
+ Assert.fail("Fail " + e);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTestable.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTestable.java
new file mode 100644
index 000000000000..ac1893413c50
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTestable.java
@@ -0,0 +1,40 @@
+/*
+ * 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.app.people.IPeopleManager;
+import android.content.Context;
+import android.content.pm.LauncherApps;
+import android.content.pm.ProviderInfo;
+
+public class PeopleProviderTestable extends PeopleProvider {
+
+ public void initializeForTesting(Context context, String authority) {
+ ProviderInfo info = new ProviderInfo();
+ info.authority = authority;
+
+ attachInfoForTesting(context, info);
+ }
+
+ void setLauncherApps(LauncherApps launcherApps) {
+ mLauncherApps = launcherApps;
+ }
+
+ void setPeopleManager(IPeopleManager peopleManager) {
+ mPeopleManager = peopleManager;
+ }
+}
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 6db21f9159ec..d79155cbb2fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -16,10 +16,18 @@
package com.android.systemui.people;
+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 com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
+import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
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;
@@ -38,6 +46,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Person;
import android.app.people.ConversationChannel;
+import android.app.people.ConversationStatus;
import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
@@ -45,25 +54,32 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.database.Cursor;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
-import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.ContactsContract;
import android.provider.Settings;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.widget.RemoteViews;
+import android.widget.TextView;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import org.junit.Before;
import org.junit.Test;
@@ -82,34 +98,105 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
private static final int WIDGET_ID_WITH_SHORTCUT = 1;
private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
- private static final String SHORTCUT_ID = "101";
+ private static final String SHORTCUT_ID_1 = "101";
+ private static final String SHORTCUT_ID_2 = "202";
+ private static final String SHORTCUT_ID_3 = "303";
+ private static final String SHORTCUT_ID_4 = "404";
private static final String NOTIFICATION_KEY = "notification_key";
private static final String NOTIFICATION_CONTENT = "notification_content";
private static final String TEST_LOOKUP_KEY = "lookup_key";
+ private static final String NOTIFICATION_TEXT_1 = "notification_text_1";
+ private static final String NOTIFICATION_TEXT_2 = "notification_text_2";
+ private static final String NOTIFICATION_TEXT_3 = "notification_text_3";
+ private static final String NOTIFICATION_TEXT_4 = "notification_text_4";
private static final int TEST_COLUMN_INDEX = 1;
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 GAME_DESCRIPTION = "Playing a game!";
+ private static final String NAME = "username";
private static final Person PERSON = new Person.Builder()
.setName("name")
.setKey("abc")
.setUri(URI.toString())
.setBot(false)
.build();
+ private static final PeopleSpaceTile PERSON_TILE_WITHOUT_NOTIFICATION =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_1, NAME, ICON, new Intent())
+ .setLastInteractionTimestamp(0L)
+ .build();
private static final PeopleSpaceTile PERSON_TILE =
new PeopleSpaceTile
- .Builder(SHORTCUT_ID, "username", ICON, new Intent())
+ .Builder(SHORTCUT_ID_1, NAME, ICON, new Intent())
+ .setLastInteractionTimestamp(123L)
.setNotificationKey(NOTIFICATION_KEY)
.setNotificationContent(NOTIFICATION_CONTENT)
.setNotificationDataUri(URI)
.build();
+ private static final ConversationStatus GAME_STATUS =
+ new ConversationStatus
+ .Builder(PERSON_TILE.getId(), ACTIVITY_GAME)
+ .setDescription(GAME_DESCRIPTION)
+ .build();
+ private static final ConversationStatus NEW_STORY_WITH_AVAILABILITY =
+ new ConversationStatus
+ .Builder(PERSON_TILE.getId(), ACTIVITY_NEW_STORY)
+ .setAvailability(AVAILABILITY_AVAILABLE)
+ .build();
private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
- SHORTCUT_ID).setLongLabel(
- "name").setPerson(PERSON)
+ SHORTCUT_ID_1).setLongLabel(
+ NAME).setPerson(PERSON)
.build();
private final ShortcutInfo mShortcutInfoWithoutPerson = new ShortcutInfo.Builder(mContext,
- SHORTCUT_ID).setLongLabel(
- "name")
+ SHORTCUT_ID_1).setLongLabel(
+ NAME)
+ .build();
+ private final Notification mNotification1 = new Notification.Builder(mContext, "test")
+ .setContentTitle("TEST_TITLE")
+ .setContentText("TEST_TEXT")
+ .setShortcutId(SHORTCUT_ID_1)
+ .setStyle(new Notification.MessagingStyle(PERSON)
+ .addMessage(new Notification.MessagingStyle.Message(
+ NOTIFICATION_TEXT_1, 0, PERSON))
+ .addMessage(new Notification.MessagingStyle.Message(
+ NOTIFICATION_TEXT_2, 20, PERSON))
+ .addMessage(new Notification.MessagingStyle.Message(
+ NOTIFICATION_TEXT_3, 10, PERSON))
+ )
+ .build();
+ private final Notification mNotification2 = new Notification.Builder(mContext, "test2")
+ .setContentTitle("TEST_TITLE")
+ .setContentText("OTHER_TEXT")
+ .setShortcutId(SHORTCUT_ID_2)
+ .setStyle(new Notification.MessagingStyle(PERSON)
+ .addMessage(new Notification.MessagingStyle.Message(
+ NOTIFICATION_TEXT_4, 0, PERSON))
+ )
+ .build();
+ private final Notification mNotification3 = new Notification.Builder(mContext, "test2")
+ .setContentTitle("TEST_TITLE")
+ .setContentText("OTHER_TEXT")
+ .setShortcutId(SHORTCUT_ID_3)
+ .setStyle(new Notification.MessagingStyle(PERSON))
+ .build();
+ private final NotificationEntry mNotificationEntry1 = new NotificationEntryBuilder()
+ .setNotification(mNotification1)
+ .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_1).build())
+ .setUser(UserHandle.of(0))
+ .setPkg(PACKAGE_NAME)
+ .build();
+ private final NotificationEntry mNotificationEntry2 = new NotificationEntryBuilder()
+ .setNotification(mNotification2)
+ .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_2).build())
+ .setUser(UserHandle.of(0))
+ .setPkg(PACKAGE_NAME)
+ .build();
+ private final NotificationEntry mNotificationEntry3 = new NotificationEntryBuilder()
+ .setNotification(mNotification3)
+ .setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID_3).build())
+ .setUser(UserHandle.of(0))
+ .setPkg(PACKAGE_NAME)
.build();
@Mock
@@ -130,9 +217,13 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
private ContentResolver mMockContentResolver;
@Mock
private Context mMockContext;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private NotificationEntryManager mNotificationEntryManager;
@Before
- public void setUp() throws RemoteException {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
@@ -152,15 +243,23 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
isNull())).thenReturn(mMockCursor);
when(mMockContext.getString(R.string.birthday_status)).thenReturn(
mContext.getString(R.string.birthday_status));
+ when(mMockContext.getString(R.string.basic_status)).thenReturn(
+ mContext.getString(R.string.basic_status));
+ when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mMockContext.getString(R.string.over_timestamp)).thenReturn(
+ mContext.getString(R.string.over_timestamp));
+ when(mPackageManager.getApplicationIcon(anyString())).thenReturn(null);
+ when(mNotificationEntryManager.getVisibleNotifications())
+ .thenReturn(List.of(mNotificationEntry1, mNotificationEntry2, mNotificationEntry3));
}
@Test
public void testGetTilesReturnsSortedListWithMultipleRecentConversations() throws Exception {
// Ensure the less-recent Important conversation is before more recent conversations.
ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID, false, 3);
+ SHORTCUT_ID_1, false, 3);
ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID + 1,
+ SHORTCUT_ID_1 + 1,
true, 1);
when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
new ParceledListSlice(Arrays.asList(
@@ -169,9 +268,9 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
// Ensure the non-Important conversation is sorted between these recent conversations.
ConversationChannel recentConversationBeforeNonImportantConversation =
getConversationChannel(
- SHORTCUT_ID + 2, 4);
+ SHORTCUT_ID_1 + 2, 4);
ConversationChannel recentConversationAfterNonImportantConversation =
- getConversationChannel(SHORTCUT_ID + 3,
+ getConversationChannel(SHORTCUT_ID_1 + 3,
2);
when(mPeopleManager.getRecentConversations()).thenReturn(
new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation,
@@ -179,7 +278,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
List<String> orderedShortcutIds = PeopleSpaceUtils.getTiles(
mContext, mNotificationManager, mPeopleManager,
- mLauncherApps).stream().map(tile -> tile.getId()).collect(Collectors.toList());
+ mLauncherApps, mNotificationEntryManager)
+ .stream().map(tile -> tile.getId()).collect(Collectors.toList());
assertThat(orderedShortcutIds).containsExactly(
// Even though the oldest conversation, should be first since "important"
@@ -196,11 +296,11 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
throws Exception {
// Ensure the less-recent Important conversation is before more recent conversations.
ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID, false, 3);
+ SHORTCUT_ID_1, false, 3);
ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID + 1, true, 3);
+ SHORTCUT_ID_1 + 1, true, 3);
ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID + 2,
+ SHORTCUT_ID_1 + 2,
true, 1);
when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
new ParceledListSlice(Arrays.asList(
@@ -210,9 +310,9 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
// Ensure the non-Important conversation is sorted between these recent conversations.
ConversationChannel recentConversationBeforeNonImportantConversation =
getConversationChannel(
- SHORTCUT_ID + 3, 4);
+ SHORTCUT_ID_1 + 3, 4);
ConversationChannel recentConversationAfterNonImportantConversation =
- getConversationChannel(SHORTCUT_ID + 4,
+ getConversationChannel(SHORTCUT_ID_1 + 4,
2);
when(mPeopleManager.getRecentConversations()).thenReturn(
new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation,
@@ -220,7 +320,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
List<String> orderedShortcutIds = PeopleSpaceUtils.getTiles(
mContext, mNotificationManager, mPeopleManager,
- mLauncherApps).stream().map(tile -> tile.getId()).collect(Collectors.toList());
+ mLauncherApps, mNotificationEntryManager)
+ .stream().map(tile -> tile.getId()).collect(Collectors.toList());
assertThat(orderedShortcutIds).containsExactly(
// Important conversations should be sorted at the beginning.
@@ -238,7 +339,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
Notification notification = new Notification.Builder(mContext, "test")
.setContentTitle("TEST_TITLE")
.setContentText("TEST_TEXT")
- .setShortcutId(SHORTCUT_ID)
+ .setShortcutId(SHORTCUT_ID_1)
.build();
StatusBarNotification sbn = new SbnBuilder()
.setNotification(notification)
@@ -341,24 +442,126 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
@Test
public void testGetLastMessagingStyleMessage() {
- Notification notification = new Notification.Builder(mContext, "test")
- .setContentTitle("TEST_TITLE")
- .setContentText("TEST_TEXT")
- .setShortcutId(SHORTCUT_ID)
- .setStyle(new Notification.MessagingStyle(PERSON)
- .addMessage(new Notification.MessagingStyle.Message("text1", 0, PERSON))
- .addMessage(new Notification.MessagingStyle.Message("text2", 20, PERSON))
- .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
- )
- .build();
StatusBarNotification sbn = new SbnBuilder()
- .setNotification(notification)
+ .setNotification(mNotification1)
.build();
Notification.MessagingStyle.Message lastMessage =
PeopleSpaceUtils.getLastMessagingStyleMessage(sbn);
- assertThat(lastMessage.getText()).isEqualTo("text2");
+ assertThat(lastMessage.getText().toString()).isEqualTo(NOTIFICATION_TEXT_2);
+ }
+
+ @Test
+ public void testAugmentTileFromNotification() {
+ StatusBarNotification sbn = new SbnBuilder()
+ .setNotification(mNotification1)
+ .build();
+
+ PeopleSpaceTile tile =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ PeopleSpaceTile actual = PeopleSpaceUtils
+ .augmentTileFromNotification(tile, sbn);
+
+ assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
+ }
+
+ @Test
+ public void testAugmentTileFromNotificationNoContent() {
+ StatusBarNotification sbn = new SbnBuilder()
+ .setNotification(mNotification3)
+ .build();
+
+ PeopleSpaceTile tile =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_3, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ PeopleSpaceTile actual = PeopleSpaceUtils
+ .augmentTileFromNotification(tile, sbn);
+
+ assertThat(actual.getNotificationKey()).isEqualTo(null);
+ assertThat(actual.getNotificationContent()).isEqualTo(null);
+ }
+
+ @Test
+ public void testAugmentTileFromVisibleNotifications() {
+ PeopleSpaceTile tile =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ PeopleSpaceTile actual = PeopleSpaceUtils
+ .augmentTileFromVisibleNotifications(tile,
+ Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
+
+ assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
+ }
+
+ @Test
+ public void testAugmentTileFromVisibleNotificationsDifferentShortcutId() {
+ PeopleSpaceTile tile =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_4, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ PeopleSpaceTile actual = PeopleSpaceUtils
+ .augmentTileFromVisibleNotifications(tile,
+ Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1));
+
+ assertThat(actual.getNotificationContent()).isEqualTo(null);
+ }
+
+ @Test
+ public void testAugmentTilesFromVisibleNotificationsSingleTile() {
+ PeopleSpaceTile tile =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ List<PeopleSpaceTile> actualList = PeopleSpaceUtils
+ .augmentTilesFromVisibleNotifications(List.of(tile), mNotificationEntryManager);
+
+ assertThat(actualList.size()).isEqualTo(1);
+ assertThat(actualList.get(0).getNotificationContent().toString())
+ .isEqualTo(NOTIFICATION_TEXT_2);
+
+ verify(mNotificationEntryManager, times(1)).getVisibleNotifications();
+ }
+
+ @Test
+ public void testAugmentTilesFromVisibleNotificationsMultipleTiles() {
+ PeopleSpaceTile tile1 =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(1)
+ .build();
+ PeopleSpaceTile tile2 =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_2, "userName2", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
+ .build();
+ List<PeopleSpaceTile> actualList = PeopleSpaceUtils
+ .augmentTilesFromVisibleNotifications(List.of(tile1, tile2),
+ mNotificationEntryManager);
+
+ assertThat(actualList.size()).isEqualTo(2);
+ assertThat(actualList.get(0).getNotificationContent().toString())
+ .isEqualTo(NOTIFICATION_TEXT_2);
+ assertThat(actualList.get(1).getNotificationContent().toString())
+ .isEqualTo(NOTIFICATION_TEXT_4);
+
+ verify(mNotificationEntryManager, times(1)).getVisibleNotifications();
}
@Test
@@ -455,6 +658,137 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
any());
}
+ @Test
+ public void testCreateRemoteViewsWithLastInteractionTime() {
+ RemoteViews views = PeopleSpaceUtils.createRemoteViews(mMockContext,
+ PERSON_TILE_WITHOUT_NOTIFICATION, 0);
+ View result = views.apply(mContext, null);
+
+ TextView name = (TextView) result.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ // Has last interaction.
+ TextView lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
+ assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
+ // 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);
+ assertEquals(View.VISIBLE, personIcon.getVisibility());
+ assertEquals(View.GONE, personIconWithStory.getVisibility());
+ // No status.
+ assertThat((View) result.findViewById(R.id.status)).isNull();
+ }
+
+ @Test
+ public void testCreateRemoteViewsWithGameTypeOnlyIsIgnored() {
+ PeopleSpaceTile tileWithAvailabilityAndNewStory =
+ PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
+ Arrays.asList(NEW_STORY_WITH_AVAILABILITY,
+ new ConversationStatus.Builder(
+ PERSON_TILE_WITHOUT_NOTIFICATION.getId(),
+ ACTIVITY_GAME).build())).build();
+ RemoteViews views = PeopleSpaceUtils.createRemoteViews(mMockContext,
+ tileWithAvailabilityAndNewStory, 0);
+ View result = views.apply(mContext, null);
+
+ TextView name = (TextView) result.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ // Has last interaction over status.
+ TextView lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
+ assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
+ // 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());
+ // No status.
+ assertThat((View) result.findViewById(R.id.status)).isNull();
+ }
+
+ @Test
+ public void testCreateRemoteViewsWithBirthdayTypeOnlyIsNotIgnored() {
+ PeopleSpaceTile tileWithStatusTemplate =
+ PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
+ Arrays.asList(
+ NEW_STORY_WITH_AVAILABILITY, new ConversationStatus.Builder(
+ PERSON_TILE_WITHOUT_NOTIFICATION.getId(),
+ ACTIVITY_BIRTHDAY).build())).build();
+ RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
+ tileWithStatusTemplate, 0);
+ View result = views.apply(mContext, null);
+
+ TextView name = (TextView) result.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ // 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 status text from backup text.
+ TextView statusContent = (TextView) result.findViewById(R.id.status);
+ assertEquals(statusContent.getText(), mContext.getString(R.string.birthday_status));
+ }
+
+ @Test
+ public void testCreateRemoteViewsWithStatusTemplate() {
+ PeopleSpaceTile tileWithStatusTemplate =
+ PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
+ Arrays.asList(GAME_STATUS,
+ NEW_STORY_WITH_AVAILABILITY)).build();
+ RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
+ tileWithStatusTemplate, 0);
+ View result = views.apply(mContext, null);
+
+ TextView name = (TextView) result.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ // 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 status.
+ TextView statusContent = (TextView) result.findViewById(R.id.status);
+ assertEquals(statusContent.getText(), GAME_DESCRIPTION);
+ }
+
+ @Test
+ public void testCreateRemoteViewsWithNotificationTemplate() {
+ PeopleSpaceTile tileWithStatusAndNotification = PERSON_TILE.toBuilder()
+ .setNotificationDataUri(null)
+ .setStatuses(Arrays.asList(GAME_STATUS,
+ NEW_STORY_WITH_AVAILABILITY)).build();
+ RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
+ tileWithStatusAndNotification, 0);
+ 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"));
+ // 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 notification content.
+ TextView statusContent = (TextView) result.findViewById(R.id.content);
+ assertEquals(statusContent.getText(), NOTIFICATION_CONTENT);
+ }
+
private ConversationChannelWrapper getConversationChannelWrapper(String shortcutId,
boolean importantConversation, long lastInteractionTimestamp) throws Exception {
ConversationChannelWrapper convo = new ConversationChannelWrapper();
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 ef314ad16556..9470141178dd 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
@@ -421,6 +421,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext)
.setContentTitle("TEST_TITLE")
@@ -429,15 +430,21 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
.build();
StatusBarNotification sbn = new SbnBuilder()
.setNotification(notificationWithoutMessagingStyle)
+ .setPkg(TEST_PACKAGE_A)
+ .setUid(0)
.build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mAppWidgetManager, never())
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
- verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle options = requireNonNull(mBundleArgumentCaptor.getValue());
+ assertThat((PeopleSpaceTile) options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE))
+ .isEqualTo(PERSON_TILE);
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
any());
}
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 f86b4651056b..072f7b8a7756 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
@@ -100,12 +100,12 @@ class PrivacyDialogControllerTest : SysuiTestCase() {
private val dialogProvider = object : PrivacyDialogController.DialogProvider {
var list: List<PrivacyDialog.PrivacyElement>? = null
- var starter: ((String) -> Unit)? = null
+ var starter: ((String, Int) -> Unit)? = null
override fun makeDialog(
context: Context,
list: List<PrivacyDialog.PrivacyElement>,
- starter: (String) -> Unit
+ starter: (String, Int) -> Unit
): PrivacyDialog {
this.list = list
this.starter = starter
@@ -210,7 +210,7 @@ class PrivacyDialogControllerTest : SysuiTestCase() {
fun testSingleElementInList() {
val usage = createMockPermGroupUsage(
packageName = TEST_PACKAGE_NAME,
- uid = generateUidForUser(0),
+ uid = generateUidForUser(USER_ID),
permGroupName = PERM_CAMERA,
lastAccess = 5L,
isActive = true,
@@ -224,6 +224,8 @@ class PrivacyDialogControllerTest : SysuiTestCase() {
val expected = PrivacyDialog.PrivacyElement(
type = PrivacyType.TYPE_CAMERA,
+ packageName = TEST_PACKAGE_NAME,
+ userId = USER_ID,
applicationName = TEST_PACKAGE_NAME,
attribution = TEST_ATTRIBUTION,
lastActiveTimestamp = 5L,
@@ -459,13 +461,28 @@ class PrivacyDialogControllerTest : SysuiTestCase() {
controller.showDialog(context)
exhaustExecutors()
- dialogProvider.starter?.invoke(PERM_MICROPHONE)
+ dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
+ verify(activityStarter)
+ .startActivity(capture(intentCaptor), eq(true), any<ActivityStarter.Callback>())
+
+ assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_MANAGE_APP_PERMISSIONS)
+ assertThat(intentCaptor.value.getStringExtra(Intent.EXTRA_PACKAGE_NAME))
+ .isEqualTo(TEST_PACKAGE_NAME)
+ assertThat(intentCaptor.value.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle)
+ .isEqualTo(UserHandle.of(USER_ID))
+ }
+
+ @Test
+ fun testStartActivityCorrectIntent_enterpriseUser() {
+ controller.showDialog(context)
+ exhaustExecutors()
+
+ dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, ENT_USER_ID)
verify(activityStarter)
.startActivity(capture(intentCaptor), eq(true), any<ActivityStarter.Callback>())
- assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_MANAGE_PERMISSION_APPS)
- assertThat(intentCaptor.value.getStringExtra(Intent.EXTRA_PERMISSION_GROUP_NAME))
- .isEqualTo(PERM_MICROPHONE)
+ assertThat(intentCaptor.value.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle)
+ .isEqualTo(UserHandle.of(ENT_USER_ID))
}
@Test
@@ -473,7 +490,7 @@ class PrivacyDialogControllerTest : SysuiTestCase() {
controller.showDialog(context)
exhaustExecutors()
- dialogProvider.starter?.invoke(PERM_MICROPHONE)
+ dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
verify(activityStarter).startActivity(any(), eq(true), capture(activityStartedCaptor))
activityStartedCaptor.value.onActivityStarted(ActivityManager.START_DELIVERED_TO_TOP)
@@ -486,7 +503,7 @@ class PrivacyDialogControllerTest : SysuiTestCase() {
controller.showDialog(context)
exhaustExecutors()
- dialogProvider.starter?.invoke(PERM_MICROPHONE)
+ dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
verify(activityStarter).startActivity(any(), eq(true), capture(activityStartedCaptor))
activityStartedCaptor.value.onActivityStarted(ActivityManager.START_ABORTED)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
index eb5dd4e6fef6..28b44d94effb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
@@ -40,8 +40,13 @@ import org.mockito.MockitoAnnotations
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class PrivacyDialogTest : SysuiTestCase() {
+ companion object {
+ private const val TEST_PACKAGE_NAME = "test_pkg"
+ private const val TEST_USER_ID = 0
+ }
+
@Mock
- private lateinit var starter: (String) -> Unit
+ private lateinit var starter: (String, Int) -> Unit
private lateinit var dialog: PrivacyDialog
@@ -58,10 +63,12 @@ class PrivacyDialogTest : SysuiTestCase() {
}
@Test
- fun testStarterCalledWithCorrectPermGroupName() {
+ fun testStarterCalledWithCorrectParams() {
val list = listOf(
PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_MICROPHONE,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
"App",
null,
0L,
@@ -73,7 +80,7 @@ class PrivacyDialogTest : SysuiTestCase() {
dialog = PrivacyDialog(context, list, starter)
dialog.show()
dialog.requireViewById<View>(R.id.privacy_item).callOnClick()
- verify(starter).invoke(PrivacyType.TYPE_MICROPHONE.permGroupName)
+ verify(starter).invoke(TEST_PACKAGE_NAME, TEST_USER_ID)
}
@Test
@@ -104,6 +111,8 @@ class PrivacyDialogTest : SysuiTestCase() {
val list = listOf(
PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_CAMERA,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
"App",
null,
0L,
@@ -113,6 +122,8 @@ class PrivacyDialogTest : SysuiTestCase() {
),
PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_MICROPHONE,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
"App",
null,
0L,
@@ -130,6 +141,8 @@ class PrivacyDialogTest : SysuiTestCase() {
fun testUsingText() {
val element = PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_CAMERA,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
"App",
null,
0L,
@@ -154,6 +167,8 @@ class PrivacyDialogTest : SysuiTestCase() {
fun testRecentText() {
val element = PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_MICROPHONE,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
"App",
null,
0L,
@@ -178,6 +193,8 @@ class PrivacyDialogTest : SysuiTestCase() {
fun testEnterprise() {
val element = PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_MICROPHONE,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
"App",
null,
0L,
@@ -198,6 +215,8 @@ class PrivacyDialogTest : SysuiTestCase() {
fun testPhoneCall() {
val element = PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_MICROPHONE,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
"App",
null,
0L,
@@ -218,6 +237,8 @@ class PrivacyDialogTest : SysuiTestCase() {
fun testAttribution() {
val element = PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_MICROPHONE,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
"App",
"attribution",
0L,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 0dc268a3c186..fb817eac3e10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.qs;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -28,6 +30,8 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -37,6 +41,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.MediaHost;
@@ -44,6 +49,7 @@ import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.util.animation.DisappearParameters;
import org.junit.Before;
@@ -86,17 +92,25 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
QSTileView mQSTileView;
@Mock
PagedTileLayout mPagedTileLayout;
+ @Mock
+ FeatureFlags mFeatureFlags;
+ @Mock
+ Resources mResources;
+ @Mock
+ Configuration mConfiguration;
private QSPanelControllerBase<QSPanel> mController;
+
+
/** Implementation needed to ensure we have a reflectively-available class name. */
private class TestableQSPanelControllerBase extends QSPanelControllerBase<QSPanel> {
protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host,
QSCustomizerController qsCustomizerController, MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager) {
+ DumpManager dumpManager, FeatureFlags featureFlags) {
super(view, host, qsCustomizerController, true, mediaHost, metricsLogger, uiEventLogger,
- qsLogger, dumpManager);
+ qsLogger, dumpManager, featureFlags);
}
@Override
@@ -121,10 +135,12 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
when(mQSTileRevealControllerFactory.create(any(), any()))
.thenReturn(mQSTileRevealController);
when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters());
+ when(mQSPanel.getResources()).thenReturn(mResources);
+ when(mResources.getConfiguration()).thenReturn(mConfiguration);
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags);
mController.init();
reset(mQSTileRevealController);
@@ -136,7 +152,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
QSPanelControllerBase<QSPanel> controller = new TestableQSPanelControllerBase(mQSPanel,
mQSTileHost, mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager) {
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags) {
@Override
protected QSTileRevealController createTileRevealController() {
return mQSTileRevealController;
@@ -218,4 +234,18 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
verify(mQSLogger).logAllTilesChangeListening(false, "QSPanel", "dnd");
verify(mPagedTileLayout).setListening(false, mUiEventLogger);
}
+
+
+ @Test
+ public void testShouldUzeHorizontalLayout_falseForSplitShade() {
+ mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ when(mMediaHost.getVisible()).thenReturn(true);
+
+ when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false);
+ assertThat(mController.shouldUseHorizontalLayout()).isTrue();
+
+ when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+ assertThat(mController.shouldUseHorizontalLayout()).isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index 4381158c3415..0dfebab59feb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -41,6 +41,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.brightness.BrightnessController;
import com.android.systemui.settings.brightness.BrightnessSlider;
import com.android.systemui.settings.brightness.ToggleSlider;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.animation.DisappearParameters;
@@ -93,6 +94,8 @@ public class QSPanelControllerTest extends SysuiTestCase {
QSTileView mQSTileView;
@Mock
PagedTileLayout mPagedTileLayout;
+ @Mock
+ FeatureFlags mFeatureFlags;
private QSPanelController mController;
@@ -118,7 +121,9 @@ public class QSPanelControllerTest extends SysuiTestCase {
mQSTileHost, mQSCustomizerController, true, mMediaHost,
mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
- /* labelsFlag */ false);
+ /* labelsFlag */ false,
+ mFeatureFlags
+ );
mController.init();
}
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 fd0715bbca29..862e3747f602 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -443,9 +443,18 @@ public class QSSecurityFooterTest extends SysuiTestCase {
@Test
public void testGetNetworkLoggingMessage() {
- assertEquals(null, mFooter.getNetworkLoggingMessage(false));
+ // Test network logging message on a device with a device owner.
+ // Network traffic may be monitored on the device.
+ assertEquals(null, mFooter.getNetworkLoggingMessage(true, false));
assertEquals(mContext.getString(R.string.monitoring_description_management_network_logging),
- mFooter.getNetworkLoggingMessage(true));
+ mFooter.getNetworkLoggingMessage(true, true));
+
+ // Test network logging message on a device with a managed profile owner
+ // Network traffic may be monitored on the work profile.
+ assertEquals(null, mFooter.getNetworkLoggingMessage(false, false));
+ assertEquals(
+ mContext.getString(R.string.monitoring_description_managed_profile_network_logging),
+ mFooter.getNetworkLoggingMessage(false, true));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 107160f47f02..587020090433 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -27,6 +27,7 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTileView
import com.android.systemui.qs.customize.QSCustomizerController
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.statusbar.FeatureFlags
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -63,6 +64,8 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
private lateinit var tileLayout: TileLayout
@Mock
private lateinit var tileView: QSTileView
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
private lateinit var controller: QuickQSPanelController
@@ -84,7 +87,8 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
uiEventLogger,
qsLogger,
dumpManager,
- false
+ false,
+ featureFlags
)
controller.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index d504c3e12ea5..57831b328bf2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -98,7 +98,7 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest {
mQSCarrierGroupController = new QSCarrierGroupController.Builder(
mActivityStarter, handler, TestableLooper.get(this).getLooper(),
- mNetworkController, mCarrierTextControllerBuilder)
+ mNetworkController, mCarrierTextControllerBuilder, mContext)
.setQSCarrierGroup(mQSCarrierGroup)
.build();
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
new file mode 100644
index 000000000000..d3dbe2bf7d2b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -0,0 +1,295 @@
+/*
+ * 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 android.os.Handler
+import android.provider.Settings
+import android.service.quicksettings.Tile
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.lifecycle.LifecycleOwner
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.SysuiTestCase
+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
+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.KeyguardStateController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SecureSettings
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Answers
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class DeviceControlsTileTest : 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
+ private lateinit var controlsComponent: ControlsComponent
+ @Mock
+ private lateinit var controlsUiController: ControlsUiController
+ @Mock
+ private lateinit var controlsListingController: ControlsListingController
+ @Mock
+ 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
+ @Mock
+ private lateinit var uiEventLogger: UiEventLogger
+ @Captor
+ private lateinit var listingCallbackCaptor:
+ ArgumentCaptor<ControlsListingController.ControlsListingCallback>
+
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var tile: DeviceControlsTile
+
+ @Mock
+ private lateinit var keyguardStateController: KeyguardStateController
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private lateinit var userTracker: UserTracker
+ @Mock
+ private lateinit var lockPatternUtils: LockPatternUtils
+ @Mock
+ private lateinit var secureSettings: SecureSettings
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+
+ `when`(qsHost.context).thenReturn(mContext)
+ `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+
+ controlsComponent = ControlsComponent(
+ true,
+ mContext,
+ { controlsController },
+ { controlsUiController },
+ { controlsListingController },
+ lockPatternUtils,
+ keyguardStateController,
+ userTracker,
+ secureSettings
+ )
+
+ globalSettings = FakeSettings()
+
+ globalSettings.putInt(DeviceControlsTile.SETTINGS_FLAG, 1)
+ `when`(featureFlags.isKeyguardLayoutEnabled).thenReturn(true)
+
+ `when`(userTracker.userHandle.identifier).thenReturn(0)
+
+ tile = createTile()
+ }
+
+ @Test
+ fun testAvailable() {
+ `when`(controlsController.available).thenReturn(true)
+ assertThat(tile.isAvailable).isTrue()
+ }
+
+ @Test
+ fun testNotAvailableFeature() {
+ `when`(controlsController.available).thenReturn(true)
+ `when`(featureFlags.isKeyguardLayoutEnabled).thenReturn(false)
+
+ assertThat(tile.isAvailable).isFalse()
+ }
+
+ @Test
+ fun testNotAvailableControls() {
+ controlsComponent = ControlsComponent(
+ false,
+ mContext,
+ { controlsController },
+ { controlsUiController },
+ { controlsListingController },
+ lockPatternUtils,
+ keyguardStateController,
+ userTracker,
+ secureSettings
+ )
+ tile = createTile()
+
+ assertThat(tile.isAvailable).isFalse()
+ }
+
+ @Test
+ fun testNotAvailableFlag() {
+ globalSettings.putInt(DeviceControlsTile.SETTINGS_FLAG, 0)
+ tile = createTile()
+
+ assertThat(tile.isAvailable).isFalse()
+ }
+
+ @Test
+ fun testObservingCallback() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ any(ControlsListingController.ControlsListingCallback::class.java)
+ )
+ }
+
+ @Test
+ fun testLongClickIntent() {
+ assertThat(tile.longClickIntent.action).isEqualTo(Settings.ACTION_DEVICE_CONTROLS_SETTINGS)
+ }
+
+ @Test
+ fun testUnavailableByDefault() {
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_UNAVAILABLE)
+ }
+
+ @Test
+ fun testStateUnavailableIfNoListings() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ capture(listingCallbackCaptor)
+ )
+
+ listingCallbackCaptor.value.onServicesUpdated(emptyList())
+ testableLooper.processAllMessages()
+
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_UNAVAILABLE)
+ }
+
+ @Test
+ fun testStateAvailableIfListings() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ capture(listingCallbackCaptor)
+ )
+
+ listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
+ testableLooper.processAllMessages()
+
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_ACTIVE)
+ }
+
+ @Test
+ fun testMoveBetweenStates() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ capture(listingCallbackCaptor)
+ )
+
+ listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
+ testableLooper.processAllMessages()
+
+ listingCallbackCaptor.value.onServicesUpdated(emptyList())
+ testableLooper.processAllMessages()
+
+ assertThat(tile.state.state).isEqualTo(Tile.STATE_UNAVAILABLE)
+ }
+
+ @Test
+ fun testNoDialogWhenUnavailable() {
+ tile.click()
+ testableLooper.processAllMessages()
+
+ verify(controlsDialog, never()).show(any(ControlsUiController::class.java))
+ }
+
+ @Test
+ fun testDialogShowWhenAvailable() {
+ verify(controlsListingController).observe(
+ any(LifecycleOwner::class.java),
+ capture(listingCallbackCaptor)
+ )
+
+ listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
+ testableLooper.processAllMessages()
+
+ tile.click()
+ testableLooper.processAllMessages()
+
+ verify(controlsDialog).show(controlsUiController)
+ }
+
+ @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()
+ }
+
+ private fun createTile(): DeviceControlsTile {
+ return DeviceControlsTile(
+ qsHost,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ controlsComponent,
+ featureFlags,
+ { controlsDialog },
+ globalSettings
+ )
+ }
+}
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 78af20f9db31..ffd747e09e23 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
@@ -75,6 +75,7 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase {
mFakeSettings = new FakeSettings();
mTile = new ReduceBrightColorsTile(
+ true,
mHost,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
@@ -95,7 +96,6 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase {
assertEquals(Tile.STATE_INACTIVE, mTile.getState().state);
assertEquals(mTile.getState().label.toString(),
mContext.getString(R.string.quick_settings_reduce_bright_colors_label));
- assertEquals(mTile.getState().secondaryLabel.toString(), "");
}
@Test
@@ -128,13 +128,5 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase {
assertEquals(Tile.STATE_ACTIVE, mTile.getState().state);
assertEquals(mTile.getState().label.toString(),
mContext.getString(R.string.quick_settings_reduce_bright_colors_label));
-
- final int intensity = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mUserTracker.getUserId());
-
- assertEquals(
- mContext.getString(
- R.string.quick_settings_reduce_bright_colors_secondary_label, intensity),
- mTile.getState().secondaryLabel.toString());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
index 451c78fd1202..6d2b8e415e96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
@@ -43,7 +43,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
import org.junit.Before;
import org.junit.Test;
@@ -78,7 +78,7 @@ public class OverviewProxyServiceTest extends SysuiTestCase {
@Mock private Optional<com.android.wm.shell.onehanded.OneHanded> mMockOneHandedOptional;
@Mock private PackageManager mPackageManager;
@Mock private SysUiState mMockSysUiState;
- @Mock private Transitions mMockTransitions;
+ @Mock private RemoteTransitions mMockTransitions;
@Before
public void setUp() throws RemoteException {
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 c1c637129d85..580f800fbc44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
@@ -57,6 +57,8 @@ import org.mockito.stubbing.Answer;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class ScrollCaptureClientTest extends SysuiTestCase {
+ private static final float MAX_PAGES = 3f;
+
private Context mContext;
private IWindowManager mWm;
@@ -96,7 +98,7 @@ public class ScrollCaptureClientTest extends SysuiTestCase {
Connection conn = mConnectionConsumer.getValue();
- conn.start(mSessionConsumer);
+ conn.start(mSessionConsumer, MAX_PAGES);
verify(mSessionConsumer, timeout(100)).accept(any(Session.class));
Session session = mSessionConsumer.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
index bd3725942eca..4c84df2769a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
@@ -34,7 +34,7 @@ public class ScrollViewActivity extends Activity {
linearLayout.setOrientation(LinearLayout.VERTICAL);
TextView text = new TextView(this);
text.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 40);
- text.setText(com.android.systemui.R.string.test_content);
+ text.setText(com.android.systemui.tests.R.string.test_content);
linearLayout.addView(text);
scrollView.addView(linearLayout);
setContentView(scrollView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 2c96535b52bd..ce14bca081ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar;
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
@@ -27,13 +29,11 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.Instrumentation;
@@ -51,7 +51,6 @@ import android.os.BatteryManager;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserManager;
-import android.view.View;
import android.view.ViewGroup;
import androidx.test.InstrumentationRegistry;
@@ -60,18 +59,21 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.app.IBatteryStats;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.keyguard.KeyguardIndication;
+import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
import com.android.systemui.statusbar.phone.LockIcon;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.wakelock.WakeLockFake;
import org.junit.After;
@@ -93,6 +95,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
private static final String ORGANIZATION_NAME = "organization";
private String mDisclosureWithOrganization;
+ private String mDisclosureGeneric;
@Mock
private DevicePolicyManager mDevicePolicyManager;
@@ -101,7 +104,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
- private KeyguardIndicationTextView mDisclosure;
+ private KeyguardIndicationTextView mIndicationAreaBottom;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
@@ -118,8 +121,20 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
private IBatteryStats mIBatteryStats;
@Mock
private DockManager mDockManager;
+ @Mock
+ private KeyguardIndicationRotateTextViewController mRotateTextViewController;
@Captor
private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
+ @Captor
+ private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
+ @Captor
+ private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+ @Captor
+ private ArgumentCaptor<KeyguardIndication> mKeyguardIndicationCaptor;
+ private StatusBarStateController.StateListener mStatusBarStateListener;
+ private BroadcastReceiver mBroadcastReceiver;
+ private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+
private KeyguardIndicationTextView mTextView;
private KeyguardIndicationController mController;
@@ -140,15 +155,15 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class));
mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name,
ORGANIZATION_NAME);
+ mDisclosureGeneric = mContext.getString(R.string.do_disclosure_generic);
when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true);
when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true);
- when(mIndicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure))
- .thenReturn(mDisclosure);
+ when(mIndicationArea.findViewById(R.id.keyguard_indication_text_bottom))
+ .thenReturn(mIndicationAreaBottom);
when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView);
- when(mDisclosure.getAlpha()).thenReturn(1f);
mWakeLock = new WakeLockFake();
mWakeLockBuilder = new WakeLockFake.Builder(mContext);
@@ -168,11 +183,15 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mController = new KeyguardIndicationController(mContext, mWakeLockBuilder,
mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats,
- mUserManager);
+ mUserManager, mExecutor);
mController.setIndicationArea(mIndicationArea);
+ verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
+ mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue();
+ verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverCaptor.capture(), any());
+ mBroadcastReceiver = mBroadcastReceiverCaptor.getValue();
+ mController.mRotateTextViewController = mRotateTextViewController;
mController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
clearInvocations(mIBatteryStats);
- verify(mDisclosure).getAlpha();
}
@Test
@@ -223,7 +242,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
createController();
verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
mController.setVisible(true);
- mController.setDozing(true);
+ mStatusBarStateListener.onDozingChanged(true);
mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_POOR);
});
@@ -241,7 +260,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
createController();
verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
mController.setVisible(true);
- mController.setDozing(true);
+ mStatusBarStateListener.onDozingChanged(true);
mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE);
});
@@ -255,136 +274,108 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
@Test
public void disclosure_unmanaged() {
+ createController();
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(false);
- createController();
+ sendUpdateDisclosureBroadcast();
- verify(mDisclosure).setVisibility(View.GONE);
- verifyNoMoreInteractions(mDisclosure);
+ verify(mRotateTextViewController).hideIndication(INDICATION_TYPE_DISCLOSURE);
+ verify(mRotateTextViewController, never()).updateIndication(eq(INDICATION_TYPE_DISCLOSURE),
+ any(), anyBoolean());
}
@Test
- public void disclosure_deviceOwner_noOwnerName() {
+ public void disclosure_deviceOwner_noOrganizationName() {
+ createController();
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
- createController();
+ sendUpdateDisclosureBroadcast();
- verify(mDisclosure).setVisibility(View.VISIBLE);
- verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
- verifyNoMoreInteractions(mDisclosure);
+ verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE),
+ mKeyguardIndicationCaptor.capture(), eq(false));
+ assertThat(mKeyguardIndicationCaptor.getValue().getMessage())
+ .isEqualTo(mDisclosureGeneric);
}
@Test
- public void disclosure_orgOwnedDeviceWithManagedProfile_noOwnerName() {
+ public void disclosure_orgOwnedDeviceWithManagedProfile_noOrganizationName() {
+ createController();
when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(true);
when(mUserManager.getProfiles(anyInt())).thenReturn(Collections.singletonList(
new UserInfo(10, /* name */ null, /* flags */ FLAG_MANAGED_PROFILE)));
when(mDevicePolicyManager.getOrganizationNameForUser(eq(10))).thenReturn(null);
- createController();
+ sendUpdateDisclosureBroadcast();
- verify(mDisclosure).setVisibility(View.VISIBLE);
- verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
- verifyNoMoreInteractions(mDisclosure);
+ verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE),
+ mKeyguardIndicationCaptor.capture(), eq(false));
+ assertThat(mKeyguardIndicationCaptor.getValue().getMessage())
+ .isEqualTo(mDisclosureGeneric);
}
@Test
- public void disclosure_hiddenWhenDozing() {
- when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
- when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
+ public void disclosure_deviceOwner_withOrganizationName() {
createController();
-
- mController.setVisible(true);
- mController.onDozeAmountChanged(1, 1);
- mController.setDozing(true);
-
- verify(mDisclosure).setVisibility(View.VISIBLE);
- verify(mDisclosure).setAlpha(0f);
- verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
- verifyNoMoreInteractions(mDisclosure);
- }
-
- @Test
- public void disclosure_visibleWhenDozing() {
- when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
- when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
- createController();
-
- mController.setVisible(true);
- mController.onDozeAmountChanged(0, 0);
- mController.setDozing(false);
-
- verify(mDisclosure).setVisibility(View.VISIBLE);
- verify(mDisclosure).setAlpha(1f);
- verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
- verifyNoMoreInteractions(mDisclosure);
- }
-
- @Test
- public void disclosure_deviceOwner_withOwnerName() {
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME);
- createController();
+ sendUpdateDisclosureBroadcast();
- verify(mDisclosure).setVisibility(View.VISIBLE);
- verify(mDisclosure).switchIndication(mDisclosureWithOrganization);
- verifyNoMoreInteractions(mDisclosure);
+ verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE),
+ mKeyguardIndicationCaptor.capture(), eq(false));
+ assertThat(mKeyguardIndicationCaptor.getValue().getMessage())
+ .isEqualTo(mDisclosureWithOrganization);
}
@Test
- public void disclosure_orgOwnedDeviceWithManagedProfile_withOwnerName() {
+ public void disclosure_orgOwnedDeviceWithManagedProfile_withOrganizationName() {
+ createController();
when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(true);
when(mUserManager.getProfiles(anyInt())).thenReturn(Collections.singletonList(
new UserInfo(10, /* name */ null, FLAG_MANAGED_PROFILE)));
when(mDevicePolicyManager.getOrganizationNameForUser(eq(10))).thenReturn(ORGANIZATION_NAME);
- createController();
+ sendUpdateDisclosureBroadcast();
- verify(mDisclosure).setVisibility(View.VISIBLE);
- verify(mDisclosure).switchIndication(mDisclosureWithOrganization);
- verifyNoMoreInteractions(mDisclosure);
+ verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE),
+ mKeyguardIndicationCaptor.capture(), eq(false));
+ assertThat(mKeyguardIndicationCaptor.getValue().getMessage())
+ .isEqualTo(mDisclosureWithOrganization);
}
@Test
public void disclosure_updateOnTheFly() {
- ArgumentCaptor<BroadcastReceiver> receiver = ArgumentCaptor.forClass(
- BroadcastReceiver.class);
- doNothing().when(mBroadcastDispatcher).registerReceiver(receiver.capture(), any());
-
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
createController();
- final KeyguardUpdateMonitorCallback monitor = mController.getKeyguardCallback();
- reset(mDisclosure);
-
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
- receiver.getValue().onReceive(mContext, new Intent());
+ sendUpdateDisclosureBroadcast();
- verify(mDisclosure).setVisibility(View.VISIBLE);
- verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
- verifyNoMoreInteractions(mDisclosure);
- reset(mDisclosure);
+ verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE),
+ mKeyguardIndicationCaptor.capture(), eq(false));
+ assertThat(mKeyguardIndicationCaptor.getValue().getMessage())
+ .isEqualTo(mDisclosureGeneric);
+ reset(mRotateTextViewController);
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME);
- receiver.getValue().onReceive(mContext, new Intent());
+ sendUpdateDisclosureBroadcast();
- verify(mDisclosure).setVisibility(View.VISIBLE);
- verify(mDisclosure).switchIndication(mDisclosureWithOrganization);
- verifyNoMoreInteractions(mDisclosure);
- reset(mDisclosure);
+ verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE),
+ mKeyguardIndicationCaptor.capture(), eq(false));
+ assertThat(mKeyguardIndicationCaptor.getValue().getMessage())
+ .isEqualTo(mDisclosureWithOrganization);
+ reset(mRotateTextViewController);
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
- receiver.getValue().onReceive(mContext, new Intent());
+ sendUpdateDisclosureBroadcast();
- verify(mDisclosure).setVisibility(View.GONE);
- verifyNoMoreInteractions(mDisclosure);
+ verify(mRotateTextViewController).hideIndication(INDICATION_TYPE_DISCLOSURE);
}
@Test
public void transientIndication_holdsWakeLock_whenDozing() {
createController();
- mController.setDozing(true);
+ mStatusBarStateListener.onDozingChanged(true);
mController.showTransientIndication("Test");
assertTrue(mWakeLock.isHeld());
@@ -394,7 +385,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
public void transientIndication_releasesWakeLock_afterHiding() {
createController();
- mController.setDozing(true);
+ mStatusBarStateListener.onDozingChanged(true);
mController.showTransientIndication("Test");
mController.hideTransientIndication();
@@ -406,7 +397,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> {
createController();
- mController.setDozing(true);
+ mStatusBarStateListener.onDozingChanged(true);
mController.showTransientIndication("Test");
mController.hideTransientIndicationDelayed(0);
});
@@ -425,7 +416,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mController.setVisible(true);
mController.showTransientIndication("Test");
- mController.setDozing(true);
+ mStatusBarStateListener.onDozingChanged(true);
assertThat(mTextView.getText()).isEqualTo("Test");
assertThat(mTextView.getCurrentTextColor()).isEqualTo(Color.WHITE);
@@ -442,7 +433,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, message,
BiometricSourceType.FACE);
assertThat(mTextView.getText()).isEqualTo(message);
- mController.setDozing(true);
+ mStatusBarStateListener.onDozingChanged(true);
assertThat(mTextView.getText()).isNotEqualTo(message);
}
@@ -457,7 +448,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
"A message", BiometricSourceType.FACE);
assertThat(mTextView.getText()).isEqualTo(message);
- mController.setDozing(true);
+ mStatusBarStateListener.onDozingChanged(true);
assertThat(mTextView.getText()).isNotEqualTo(message);
}
@@ -528,8 +519,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
public void setDozing_noIBatteryCalls() throws RemoteException {
createController();
mController.setVisible(true);
- mController.setDozing(true);
- mController.setDozing(false);
+ mStatusBarStateListener.onDozingChanged(true);
+ mStatusBarStateListener.onDozingChanged(false);
verify(mIBatteryStats, never()).computeChargeTimeRemaining();
}
@@ -537,7 +528,6 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
public void updateMonitor_listener() {
createController();
verify(mKeyguardStateController).addCallback(eq(mController));
- verify(mStatusBarStateController).addCallback(eq(mController));
verify(mKeyguardUpdateMonitor, times(2)).registerCallback(any());
}
@@ -612,10 +602,24 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
0 /* maxChargingWattage */, true /* present */);
mController.getKeyguardCallback().onRefreshBatteryInfo(status);
- mController.setDozing(true);
+ mStatusBarStateListener.onDozingChanged(true);
mController.setVisible(true);
String percentage = NumberFormat.getPercentInstance().format(90 / 100f);
assertThat(mTextView.getText()).isEqualTo(percentage);
}
+
+ @Test
+ public void onRequireUnlockForNfc_showsRequireUnlockForNfcIndication() {
+ createController();
+ String message = mContext.getString(R.string.require_unlock_for_nfc);
+ mController.getKeyguardCallback().onRequireUnlockForNfc();
+ mController.setVisible(true);
+
+ assertThat(mTextView.getText()).isEqualTo(message);
+ }
+
+ private void sendUpdateDisclosureBroadcast() {
+ mBroadcastReceiver.onReceive(mContext, new Intent());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
index d131dceb70db..e16d4d71efe7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
@@ -36,16 +36,20 @@ import static org.junit.Assert.assertTrue;
import android.app.Notification;
import android.app.NotificationChannel;
+import android.os.Handler;
import android.os.UserHandle;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.util.Pair;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.util.DeviceConfigProxyFake;
import junit.framework.Assert;
@@ -55,38 +59,44 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public class AssistantFeedbackControllerTest extends SysuiTestCase {
- private static final int ON = 1;
- private static final int OFF = 0;
private static final String TEST_PACKAGE_NAME = "test_package";
private static final int TEST_UID = 1;
private AssistantFeedbackController mAssistantFeedbackController;
+ private DeviceConfigProxyFake mProxyFake;
+ private TestableLooper mTestableLooper;
+
private StatusBarNotification mSbn;
@Before
public void setUp() {
- mAssistantFeedbackController = new AssistantFeedbackController(mContext);
- switchSetting(ON);
+ mProxyFake = new DeviceConfigProxyFake();
+ mTestableLooper = TestableLooper.get(this);
+ mAssistantFeedbackController = new AssistantFeedbackController(
+ new Handler(mTestableLooper.getLooper()),
+ mContext, mProxyFake);
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
0, null, TEST_UID, 0, new Notification(),
UserHandle.CURRENT, null, 0);
}
@Test
- public void testUserControls_settingDisabled() {
- switchSetting(OFF);
+ public void testFlagDisabled() {
+ switchFlag("false");
assertFalse(mAssistantFeedbackController.isFeedbackEnabled());
}
@Test
- public void testUserControls_settingEnabled() {
+ public void testFlagEnabled() {
+ switchFlag("true");
assertTrue(mAssistantFeedbackController.isFeedbackEnabled());
}
@Test
- public void testFeedback_settingDisabled() {
- switchSetting(OFF);
+ public void testFeedback_flagDisabled() {
+ switchFlag("false");
assertEquals(STATUS_UNCHANGED, mAssistantFeedbackController.getFeedbackStatus(
getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED)));
assertFalse(mAssistantFeedbackController.showFeedbackIndicator(
@@ -95,6 +105,7 @@ public class AssistantFeedbackControllerTest extends SysuiTestCase {
@Test
public void testFeedback_changedImportance() {
+ switchFlag("true");
NotificationEntry entry = getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_UNCHANGED);
assertEquals(STATUS_PROMOTED, mAssistantFeedbackController.getFeedbackStatus(entry));
assertTrue(mAssistantFeedbackController.showFeedbackIndicator(entry));
@@ -110,6 +121,7 @@ public class AssistantFeedbackControllerTest extends SysuiTestCase {
@Test
public void testFeedback_changedRanking() {
+ switchFlag("true");
NotificationEntry entry =
getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_PROMOTED);
assertEquals(STATUS_PROMOTED, mAssistantFeedbackController.getFeedbackStatus(entry));
@@ -121,8 +133,8 @@ public class AssistantFeedbackControllerTest extends SysuiTestCase {
}
@Test
- public void testGetFeedbackResources_settingDisabled() {
- switchSetting(OFF);
+ public void testGetFeedbackResources_flagDisabled() {
+ switchFlag("false");
Assert.assertEquals(new Pair(0, 0), mAssistantFeedbackController.getFeedbackResources(
getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED)));
}
@@ -138,9 +150,10 @@ public class AssistantFeedbackControllerTest extends SysuiTestCase {
.build();
}
- private void switchSetting(int setting) {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.NOTIFICATION_FEEDBACK_ENABLED, setting);
- mAssistantFeedbackController.update(null);
+ private void switchFlag(String enabled) {
+ mProxyFake.setProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.ENABLE_NAS_FEEDBACK,
+ enabled, false);
+ mTestableLooper.processAllMessages();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
index 7eeae67c9fdf..e6287e7063d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
@@ -38,8 +38,8 @@ import android.widget.RemoteViews;
import androidx.palette.graphics.Palette;
import androidx.test.runner.AndroidJUnit4;
-import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.tests.R;
import org.junit.After;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 6b0a23f2b4ef..8375e7cceb28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -50,7 +50,6 @@ import android.widget.TextView;
import androidx.test.filters.SmallTest;
import androidx.test.filters.Suppress;
-import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -59,8 +58,10 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
-import com.android.systemui.statusbar.policy.InflatedSmartReplies;
-import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflater;
+import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
+import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
+import com.android.systemui.statusbar.policy.SmartReplyStateInflater;
+import com.android.systemui.tests.R;
import org.junit.Assert;
import org.junit.Before;
@@ -87,11 +88,24 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
@Mock private NotifRemoteViewCache mCache;
@Mock private ConversationNotificationProcessor mConversationNotificationProcessor;
- @Mock private InflatedSmartReplies mInflatedSmartReplies;
+ @Mock private InflatedSmartReplyState mInflatedSmartReplyState;
+ @Mock private InflatedSmartReplyViewHolder mInflatedSmartReplies;
+
+ private final SmartReplyStateInflater mSmartReplyStateInflater =
+ new SmartReplyStateInflater() {
+ @Override
+ public InflatedSmartReplyViewHolder inflateSmartReplyViewHolder(
+ Context sysuiContext, Context notifPackageContext, NotificationEntry entry,
+ InflatedSmartReplyState existingSmartReplyState,
+ InflatedSmartReplyState newSmartReplyState) {
+ return mInflatedSmartReplies;
+ }
- private final SmartRepliesAndActionsInflater mSmartRepliesAndActionsInflater =
- (sysuiContext, notifPackageContext, entry, existingRepliesAndAction) ->
- mInflatedSmartReplies;
+ @Override
+ public InflatedSmartReplyState inflateSmartReplyState(NotificationEntry entry) {
+ return mInflatedSmartReplyState;
+ }
+ };
@Before
public void setUp() throws Exception {
@@ -114,7 +128,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
mConversationNotificationProcessor,
mock(MediaFeatureFlag.class),
mock(Executor.class),
- mSmartRepliesAndActionsInflater);
+ mSmartReplyStateInflater);
}
@Test
@@ -130,7 +144,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
FLAG_CONTENT_VIEW_ALL,
builder,
mContext,
- mSmartRepliesAndActionsInflater);
+ mSmartReplyStateInflater);
verify(builder).createHeadsUpContentView(true);
}
@@ -147,7 +161,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
FLAG_CONTENT_VIEW_ALL,
builder,
mContext,
- mSmartRepliesAndActionsInflater);
+ mSmartReplyStateInflater);
verify(builder).createContentView(true);
}
@@ -386,7 +400,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
@Override
public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
- OnViewAppliedListener listener, OnClickHandler handler) {
+ OnViewAppliedListener listener, InteractionHandler handler) {
mHandler.post(() -> listener.onError(new RuntimeException("Failed to inflate async")));
return new CancellationSignal();
}
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 ababebdd807c..97313bafb8a8 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
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
+import android.content.Context;
import android.content.pm.LauncherApps;
import android.os.Handler;
import android.service.notification.NotificationListenerService;
@@ -80,7 +81,9 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationRowCom
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.InflatedSmartReplies;
+import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
+import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
+import com.android.systemui.statusbar.policy.SmartReplyStateInflater;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.time.FakeSystemClock;
@@ -140,7 +143,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
@Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
@Mock private NotificationRowComponent.Builder mNotificationRowComponentBuilder;
@Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
- @Mock private InflatedSmartReplies mInflatedSmartReplies;
+ @Mock private InflatedSmartReplyState mInflatedSmartReplyState;
+ @Mock private InflatedSmartReplyViewHolder mInflatedSmartReplies;
private StatusBarNotification mSbn;
private NotificationListenerService.RankingMap mRankingMap;
@@ -206,8 +210,21 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
mock(ConversationNotificationProcessor.class),
mock(MediaFeatureFlag.class),
mBgExecutor,
- (sysuiContext, notifPackageContext, entry, existingRepliesAndAction) ->
- mInflatedSmartReplies);
+ new SmartReplyStateInflater() {
+ @Override
+ public InflatedSmartReplyState inflateSmartReplyState(NotificationEntry entry) {
+ return mInflatedSmartReplyState;
+ }
+
+ @Override
+ public InflatedSmartReplyViewHolder inflateSmartReplyViewHolder(
+ Context sysuiContext, Context notifPackageContext,
+ NotificationEntry entry,
+ InflatedSmartReplyState existingSmartReplyState,
+ InflatedSmartReplyState newSmartReplyState) {
+ return mInflatedSmartReplies;
+ }
+ });
mRowContentBindStage = new RowContentBindStage(
binder,
mock(NotifInflationErrorManager.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 6fcc7fa9376c..f3813fcaf6fc 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
@@ -43,7 +43,6 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.widget.RemoteViews;
-import com.android.systemui.R;
import com.android.systemui.TestableDependency;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.media.MediaFeatureFlag;
@@ -67,7 +66,10 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.policy.InflatedSmartReplies;
+import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
+import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
+import com.android.systemui.statusbar.policy.SmartReplyStateInflater;
+import com.android.systemui.tests.R;
import com.android.systemui.wmshell.BubblesManager;
import com.android.systemui.wmshell.BubblesTestActivity;
import com.android.wm.shell.bubbles.Bubbles;
@@ -139,8 +141,7 @@ public class NotificationTestHelper {
mock(ConversationNotificationProcessor.class),
mock(MediaFeatureFlag.class),
mock(Executor.class),
- (sysuiContext, notifPackageContext, entry, existingRepliesAndAction) ->
- mock(InflatedSmartReplies.class));
+ new MockSmartReplyInflater());
contentBinder.setInflateSynchronously(true);
mBindStage = new RowContentBindStage(contentBinder,
mock(NotifInflationErrorManager.class),
@@ -464,4 +465,19 @@ public class NotificationTestHelper {
.setDesiredHeight(314)
.build();
}
+
+ private static class MockSmartReplyInflater implements SmartReplyStateInflater {
+ @Override
+ public InflatedSmartReplyState inflateSmartReplyState(NotificationEntry entry) {
+ return mock(InflatedSmartReplyState.class);
+ }
+
+ @Override
+ public InflatedSmartReplyViewHolder inflateSmartReplyViewHolder(Context sysuiContext,
+ Context notifPackageContext, NotificationEntry entry,
+ InflatedSmartReplyState existingSmartReplyState,
+ InflatedSmartReplyState newSmartReplyState) {
+ return mock(InflatedSmartReplyViewHolder.class);
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
index a147c8d0f121..45f7c5a6fdc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
@@ -24,10 +24,10 @@ import android.widget.RemoteViews;
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.tests.R;
import org.junit.Assert;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index fa253e62ef0a..421c6f4aab0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.phone;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.reset;
@@ -35,6 +37,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.tuner.TunerService;
@@ -57,6 +60,7 @@ public class DozeParametersTest extends SysuiTestCase {
@Mock private PowerManager mPowerManager;
@Mock private TunerService mTunerService;
@Mock private BatteryController mBatteryController;
+ @Mock private FeatureFlags mFeatureFlags;
@Before
public void setup() {
@@ -67,7 +71,8 @@ public class DozeParametersTest extends SysuiTestCase {
mAlwaysOnDisplayPolicy,
mPowerManager,
mBatteryController,
- mTunerService
+ mTunerService,
+ mFeatureFlags
);
}
@Test
@@ -111,4 +116,37 @@ public class DozeParametersTest extends SysuiTestCase {
assertThat(mDozeParameters.getAlwaysOn()).isFalse();
}
+
+ @Test
+ public void testControlUnlockedScreenOffAnimation_dozeAfterScreenOff_false() {
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
+ when(mFeatureFlags.useNewLockscreenAnimations()).thenReturn(true);
+
+ assertTrue(mDozeParameters.shouldControlUnlockedScreenOff());
+
+ // Trigger the setter for the current value.
+ mDozeParameters.setControlScreenOffAnimation(mDozeParameters.shouldControlScreenOff());
+
+ // We should have asked power manager not to doze after screen off no matter what, since
+ // we're animating and controlling screen off.
+ verify(mPowerManager).setDozeAfterScreenOff(eq(false));
+ }
+
+ @Test
+ public void testControlUnlockedScreenOffAnimationDisabled_dozeAfterScreenOff() {
+ when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
+ when(mFeatureFlags.useNewLockscreenAnimations()).thenReturn(false);
+
+ assertFalse(mDozeParameters.shouldControlUnlockedScreenOff());
+
+ // Trigger the setter for the current value.
+ mDozeParameters.setControlScreenOffAnimation(mDozeParameters.shouldControlScreenOff());
+
+ // We should have asked power manager to doze only if we're not controlling screen off
+ // normally.
+ verify(mPowerManager).setDozeAfterScreenOff(
+ eq(!mDozeParameters.shouldControlScreenOff()));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index ee1d758e7ae2..9b623f950505 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -393,10 +393,11 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
private void positionClock() {
mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight,
- mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mPreferredClockY,
+ mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight,
+ 0 /* userSwitchHeight */, mPreferredClockY, 0 /* userSwitchPreferredY */,
mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG, false /* bypassEnabled */,
- 0 /* unlockedStackScrollerPadding */, false /* udfpsEnrolled */,
- mQsExpansion, mCutoutTopInset);
+ 0 /* unlockedStackScrollerPadding */, false /* udfpsEnrolled */, mQsExpansion,
+ mCutoutTopInset);
mClockPositionAlgorithm.run(mClockPosition);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
index 0e775047aef7..6068f0d17720 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
@@ -51,21 +51,21 @@ public class KeyguardIndicationTextViewTest extends SysuiTestCase {
@Test
public void switchIndication_null_hideIndication() {
- mKeyguardIndicationTextView.switchIndication(null /* text */);
+ mKeyguardIndicationTextView.switchIndication(null /* text */, null);
assertThat(mKeyguardIndicationTextView.getText()).isEqualTo("");
}
@Test
public void switchIndication_emptyText_hideIndication() {
- mKeyguardIndicationTextView.switchIndication("" /* text */);
+ mKeyguardIndicationTextView.switchIndication("" /* text */, null);
assertThat(mKeyguardIndicationTextView.getText()).isEqualTo("");
}
@Test
public void switchIndication_newText_updateProperly() {
- mKeyguardIndicationTextView.switchIndication("test_indication" /* text */);
+ mKeyguardIndicationTextView.switchIndication("test_indication" /* text */, null);
assertThat(mKeyguardIndicationTextView.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mKeyguardIndicationTextView.getText()).isEqualTo("test_indication");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index c07ba723ab43..e788a1c0954b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -37,6 +37,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricSourceType;
import android.os.PowerManager;
+import android.os.UserManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.DisplayMetrics;
@@ -47,6 +48,8 @@ import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.constraintlayout.widget.ConstraintSet;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -57,7 +60,9 @@ import com.android.keyguard.KeyguardClockSwitchController;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardStatusViewController;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
+import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
@@ -193,6 +198,12 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
@Mock
+ private KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory;
+ @Mock
+ private KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
+ @Mock
+ private QSDetailDisplayer mQSDetailDisplayer;
+ @Mock
private KeyguardStatusViewComponent mKeyguardStatusViewComponent;
@Mock
private KeyguardClockSwitchController mKeyguardClockSwitchController;
@@ -213,11 +224,13 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
- private NotificationsQuickSettingsContainer mNotificationContainerParent;
- @Mock
private AmbientState mAmbientState;
+ @Mock
+ private UserManager mUserManager;
+
private NotificationPanelViewController mNotificationPanelViewController;
private View.AccessibilityDelegate mAccessibiltyDelegate;
+ private NotificationsQuickSettingsContainer mNotificationContainerParent;
@Before
public void setup() {
@@ -250,6 +263,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
when(mView.findViewById(R.id.keyguard_status_view))
.thenReturn(mock(KeyguardStatusView.class));
when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
+ mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null);
when(mView.findViewById(R.id.notification_container_parent))
.thenReturn(mNotificationContainerParent);
FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
@@ -299,11 +313,14 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mBiometricUnlockController, mStatusBarKeyguardViewManager,
mNotificationStackScrollLayoutController,
mKeyguardStatusViewComponentFactory,
+ mKeyguardQsUserSwitchComponentFactory,
+ mKeyguardUserSwitcherComponentFactory,
+ mQSDetailDisplayer,
mGroupManager,
mNotificationAreaController,
mAuthController,
- new QSDetailDisplayer(),
mScrimController,
+ mUserManager,
mMediaDataManager,
mAmbientState,
mFeatureFlags,
@@ -425,16 +442,11 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Test
public void testAllChildrenOfNotificationContainer_haveIds() {
- when(mNotificationContainerParent.getChildCount()).thenReturn(2);
when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
- View view1 = new View(mContext);
- view1.setId(1);
- when(mNotificationContainerParent.getChildAt(0)).thenReturn(view1);
-
- View view2 = mock(View.class);
- when(mNotificationContainerParent.getChildAt(1)).thenReturn(view2);
+ mNotificationContainerParent.addView(newViewWithId(1));
+ mNotificationContainerParent.addView(newViewWithId(View.NO_ID));
mNotificationPanelViewController.updateResources();
@@ -442,6 +454,51 @@ public class NotificationPanelViewTest extends SysuiTestCase {
assertThat(mNotificationContainerParent.getChildAt(1).getId()).isNotEqualTo(View.NO_ID);
}
+ @Test
+ public void testSinglePaneShadeLayout_isAlignedToParent() {
+ when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false);
+ mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame));
+ mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller));
+
+ mNotificationPanelViewController.updateResources();
+
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.clone(mNotificationContainerParent);
+ ConstraintSet.Layout qsFrameLayout = constraintSet.getConstraint(R.id.qs_frame).layout;
+ ConstraintSet.Layout stackScrollerLayout = constraintSet.getConstraint(
+ R.id.notification_stack_scroller).layout;
+ assertThat(qsFrameLayout.endToEnd).isEqualTo(ConstraintSet.PARENT_ID);
+ assertThat(stackScrollerLayout.startToStart).isEqualTo(ConstraintSet.PARENT_ID);
+ }
+
+ @Test
+ public void testSplitShadeLayout_isAlignedToGuideline() {
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+ when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+ mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame));
+ mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller));
+
+ mNotificationPanelViewController.updateResources();
+
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.clone(mNotificationContainerParent);
+ ConstraintSet.Layout qsFrameLayout = constraintSet.getConstraint(R.id.qs_frame).layout;
+ ConstraintSet.Layout stackScrollerLayout = constraintSet.getConstraint(
+ R.id.notification_stack_scroller).layout;
+ assertThat(qsFrameLayout.endToEnd).isEqualTo(R.id.qs_edge_guideline);
+ assertThat(stackScrollerLayout.startToStart).isEqualTo(R.id.qs_edge_guideline);
+ }
+
+ private View newViewWithId(int id) {
+ View view = new View(mContext);
+ view.setId(id);
+ ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ // required as cloning ConstraintSet fails if view doesn't have layout params
+ view.setLayoutParams(layoutParams);
+ return view;
+ }
+
private void onTouchEvent(MotionEvent ev) {
mTouchHandler.onTouch(mView, ev);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index cae488a561a2..253460db0d07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -97,6 +97,7 @@ import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.settings.brightness.BrightnessSlider;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -258,6 +259,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private DemoModeController mDemoModeController;
@Mock private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
@Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
+ @Mock private FeatureFlags mFeatureFlags;
private ShadeController mShadeController;
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private InitController mInitController = new InitController();
@@ -418,7 +420,8 @@ public class StatusBarTest extends SysuiTestCase {
mNotificationShadeDepthControllerLazy,
mStatusBarTouchableRegionManager,
mNotificationIconAreaController,
- mBrightnessSliderFactory);
+ mBrightnessSliderFactory,
+ mFeatureFlags);
when(mNotificationShadeWindowView.findViewById(R.id.lock_icon_container)).thenReturn(
mLockIconContainer);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index baa8e1f9b69d..ef3ef477c3a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -26,12 +26,12 @@ import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.settingslib.R;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.tests.R;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
index 32675c92d3f9..5e2fa98178f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
@@ -45,7 +45,7 @@ import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.statusbar.NotificationEntryHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions;
+import com.android.systemui.statusbar.policy.InflatedSmartReplyState.SuppressedActions;
import com.android.systemui.statusbar.policy.SmartReplyView.SmartActions;
import com.android.systemui.statusbar.policy.SmartReplyView.SmartReplies;
@@ -74,13 +74,12 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
@Mock private ActivityManagerWrapper mActivityManagerWrapper;
@Mock private PackageManagerWrapper mPackageManagerWrapper;
@Mock private DevicePolicyManagerWrapper mDevicePolicyManagerWrapper;
- @Mock private SmartRepliesAndActions mSmartRepliesAndActions;
@Mock private SmartReplyInflater mSmartReplyInflater;
@Mock private SmartActionInflater mSmartActionInflater;
private Icon mActionIcon;
private NotificationEntry mEntry;
- private SmartRepliesAndActionsInflaterImpl mSmartRepliesInflater;
+ private SmartReplyStateInflaterImpl mSmartReplyStateInflater;
@Before
@UiThreadTest
@@ -101,7 +100,7 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
when(mActivityManagerWrapper.isLockTaskKioskModeActive()).thenReturn(false);
- mSmartRepliesInflater = new SmartRepliesAndActionsInflaterImpl(
+ mSmartReplyStateInflater = new SmartReplyStateInflaterImpl(
mSmartReplyConstants,
mActivityManagerWrapper,
mPackageManagerWrapper,
@@ -118,11 +117,13 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
setupAppGeneratedSuggestions(smartReplies, smartActions);
when(mSmartReplyConstants.isEnabled()).thenReturn(false);
- SmartRepliesAndActions repliesAndActions =
- mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
+ InflatedSmartReplyState smartReplyState =
+ mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry);
- assertThat(repliesAndActions.smartReplies).isNull();
- assertThat(repliesAndActions.smartActions).isNull();
+ assertThat(smartReplyState.getSmartReplies()).isNull();
+ assertThat(smartReplyState.getSmartActions()).isNull();
+ assertThat(smartReplyState.getSuppressedActions()).isNull();
+ assertThat(smartReplyState.getHasPhishingAction()).isFalse();
}
@Test
@@ -134,11 +135,13 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
when(mSmartReplyConstants.isEnabled()).thenReturn(false);
- SmartRepliesAndActions repliesAndActions =
- mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
+ InflatedSmartReplyState smartReplyState =
+ mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry);
- assertThat(repliesAndActions.smartReplies).isNull();
- assertThat(repliesAndActions.smartActions).isNull();
+ assertThat(smartReplyState.getSmartReplies()).isNull();
+ assertThat(smartReplyState.getSmartActions()).isNull();
+ assertThat(smartReplyState.getSuppressedActions()).isNull();
+ assertThat(smartReplyState.getHasPhishingAction()).isFalse();
}
@Test
@@ -146,12 +149,15 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
setupAppGeneratedReplies(smartReplies);
- SmartRepliesAndActions repliesAndActions =
- mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
+ InflatedSmartReplyState smartReplyState =
+ mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry);
- assertThat(repliesAndActions.smartReplies.choices).isEqualTo(Arrays.asList(smartReplies));
- assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
- assertThat(repliesAndActions.smartActions).isNull();
+ assertThat(smartReplyState.getSmartReplies().choices)
+ .containsExactlyElementsIn(smartReplies).inOrder();
+ assertThat(smartReplyState.getSmartReplies().fromAssistant).isFalse();
+ assertThat(smartReplyState.getSmartActions()).isNull();
+ assertThat(smartReplyState.getSuppressedActions()).isNull();
+ assertThat(smartReplyState.getHasPhishingAction()).isFalse();
}
@Test
@@ -161,13 +167,17 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
createActions("Test Action 1", "Test Action 2");
setupAppGeneratedSuggestions(smartReplies, smartActions);
- SmartRepliesAndActions repliesAndActions =
- mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
-
- assertThat(repliesAndActions.smartReplies.choices).isEqualTo(Arrays.asList(smartReplies));
- assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
- assertThat(repliesAndActions.smartActions.actions).isEqualTo(smartActions);
- assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
+ InflatedSmartReplyState smartReplyState =
+ mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry);
+
+ assertThat(smartReplyState.getSmartReplies().choices)
+ .containsExactlyElementsIn(smartReplies).inOrder();
+ assertThat(smartReplyState.getSmartReplies().fromAssistant).isFalse();
+ assertThat(smartReplyState.getSmartActions().actions)
+ .containsExactlyElementsIn(smartActions).inOrder();
+ assertThat(smartReplyState.getSmartActions().fromAssistant).isFalse();
+ assertThat(smartReplyState.getSuppressedActions()).isNull();
+ assertThat(smartReplyState.getHasPhishingAction()).isFalse();
}
@Test
@@ -180,12 +190,15 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
.setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2"))
.build();
- SmartRepliesAndActions repliesAndActions =
- mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
+ InflatedSmartReplyState smartReplyState =
+ mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry);
- assertThat(repliesAndActions.smartReplies.choices).isEqualTo(mEntry.getSmartReplies());
- assertThat(repliesAndActions.smartReplies.fromAssistant).isTrue();
- assertThat(repliesAndActions.smartActions).isNull();
+ assertThat(smartReplyState.getSmartReplies().choices)
+ .containsExactlyElementsIn(mEntry.getSmartReplies()).inOrder();
+ assertThat(smartReplyState.getSmartReplies().fromAssistant).isTrue();
+ assertThat(smartReplyState.getSmartActions()).isNull();
+ assertThat(smartReplyState.getSuppressedActions()).isNull();
+ assertThat(smartReplyState.getHasPhishingAction()).isFalse();
}
@Test
@@ -197,11 +210,13 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
NotificationEntryHelper.modifyRanking(mEntry)
.setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2"))
.build();
- SmartRepliesAndActions repliesAndActions =
- mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
+ InflatedSmartReplyState smartReplyState =
+ mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry);
- assertThat(repliesAndActions.smartReplies).isNull();
- assertThat(repliesAndActions.smartActions).isNull();
+ assertThat(smartReplyState.getSmartReplies()).isNull();
+ assertThat(smartReplyState.getSmartActions()).isNull();
+ assertThat(smartReplyState.getSuppressedActions()).isNull();
+ assertThat(smartReplyState.getHasPhishingAction()).isFalse();
}
@Test
@@ -214,13 +229,50 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
.setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2"))
.build();
- SmartRepliesAndActions repliesAndActions =
- mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
+ InflatedSmartReplyState smartReplyState =
+ mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry);
+
+ assertThat(smartReplyState.getSmartReplies()).isNull();
+ assertThat(smartReplyState.getSmartActions().actions)
+ .containsExactlyElementsIn(mEntry.getSmartActions()).inOrder();
+ assertThat(smartReplyState.getSmartActions().fromAssistant).isTrue();
+ assertThat(smartReplyState.getSuppressedActions()).isNull();
+ assertThat(smartReplyState.getHasPhishingAction()).isFalse();
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_sysGeneratedPhishingSmartAction() {
+ // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+ // actions.
+ setupAppGeneratedReplies(null /* smartReplies */);
+
+ mNotification.actions = new Notification.Action[]{
+ createAction("Details"),
+ createActionBuilder("Reply").addRemoteInput(
+ new RemoteInput.Builder("key").build()).build()
+ };
+
+ modifyRanking(mEntry)
+ .setSmartActions(
+ createAction("Sys Smart Action 1"),
+ createActionBuilder("Sys Smart Action 2")
+ .setContextual(true)
+ .setSemanticAction(Notification.Action
+ .SEMANTIC_ACTION_CONVERSATION_IS_PHISHING)
+ .build())
+ .build();
- assertThat(repliesAndActions.smartReplies).isNull();
- assertThat(repliesAndActions.smartActions.actions)
- .isEqualTo(mEntry.getSmartActions());
- assertThat(repliesAndActions.smartActions.fromAssistant).isTrue();
+ InflatedSmartReplyState smartReplyState =
+ mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry);
+
+ assertThat(smartReplyState.getSmartReplies()).isNull();
+ assertThat(smartReplyState.getSmartActions().actions)
+ .containsExactlyElementsIn(mEntry.getSmartActions()).inOrder();
+ assertThat(smartReplyState.getSmartActions().fromAssistant).isTrue();
+ assertThat(smartReplyState.getSuppressedActions()).isNotNull();
+ assertThat(smartReplyState.getSuppressedActions().getSuppressedActionIndices())
+ .containsExactly(1);
+ assertThat(smartReplyState.getHasPhishingAction()).isTrue();
}
@Test
@@ -237,14 +289,17 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
.setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2"))
.build();
- SmartRepliesAndActions repliesAndActions =
- mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
-
- assertThat(repliesAndActions.smartReplies.choices)
- .isEqualTo(Arrays.asList(appGenSmartReplies));
- assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
- assertThat(repliesAndActions.smartActions.actions).isEqualTo(appGenSmartActions);
- assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
+ InflatedSmartReplyState smartReplyState =
+ mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry);
+
+ assertThat(smartReplyState.getSmartReplies().choices)
+ .containsExactlyElementsIn(Arrays.asList(appGenSmartReplies)).inOrder();
+ assertThat(smartReplyState.getSmartReplies().fromAssistant).isFalse();
+ assertThat(smartReplyState.getSmartActions().actions)
+ .containsExactlyElementsIn(appGenSmartActions).inOrder();
+ assertThat(smartReplyState.getSmartActions().fromAssistant).isFalse();
+ assertThat(smartReplyState.getSuppressedActions()).isNull();
+ assertThat(smartReplyState.getHasPhishingAction()).isFalse();
}
@Test
@@ -259,11 +314,13 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
.setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2"))
.build();
- SmartRepliesAndActions repliesAndActions =
- mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
+ InflatedSmartReplyState smartReplyState =
+ mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry);
- assertThat(repliesAndActions.smartActions).isNull();
- assertThat(repliesAndActions.smartReplies).isNull();
+ assertThat(smartReplyState.getSmartActions()).isNull();
+ assertThat(smartReplyState.getSmartReplies()).isNull();
+ assertThat(smartReplyState.getSuppressedActions()).isNull();
+ assertThat(smartReplyState.getHasPhishingAction()).isFalse();
}
@Test
@@ -281,13 +338,15 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
.setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2"))
.build();
- SmartRepliesAndActions repliesAndActions =
- mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
+ InflatedSmartReplyState smartReplyState =
+ mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry);
- assertThat(repliesAndActions.smartReplies.choices).isEqualTo(
- mEntry.getSmartReplies());
+ assertThat(smartReplyState.getSmartReplies().choices)
+ .containsExactlyElementsIn(mEntry.getSmartReplies()).inOrder();
// Since no apps are whitelisted no actions should be shown.
- assertThat(repliesAndActions.smartActions.actions).isEmpty();
+ assertThat(smartReplyState.getSmartActions().actions).isEmpty();
+ assertThat(smartReplyState.getSuppressedActions()).isNull();
+ assertThat(smartReplyState.getHasPhishingAction()).isFalse();
}
@Test
@@ -317,13 +376,14 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
.setSmartActions(actions)
.build();
- SmartRepliesAndActions repliesAndActions =
- mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
+ InflatedSmartReplyState smartReplyState =
+ mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry);
// Only the action for the whitelisted package should be allowed.
- assertThat(repliesAndActions.smartActions.actions.size()).isEqualTo(1);
- assertThat(repliesAndActions.smartActions.actions.get(0)).isEqualTo(
- mEntry.getSmartActions().get(0));
+ assertThat(smartReplyState.getSmartActions().actions)
+ .containsExactly(mEntry.getSmartActions().get(0));
+ assertThat(smartReplyState.getSuppressedActions()).isNull();
+ assertThat(smartReplyState.getHasPhishingAction()).isFalse();
}
@Test
@@ -340,14 +400,16 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
.setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2"))
.build();
- SmartRepliesAndActions repliesAndActions =
- mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
+ InflatedSmartReplyState smartReplyState =
+ mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry);
// We don't restrict replies or actions in screen pinning mode.
- assertThat(repliesAndActions.smartReplies.choices).isEqualTo(
- mEntry.getSmartReplies());
- assertThat(repliesAndActions.smartActions.actions).isEqualTo(
- mEntry.getSmartActions());
+ assertThat(smartReplyState.getSmartReplies().choices)
+ .containsExactlyElementsIn(mEntry.getSmartReplies()).inOrder();
+ assertThat(smartReplyState.getSmartActions().actions)
+ .containsExactlyElementsIn(mEntry.getSmartActions()).inOrder();
+ assertThat(smartReplyState.getSuppressedActions()).isNull();
+ assertThat(smartReplyState.getHasPhishingAction()).isFalse();
}
@Test
@@ -360,17 +422,24 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
List<Notification.Action> rightActions = Arrays.asList(
createAction("firstAction"),
createAction("secondAction"));
+ List<Integer> leftSuppressed = Arrays.asList(1, 2);
+ List<Integer> rightSuppressed = Arrays.asList(1, 2);
+ boolean leftPhishing = true;
+ boolean rightPhishing = true;
- SmartRepliesAndActions leftRepliesAndActions = new SmartRepliesAndActions(
+ InflatedSmartReplyState leftRepliesAndActions = new InflatedSmartReplyState(
new SmartReplies(leftReplies, null, null, false /* fromAssistant */),
- new SmartActions(leftActions, false /* fromAssistant */));
- SmartRepliesAndActions rightRepliesAndActions = new SmartRepliesAndActions(
+ new SmartActions(leftActions, false /* fromAssistant */),
+ new SuppressedActions(leftSuppressed),
+ leftPhishing);
+ InflatedSmartReplyState rightRepliesAndActions = new InflatedSmartReplyState(
new SmartReplies(rightReplies, null, null, false /* fromAssistant */),
- new SmartActions(rightActions, false /* fromAssistant */));
+ new SmartActions(rightActions, false /* fromAssistant */),
+ new SuppressedActions(rightSuppressed),
+ rightPhishing);
- assertThat(
- SmartRepliesAndActionsInflaterKt
- .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions))
+ assertThat(SmartReplyStateInflaterKt
+ .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions))
.isTrue();
}
@@ -384,16 +453,25 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
List<Notification.Action> rightActions = Arrays.asList(
createAction("firstAction"),
createAction("secondAction"));
+ List<Integer> leftSuppressed = Arrays.asList(1, 2);
+ List<Integer> rightSuppressed = Arrays.asList(1, 2);
+ boolean leftPhishing = true;
+ boolean rightPhishing = true;
- SmartRepliesAndActions leftRepliesAndActions = new SmartRepliesAndActions(
+ InflatedSmartReplyState leftRepliesAndActions = new InflatedSmartReplyState(
new SmartReplies(leftReplies, null, null, false /* fromAssistant */),
- new SmartActions(leftActions, false /* fromAssistant */));
- SmartRepliesAndActions rightRepliesAndActions = new SmartRepliesAndActions(
+ new SmartActions(leftActions, false /* fromAssistant */),
+ new SuppressedActions(leftSuppressed),
+ leftPhishing);
+ InflatedSmartReplyState rightRepliesAndActions = new InflatedSmartReplyState(
new SmartReplies(rightReplies, null, null, false /* fromAssistant */),
- new SmartActions(rightActions, false /* fromAssistant */));
+ new SmartActions(rightActions, false /* fromAssistant */),
+ new SuppressedActions(rightSuppressed),
+ rightPhishing);
- assertThat(SmartRepliesAndActionsInflaterKt.areSuggestionsSimilar(
- leftRepliesAndActions, rightRepliesAndActions)).isFalse();
+ assertThat(SmartReplyStateInflaterKt
+ .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions))
+ .isFalse();
}
@Test
@@ -406,16 +484,87 @@ public class InflatedSmartRepliesTest extends SysuiTestCase {
List<Notification.Action> rightActions = Arrays.asList(
createAction("firstAction"),
createAction("not secondAction"));
+ List<Integer> leftSuppressed = Arrays.asList(1, 2);
+ List<Integer> rightSuppressed = Arrays.asList(1, 2);
+ boolean leftPhishing = true;
+ boolean rightPhishing = true;
+
+ InflatedSmartReplyState leftRepliesAndActions = new InflatedSmartReplyState(
+ new SmartReplies(leftReplies, null, null, false /* fromAssistant */),
+ new SmartActions(leftActions, false /* fromAssistant */),
+ new SuppressedActions(leftSuppressed),
+ leftPhishing);
+ InflatedSmartReplyState rightRepliesAndActions = new InflatedSmartReplyState(
+ new SmartReplies(rightReplies, null, null, false /* fromAssistant */),
+ new SmartActions(rightActions, false /* fromAssistant */),
+ new SuppressedActions(rightSuppressed),
+ rightPhishing);
+
+ assertThat(SmartReplyStateInflaterKt
+ .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions))
+ .isFalse();
+ }
+
+ @Test
+ public void areSuggestionsSimilar_falseForDifferentSuppressedActions() {
+ List<CharSequence> leftReplies = createReplies("first reply", "second reply");
+ List<CharSequence> rightReplies = createReplies("first reply", "second reply");
+ List<Notification.Action> leftActions = Arrays.asList(
+ createAction("firstAction"),
+ createAction("secondAction"));
+ List<Notification.Action> rightActions = Arrays.asList(
+ createAction("firstAction"),
+ createAction("secondAction"));
+ List<Integer> leftSuppressed = Arrays.asList(1, 2);
+ List<Integer> rightSuppressed = Arrays.asList(1, 3);
+ boolean leftPhishing = true;
+ boolean rightPhishing = true;
+
+ InflatedSmartReplyState leftRepliesAndActions = new InflatedSmartReplyState(
+ new SmartReplies(leftReplies, null, null, false /* fromAssistant */),
+ new SmartActions(leftActions, false /* fromAssistant */),
+ new SuppressedActions(leftSuppressed),
+ leftPhishing);
+ InflatedSmartReplyState rightRepliesAndActions = new InflatedSmartReplyState(
+ new SmartReplies(rightReplies, null, null, false /* fromAssistant */),
+ new SmartActions(rightActions, false /* fromAssistant */),
+ new SuppressedActions(rightSuppressed),
+ rightPhishing);
+
+ assertThat(SmartReplyStateInflaterKt
+ .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions))
+ .isFalse();
+ }
+
+ @Test
+ public void areSuggestionsSimilar_falseForDifferentPhishing() {
+ List<CharSequence> leftReplies = createReplies("first reply", "second reply");
+ List<CharSequence> rightReplies = createReplies("first reply", "second reply");
+ List<Notification.Action> leftActions = Arrays.asList(
+ createAction("firstAction"),
+ createAction("secondAction"));
+ List<Notification.Action> rightActions = Arrays.asList(
+ createAction("firstAction"),
+ createAction("secondAction"));
+ List<Integer> leftSuppressed = Arrays.asList(1, 2);
+ List<Integer> rightSuppressed = Arrays.asList(1, 2);
+ boolean leftPhishing = true;
+ boolean rightPhishing = false;
- SmartRepliesAndActions leftRepliesAndActions = new SmartRepliesAndActions(
+ InflatedSmartReplyState leftRepliesAndActions = new InflatedSmartReplyState(
new SmartReplies(leftReplies, null, null, false /* fromAssistant */),
- new SmartActions(leftActions, false /* fromAssistant */));
- SmartRepliesAndActions rightRepliesAndActions = new SmartRepliesAndActions(
+ new SmartActions(leftActions, false /* fromAssistant */),
+ new SuppressedActions(leftSuppressed),
+ leftPhishing);
+ InflatedSmartReplyState rightRepliesAndActions = new InflatedSmartReplyState(
new SmartReplies(rightReplies, null, null, false /* fromAssistant */),
- new SmartActions(rightActions, false /* fromAssistant */));
+ new SmartActions(rightActions, false /* fromAssistant */),
+ new SuppressedActions(rightSuppressed),
+ rightPhishing);
- assertThat(SmartRepliesAndActionsInflaterKt.areSuggestionsSimilar(
- leftRepliesAndActions, rightRepliesAndActions)).isFalse();
+ assertThat(SmartReplyStateInflaterKt
+ .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions))
+ .isFalse();
}
private void setupAppGeneratedReplies(CharSequence[] smartReplies) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
index fc1a79105db1..e479882ac50a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
@@ -60,9 +60,9 @@ class KeyguardUserSwitcherAdapterTest : SysuiTestCase() {
@Mock
private lateinit var layoutInflater: LayoutInflater
@Mock
- private lateinit var keyguardUserSwitcher: KeyguardUserSwitcher
+ private lateinit var keyguardUserSwitcherController: KeyguardUserSwitcherController
- private lateinit var adapter: KeyguardUserSwitcher.KeyguardUserAdapter
+ private lateinit var adapter: KeyguardUserSwitcherController.KeyguardUserAdapter
private lateinit var picture: Bitmap
@Before
@@ -72,8 +72,11 @@ class KeyguardUserSwitcherAdapterTest : SysuiTestCase() {
mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, layoutInflater)
`when`(layoutInflater.inflate(anyInt(), any(ViewGroup::class.java), anyBoolean()))
.thenReturn(inflatedUserDetailItemView)
- adapter = KeyguardUserSwitcher.KeyguardUserAdapter(mContext, userSwitcherController,
- keyguardUserSwitcher)
+ adapter = KeyguardUserSwitcherController.KeyguardUserAdapter(
+ mContext,
+ mContext.resources,
+ LayoutInflater.from(mContext),
+ userSwitcherController, keyguardUserSwitcherController)
picture = UserIcons.convertToBitmap(mContext.getDrawable(R.drawable.ic_avatar_user))
}
@@ -118,11 +121,11 @@ class KeyguardUserSwitcherAdapterTest : SysuiTestCase() {
}
@Test
- fun shouldRemoveOnClickListener_currentUser_notGuestUser_oldViewIsSameType() {
+ fun shouldSetOnOnClickListener_currentUser_notGuestUser_oldViewIsSameType() {
val v: UserDetailItemView? = createViewFromSameType(
isCurrentUser = true, isGuestUser = false)
assertNotNull(v)
- verify(v)!!.setOnClickListener(null)
+ verify(v)!!.setOnClickListener(adapter)
}
@Test
@@ -150,11 +153,11 @@ class KeyguardUserSwitcherAdapterTest : SysuiTestCase() {
}
@Test
- fun shouldRemoveOnClickListener_currentUser_notGuestUser_oldViewIsDifferentType() {
+ fun shouldSetOnOnClickListener_currentUser_notGuestUser_oldViewIsDifferentType() {
val v: UserDetailItemView? = createViewFromDifferentType(
isCurrentUser = true, isGuestUser = false)
assertNotNull(v)
- verify(v)!!.setOnClickListener(null)
+ verify(v)!!.setOnClickListener(adapter)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index c67b055d1436..35fd40602d85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -430,6 +430,10 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
updateSignalStrength();
}
+ public void setImsType(int imsType) {
+ mMobileSignalController.setImsType(imsType);
+ }
+
public void setIsGsm(boolean gsm) {
when(mSignalStrength.isGsm()).thenReturn(gsm);
updateSignalStrength();
@@ -636,6 +640,14 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
}
}
+ protected void verifyLastCallStrength(int icon) {
+ ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
+ verify(mCallbackHandler, Mockito.atLeastOnce()).setCallIndicator(
+ iconArg.capture(),
+ anyInt());
+ assertEquals("Call strength, in status bar", icon, (int) iconArg.getValue().icon);
+ }
+
protected void assertNetworkNameEquals(String expected) {
assertEquals("Network name", expected, mMobileSignalController.getState().networkName);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 10166cb0c43f..fc1a08ac3874 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -15,6 +15,7 @@ import android.net.NetworkInfo;
import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
+import android.telephony.CellSignalStrength;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -243,6 +244,28 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest {
}
}
+ @Test
+ public void testCallStrengh() {
+ String testSsid = "Test SSID";
+ setWifiEnabled(true);
+ setWifiState(true, testSsid);
+ // Set the ImsType to be IMS_TYPE_WLAN
+ setImsType(2);
+ setWifiLevel(1);
+ for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
+ setWifiLevel(testLevel);
+ verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[testLevel]);
+ }
+ // Set the ImsType to be IMS_TYPE_WWAN
+ setImsType(1);
+ for (int testStrength = 0;
+ testStrength < CellSignalStrength.getNumSignalStrengthLevels(); testStrength++) {
+ setupDefaultSignal();
+ setLevel(testStrength);
+ verifyLastCallStrength(TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[testStrength]);
+ }
+ }
+
protected void setWifiActivity(int activity) {
// TODO: Not this, because this variable probably isn't sticking around.
mNetworkController.mWifiSignalController.setActivity(activity);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
index edaff5ff0f10..45828c3f73ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
@@ -32,13 +32,18 @@ import static com.android.systemui.theme.ThemeOverlayApplier.THEME_CATEGORIES;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
+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.verify;
import static org.mockito.Mockito.when;
+import android.content.om.FabricatedOverlay;
+import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.content.om.OverlayManager;
+import android.content.om.OverlayManagerTransaction;
import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -70,11 +75,12 @@ public class ThemeOverlayApplierTest extends SysuiTestCase {
private static final String TEST_DISABLED_PREFIX = "com.example.";
private static final String TEST_ENABLED_PREFIX = "com.example.enabled.";
- private static final Map<String, String> ALL_CATEGORIES_MAP = Maps.newArrayMap();
+ private static final Map<String, OverlayIdentifier> ALL_CATEGORIES_MAP = Maps.newArrayMap();
static {
for (String category : THEME_CATEGORIES) {
- ALL_CATEGORIES_MAP.put(category, TEST_DISABLED_PREFIX + category);
+ ALL_CATEGORIES_MAP.put(category,
+ new OverlayIdentifier(TEST_DISABLED_PREFIX + category));
}
}
@@ -87,6 +93,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase {
OverlayManager mOverlayManager;
@Mock
DumpManager mDumpManager;
+ @Mock
+ OverlayManagerTransaction.Builder mTransactionBuilder;
private ThemeOverlayApplier mManager;
@@ -94,7 +102,12 @@ public class ThemeOverlayApplierTest extends SysuiTestCase {
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
mManager = new ThemeOverlayApplier(mOverlayManager, MoreExecutors.directExecutor(),
- LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE, mDumpManager);
+ LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE, mDumpManager) {
+ @Override
+ protected OverlayManagerTransaction.Builder getTransactionBuilder() {
+ return mTransactionBuilder;
+ }
+ };
when(mOverlayManager.getOverlayInfosForTarget(ANDROID_PACKAGE, UserHandle.SYSTEM))
.thenReturn(Lists.newArrayList(
createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ACCENT_COLOR,
@@ -147,24 +160,26 @@ public class ThemeOverlayApplierTest extends SysuiTestCase {
@Test
public void allCategoriesSpecified_allEnabledExclusively() {
- mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, TEST_USER_HANDLES);
+ mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER_HANDLES);
+ verify(mOverlayManager).commit(any());
- for (String overlayPackage : ALL_CATEGORIES_MAP.values()) {
- verify(mOverlayManager).setEnabledExclusiveInCategory(overlayPackage, TEST_USER);
+ for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
+ verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
+ eq(TEST_USER.getIdentifier()));
}
}
@Test
public void allCategoriesSpecified_sysuiCategoriesAlsoAppliedToSysuiUser() {
- mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, TEST_USER_HANDLES);
+ mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER_HANDLES);
- for (Map.Entry<String, String> entry : ALL_CATEGORIES_MAP.entrySet()) {
+ for (Map.Entry<String, OverlayIdentifier> entry : ALL_CATEGORIES_MAP.entrySet()) {
if (SYSTEM_USER_CATEGORIES.contains(entry.getKey())) {
- verify(mOverlayManager).setEnabledExclusiveInCategory(
- entry.getValue(), UserHandle.SYSTEM);
+ verify(mTransactionBuilder).setEnabled(eq(entry.getValue()), eq(true),
+ eq(UserHandle.SYSTEM.getIdentifier()));
} else {
- verify(mOverlayManager, never()).setEnabledExclusiveInCategory(
- entry.getValue(), UserHandle.SYSTEM);
+ verify(mTransactionBuilder, never()).setEnabled(
+ eq(entry.getValue()), eq(true), eq(UserHandle.SYSTEM.getIdentifier()));
}
}
}
@@ -174,17 +189,34 @@ public class ThemeOverlayApplierTest extends SysuiTestCase {
Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
UserHandle newUserHandle = UserHandle.of(10);
userHandles.add(newUserHandle);
- mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, userHandles);
+ mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, userHandles);
+
+ for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
+ verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
+ eq(TEST_USER.getIdentifier()));
+ verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
+ eq(newUserHandle.getIdentifier()));
+ }
+ }
+
+ @Test
+ public void applyCurrentUserOverlays_createsPendingOverlays() {
+ Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
+ UserHandle newUserHandle = UserHandle.of(10);
+ userHandles.add(newUserHandle);
+ FabricatedOverlay[] pendingCreation = new FabricatedOverlay[] {
+ mock(FabricatedOverlay.class)
+ };
+ mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation, userHandles);
- for (String overlayPackage : ALL_CATEGORIES_MAP.values()) {
- verify(mOverlayManager).setEnabledExclusiveInCategory(overlayPackage, TEST_USER);
- verify(mOverlayManager).setEnabledExclusiveInCategory(overlayPackage, newUserHandle);
+ for (FabricatedOverlay overlay : pendingCreation) {
+ verify(mTransactionBuilder).registerFabricatedOverlay(eq(overlay));
}
}
@Test
public void allCategoriesSpecified_overlayManagerNotQueried() {
- mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, TEST_USER_HANDLES);
+ mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER_HANDLES);
verify(mOverlayManager, never())
.getOverlayInfosForTarget(anyString(), any(UserHandle.class));
@@ -192,48 +224,56 @@ public class ThemeOverlayApplierTest extends SysuiTestCase {
@Test
public void someCategoriesSpecified_specifiedEnabled_unspecifiedDisabled() {
- Map<String, String> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP);
+ Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP);
categoryToPackage.remove(OVERLAY_CATEGORY_ICON_SETTINGS);
categoryToPackage.remove(OVERLAY_CATEGORY_ICON_ANDROID);
- mManager.applyCurrentUserOverlays(categoryToPackage, TEST_USER_HANDLES);
+ mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER_HANDLES);
- for (String overlayPackage : categoryToPackage.values()) {
- verify(mOverlayManager).setEnabledExclusiveInCategory(overlayPackage, TEST_USER);
+ for (OverlayIdentifier overlayPackage : categoryToPackage.values()) {
+ verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
+ eq(TEST_USER.getIdentifier()));
}
- verify(mOverlayManager).setEnabled(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_SETTINGS,
- false, TEST_USER);
- verify(mOverlayManager).setEnabled(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_ANDROID,
- false, TEST_USER);
+ verify(mTransactionBuilder).setEnabled(
+ eq(new OverlayIdentifier(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_SETTINGS)),
+ eq(false), eq(TEST_USER.getIdentifier()));
+ verify(mTransactionBuilder).setEnabled(
+ eq(new OverlayIdentifier(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_ANDROID)),
+ eq(false), eq(TEST_USER.getIdentifier()));
}
@Test
public void zeroCategoriesSpecified_allDisabled() {
- mManager.applyCurrentUserOverlays(Maps.newArrayMap(), TEST_USER_HANDLES);
+ mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER_HANDLES);
for (String category : THEME_CATEGORIES) {
- verify(mOverlayManager).setEnabled(TEST_ENABLED_PREFIX + category, false, TEST_USER);
+ verify(mTransactionBuilder).setEnabled(
+ eq(new OverlayIdentifier(TEST_ENABLED_PREFIX + category)), eq(false),
+ eq(TEST_USER.getIdentifier()));
}
}
@Test
public void nonThemeCategorySpecified_ignored() {
- Map<String, String> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP);
- categoryToPackage.put("blah.category", "com.example.blah.category");
+ Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP);
+ categoryToPackage.put("blah.category", new OverlayIdentifier("com.example.blah.category"));
- mManager.applyCurrentUserOverlays(categoryToPackage, TEST_USER_HANDLES);
+ mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER_HANDLES);
- verify(mOverlayManager, never()).setEnabled("com.example.blah.category", false, TEST_USER);
- verify(mOverlayManager, never()).setEnabledExclusiveInCategory("com.example.blah.category",
- TEST_USER);
+ verify(mTransactionBuilder, never()).setEnabled(
+ eq(new OverlayIdentifier("com.example.blah.category")), eq(false),
+ eq(TEST_USER.getIdentifier()));
+ verify(mTransactionBuilder, never()).setEnabled(
+ eq(new OverlayIdentifier("com.example.blah.category")), eq(true),
+ eq(TEST_USER.getIdentifier()));
}
@Test
public void overlayManagerOnlyQueriedForUnspecifiedPackages() {
- Map<String, String> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP);
+ Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP);
categoryToPackage.remove(OVERLAY_CATEGORY_ICON_SETTINGS);
- mManager.applyCurrentUserOverlays(categoryToPackage, TEST_USER_HANDLES);
+ mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER_HANDLES);
verify(mOverlayManager).getOverlayInfosForTarget(SETTINGS_PACKAGE, UserHandle.SYSTEM);
verify(mOverlayManager, never()).getOverlayInfosForTarget(ANDROID_PACKAGE,
@@ -247,7 +287,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase {
private static OverlayInfo createOverlayInfo(String packageName, String targetPackageName,
String category, boolean enabled) {
- return new OverlayInfo(packageName, targetPackageName, null, category, "",
- enabled ? OverlayInfo.STATE_ENABLED : OverlayInfo.STATE_DISABLED, 0, 0, false);
+ return new OverlayInfo(packageName, null, targetPackageName, null, category, "",
+ enabled ? OverlayInfo.STATE_ENABLED : OverlayInfo.STATE_DISABLED, 0, 0, false,
+ false);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index d33fac0d3b25..aa385effa931 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -16,9 +16,8 @@
package com.android.systemui.theme;
-import static com.android.systemui.theme.ThemeOverlayApplier.MONET_ACCENT_COLOR_PACKAGE;
-import static com.android.systemui.theme.ThemeOverlayApplier.MONET_SYSTEM_PALETTE_PACKAGE;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR;
+import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_NEUTRAL_PALETTE;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
import static com.android.systemui.theme.ThemeOverlayController.USE_LOCK_SCREEN_WALLPAPER;
@@ -27,12 +26,15 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
+import android.content.om.FabricatedOverlay;
+import android.content.om.OverlayIdentifier;
import android.graphics.Color;
import android.os.Handler;
import android.os.UserHandle;
@@ -40,11 +42,13 @@ import android.os.UserManager;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
+import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.settings.SecureSettings;
@@ -56,7 +60,6 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
@@ -85,6 +88,8 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
private KeyguardStateController mKeyguardStateController;
@Mock
private DumpManager mDumpManager;
+ @Mock
+ private FeatureFlags mFeatureFlags;
@Captor
private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallback;
@Captor
@@ -93,10 +98,20 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ when(mFeatureFlags.isMonetEnabled()).thenReturn(true);
mThemeOverlayController = new ThemeOverlayController(null /* context */,
mBroadcastDispatcher, mBgHandler, mMainExecutor, mBgExecutor, mThemeOverlayApplier,
mSecureSettings, mWallpaperManager, mUserManager, mKeyguardStateController,
- mDumpManager);
+ mDumpManager, mFeatureFlags) {
+ @Nullable
+ @Override
+ protected FabricatedOverlay getOverlay(int color, int type) {
+ FabricatedOverlay overlay = mock(FabricatedOverlay.class);
+ when(overlay.getIdentifier())
+ .thenReturn(new OverlayIdentifier(Integer.toHexString(color | 0xff000000)));
+ return overlay;
+ }
+ };
mThemeOverlayController.start();
if (USE_LOCK_SCREEN_WALLPAPER) {
@@ -106,10 +121,6 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
verify(mWallpaperManager).addOnColorsChangedListener(mColorsListener.capture(), eq(null),
eq(UserHandle.USER_ALL));
verify(mDumpManager).registerDumpable(any(), any());
-
- List<Integer> colorList = List.of(Color.RED, Color.BLUE, 0x0CCCCC, 0x000CCC);
- when(mThemeOverlayApplier.getAvailableAccentColors()).thenReturn(colorList);
- when(mThemeOverlayApplier.getAvailableSystemColors()).thenReturn(colorList);
}
@Test
@@ -128,17 +139,19 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
- ArgumentCaptor<Map<String, String>> themeOverlays = ArgumentCaptor.forClass(Map.class);
+ ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
+ ArgumentCaptor.forClass(Map.class);
- verify(mThemeOverlayApplier).getAvailableSystemColors();
- verify(mThemeOverlayApplier).getAvailableAccentColors();
- verify(mThemeOverlayApplier).applyCurrentUserOverlays(themeOverlays.capture(), any());
+ verify(mThemeOverlayApplier)
+ .applyCurrentUserOverlays(themeOverlays.capture(), any(), any());
// Assert that we received the colors that we were expecting
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
- .isEqualTo(MONET_SYSTEM_PALETTE_PACKAGE + "FF0000");
+ .isEqualTo(new OverlayIdentifier("ffff0000"));
+ assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_NEUTRAL_PALETTE))
+ .isEqualTo(new OverlayIdentifier("ffff0000"));
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
- .isEqualTo(MONET_ACCENT_COLOR_PACKAGE + "0000FF");
+ .isEqualTo(new OverlayIdentifier("ff0000ff"));
// Should not ask again if changed to same value
mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
@@ -146,69 +159,49 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
}
@Test
- public void onWallpaperColorsChanged_whiteTheme() {
- WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.WHITE),
- Color.valueOf(Color.BLUE), null);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
- ArgumentCaptor<Map<String, String>> themeOverlays = ArgumentCaptor.forClass(Map.class);
-
- verify(mThemeOverlayApplier).applyCurrentUserOverlays(themeOverlays.capture(), any());
-
- // Assert that we received the colors that we were expecting
- assertThat(themeOverlays.getValue().containsKey(OVERLAY_CATEGORY_SYSTEM_PALETTE)).isFalse();
- }
-
- @Test
- public void onWallpaperColorsChanged_blackTheme() {
- WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.BLACK),
+ public void onWallpaperColorsChanged_preservesWallpaperPickerTheme() {
+ // Should ask for a new theme when wallpaper colors change
+ WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
- mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
- ArgumentCaptor<Map<String, String>> themeOverlays = ArgumentCaptor.forClass(Map.class);
- verify(mThemeOverlayApplier).applyCurrentUserOverlays(themeOverlays.capture(), any());
-
- // Assert that we received the colors that we were expecting
- assertThat(themeOverlays.getValue().containsKey(OVERLAY_CATEGORY_SYSTEM_PALETTE)).isFalse();
- }
+ String jsonString =
+ "{\"android.theme.customization.system_palette\":\"override.package.name\"}";
+ when(mSecureSettings.getStringForUser(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+ .thenReturn(jsonString);
- @Test
- public void onWallpaperColorsChanged_addsLeadingZerosToColors() {
- // Should ask for a new theme when wallpaper colors change
- WallpaperColors mainColors = new WallpaperColors(Color.valueOf(0x0CCCCC),
- Color.valueOf(0x000CCC), null);
mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
- ArgumentCaptor<Map<String, String>> themeOverlays = ArgumentCaptor.forClass(Map.class);
+ ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
+ ArgumentCaptor.forClass(Map.class);
- verify(mThemeOverlayApplier).applyCurrentUserOverlays(themeOverlays.capture(), any());
+ verify(mThemeOverlayApplier)
+ .applyCurrentUserOverlays(themeOverlays.capture(), any(), any());
// Assert that we received the colors that we were expecting
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
- .isEqualTo(MONET_SYSTEM_PALETTE_PACKAGE + "0CCCCC");
- assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
- .isEqualTo(MONET_ACCENT_COLOR_PACKAGE + "000CCC");
+ .isEqualTo(new OverlayIdentifier("override.package.name"));
}
@Test
- public void onWallpaperColorsChanged_preservesWallpaperPickerTheme() {
- // Should ask for a new theme when wallpaper colors change
+ public void onWallpaperColorsChanged_parsesColorsFromWallpaperPicker() {
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
String jsonString =
- "{\"android.theme.customization.system_palette\":\"override.package.name\"}";
+ "{\"android.theme.customization.system_palette\":\"00FF00\"}";
when(mSecureSettings.getStringForUser(
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
.thenReturn(jsonString);
mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
- ArgumentCaptor<Map<String, String>> themeOverlays = ArgumentCaptor.forClass(Map.class);
+ ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
+ ArgumentCaptor.forClass(Map.class);
- verify(mThemeOverlayApplier).getAvailableSystemColors();
- verify(mThemeOverlayApplier).getAvailableAccentColors();
- verify(mThemeOverlayApplier).applyCurrentUserOverlays(themeOverlays.capture(), any());
+ verify(mThemeOverlayApplier)
+ .applyCurrentUserOverlays(themeOverlays.capture(), any(), any());
// Assert that we received the colors that we were expecting
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
- .isEqualTo("override.package.name");
+ .isEqualTo(new OverlayIdentifier("ff00ff00"));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index c743fd07c492..365c62cddbdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -57,8 +57,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.statusbar.FeatureFlags;
import org.junit.Before;
import org.junit.Test;
@@ -88,7 +87,6 @@ public class ToastUITest extends SysuiTestCase {
private static final String TEXT = "Hello World";
private static final int MESSAGE_RES_ID = R.id.message;
- private FakeExecutor mFakeDelayableExecutor = new FakeExecutor(new FakeSystemClock());
private Context mContextSpy;
private ToastUI mToastUI;
@Mock private LayoutInflater mLayoutInflater;
@@ -99,6 +97,7 @@ public class ToastUITest extends SysuiTestCase {
@Mock private PluginManager mPluginManager;
@Mock private DumpManager mDumpManager;
@Mock private ToastLogger mToastLogger;
+ @Mock private FeatureFlags mFeatureFlags;
@Mock private ITransientNotificationCallback mCallback;
@Captor private ArgumentCaptor<View> mViewCaptor;
@@ -107,12 +106,9 @@ public class ToastUITest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
-
- // This is because inflate will result in WindowManager (WM) calls, which will fail since we
- // are mocking it, so we mock LayoutInflater with the view obtained before mocking WM.
- View view = ToastPresenter.getTextToastView(mContext, TEXT);
- when(mLayoutInflater.inflate(eq(TEXT_TOAST_LAYOUT), any())).thenReturn(view);
- mContext.addMockSystemService(LayoutInflater.class, mLayoutInflater);
+ when(mLayoutInflater.inflate(eq(TEXT_TOAST_LAYOUT), any())).thenReturn(
+ ToastPresenter.getTextToastView(mContext, TEXT));
+ when(mFeatureFlags.isToastStyleEnabled()).thenReturn(false);
mContext.addMockSystemService(WindowManager.class, mWindowManager);
mContextSpy = spy(mContext);
@@ -120,8 +116,8 @@ public class ToastUITest extends SysuiTestCase {
doReturn(mContextSpy).when(mContextSpy).createContextAsUser(any(), anyInt());
mToastUI = new ToastUI(mContextSpy, mCommandQueue, mNotificationManager,
- mAccessibilityManager, new ToastFactory(mPluginManager, mDumpManager),
- mFakeDelayableExecutor, mToastLogger);
+ mAccessibilityManager, new ToastFactory(mLayoutInflater, mPluginManager,
+ mDumpManager, mFeatureFlags), mToastLogger);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index c0af15b1f96d..203ece9532ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -19,8 +19,8 @@ import android.testing.LeakCheck;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.NoCallingIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
import java.util.List;
@@ -66,7 +66,7 @@ public class FakeStatusBarIconController extends BaseLeakChecker<IconManager>
}
@Override
- public void setNoCallingIcons(String slot, List<NoCallingIconState> states) {
+ public void setCallIndicatorIcons(String slot, List<CallIndicatorIconState> states) {
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 76269dda8245..f1fc0b7723fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -89,8 +89,6 @@ import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.Bubble;
@@ -297,7 +295,7 @@ public class BubblesTest extends SysuiTestCase {
mBubblesManager = new BubblesManager(
mContext,
- mBubbleController.getImpl(),
+ mBubbleController.asBubbles(),
mNotificationShadeWindowController,
mStatusBarStateController,
mShadeController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java
index f4d96a123624..ab329c894fb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java
@@ -20,7 +20,7 @@ import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
-import com.android.systemui.R;
+import com.android.systemui.tests.R;
/**
* Referenced by NotificationTestHelper#makeBubbleMetadata
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index 5340ff7e967c..9e10b21ce3b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -261,7 +261,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
mBubblesManager = new BubblesManager(
mContext,
- mBubbleController.getImpl(),
+ mBubbleController.asBubbles(),
mNotificationShadeWindowController,
mStatusBarStateController,
mShadeController,
diff --git a/packages/SystemUI/tools/lint/baseline.xml b/packages/SystemUI/tools/lint/baseline.xml
index 3e494032b8e2..9a2e3207a6f4 100644
--- a/packages/SystemUI/tools/lint/baseline.xml
+++ b/packages/SystemUI/tools/lint/baseline.xml
@@ -1262,17 +1262,6 @@
<issue
id="MergeRootFrame"
message="This `&lt;FrameLayout>` can be replaced with a `&lt;merge>` tag"
- errorLine1="&lt;FrameLayout"
- errorLine2="^">
- <location
- file="res/layout/pip_menu_activity.xml"
- line="16"
- column="1"/>
- </issue>
-
- <issue
- id="MergeRootFrame"
- message="This `&lt;FrameLayout>` can be replaced with a `&lt;merge>` tag"
errorLine1="&lt;FrameLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;"
errorLine2="^">
<location
diff --git a/packages/VpnDialogs/Android.bp b/packages/VpnDialogs/Android.bp
index 6f2f50c0ddd4..05135b2bebf9 100644
--- a/packages/VpnDialogs/Android.bp
+++ b/packages/VpnDialogs/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "VpnDialogs",
certificate: "platform",
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
index 4d95ef13a2a8..6dcad255eee4 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
@@ -20,14 +20,11 @@ import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.net.IConnectivityManager;
+import android.net.ConnectivityManager;
import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.SpannableStringBuilder;
@@ -45,7 +42,7 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity
private static final String TAG = "VpnDisconnected";
- private IConnectivityManager mService;
+ private ConnectivityManager mService;
private int mUserId;
private String mVpnPackage;
@@ -53,10 +50,9 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mService = IConnectivityManager.Stub.asInterface(
- ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
mUserId = UserHandle.myUserId();
- mVpnPackage = getAlwaysOnVpnPackage();
+ final ConnectivityManager cm = getSystemService(ConnectivityManager.class);
+ mVpnPackage = cm.getAlwaysOnVpnPackageForUser(mUserId);
if (mVpnPackage == null) {
finish();
return;
@@ -102,15 +98,6 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity
}
}
- private String getAlwaysOnVpnPackage() {
- try {
- return mService.getAlwaysOnVpnPackage(mUserId);
- } catch (RemoteException e) {
- Log.e(TAG, "Can't getAlwaysOnVpnPackage()", e);
- return null;
- }
- }
-
private CharSequence getVpnLabel() {
try {
return VpnConfig.getVpnLabel(this, mVpnPackage);
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index e66f2cc17a7f..aab01d03b96d 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -18,15 +18,12 @@ package com.android.vpndialogs;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import android.net.IConnectivityManager;
+import android.net.ConnectivityManager;
import android.net.VpnManager;
import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.Html;
@@ -48,7 +45,8 @@ public class ConfirmDialog extends AlertActivity
private String mPackage;
- private IConnectivityManager mService;
+ private ConnectivityManager mCm; // TODO: switch entirely to VpnManager once VPN code moves
+ private VpnManager mVm;
public ConfirmDialog() {
this(VpnManager.TYPE_VPN_SERVICE);
@@ -62,10 +60,10 @@ public class ConfirmDialog extends AlertActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPackage = getCallingPackage();
- mService = IConnectivityManager.Stub.asInterface(
- ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+ mCm = getSystemService(ConnectivityManager.class);
+ mVm = getSystemService(VpnManager.class);
- if (prepareVpn()) {
+ if (mVm.prepareVpn(mPackage, null, UserHandle.myUserId())) {
setResult(RESULT_OK);
finish();
return;
@@ -74,7 +72,7 @@ public class ConfirmDialog extends AlertActivity
finish();
return;
}
- final String alwaysOnVpnPackage = getAlwaysOnVpnPackage();
+ final String alwaysOnVpnPackage = mCm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId());
// Can't prepare new vpn app when another vpn is always-on
if (alwaysOnVpnPackage != null && !alwaysOnVpnPackage.equals(mPackage)) {
finish();
@@ -97,24 +95,6 @@ public class ConfirmDialog extends AlertActivity
button.setFilterTouchesWhenObscured(true);
}
- private String getAlwaysOnVpnPackage() {
- try {
- return mService.getAlwaysOnVpnPackage(UserHandle.myUserId());
- } catch (RemoteException e) {
- Log.e(TAG, "fail to call getAlwaysOnVpnPackage", e);
- // Fallback to null to show the dialog
- return null;
- }
- }
-
- private boolean prepareVpn() {
- try {
- return mService.prepareVpn(mPackage, null, UserHandle.myUserId());
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
- }
-
private CharSequence getVpnLabel() {
try {
return VpnConfig.getVpnLabel(this, mPackage);
@@ -146,10 +126,10 @@ public class ConfirmDialog extends AlertActivity
@Override
public void onClick(DialogInterface dialog, int which) {
try {
- if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) {
+ if (mVm.prepareVpn(null, mPackage, UserHandle.myUserId())) {
// Authorize this app to initiate VPN connections in the future without user
// intervention.
- mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
+ mVm.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
setResult(RESULT_OK);
}
} catch (Exception e) {
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index 01dca7e30e64..1fc74f704f62 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -16,13 +16,11 @@
package com.android.vpndialogs;
-import android.content.Context;
import android.content.DialogInterface;
-import android.net.IConnectivityManager;
+import android.net.VpnManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
@@ -41,7 +39,7 @@ public class ManageDialog extends AlertActivity implements
private VpnConfig mConfig;
- private IConnectivityManager mService;
+ private VpnManager mVm;
private TextView mDuration;
private TextView mDataTransmitted;
@@ -55,11 +53,9 @@ public class ManageDialog extends AlertActivity implements
super.onCreate(savedInstanceState);
try {
+ mVm = getSystemService(VpnManager.class);
- mService = IConnectivityManager.Stub.asInterface(
- ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
-
- mConfig = mService.getVpnConfig(UserHandle.myUserId());
+ mConfig = mVm.getVpnConfig(UserHandle.myUserId());
// mConfig can be null if we are a restricted user, in that case don't show this dialog
if (mConfig == null) {
@@ -118,9 +114,9 @@ public class ManageDialog extends AlertActivity implements
} else if (which == DialogInterface.BUTTON_NEUTRAL) {
final int myUserId = UserHandle.myUserId();
if (mConfig.legacy) {
- mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
+ mVm.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
} else {
- mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
+ mVm.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
}
}
} catch (Exception e) {
diff --git a/packages/WAPPushManager/Android.bp b/packages/WAPPushManager/Android.bp
index 0b62c72c749f..71ec81909579 100644
--- a/packages/WAPPushManager/Android.bp
+++ b/packages/WAPPushManager/Android.bp
@@ -1,5 +1,24 @@
// Copyright 2007-2008 The Android Open Source Project
+package {
+ default_applicable_licenses: [
+ "frameworks_base_packages_WAPPushManager_license",
+ ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_packages_WAPPushManager_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
android_app {
name: "WAPPushManager",
defaults: ["platform_app_defaults"],
diff --git a/packages/WAPPushManager/tests/Android.bp b/packages/WAPPushManager/tests/Android.bp
index 25c6121324ea..0a179383fab6 100644
--- a/packages/WAPPushManager/tests/Android.bp
+++ b/packages/WAPPushManager/tests/Android.bp
@@ -12,6 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_packages_WAPPushManager_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: [
+ "frameworks_base_packages_WAPPushManager_license",
+ ],
+}
+
android_test {
name: "WAPPushManagerTests",
libs: [
diff --git a/packages/WallpaperBackup/Android.bp b/packages/WallpaperBackup/Android.bp
index e52d53eb4082..d142f25c6a62 100644
--- a/packages/WallpaperBackup/Android.bp
+++ b/packages/WallpaperBackup/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "WallpaperBackup",
defaults: ["platform_app_defaults"],
diff --git a/packages/WallpaperCropper/Android.bp b/packages/WallpaperCropper/Android.bp
index df97a3c08156..23ec23c9c51a 100644
--- a/packages/WallpaperCropper/Android.bp
+++ b/packages/WallpaperCropper/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "WallpaperCropper",
defaults: ["platform_app_defaults"],
diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk
index cdc09034e75b..99dfd9eab891 100644
--- a/packages/overlays/Android.mk
+++ b/packages/overlays/Android.mk
@@ -16,6 +16,9 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := frameworks-base-overlays
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
LOCAL_REQUIRED_MODULES := \
AccentColorBlackOverlay \
AccentColorCinnamonOverlay \
@@ -84,6 +87,9 @@ include $(BUILD_PHONY_PACKAGE)
include $(CLEAR_VARS)
LOCAL_MODULE := frameworks-base-overlays-debug
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
include $(BUILD_PHONY_PACKAGE)
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/overlays/tests/Android.bp b/packages/overlays/tests/Android.bp
index 728ebe21533f..b781602399a3 100644
--- a/packages/overlays/tests/Android.bp
+++ b/packages/overlays/tests/Android.bp
@@ -11,6 +11,16 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "OverlayTests",
certificate: "platform",
diff --git a/packages/services/CameraExtensionsProxy/Android.bp b/packages/services/CameraExtensionsProxy/Android.bp
index 54b7453da585..e2e4af2a35f4 100644
--- a/packages/services/CameraExtensionsProxy/Android.bp
+++ b/packages/services/CameraExtensionsProxy/Android.bp
@@ -18,6 +18,7 @@ android_app {
name: "CameraExtensionsProxy",
srcs: ["src/**/*.java"],
libs: ["androidx.camera.extensions.stub"],
+ optional_uses_libs: ["androidx.camera.extensions.impl"],
platform_apis: true,
certificate: "platform",
}
diff --git a/packages/services/PacProcessor/Android.bp b/packages/services/PacProcessor/Android.bp
index 1fd972ce4b3d..dd50e25547c3 100644
--- a/packages/services/PacProcessor/Android.bp
+++ b/packages/services/PacProcessor/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "PacProcessor",
srcs: ["src/**/*.java"],
diff --git a/packages/services/Proxy/Android.bp b/packages/services/Proxy/Android.bp
index d93c9f8d9941..4d76db725d1f 100644
--- a/packages/services/Proxy/Android.bp
+++ b/packages/services/Proxy/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "ProxyHandler",
srcs: ["src/**/*.java"],
diff --git a/proto/Android.bp b/proto/Android.bp
index 86d8ee3a662f..a5e13350ebd2 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library_static {
name: "framework-protos",
host_supported: true,
diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk
index 5c3f2d83fb00..e41073bcdb76 100644
--- a/rs/jni/Android.mk
+++ b/rs/jni/Android.mk
@@ -28,6 +28,9 @@ LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
LOCAL_MODULE:= librs_jni
+LOCAL_LICENSE_KINDS:= SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS:= notice
+LOCAL_NOTICE_FILE:= $(LOCAL_PATH)/../../NOTICE
LOCAL_MODULE_TAGS := optional
LOCAL_REQUIRED_MODULES := libRS
diff --git a/samples/demo/haptic-assessment/Android.bp b/samples/demo/haptic-assessment/Android.bp
index 1c006091a755..4d54550f3762 100644
--- a/samples/demo/haptic-assessment/Android.bp
+++ b/samples/demo/haptic-assessment/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "HapticAssessment",
manifest: "AndroidManifest.xml",
@@ -31,4 +40,4 @@ android_app {
"res",
],
dxflags: ["--multi-dex"],
-} \ No newline at end of file
+}
diff --git a/sax/tests/saxtests/Android.bp b/sax/tests/saxtests/Android.bp
index 5889f769a645..cbd19c36dd73 100644
--- a/sax/tests/saxtests/Android.bp
+++ b/sax/tests/saxtests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksSaxTests",
// Include all test java files.
diff --git a/services/Android.bp b/services/Android.bp
index 61591c2c29bd..f6bb157312e0 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services-main-sources",
srcs: ["java/**/*.java"],
@@ -6,7 +15,7 @@ filegroup {
}
filegroup {
- name: "services-all-sources",
+ name: "services-non-updatable-sources",
srcs: [
":services.core-sources",
":services.core-sources-am-wm",
@@ -33,11 +42,20 @@ filegroup {
":services.startop.iorap-sources",
":services.systemcaptions-sources",
":services.translation-sources",
+ ":services.texttospeech-sources",
":services.usage-sources",
":services.usb-sources",
":services.voiceinteraction-sources",
":services.wifi-sources",
- ":service-media-s-sources", // TODO (b/177640454)
+ ],
+ visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "services-all-sources",
+ srcs: [
+ ":services-non-updatable-sources",
+ ":service-media-s-sources",
":service-permission-sources",
":service-statsd-sources",
],
@@ -83,6 +101,7 @@ java_library {
"services.startop",
"services.systemcaptions",
"services.translation",
+ "services.texttospeech",
"services.usage",
"services.usb",
"services.voiceinteraction",
@@ -123,9 +142,8 @@ filegroup {
// API stub
// =============================================================
-droidstubs {
- name: "services-stubs.sources",
- srcs: [":services-all-sources"],
+stubs_defaults {
+ name: "services-stubs-default",
installable: false,
args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)" +
" --hide-annotation android.annotation.Hide" +
@@ -135,7 +153,13 @@ droidstubs {
" --hide DeprecationMismatch" +
" --hide HiddenTypedefConstant",
visibility: ["//visibility:private"],
- filter_packages: ["com.android."],
+ filter_packages: ["com.android."]
+}
+
+droidstubs {
+ name: "services-stubs.sources",
+ srcs: [":services-all-sources"],
+ defaults: ["services-stubs-default"],
check_api: {
current: {
api_file: "api/current.txt",
@@ -181,3 +205,34 @@ java_library {
dir: "apistubs/android/system-server",
},
}
+
+droidstubs {
+ name: "services-non-updatable-stubs.sources",
+ srcs: [":services-non-updatable-sources"],
+ defaults: ["services-stubs-default"],
+ check_api: {
+ current: {
+ api_file: "api/non-updatable-current.txt",
+ removed_api_file: "api/non-updatable-removed.txt",
+ },
+ api_lint: {
+ enabled: true,
+ new_since: ":android-non-updatable.api.system-server.latest",
+ baseline_file: "api/non-updatable-lint-baseline.txt",
+ },
+ },
+ dists: [
+ {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/system-server/api",
+ dest: "android-non-updatable.txt",
+ tag: ".api.txt"
+ },
+ {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/system-server/api",
+ dest: "android-non-updatable-removed.txt",
+ tag: ".removed-api.txt",
+ },
+ ]
+} \ No newline at end of file
diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp
index 65313fcfc426..1698e9a70245 100644
--- a/services/accessibility/Android.bp
+++ b/services/accessibility/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.accessibility-sources",
srcs: ["java/**/*.java"],
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 2626654aabf2..a9e8ea03eab8 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -372,12 +372,11 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
- final boolean isTouchableDisplay = mWindowManagerService.isTouchableDisplay(displayId);
synchronized (mLock) {
if (mSecurityPolicy.canPerformGestures(this)) {
MotionEventInjector motionEventInjector =
mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId);
- if (motionEventInjector != null && isTouchableDisplay) {
+ if (mWindowManagerService.isTouchOrFaketouchDevice()) {
motionEventInjector.injectEvents(
gestureSteps.getList(), mServiceInterface, sequence, displayId);
} else {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
index b36626f9d736..0d323fb186c9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
@@ -23,6 +23,9 @@ import android.os.Process;
import android.os.ShellCommand;
import android.os.UserHandle;
+import com.android.server.LocalServices;
+import com.android.server.wm.WindowManagerInternal;
+
import java.io.PrintWriter;
/**
@@ -31,11 +34,13 @@ import java.io.PrintWriter;
final class AccessibilityShellCommand extends ShellCommand {
final @NonNull AccessibilityManagerService mService;
final @NonNull SystemActionPerformer mSystemActionPerformer;
+ final @NonNull WindowManagerInternal mWindowManagerService;
AccessibilityShellCommand(@NonNull AccessibilityManagerService service,
@NonNull SystemActionPerformer systemActionPerformer) {
mService = service;
mSystemActionPerformer = systemActionPerformer;
+ mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
}
@Override
@@ -53,6 +58,10 @@ final class AccessibilityShellCommand extends ShellCommand {
case "call-system-action": {
return runCallSystemAction();
}
+ case "start-trace":
+ return startTrace();
+ case "stop-trace":
+ return stopTrace();
}
return -1;
}
@@ -98,6 +107,20 @@ final class AccessibilityShellCommand extends ShellCommand {
return -1;
}
+ private int startTrace() {
+ WindowManagerInternal.AccessibilityControllerInternal ac =
+ mWindowManagerService.getAccessibilityController();
+ ac.startTrace();
+ return 0;
+ }
+
+ private int stopTrace() {
+ WindowManagerInternal.AccessibilityControllerInternal ac =
+ mWindowManagerService.getAccessibilityController();
+ ac.stopTrace();
+ return 0;
+ }
+
private Integer parseUserId() {
final String option = getNextOption();
if (option != null) {
@@ -123,5 +146,9 @@ final class AccessibilityShellCommand extends ShellCommand {
pw.println(" Get whether binding to services provided by instant apps is allowed.");
pw.println(" call-system-action <ACTION_ID>");
pw.println(" Calls the system action with the given action id.");
+ pw.println(" start-trace");
+ pw.println(" Start the debug tracing.");
+ pw.println(" stop-trace");
+ pw.println(" Stop the debug tracing.");
}
-} \ No newline at end of file
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 22efd3718d7a..eb30fdeddd90 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -118,6 +118,9 @@ class AccessibilityUserState {
private int mNonInteractiveUiTimeout = 0;
private int mInteractiveUiTimeout = 0;
private int mLastSentClientState = -1;
+
+ /** {@code true} if the device config supports magnification area. */
+ private final boolean mSupportMagnificationArea;
// The magnification mode of default display.
private int mMagnificationMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
// The magnification capabilities used to know magnification mode could be switched.
@@ -138,6 +141,10 @@ class AccessibilityUserState {
private int mSoftKeyboardShowMode = SHOW_MODE_AUTO;
boolean isValidMagnificationModeLocked() {
+ if (!mSupportMagnificationArea
+ && mMagnificationMode == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
+ return false;
+ }
return (mMagnificationCapabilities & mMagnificationMode) != 0;
}
@@ -156,6 +163,8 @@ class AccessibilityUserState {
R.color.accessibility_focus_highlight_color);
mFocusStrokeWidth = mFocusStrokeWidthDefaultValue;
mFocusColor = mFocusColorDefaultValue;
+ mSupportMagnificationArea = mContext.getResources().getBoolean(
+ R.bool.config_magnification_area);
}
boolean isHandlingAccessibilityEventsLocked() {
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index 25f4abe2cb14..903a07140eae 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -19,12 +19,12 @@ package com.android.server.accessibility.gestures;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
-import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
@@ -147,15 +147,15 @@ class GestureManifold implements GestureMatcher.StateChangeListener {
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP, this));
mMultiFingerGestures.add(
- new MultiFingerMultiTapAndHold(
- mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD, this));
- mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP, this));
mMultiFingerGestures.add(
new MultiFingerMultiTapAndHold(
mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD, this));
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP, this));
+ mMultiFingerGestures.add(
+ new MultiFingerMultiTapAndHold(
+ mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD, this));
// Three-finger taps.
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 3, 1, GESTURE_3_FINGER_SINGLE_TAP, this));
diff --git a/services/api/non-updatable-current.txt b/services/api/non-updatable-current.txt
new file mode 100644
index 000000000000..3c72d38927bc
--- /dev/null
+++ b/services/api/non-updatable-current.txt
@@ -0,0 +1,55 @@
+// Signature format: 2.0
+package com.android.server {
+
+ public final class LocalManagerRegistry {
+ method public static <T> void addManager(@NonNull Class<T>, @NonNull T);
+ method @Nullable public static <T> T getManager(@NonNull Class<T>);
+ }
+
+ public abstract class SystemService {
+ ctor public SystemService(@NonNull android.content.Context);
+ method @NonNull public final android.content.Context getContext();
+ method public boolean isUserSupported(@NonNull com.android.server.SystemService.TargetUser);
+ method public void onBootPhase(int);
+ method public abstract void onStart();
+ method public void onUserStarting(@NonNull com.android.server.SystemService.TargetUser);
+ method public void onUserStopped(@NonNull com.android.server.SystemService.TargetUser);
+ method public void onUserStopping(@NonNull com.android.server.SystemService.TargetUser);
+ method public void onUserSwitching(@Nullable com.android.server.SystemService.TargetUser, @NonNull com.android.server.SystemService.TargetUser);
+ method public void onUserUnlocked(@NonNull com.android.server.SystemService.TargetUser);
+ method public void onUserUnlocking(@NonNull com.android.server.SystemService.TargetUser);
+ method protected final void publishBinderService(@NonNull String, @NonNull android.os.IBinder);
+ method protected final void publishBinderService(@NonNull String, @NonNull android.os.IBinder, boolean);
+ field public static final int PHASE_ACTIVITY_MANAGER_READY = 550; // 0x226
+ field public static final int PHASE_BOOT_COMPLETED = 1000; // 0x3e8
+ field public static final int PHASE_DEVICE_SPECIFIC_SERVICES_READY = 520; // 0x208
+ field public static final int PHASE_LOCK_SETTINGS_READY = 480; // 0x1e0
+ field public static final int PHASE_SYSTEM_SERVICES_READY = 500; // 0x1f4
+ field public static final int PHASE_THIRD_PARTY_APPS_CAN_START = 600; // 0x258
+ field public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100; // 0x64
+ }
+
+ public static final class SystemService.TargetUser {
+ method @NonNull public android.os.UserHandle getUserHandle();
+ }
+
+}
+
+package com.android.server.role {
+
+ public interface RoleServicePlatformHelper {
+ method @NonNull public String computePackageStateHash(int);
+ method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getLegacyRoleState(int);
+ }
+
+}
+
+package com.android.server.wifi {
+
+ public class SupplicantManager {
+ method public static void start();
+ method public static void stop();
+ }
+
+}
+
diff --git a/services/api/non-updatable-lint-baseline.txt b/services/api/non-updatable-lint-baseline.txt
new file mode 100644
index 000000000000..b46d21edd44c
--- /dev/null
+++ b/services/api/non-updatable-lint-baseline.txt
@@ -0,0 +1,9 @@
+// Baseline format: 1.0
+NotCloseable: com.android.server.wifi.SupplicantManager:
+ Classes that release resources (stop()) should implement AutoClosable and CloseGuard: class com.android.server.wifi.SupplicantManager
+
+
+ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder):
+ Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder)}
+ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder, boolean):
+ Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder,boolean)}
diff --git a/services/api/non-updatable-removed.txt b/services/api/non-updatable-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/services/api/non-updatable-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/appprediction/Android.bp b/services/appprediction/Android.bp
index bc43db1fa116..53c461b23651 100644
--- a/services/appprediction/Android.bp
+++ b/services/appprediction/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.appprediction-sources",
srcs: ["java/**/*.java"],
diff --git a/services/appwidget/Android.bp b/services/appwidget/Android.bp
index e46e5c82a917..8119073fdf7f 100644
--- a/services/appwidget/Android.bp
+++ b/services/appwidget/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.appwidget-sources",
srcs: ["java/**/*.java"],
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index e4a86c3744ce..809304bb24ae 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -2624,12 +2624,26 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
info.minWidth = value != null ? value.data : 0;
value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
info.minHeight = value != null ? value.data : 0;
+
value = sa.peekValue(
com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
info.minResizeWidth = value != null ? value.data : info.minWidth;
value = sa.peekValue(
com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
info.minResizeHeight = value != null ? value.data : info.minHeight;
+
+ value = sa.peekValue(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_maxResizeWidth);
+ info.maxResizeWidth = value != null ? value.data : 0;
+ value = sa.peekValue(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_maxResizeHeight);
+ info.maxResizeHeight = value != null ? value.data : 0;
+
+ info.targetCellWidth = sa.getInt(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_targetCellWidth, 0);
+ info.targetCellHeight = sa.getInt(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_targetCellHeight, 0);
+
info.updatePeriodMillis = sa.getInt(
com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
info.initialLayout = sa.getResourceId(
@@ -2647,6 +2661,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
info.icon = activityInfo.getIconResource();
info.previewImage = sa.getResourceId(
com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
+ info.previewLayout = sa.getResourceId(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_previewLayout, 0);
info.autoAdvanceViewId = sa.getResourceId(
com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
info.resizeMode = sa.getInt(
diff --git a/services/autofill/Android.bp b/services/autofill/Android.bp
index c44806642427..eb23f2f9b435 100644
--- a/services/autofill/Android.bp
+++ b/services/autofill/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.autofill-sources",
srcs: ["java/**/*.java"],
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 67f654e6360f..6d72ca7bae25 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1492,7 +1492,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
final Dataset dataset = (Dataset) result;
final Dataset oldDataset = authenticatedResponse.getDatasets().get(datasetIdx);
- if (!isPinnedDataset(oldDataset)) {
+ if (!isAuthResultDatasetEphemeral(oldDataset, data)) {
authenticatedResponse.getDatasets().set(datasetIdx, dataset);
}
autoFill(requestId, datasetIdx, dataset, false);
@@ -1513,6 +1513,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
+ * Returns whether the dataset returned from the authentication result is ephemeral or not.
+ * See {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET} for more
+ * information.
+ */
+ private static boolean isAuthResultDatasetEphemeral(@Nullable Dataset oldDataset,
+ @NonNull Bundle authResultData) {
+ if (authResultData.containsKey(
+ AutofillManager.EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) {
+ return authResultData.getBoolean(
+ AutofillManager.EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET);
+ }
+ return isPinnedDataset(oldDataset);
+ }
+
+ /**
* A dataset can potentially have multiple fields, and it's possible that some of the fields'
* has inline presentation and some don't. It's also possible that some of the fields'
* inline presentation is pinned and some isn't. So the concept of whether a dataset is
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 890208720f97..27ea3d618afa 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -203,7 +203,7 @@ final class FillUi {
.getInteger(com.android.internal.R.integer.autofill_max_visible_datasets);
}
- final RemoteViews.OnClickHandler interceptionHandler = (view, pendingIntent, r) -> {
+ final RemoteViews.InteractionHandler interceptionHandler = (view, pendingIntent, r) -> {
if (pendingIntent != null) {
mCallback.startIntentSender(pendingIntent.getIntentSender());
}
@@ -258,10 +258,11 @@ final class FillUi {
+ mVisibleDatasetsMaxCount);
}
- RemoteViews.OnClickHandler clickBlocker = null;
+ RemoteViews.InteractionHandler interactionBlocker = null;
if (headerPresentation != null) {
- clickBlocker = newClickBlocker();
- mHeader = headerPresentation.applyWithTheme(mContext, null, clickBlocker, mThemeId);
+ interactionBlocker = newInteractionBlocker();
+ mHeader = headerPresentation.applyWithTheme(
+ mContext, null, interactionBlocker, mThemeId);
final LinearLayout headerContainer =
decor.findViewById(R.id.autofill_dataset_header);
applyCancelAction(mHeader, response.getCancelIds());
@@ -276,11 +277,11 @@ final class FillUi {
final LinearLayout footerContainer =
decor.findViewById(R.id.autofill_dataset_footer);
if (footerContainer != null) {
- if (clickBlocker == null) { // already set for header
- clickBlocker = newClickBlocker();
+ if (interactionBlocker == null) { // already set for header
+ interactionBlocker = newInteractionBlocker();
}
mFooter = footerPresentation.applyWithTheme(
- mContext, null, clickBlocker, mThemeId);
+ mContext, null, interactionBlocker, mThemeId);
applyCancelAction(mFooter, response.getCancelIds());
// Footer not supported on some platform e.g. TV
if (sVerbose) Slog.v(TAG, "adding footer");
@@ -397,9 +398,9 @@ final class FillUi {
}
/**
- * Creates a remoteview interceptor used to block clicks.
+ * Creates a remoteview interceptor used to block clicks or other interactions.
*/
- private RemoteViews.OnClickHandler newClickBlocker() {
+ private RemoteViews.InteractionHandler newInteractionBlocker() {
return (view, pendingIntent, response) -> {
if (sVerbose) Slog.v(TAG, "Ignoring click on " + view);
return true;
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 9d46c26022bf..826a98afe2f8 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -388,18 +388,20 @@ final class SaveUi {
}
}
- final RemoteViews.OnClickHandler handler = (view, pendingIntent, response) -> {
- Intent intent = response.getLaunchOptions(view).first;
- final boolean isValid = isValidLink(pendingIntent, intent);
- if (!isValid) {
- final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_SAVE_LINK_TAPPED, mType);
- log.setType(MetricsEvent.TYPE_UNKNOWN);
- mMetricsLogger.write(log);
- return false;
- }
+ final RemoteViews.InteractionHandler handler =
+ (view, pendingIntent, response) -> {
+ Intent intent = response.getLaunchOptions(view).first;
+ final boolean isValid = isValidLink(pendingIntent, intent);
+ if (!isValid) {
+ final LogMaker log =
+ newLogMaker(MetricsEvent.AUTOFILL_SAVE_LINK_TAPPED, mType);
+ log.setType(MetricsEvent.TYPE_UNKNOWN);
+ mMetricsLogger.write(log);
+ return false;
+ }
- startIntentSenderWithRestore(pendingIntent, intent);
- return true;
+ startIntentSenderWithRestore(pendingIntent, intent);
+ return true;
};
try {
diff --git a/services/backup/Android.bp b/services/backup/Android.bp
index 68376c5ad178..ead8aff30147 100644
--- a/services/backup/Android.bp
+++ b/services/backup/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.backup-sources",
srcs: ["java/**/*.java"],
diff --git a/services/backup/backuplib/Android.bp b/services/backup/backuplib/Android.bp
index 00f51c960636..5a28891f88c9 100644
--- a/services/backup/backuplib/Android.bp
+++ b/services/backup/backuplib/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "backuplib-sources",
srcs: ["java/**/*.java"],
diff --git a/services/companion/Android.bp b/services/companion/Android.bp
index 6aa54c48e3ed..4ae9365d3b9f 100644
--- a/services/companion/Android.bp
+++ b/services/companion/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.companion-sources",
srcs: ["java/**/*.java"],
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 55e3ef2e427f..76c8d3001158 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -17,9 +17,17 @@
package com.android.server.companion;
+import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
+import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
+import static android.content.Context.BIND_IMPORTANT;
+import static android.content.pm.PackageManager.MATCH_ALL;
+
+import static com.android.internal.util.CollectionUtils.any;
import static com.android.internal.util.CollectionUtils.emptyIfNull;
+import static com.android.internal.util.CollectionUtils.filter;
import static com.android.internal.util.CollectionUtils.find;
import static com.android.internal.util.CollectionUtils.forEach;
+import static com.android.internal.util.CollectionUtils.map;
import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -40,22 +48,32 @@ import android.app.PendingIntent;
import android.app.role.RoleManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
import android.companion.Association;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
+import android.companion.CompanionDeviceService;
import android.companion.DeviceNotAssociatedException;
import android.companion.ICompanionDeviceDiscoveryService;
import android.companion.ICompanionDeviceManager;
+import android.companion.ICompanionDeviceService;
import android.companion.IFindDeviceCallback;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.net.NetworkPolicyManager;
import android.os.Binder;
@@ -77,6 +95,7 @@ import android.permission.PermissionControllerManager;
import android.provider.Settings;
import android.provider.SettingsStringUtil.ComponentNameSet;
import android.text.BidiFormatter;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.ExceptionUtils;
@@ -115,6 +134,7 @@ import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -135,9 +155,14 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
".DeviceDiscoveryService");
+ private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000;
+ private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000;
+
private static final boolean DEBUG = false;
private static final String LOG_TAG = "CompanionDeviceManagerService";
+ private static final long PAIR_WITHOUT_PROMPT_WINDOW_MS = 10 * 60 * 1000; // 10 min
+
private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
@@ -146,18 +171,24 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
private static final String XML_ATTR_PACKAGE = "package";
private static final String XML_ATTR_DEVICE = "device";
private static final String XML_ATTR_PROFILE = "profile";
- private static final String XML_ATTR_PERSISTENT_PROFILE_GRANTS = "persistent_profile_grants";
+ private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby";
+ private static final String XML_ATTR_TIME_APPROVED = "time_approved";
private static final String XML_FILE_NAME = "companion_device_manager_associations.xml";
private final CompanionDeviceManagerImpl mImpl;
private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>();
private PowerWhitelistManager mPowerWhitelistManager;
private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
+ /** userId -> packageName -> serviceConnector */
+ private PerUser<ArrayMap<String, ServiceConnector<ICompanionDeviceService>>>
+ mDeviceListenerServiceConnectors;
private IAppOpsService mAppOpsManager;
private RoleManager mRoleManager;
private BluetoothAdapter mBluetoothAdapter;
+ private UserManager mUserManager;
private IFindDeviceCallback mFindDeviceCallback;
+ private ScanCallback mBleScanCallback = new BleScanCallback();
private AssociationRequest mRequest;
private String mCallingPackage;
private AndroidFuture<Association> mOngoingDeviceDiscovery;
@@ -165,9 +196,16 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener =
new BluetoothDeviceConnectedListener();
+ private BleStateBroadcastReceiver mBleStateBroadcastReceiver = new BleStateBroadcastReceiver();
private List<String> mCurrentlyConnectedDevices = new ArrayList<>();
+ private ArrayMap<String, Date> mDevicesLastNearby = new ArrayMap<>();
+ private UnbindDeviceListenersRunnable
+ mUnbindDeviceListenersRunnable = new UnbindDeviceListenersRunnable();
+ private ArrayMap<String, TriggerDeviceDisappearedRunnable> mTriggerDeviceDisappearedRunnables =
+ new ArrayMap<>();
private final Object mLock = new Object();
+ private final Handler mMainHandler = Handler.getMain();
/** userId -> [association] */
@GuardedBy("mLock")
@@ -189,6 +227,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mPermissionControllerManager = requireNonNull(
context.getSystemService(PermissionControllerManager.class));
+ mUserManager = context.getSystemService(UserManager.class);
Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
@@ -201,6 +240,16 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
};
+ mDeviceListenerServiceConnectors = new PerUser<ArrayMap<String,
+ ServiceConnector<ICompanionDeviceService>>>() {
+ @NonNull
+ @Override
+ protected ArrayMap<String, ServiceConnector<ICompanionDeviceService>> create(
+ int userId) {
+ return new ArrayMap<>();
+ }
+ };
+
registerPackageMonitor();
}
@@ -208,14 +257,20 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
new PackageMonitor() {
@Override
public void onPackageRemoved(String packageName, int uid) {
+ Slog.d(LOG_TAG, "onPackageRemoved(packageName = " + packageName
+ + ", uid = " + uid + ")");
+ int userId = getChangingUserId();
updateAssociations(
as -> CollectionUtils.filter(as,
a -> !Objects.equals(a.getPackageName(), packageName)),
- getChangingUserId());
+ userId);
+
+ unbindDevicePresenceListener(packageName, userId);
}
@Override
public void onPackageModified(String packageName) {
+ Slog.d(LOG_TAG, "onPackageModified(packageName = " + packageName + ")");
int userId = getChangingUserId();
forEach(getAllAssociations(userId, packageName), association -> {
updateSpecialAccessPermissionForAssociatedPackage(association);
@@ -225,6 +280,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
}
+ private void unbindDevicePresenceListener(String packageName, int userId) {
+ ServiceConnector<ICompanionDeviceService> deviceListener =
+ mDeviceListenerServiceConnectors.forUser(userId)
+ .remove(packageName);
+ if (deviceListener != null) {
+ deviceListener.unbind();
+ }
+ }
+
@Override
public void onStart() {
publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl);
@@ -233,11 +297,17 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ // Init Bluetooth
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter != null) {
mBluetoothAdapter.registerBluetoothConnectionCallback(
getContext().getMainExecutor(),
mBluetoothDeviceConnectedListener);
+ getContext().registerReceiver(
+ mBleStateBroadcastReceiver, mBleStateBroadcastReceiver.mIntentFilter);
+ initBleScanning();
+ } else {
+ Slog.w(LOG_TAG, "No BluetoothAdapter available");
}
}
}
@@ -257,6 +327,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
void maybeGrantAutoRevokeExemptions() {
+ Slog.d(LOG_TAG, "maybeGrantAutoRevokeExemptions()");
PackageManager pm = getContext().getPackageManager();
for (int userId : LocalServices.getService(UserManagerInternal.class).getUserIds()) {
SharedPreferences pref = getContext().getSharedPreferences(
@@ -276,7 +347,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
int uid = pm.getPackageUidAsUser(a.getPackageName(), userId);
exemptFromAutoRevoke(a.getPackageName(), uid);
} catch (PackageManager.NameNotFoundException e) {
- Log.w(LOG_TAG, "Unknown companion package: " + a.getPackageName(), e);
+ Slog.w(LOG_TAG, "Unknown companion package: " + a.getPackageName(), e);
}
}
} finally {
@@ -287,10 +358,13 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
@Override
public void binderDied() {
- Handler.getMain().post(this::cleanup);
+ Slog.w(LOG_TAG, "binderDied()");
+ mMainHandler.post(this::cleanup);
}
private void cleanup() {
+ Slog.d(LOG_TAG, "cleanup(); discovery = "
+ + mOngoingDeviceDiscovery + ", request = " + mRequest);
synchronized (mLock) {
AndroidFuture<Association> ongoingDeviceDiscovery = mOngoingDeviceDiscovery;
if (ongoingDeviceDiscovery != null && !ongoingDeviceDiscovery.isDone()) {
@@ -333,10 +407,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
AssociationRequest request,
IFindDeviceCallback callback,
String callingPackage) throws RemoteException {
- if (DEBUG) {
- Slog.i(LOG_TAG, "associate(request = " + request + ", callback = " + callback
- + ", callingPackage = " + callingPackage + ")");
- }
+ Slog.i(LOG_TAG, "associate(request = " + request + ", callback = " + callback
+ + ", callingPackage = " + callingPackage + ")");
checkNotNull(request, "Request cannot be null");
checkNotNull(callback, "Callback cannot be null");
checkCallerIsSystemOr(callingPackage);
@@ -356,9 +428,13 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
request.getDeviceProfile());
mOngoingDeviceDiscovery = fetchProfileDescription.thenComposeAsync(description -> {
+ Slog.d(LOG_TAG, "fetchProfileDescription done: " + description);
+
request.setDeviceProfilePrivilegesDescription(description);
return mServiceConnectors.forUser(userId).postAsync(service -> {
+ Slog.d(LOG_TAG, "Connected to CDM service; starting discovery for " + request);
+
AndroidFuture<Association> future = new AndroidFuture<>();
service.startDiscovery(request, callingPackage, callback, future);
return future;
@@ -371,7 +447,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
if (err == null) {
addAssociation(association);
} else {
- Log.e(LOG_TAG, "Failed to discover device(s)", err);
+ Slog.e(LOG_TAG, "Failed to discover device(s)", err);
callback.onFailure("No devices found: " + err.getMessage());
}
cleanup();
@@ -385,6 +461,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
public void stopScan(AssociationRequest request,
IFindDeviceCallback callback,
String callingPackage) {
+ Slog.d(LOG_TAG, "stopScan(request = " + request + ")");
if (Objects.equals(request, mRequest)
&& Objects.equals(callback, mFindDeviceCallback)
&& Objects.equals(callingPackage, mCallingPackage)) {
@@ -399,7 +476,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
checkCallerIsSystemOr(callingPackage, userId);
checkUsesFeature(callingPackage, getCallingUserId());
}
- return new ArrayList<>(CollectionUtils.map(
+ return new ArrayList<>(map(
getAllAssociations(userId, callingPackage),
a -> a.getDeviceMacAddress()));
}
@@ -497,7 +574,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
return true;
}
- return CollectionUtils.any(
+ return any(
getAllAssociations(userId, packageName),
a -> Objects.equals(a.getDeviceMacAddress(), macAddress));
}
@@ -506,22 +583,18 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
public void registerDevicePresenceListenerService(
String packageName, String deviceAddress)
throws RemoteException {
- checkCanRegisterObserverService(packageName, deviceAddress);
-
- //TODO(eugenesusla) implement
+ registerDevicePresenceListenerActive(packageName, deviceAddress, true);
}
@Override
public void unregisterDevicePresenceListenerService(
String packageName, String deviceAddress)
throws RemoteException {
- checkCanRegisterObserverService(packageName, deviceAddress);
-
- //TODO(eugenesusla) implement
+ registerDevicePresenceListenerActive(packageName, deviceAddress, false);
}
- private void checkCanRegisterObserverService(String packageName, String deviceAddress)
- throws RemoteException {
+ private void registerDevicePresenceListenerActive(String packageName, String deviceAddress,
+ boolean active) throws RemoteException {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE,
"[un]registerDevicePresenceListenerService");
@@ -537,6 +610,21 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
+ " is not associated with device " + deviceAddress
+ " for user " + userId));
}
+
+ updateAssociations(associations -> map(associations, association -> {
+ if (Objects.equals(association.getPackageName(), packageName)
+ && Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
+ return new Association(
+ association.getUserId(),
+ association.getDeviceMacAddress(),
+ association.getPackageName(),
+ association.getDeviceProfile(),
+ active, /* notifyOnDeviceNearby */
+ association.getTimeApprovedMs());
+ } else {
+ return association;
+ }
+ }));
}
private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
@@ -565,6 +653,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
@Override
+ public boolean canPairWithoutPrompt(
+ String packageName, String deviceMacAddress, int userId) {
+ return CollectionUtils.any(
+ getAllAssociations(userId, packageName, deviceMacAddress),
+ a -> System.currentTimeMillis() - a.getTimeApprovedMs()
+ < PAIR_WITHOUT_PROMPT_WINDOW_MS);
+ }
+
+ @Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver)
throws RemoteException {
@@ -625,7 +722,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
getAllAssociations(association.getUserId()),
a -> !a.equals(association) && deviceProfile.equals(a.getDeviceProfile()));
if (otherAssociationWithDeviceProfile != null) {
- Log.i(LOG_TAG, "Not revoking " + deviceProfile
+ Slog.i(LOG_TAG, "Not revoking " + deviceProfile
+ " for " + association
+ " - profile still present in " + otherAssociationWithDeviceProfile);
} else {
@@ -639,7 +736,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
getContext().getMainExecutor(),
success -> {
if (!success) {
- Log.e(LOG_TAG, "Failed to revoke device profile role "
+ Slog.e(LOG_TAG, "Failed to revoke device profile role "
+ association.getDeviceProfile()
+ " to " + association.getPackageName()
+ " for user " + association.getUserId());
@@ -693,6 +790,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddress())) {
grantDeviceProfile(association);
}
+
+ if (association.isNotifyOnDeviceNearby()) {
+ restartBleScan();
+ }
}
private void exemptFromAutoRevoke(String packageName, int uid) {
@@ -703,7 +804,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
packageName,
AppOpsManager.MODE_IGNORED);
} catch (RemoteException e) {
- Log.w(LOG_TAG,
+ Slog.w(LOG_TAG,
"Error while granting auto revoke exemption for " + packageName, e);
}
}
@@ -728,9 +829,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
private void recordAssociation(Association association) {
- if (DEBUG) {
- Log.i(LOG_TAG, "recordAssociation(" + association + ")");
- }
+ Slog.i(LOG_TAG, "recordAssociation(" + association + ")");
updateAssociations(associations -> CollectionUtils.add(associations, association));
}
@@ -744,9 +843,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
final Set<Association> old = getAllAssociations(userId);
Set<Association> associations = new ArraySet<>(old);
associations = update.apply(associations);
- if (DEBUG) {
- Slog.i(LOG_TAG, "Updating associations: " + old + " --> " + associations);
- }
+ Slog.i(LOG_TAG, "Updating associations: " + old + " --> " + associations);
mCachedAssociations.put(userId, Collections.unmodifiableSet(associations));
BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
CompanionDeviceManagerService::persistAssociations,
@@ -775,9 +872,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
private void persistAssociations(Set<Association> associations, int userId) {
- if (DEBUG) {
- Slog.i(LOG_TAG, "Writing associations to disk: " + associations);
- }
+ Slog.i(LOG_TAG, "Writing associations to disk: " + associations);
final AtomicFile file = getStorageFileForUser(userId);
synchronized (file) {
file.write(out -> {
@@ -795,10 +890,12 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
association.getDeviceMacAddress());
if (association.getDeviceProfile() != null) {
tag.attribute(null, XML_ATTR_PROFILE, association.getDeviceProfile());
- tag.attribute(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS,
+ tag.attribute(null, XML_ATTR_NOTIFY_DEVICE_NEARBY,
Boolean.toString(
- association.isKeepProfilePrivilegesWhenDeviceAway()));
+ association.isNotifyOnDeviceNearby()));
}
+ tag.attribute(null, XML_ATTR_TIME_APPROVED,
+ Long.toString(association.getTimeApprovedMs()));
tag.endTag(null, XML_TAG_ASSOCIATION);
});
@@ -826,25 +923,48 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
if (mCachedAssociations.get(userId) == null) {
mCachedAssociations.put(userId, Collections.unmodifiableSet(
emptyIfNull(readAllAssociations(userId))));
- if (DEBUG) {
- Slog.i(LOG_TAG, "Read associations from disk: " + mCachedAssociations);
- }
+ Slog.i(LOG_TAG, "Read associations from disk: " + mCachedAssociations);
}
return mCachedAssociations.get(userId);
}
}
private List<UserInfo> getAllUsers() {
- return getContext().getSystemService(UserManager.class).getUsers();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ return mUserManager.getUsers();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
- @Nullable
private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) {
return CollectionUtils.filter(
getAllAssociations(userId),
a -> Objects.equals(packageFilter, a.getPackageName()));
}
+ private Set<Association> getAllAssociations() {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ ArraySet<Association> result = new ArraySet<>();
+ for (UserInfo user : mUserManager.getAliveUsers()) {
+ result.addAll(getAllAssociations(user.id));
+ }
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private Set<Association> getAllAssociations(
+ int userId, @Nullable String packageFilter, @Nullable String addressFilter) {
+ return CollectionUtils.filter(
+ getAllAssociations(userId),
+ a -> Objects.equals(packageFilter, a.getPackageName())
+ && Objects.equals(addressFilter, a.getDeviceMacAddress()));
+ }
+
private Set<Association> readAllAssociations(int userId) {
final AtomicFile file = getStorageFileForUser(userId);
@@ -865,13 +985,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
final String profile = parser.getAttributeValue(null, XML_ATTR_PROFILE);
final boolean persistentGrants = Boolean.valueOf(
- parser.getAttributeValue(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS));
+ parser.getAttributeValue(null, XML_ATTR_NOTIFY_DEVICE_NEARBY));
+ final long timeApproved = parseLongOrDefault(
+ parser.getAttributeValue(null, XML_ATTR_TIME_APPROVED), 0L);
if (appPackage == null || deviceAddress == null) continue;
result = ArrayUtils.add(result,
new Association(userId, deviceAddress, appPackage,
- profile, persistentGrants));
+ profile, persistentGrants, timeApproved));
}
return result;
} catch (XmlPullParserException | IOException e) {
@@ -882,13 +1004,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
void onDeviceConnected(String address) {
+ Slog.d(LOG_TAG, "onDeviceConnected(address = " + address + ")");
+
mCurrentlyConnectedDevices.add(address);
for (UserInfo user : getAllUsers()) {
for (Association association : getAllAssociations(user.id)) {
if (Objects.equals(address, association.getDeviceMacAddress())) {
if (association.getDeviceProfile() != null) {
- Log.i(LOG_TAG, "Granting role " + association.getDeviceProfile()
+ Slog.i(LOG_TAG, "Granting role " + association.getDeviceProfile()
+ " to " + association.getPackageName()
+ " due to device connected: " + association.getDeviceMacAddress());
grantDeviceProfile(association);
@@ -896,9 +1020,13 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
}
}
+
+ onDeviceNearby(address);
}
private void grantDeviceProfile(Association association) {
+ Slog.i(LOG_TAG, "grantDeviceProfile(association = " + association + ")");
+
if (association.getDeviceProfile() != null) {
mRoleManager.addRoleHolderAsUser(
association.getDeviceProfile(),
@@ -908,7 +1036,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
getContext().getMainExecutor(),
success -> {
if (!success) {
- Log.e(LOG_TAG, "Failed to grant device profile role "
+ Slog.e(LOG_TAG, "Failed to grant device profile role "
+ association.getDeviceProfile()
+ " to " + association.getPackageName()
+ " for user " + association.getUserId());
@@ -918,7 +1046,279 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
void onDeviceDisconnected(String address) {
+ Slog.d(LOG_TAG, "onDeviceConnected(address = " + address + ")");
+
mCurrentlyConnectedDevices.remove(address);
+
+ onDeviceDisappeared(address);
+ }
+
+ private ServiceConnector<ICompanionDeviceService> getDeviceListenerServiceConnector(
+ Association a) {
+ return mDeviceListenerServiceConnectors.forUser(a.getUserId()).computeIfAbsent(
+ a.getPackageName(),
+ pkg -> createDeviceListenerServiceConnector(a));
+ }
+
+ private ServiceConnector<ICompanionDeviceService> createDeviceListenerServiceConnector(
+ Association a) {
+ List<ResolveInfo> resolveInfos = getContext().getPackageManager().queryIntentServicesAsUser(
+ new Intent(CompanionDeviceService.SERVICE_INTERFACE), MATCH_ALL, a.getUserId());
+ List<ResolveInfo> packageResolveInfos = filter(resolveInfos,
+ info -> Objects.equals(info.serviceInfo.packageName, a.getPackageName()));
+ if (packageResolveInfos.size() != 1) {
+ Slog.w(LOG_TAG, "Device presence listener package must have exactly one "
+ + "CompanionDeviceService, but " + a.getPackageName()
+ + " has " + packageResolveInfos.size());
+ return new ServiceConnector.NoOp<>();
+ }
+ ComponentName componentName = packageResolveInfos.get(0).serviceInfo.getComponentName();
+ Slog.i(LOG_TAG, "Initializing CompanionDeviceService binding for " + componentName);
+ return new ServiceConnector.Impl<>(getContext(),
+ new Intent(CompanionDeviceService.SERVICE_INTERFACE).setComponent(componentName),
+ BIND_IMPORTANT,
+ a.getUserId(),
+ ICompanionDeviceService.Stub::asInterface);
+ }
+
+ private class BleScanCallback extends ScanCallback {
+ @Override
+ public void onScanResult(int callbackType, ScanResult result) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "onScanResult(callbackType = "
+ + callbackType + ", result = " + result + ")");
+ }
+
+ onDeviceNearby(result.getDevice().getAddress());
+ }
+
+ @Override
+ public void onBatchScanResults(List<ScanResult> results) {
+ for (int i = 0, size = results.size(); i < size; i++) {
+ onScanResult(CALLBACK_TYPE_ALL_MATCHES, results.get(i));
+ }
+ }
+
+ @Override
+ public void onScanFailed(int errorCode) {
+ if (errorCode == SCAN_FAILED_ALREADY_STARTED) {
+ // ignore - this might happen if BT tries to auto-restore scans for us in the
+ // future
+ Slog.i(LOG_TAG, "Ignoring BLE scan error: SCAN_FAILED_ALREADY_STARTED");
+ } else {
+ Slog.w(LOG_TAG, "Failed to start BLE scan: error " + errorCode);
+ }
+ }
+ }
+
+ private class BleStateBroadcastReceiver extends BroadcastReceiver {
+
+ final IntentFilter mIntentFilter =
+ new IntentFilter(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1);
+ int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+ Slog.d(LOG_TAG, "Received BT state transition broadcast: "
+ + BluetoothAdapter.nameForState(previousState)
+ + " -> " + BluetoothAdapter.nameForState(newState));
+
+ boolean bleOn = newState == BluetoothAdapter.STATE_ON
+ || newState == BluetoothAdapter.STATE_BLE_ON;
+ if (bleOn) {
+ if (mBluetoothAdapter.getBluetoothLeScanner() != null) {
+ startBleScan();
+ } else {
+ Slog.wtf(LOG_TAG, "BLE on, but BluetoothLeScanner == null");
+ }
+ }
+ }
+ }
+
+ private class UnbindDeviceListenersRunnable implements Runnable {
+
+ public String getJobId(String address) {
+ return "CDM_deviceGone_unbind_" + address;
+ }
+
+ @Override
+ public void run() {
+ Slog.i(LOG_TAG, "UnbindDeviceListenersRunnable.run(); devicesNearby = "
+ + mDevicesLastNearby);
+ int size = mDevicesLastNearby.size();
+ for (int i = 0; i < size; i++) {
+ String address = mDevicesLastNearby.keyAt(i);
+ Date lastNearby = mDevicesLastNearby.valueAt(i);
+
+ if (System.currentTimeMillis() - lastNearby.getTime()
+ >= DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS) {
+ for (Association association : getAllAssociations(address)) {
+ if (association.isNotifyOnDeviceNearby()) {
+ getDeviceListenerServiceConnector(association).unbind();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private class TriggerDeviceDisappearedRunnable implements Runnable {
+
+ private final String mAddress;
+
+ TriggerDeviceDisappearedRunnable(String address) {
+ mAddress = address;
+ }
+
+ public void schedule() {
+ Slog.d(LOG_TAG,
+ "TriggerDeviceDisappearedRunnable.schedule(address = " + mAddress + ")");
+ mMainHandler.removeCallbacks(this);
+ mMainHandler.postDelayed(this, this, DEVICE_DISAPPEARED_TIMEOUT_MS);
+ }
+
+ @Override
+ public void run() {
+ Slog.d(LOG_TAG, "TriggerDeviceDisappearedRunnable.run(address = " + mAddress + ")");
+ onDeviceDisappeared(mAddress);
+ }
+ }
+
+ private Set<Association> getAllAssociations(String deviceAddress) {
+ List<UserInfo> aliveUsers = mUserManager.getAliveUsers();
+ Set<Association> result = new ArraySet<>();
+ for (int i = 0, size = aliveUsers.size(); i < size; i++) {
+ UserInfo user = aliveUsers.get(i);
+ for (Association association : getAllAssociations(user.id)) {
+ if (Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
+ result.add(association);
+ }
+ }
+ }
+ return result;
+ }
+
+ private void onDeviceNearby(String address) {
+ Slog.i(LOG_TAG, "onDeviceNearby(address = " + address + ")");
+
+ Date timestamp = new Date();
+ Date oldTimestamp = mDevicesLastNearby.put(address, timestamp);
+
+ cancelUnbindDeviceListener(address);
+
+ mTriggerDeviceDisappearedRunnables
+ .computeIfAbsent(address, addr -> new TriggerDeviceDisappearedRunnable(address))
+ .schedule();
+
+ // Avoid spamming the app if device is already known to be nearby
+ boolean justAppeared = oldTimestamp == null
+ || timestamp.getTime() - oldTimestamp.getTime() >= DEVICE_DISAPPEARED_TIMEOUT_MS;
+ if (justAppeared) {
+ for (Association association : getAllAssociations(address)) {
+ if (association.isNotifyOnDeviceNearby()) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Device " + address
+ + " managed by " + association.getPackageName()
+ + " is nearby on " + timestamp);
+ }
+ getDeviceListenerServiceConnector(association).run(
+ service -> service.onDeviceAppeared(association.getDeviceMacAddress()));
+ }
+ }
+ }
+ }
+
+ private void onDeviceDisappeared(String address) {
+ Slog.i(LOG_TAG, "onDeviceDisappeared(address = " + address + ")");
+
+ boolean hasDeviceListeners = false;
+ for (Association association : getAllAssociations(address)) {
+ if (association.isNotifyOnDeviceNearby()) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Device " + address
+ + " managed by " + association.getPackageName()
+ + " disappeared; last seen on " + mDevicesLastNearby.get(address));
+ }
+
+ getDeviceListenerServiceConnector(association).run(
+ service -> service.onDeviceDisappeared(address));
+ hasDeviceListeners = true;
+ }
+ }
+
+ cancelUnbindDeviceListener(address);
+ if (hasDeviceListeners) {
+ mMainHandler.postDelayed(
+ mUnbindDeviceListenersRunnable,
+ mUnbindDeviceListenersRunnable.getJobId(address),
+ DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS);
+ }
+ }
+
+ private void cancelUnbindDeviceListener(String address) {
+ mMainHandler.removeCallbacks(
+ mUnbindDeviceListenersRunnable, mUnbindDeviceListenersRunnable.getJobId(address));
+ }
+
+ private void initBleScanning() {
+ Slog.i(LOG_TAG, "initBleScanning()");
+
+ boolean bluetoothReady = mBluetoothAdapter.registerServiceLifecycleCallback(
+ new BluetoothAdapter.ServiceLifecycleCallback() {
+ @Override
+ public void onBluetoothServiceUp() {
+ Slog.i(LOG_TAG, "Bluetooth stack is up");
+ startBleScan();
+ }
+
+ @Override
+ public void onBluetoothServiceDown() {
+ Slog.w(LOG_TAG, "Bluetooth stack is down");
+ }
+ });
+ if (bluetoothReady) {
+ startBleScan();
+ }
+ }
+
+ void startBleScan() {
+ Slog.i(LOG_TAG, "startBleScan()");
+
+ List<ScanFilter> filters = getBleScanFilters();
+ if (filters.isEmpty()) {
+ return;
+ }
+ BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
+ if (scanner == null) {
+ Slog.w(LOG_TAG, "scanner == null (likely BLE isn't ON yet)");
+ } else {
+ scanner.startScan(
+ filters,
+ new ScanSettings.Builder().setScanMode(SCAN_MODE_BALANCED).build(),
+ mBleScanCallback);
+ }
+ }
+
+ void restartBleScan() {
+ mBluetoothAdapter.getBluetoothLeScanner().stopScan(mBleScanCallback);
+ startBleScan();
+ }
+
+ private List<ScanFilter> getBleScanFilters() {
+ ArrayList<ScanFilter> result = new ArrayList<>();
+ ArraySet<String> addressesSeen = new ArraySet<>();
+ for (Association association : getAllAssociations()) {
+ String address = association.getDeviceMacAddress();
+ if (addressesSeen.contains(address)) {
+ continue;
+ }
+ if (association.isNotifyOnDeviceNearby()) {
+ result.add(new ScanFilter.Builder().setDeviceAddress(address).build());
+ addressesSeen.add(address);
+ }
+ }
+ return result;
}
private AndroidFuture<String> getDeviceProfilePermissionDescription(String deviceProfile) {
@@ -934,6 +1334,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
return result;
}
+ private static long parseLongOrDefault(String str, long def) {
+ try {
+ return Long.parseLong(str);
+ } catch (NumberFormatException e) {
+ Slog.w(LOG_TAG, "Failed to parse", e);
+ return def;
+ }
+ }
+
private class ShellCmd extends ShellCommand {
public static final String USAGE = "help\n"
+ "list USER_ID\n"
@@ -962,7 +1371,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
int userId = getNextArgInt();
String pkg = getNextArgRequired();
String address = getNextArgRequired();
- addAssociation(new Association(userId, address, pkg, null, false));
+ addAssociation(new Association(userId, address, pkg, null, false,
+ System.currentTimeMillis()));
}
break;
@@ -987,7 +1397,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
return 0;
} catch (Throwable t) {
- Log.e(LOG_TAG, "Error running a command: $ " + cmd, t);
+ Slog.e(LOG_TAG, "Error running a command: $ " + cmd, t);
getErrPrintWriter().println(Log.getStackTraceString(t));
return 1;
}
diff --git a/services/contentcapture/Android.bp b/services/contentcapture/Android.bp
index 688c0b1d3eec..434f239dbddc 100644
--- a/services/contentcapture/Android.bp
+++ b/services/contentcapture/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.contentcapture-sources",
srcs: ["java/**/*.java"],
diff --git a/services/contentsuggestions/Android.bp b/services/contentsuggestions/Android.bp
index 1b4d7e29c551..9f28b724e408 100644
--- a/services/contentsuggestions/Android.bp
+++ b/services/contentsuggestions/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.contentsuggestions-sources",
srcs: ["java/**/*.java"],
diff --git a/services/core/Android.bp b/services/core/Android.bp
index b97c3aee4c6d..da5e8b3c9465 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.core-sources-am-wm",
srcs: [
@@ -84,7 +93,6 @@ java_library_static {
":dumpstate_aidl",
":framework_native_aidl",
":gsiservice_aidl",
- ":idmap2_aidl",
":inputconstants_aidl",
":installd_aidl",
":storaged_aidl",
@@ -106,8 +114,7 @@ java_library_static {
"android.hardware.gnss-V1-java",
"android.hardware.power-V1-java",
"android.hardware.power-V1.0-java",
- "android.hardware.vibrator-V1-java",
- "android.net.ipsec.ike.stubs.module_lib",
+ "android.hardware.vibrator-V2-java",
"app-compat-annotations",
"framework-tethering.stubs.module_lib",
"service-permission.stubs.system_server",
@@ -132,10 +139,10 @@ java_library_static {
"android.hardware.health-V2.0-java",
"android.hardware.health-V2.1-java",
"android.hardware.light-V1-java",
- "android.hardware.tv.cec-V1.0-java",
+ "android.hardware.tv.cec-V1.1-java",
"android.hardware.weaver-V1.0-java",
- "android.hardware.biometrics.face-V1.1-java",
"android.hardware.biometrics.face-V1-java",
+ "android.hardware.biometrics.face-V1.0-java",
"android.hardware.biometrics.fingerprint-V2.3-java",
"android.hardware.biometrics.fingerprint-V1-java",
"android.hardware.oemlock-V1.0-java",
@@ -235,8 +242,5 @@ filegroup {
"java/com/android/server/connectivity/QosCallbackAgentConnection.java",
"java/com/android/server/connectivity/QosCallbackTracker.java",
"java/com/android/server/connectivity/TcpKeepaliveController.java",
- "java/com/android/server/connectivity/Vpn.java",
- "java/com/android/server/connectivity/VpnIkev2Utils.java",
- "java/com/android/server/net/LockdownVpnTracker.java",
],
}
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 6886cdefc28a..342208c8a32b 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -30,6 +30,7 @@ import android.content.pm.PackageManager.ApplicationInfoFlags;
import android.content.pm.PackageManager.ComponentInfoFlags;
import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.os.Bundle;
import android.os.Handler;
@@ -49,8 +50,8 @@ import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Collection;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -535,17 +536,17 @@ public abstract class PackageManagerInternal {
* Set which overlay to use for a package.
* @param userId The user for which to update the overlays.
* @param targetPackageName The package name of the package for which to update the overlays.
- * @param overlayPackageNames The complete list of overlay packages that should be enabled for
- * the target. Previously enabled overlays not specified in the list
- * will be disabled. Pass in null or an empty list to disable
- * all overlays. The order of the items is significant if several
- * overlays modify the same resource.
+ * @param overlayPaths The complete list of overlay paths that should be enabled for
+ * the target. Previously enabled overlays not specified in the list
+ * will be disabled. Pass in null or empty paths to disable all overlays.
+ * The order of the items is significant if several overlays modify the
+ * same resource.
* @param outUpdatedPackageNames An output list that contains the package names of packages
* affected by the update of enabled overlays.
* @return true if all packages names were known by the package manager, false otherwise
*/
public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName,
- List<String> overlayPackageNames, Collection<String> outUpdatedPackageNames);
+ @Nullable OverlayPaths overlayPaths, Set<String> outUpdatedPackageNames);
/**
* Resolves an activity intent, allowing instant apps to be resolved.
@@ -997,6 +998,18 @@ public abstract class PackageManagerInternal {
public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId);
/**
+ * Register to listen for loading progress of an installed package.
+ * The listener is automatically unregistered when the app is fully loaded.
+ * @param packageName The name of the installed package
+ * @param callback To loading reporting progress
+ * @param userId The user under which to check.
+ * @return Whether the registration was successful. It can fail if the package has not been
+ * installed yet.
+ */
+ public abstract boolean registerInstalledLoadingProgressCallback(@NonNull String packageName,
+ @NonNull InstalledLoadingProgressCallback callback, int userId);
+
+ /**
* Returns the string representation of a known package. For example,
* {@link #PACKAGE_SETUP_WIZARD} is represented by the string Setup Wizard.
*
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index 958c15c8d432..e996eb4ef674 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -17,6 +17,7 @@
package android.os;
import com.android.internal.os.BinderCallsStats;
+import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import java.util.Collection;
@@ -37,6 +38,9 @@ public abstract class BatteryStatsInternal {
*/
public abstract String[] getMobileIfaces();
+ /** Returns CPU times for system server thread groups. */
+ public abstract SystemServiceCpuThreadTimes getSystemServiceCpuThreadTimes();
+
/**
* Inform battery stats how many deferred jobs existed when the app got launched and how
* long ago was the last job execution for the app.
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ba8a6311258a..3d764f2e23a1 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -16,7 +16,6 @@
package com.android.server;
-import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK;
@@ -47,6 +46,8 @@ 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_SUSPENDED;
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;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -92,11 +93,13 @@ import android.net.IConnectivityDiagnosticsCallback;
import android.net.IConnectivityManager;
import android.net.IDnsResolver;
import android.net.INetd;
+import android.net.INetworkActivityListener;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
import android.net.INetworkStatsService;
+import android.net.IOnSetOemNetworkPreferenceListener;
import android.net.IQosCallback;
import android.net.ISocketKeepaliveCallback;
import android.net.InetAddresses;
@@ -135,12 +138,13 @@ import android.net.RouteInfoParcel;
import android.net.SocketKeepalive;
import android.net.TelephonyNetworkSpecifier;
import android.net.TetheringManager;
+import android.net.TransportInfo;
import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
import android.net.VpnManager;
-import android.net.VpnService;
+import android.net.VpnTransportInfo;
import android.net.metrics.INetdEventListener;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
@@ -148,13 +152,13 @@ import android.net.netlink.InetDiagMessage;
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
+import android.os.BatteryStatsManager;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.INetworkActivityListener;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
@@ -164,6 +168,7 @@ import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.Process;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
@@ -171,12 +176,11 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.security.Credentials;
-import android.security.KeyStore;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Log;
@@ -190,9 +194,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.net.LegacyVpnInfo;
-import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.IndentingPrintWriter;
@@ -217,9 +218,7 @@ import com.android.server.connectivity.NetworkRanker;
import com.android.server.connectivity.PermissionMonitor;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
-import com.android.server.connectivity.Vpn;
import com.android.server.net.BaseNetworkObserver;
-import com.android.server.net.LockdownVpnTracker;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.utils.PriorityDump;
@@ -288,15 +287,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
// connect anyway?" dialog after the user selects a network that doesn't validate.
private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
- // Default to 30s linger time-out. Modifiable only for testing.
+ // Default to 30s linger time-out, and 5s for nascent network. Modifiable only for testing.
private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
+ private static final int DEFAULT_NASCENT_DELAY_MS = 5_000;
// The maximum number of network request allowed per uid before an exception is thrown.
private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
@VisibleForTesting
protected int mLingerDelayMs; // Can't be final, or test subclass constructors can't change it.
+
+ @VisibleForTesting
+ protected int mNascentDelayMs;
+
protected int mNonDefaultSubscriptionLingerDelayMs;
// How long to delay to removal of a pending intent based request.
@@ -310,18 +314,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private final PerUidCounter mNetworkRequestCounter;
- private KeyStore mKeyStore;
-
- @VisibleForTesting
- @GuardedBy("mVpns")
- protected final SparseArray<Vpn> mVpns = new SparseArray<>();
-
- // TODO: investigate if mLockdownEnabled can be removed and replaced everywhere by
- // a direct call to LockdownVpnTracker.isEnabled().
- @GuardedBy("mVpns")
- private boolean mLockdownEnabled;
- @GuardedBy("mVpns")
- private LockdownVpnTracker mLockdownTracker;
+ private volatile boolean mLockdownEnabled;
/**
* Stale copy of uid rules provided by NPMS. As long as they are accessed only in internal
@@ -572,6 +565,17 @@ 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
+ */
+ private static final int EVENT_SET_OEM_NETWORK_PREFERENCE = 48;
+
+ /**
+ * Used to indicate the system default network becomes active.
+ */
+ private static final int EVENT_REPORT_NETWORK_ACTIVITY = 49;
+
+ /**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
*/
@@ -774,6 +778,27 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ // When a lockdown VPN connects, send another CONNECTED broadcast for the underlying
+ // network type, to preserve previous behaviour.
+ private void maybeSendLegacyLockdownBroadcast(@NonNull NetworkAgentInfo vpnNai) {
+ if (vpnNai != mService.getLegacyLockdownNai()) return;
+
+ if (vpnNai.declaredUnderlyingNetworks == null
+ || vpnNai.declaredUnderlyingNetworks.length != 1) {
+ Log.wtf(TAG, "Legacy lockdown VPN must have exactly one underlying network: "
+ + Arrays.toString(vpnNai.declaredUnderlyingNetworks));
+ return;
+ }
+ final NetworkAgentInfo underlyingNai = mService.getNetworkAgentInfoForNetwork(
+ vpnNai.declaredUnderlyingNetworks[0]);
+ if (underlyingNai == null) return;
+
+ final int type = underlyingNai.networkInfo.getType();
+ final DetailedState state = DetailedState.CONNECTED;
+ maybeLogBroadcast(underlyingNai, state, type, true /* isDefaultNetwork */);
+ mService.sendLegacyNetworkBroadcast(underlyingNai, state, type);
+ }
+
/** Adds the given network to the specified legacy type list. */
public void add(int type, NetworkAgentInfo nai) {
if (!isTypeSupported(type)) {
@@ -791,9 +816,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Send a broadcast if this is the first network of its type or if it's the default.
final boolean isDefaultNetwork = mService.isDefaultNetwork(nai);
+
+ // If a legacy lockdown VPN is active, override the NetworkInfo state in all broadcasts
+ // to preserve previous behaviour.
+ final DetailedState state = mService.getLegacyLockdownState(DetailedState.CONNECTED);
if ((list.size() == 1) || isDefaultNetwork) {
- maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isDefaultNetwork);
- mService.sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type);
+ maybeLogBroadcast(nai, state, type, isDefaultNetwork);
+ mService.sendLegacyNetworkBroadcast(nai, state, type);
+ }
+
+ if (type == TYPE_VPN && state == DetailedState.CONNECTED) {
+ maybeSendLegacyLockdownBroadcast(nai);
}
}
@@ -988,13 +1021,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
/**
- * Get a reference to the system keystore.
- */
- public KeyStore getKeyStore() {
- return KeyStore.getInstance();
- }
-
- /**
* @see ProxyTracker
*/
public ProxyTracker makeProxyTracker(@NonNull Context context,
@@ -1058,9 +1084,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
mMetricsLog = logger;
mNetworkRanker = new NetworkRanker();
- final NetworkRequest defaultInternetRequest = createDefaultInternetRequestForTransport(
- -1, NetworkRequest.Type.REQUEST);
- mDefaultRequest = new NetworkRequestInfo(null, defaultInternetRequest, new Binder());
+ final NetworkRequest defaultInternetRequest = createDefaultRequest();
+ mDefaultRequest = new NetworkRequestInfo(
+ defaultInternetRequest, null, new Binder(),
+ null /* attributionTags */);
mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
mDefaultNetworkRequests.add(mDefaultRequest);
mNetworkRequestInfoLogs.log("REGISTER " + mDefaultRequest);
@@ -1089,6 +1116,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
+ // TODO: Consider making the timer customizable.
+ mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS;
mNonDefaultSubscriptionLingerDelayMs = 5_000;
mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
@@ -1101,7 +1130,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler);
mNetd = netd;
- mKeyStore = mDeps.getKeyStore();
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mLocationPermissionChecker = new LocationPermissionChecker(mContext);
@@ -1192,45 +1220,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
- // Set up the listener for user state for creating user VPNs.
+ // Listen for user add/removes to inform PermissionMonitor.
// Should run on mHandler to avoid any races.
IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_STARTED);
- intentFilter.addAction(Intent.ACTION_USER_STOPPED);
intentFilter.addAction(Intent.ACTION_USER_ADDED);
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
- intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
- mUserAllContext.registerReceiver(
- mIntentReceiver,
- intentFilter,
- null /* broadcastPermission */,
- mHandler);
- mContext.createContextAsUser(UserHandle.SYSTEM, 0 /* flags */).registerReceiver(
- mUserPresentReceiver,
- new IntentFilter(Intent.ACTION_USER_PRESENT),
- null /* broadcastPermission */,
- null /* scheduler */);
-
- // Listen to package add and removal events for all users.
- intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
- intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- intentFilter.addDataScheme("package");
- mUserAllContext.registerReceiver(
- mIntentReceiver,
- intentFilter,
- null /* broadcastPermission */,
- mHandler);
-
- // Listen to lockdown VPN reset.
- intentFilter = new IntentFilter();
- intentFilter.addAction(LockdownVpnTracker.ACTION_LOCKDOWN_RESET);
- mUserAllContext.registerReceiver(
- mIntentReceiver, intentFilter, NETWORK_STACK, mHandler);
-
- mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mNMS);
+ mUserAllContext.registerReceiver(mIntentReceiver, intentFilter,
+ null /* broadcastPermission */, mHandler);
+
+ mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mHandler, mNMS, mNetd);
mSettingsObserver = new SettingsObserver(mContext, mHandler);
registerSettingsCallbacks();
@@ -1274,14 +1274,24 @@ public class ConnectivityService extends IConnectivityManager.Stub
return netCap;
}
+ private NetworkRequest createDefaultRequest() {
+ return createDefaultInternetRequestForTransport(
+ TYPE_NONE, NetworkRequest.Type.REQUEST);
+ }
+
private NetworkRequest createDefaultInternetRequestForTransport(
int transportType, NetworkRequest.Type type) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
- if (transportType > -1) {
+ if (transportType > TYPE_NONE) {
netCap.addTransportType(transportType);
}
+ return createNetworkRequest(type, netCap);
+ }
+
+ private NetworkRequest createNetworkRequest(
+ NetworkRequest.Type type, NetworkCapabilities netCap) {
return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(), type);
}
@@ -1331,7 +1341,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (enable) {
handleRegisterNetworkRequest(new NetworkRequestInfo(
- null, networkRequest, new Binder()));
+ networkRequest, null, new Binder(),
+ null /* attributionTags */));
} else {
handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID,
/* callOnUnavailable */ false);
@@ -1404,9 +1415,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private Network[] getVpnUnderlyingNetworks(int uid) {
- synchronized (mVpns) {
- if (mLockdownEnabled) return null;
- }
+ if (mLockdownEnabled) return null;
final NetworkAgentInfo nai = getVpnForUid(uid);
if (nai != null) return nai.declaredUnderlyingNetworks;
return null;
@@ -1491,11 +1500,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)) {
networkInfo.setDetailedState(DetailedState.BLOCKED, null, null);
}
- synchronized (mVpns) {
- if (mLockdownTracker != null) {
- mLockdownTracker.augmentNetworkInfo(networkInfo);
- }
- }
+ networkInfo.setDetailedState(
+ getLegacyLockdownState(networkInfo.getDetailedState()),
+ "" /* reason */, null /* extraInfo */);
}
/**
@@ -1554,14 +1561,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
return nai.network;
}
- // Public because it's used by mLockdownTracker.
- public NetworkInfo getActiveNetworkInfoUnfiltered() {
- enforceAccessPermission();
- final int uid = mDeps.getCallingUid();
- NetworkState state = getUnfilteredActiveNetworkState(uid);
- return state.networkInfo;
- }
-
@Override
public NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked) {
NetworkStack.checkNetworkStackPermission(mContext);
@@ -1666,7 +1665,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
- int userId, String callingPackageName) {
+ int userId, String callingPackageName, @Nullable String callingAttributionTag) {
// The basic principle is: if an app's traffic could possibly go over a
// network, without the app doing anything multinetwork-specific,
// (hence, by "default"), then include that network's capabilities in
@@ -1697,7 +1696,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
result.put(
nai.network,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, mDeps.getCallingUid(), callingPackageName));
+ nc, mDeps.getCallingUid(), callingPackageName,
+ callingAttributionTag));
}
}
@@ -1710,7 +1710,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
result.put(
network,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, mDeps.getCallingUid(), callingPackageName));
+ nc, mDeps.getCallingUid(), callingPackageName,
+ callingAttributionTag));
}
}
}
@@ -1785,12 +1786,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
@Override
- public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) {
+ public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName,
+ @Nullable String callingAttributionTag) {
mAppOpsManager.checkPackage(mDeps.getCallingUid(), callingPackageName);
enforceAccessPermission();
return createWithLocationInfoSanitizedIfNecessaryWhenParceled(
getNetworkCapabilitiesInternal(network),
- mDeps.getCallingUid(), callingPackageName);
+ mDeps.getCallingUid(), callingPackageName, callingAttributionTag);
}
@VisibleForTesting
@@ -1809,11 +1811,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
return newNc;
}
- private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName) {
+ private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName,
+ @Nullable String callingAttributionTag) {
final long token = Binder.clearCallingIdentity();
try {
return mLocationPermissionChecker.checkLocationPermission(
- callerPkgName, null /* featureId */, callerUid, null /* message */);
+ callerPkgName, callingAttributionTag, callerUid, null /* message */);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1822,7 +1825,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
@VisibleForTesting
@Nullable
NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) {
+ @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName,
+ @Nullable String callingAttributionTag) {
if (nc == null) {
return null;
}
@@ -1831,7 +1835,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Avoid doing location permission check if the transport info has no location sensitive
// data.
if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) {
- hasLocationPermission = hasLocationPermission(callerUid, callerPkgName);
+ hasLocationPermission =
+ hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
newNc = new NetworkCapabilities(nc, hasLocationPermission);
} else {
newNc = new NetworkCapabilities(nc, false /* parcelLocationSensitiveFields */);
@@ -1848,7 +1853,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
if (hasLocationPermission == null) {
// Location permission not checked yet, check now for masking owner UID.
- hasLocationPermission = hasLocationPermission(callerUid, callerPkgName);
+ hasLocationPermission =
+ hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
}
// Reset owner uid if the app has no location permission.
if (!hasLocationPermission) {
@@ -1906,7 +1912,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
final ArrayList<NetworkState> result = new ArrayList<>();
for (Network network : getAllNetworks()) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
- if (nai != null) {
+ // TODO: Consider include SUSPENDED networks.
+ if (nai != null && nai.networkInfo.isConnected()) {
// TODO (b/73321673) : NetworkState contains a copy of the
// NetworkCapabilities, which may contain UIDs of apps to which the
// network applies. Should the UIDs be cleared so as not to leak or
@@ -2175,22 +2182,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
isBackgroundRestricted);
}
- /**
- * Require that the caller is either in the same user or has appropriate permission to interact
- * across users.
- *
- * @param userId Target user for whatever operation the current IPC is supposed to perform.
- */
- private void enforceCrossUserPermission(int userId) {
- if (userId == UserHandle.getCallingUserId()) {
- // Not a cross-user call.
- return;
- }
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "ConnectivityService");
- }
-
private boolean checkAnyPermissionOf(String... permissions) {
for (String permission : permissions) {
if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
@@ -2271,12 +2262,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, pid, uid);
}
- private void enforceControlAlwaysOnVpnPermission() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CONTROL_ALWAYS_ON_VPN,
- "ConnectivityService");
- }
-
private void enforceNetworkStackOrSettingsPermission() {
enforceAnyPermissionOf(
android.Manifest.permission.NETWORK_SETTINGS,
@@ -2301,6 +2286,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
+ private void enforceOemNetworkPreferencesPermission() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE,
+ "ConnectivityService");
+ }
+
private boolean checkNetworkStackPermission() {
return checkAnyPermissionOf(
android.Manifest.permission.NETWORK_STACK,
@@ -2349,13 +2340,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private Intent makeGeneralIntent(NetworkInfo info, String bcastType) {
- synchronized (mVpns) {
- if (mLockdownTracker != null) {
- info = new NetworkInfo(info);
- mLockdownTracker.augmentNetworkInfo(info);
- }
- }
-
Intent intent = new Intent(bcastType);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, new NetworkInfo(info));
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
@@ -2449,10 +2433,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- // Try bringing up tracker, but KeyStore won't be ready yet for secondary users so wait
- // for user to unlock device too.
- updateLockdownVpn();
-
// Create network requests for always-on networks.
mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS));
}
@@ -2462,7 +2442,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
@Override
public void registerNetworkActivityListener(@NonNull INetworkActivityListener l) {
- // TODO: Replace network activity listener registry in ConnectivityManager from NMS to here
+ mNetworkActivityTracker.registerNetworkActivityListener(l);
}
/**
@@ -2470,7 +2450,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
@Override
public void unregisterNetworkActivityListener(@NonNull INetworkActivityListener l) {
- // TODO: Replace network activity listener registry in ConnectivityManager from NMS to here
+ mNetworkActivityTracker.unregisterNetworkActivityListener(l);
}
/**
@@ -2478,8 +2458,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
@Override
public boolean isDefaultNetworkActive() {
- // TODO: Replace isNetworkActive() in NMS.
- return false;
+ return mNetworkActivityTracker.isDefaultNetworkActive();
}
/**
@@ -2651,6 +2630,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
pw.println();
+ pw.print("Current per-app default networks: ");
+ pw.increaseIndent();
+ dumpPerAppNetworkPreferences(pw);
+ pw.decreaseIndent();
+ pw.println();
+
pw.println("Current Networks:");
pw.increaseIndent();
dumpNetworks(pw);
@@ -2746,6 +2731,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
pw.increaseIndent();
mPermissionMonitor.dump(pw);
pw.decreaseIndent();
+
+ pw.println();
+ pw.println("Legacy network activity:");
+ pw.increaseIndent();
+ mNetworkActivityTracker.dump(pw);
+ pw.decreaseIndent();
}
private void dumpNetworks(IndentingPrintWriter pw) {
@@ -2771,6 +2762,40 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ private void dumpPerAppNetworkPreferences(IndentingPrintWriter pw) {
+ pw.println("Per-App Network Preference:");
+ pw.increaseIndent();
+ if (0 == mOemNetworkPreferences.getNetworkPreferences().size()) {
+ pw.println("none");
+ } else {
+ pw.println(mOemNetworkPreferences.toString());
+ }
+ pw.decreaseIndent();
+
+ for (final NetworkRequestInfo defaultRequest : mDefaultNetworkRequests) {
+ if (mDefaultRequest == defaultRequest) {
+ continue;
+ }
+
+ final boolean isActive = null != defaultRequest.getSatisfier();
+ pw.println("Is per-app network active:");
+ pw.increaseIndent();
+ pw.println(isActive);
+ if (isActive) {
+ pw.println("Active network: " + defaultRequest.getSatisfier().network.netId);
+ }
+ pw.println("Tracked UIDs:");
+ pw.increaseIndent();
+ if (0 == defaultRequest.mRequests.size()) {
+ pw.println("none, this should never occur.");
+ } else {
+ pw.println(defaultRequest.mRequests.get(0).networkCapabilities.getUids());
+ }
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ }
+ }
+
private void dumpNetworkRequests(IndentingPrintWriter pw) {
for (NetworkRequestInfo nri : requestsSortedById()) {
pw.println(nri.toString());
@@ -2904,7 +2929,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
Log.wtf(TAG, "Non-virtual networks cannot have underlying networks");
break;
}
+
final List<Network> underlying = (List<Network>) arg.second;
+
+ if (isLegacyLockdownNai(nai)
+ && (underlying == null || underlying.size() != 1)) {
+ Log.wtf(TAG, "Legacy lockdown VPN " + nai.toShortString()
+ + " must have exactly one underlying network: " + underlying);
+ }
+
final Network[] oldUnderlying = nai.declaredUnderlyingNetworks;
nai.declaredUnderlyingNetworks = (underlying != null)
? underlying.toArray(new Network[0]) : null;
@@ -3371,7 +3404,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// 3. If this network is unneeded (which implies it is not lingering), and there is at least
// one lingered request, set inactive.
nai.updateInactivityTimer();
- if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
+ if (nai.isInactive() && nai.numForegroundNetworkRequests() > 0) {
if (DBG) log("Unsetting inactive " + nai.toShortString());
nai.unsetInactive();
logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
@@ -3513,7 +3546,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
// incorrect) behavior.
mNetworkActivityTracker.updateDataActivityTracking(
null /* newNetwork */, nai);
- notifyLockdownVpn(nai);
ensureNetworkTransitionWakelock(nai.toShortString());
}
}
@@ -3603,29 +3635,38 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void handleRegisterNetworkRequest(@NonNull final NetworkRequestInfo nri) {
+ handleRegisterNetworkRequests(Collections.singleton(nri));
+ }
+
+ private void handleRegisterNetworkRequests(@NonNull final Set<NetworkRequestInfo> nris) {
ensureRunningOnConnectivityServiceThread();
- mNetworkRequestInfoLogs.log("REGISTER " + nri);
- for (final NetworkRequest req : nri.mRequests) {
- mNetworkRequests.put(req, nri);
- if (req.isListen()) {
- for (final NetworkAgentInfo network : mNetworkAgentInfos) {
- if (req.networkCapabilities.hasSignalStrength()
- && network.satisfiesImmutableCapabilitiesOf(req)) {
- updateSignalStrengthThresholds(network, "REGISTER", req);
+ for (final NetworkRequestInfo nri : nris) {
+ mNetworkRequestInfoLogs.log("REGISTER " + nri);
+ for (final NetworkRequest req : nri.mRequests) {
+ mNetworkRequests.put(req, nri);
+ if (req.isListen()) {
+ for (final NetworkAgentInfo network : mNetworkAgentInfos) {
+ if (req.networkCapabilities.hasSignalStrength()
+ && network.satisfiesImmutableCapabilitiesOf(req)) {
+ updateSignalStrengthThresholds(network, "REGISTER", req);
+ }
}
}
}
}
+
rematchAllNetworksAndRequests();
- // If the nri is satisfied, return as its score has already been sent if needed.
- if (nri.isBeingSatisfied()) {
- return;
- }
+ for (final NetworkRequestInfo nri : nris) {
+ // If the nri is satisfied, return as its score has already been sent if needed.
+ if (nri.isBeingSatisfied()) {
+ return;
+ }
- // As this request was not satisfied on rematch and thus never had any scores sent to the
- // factories, send null now for each request of type REQUEST.
- for (final NetworkRequest req : nri.mRequests) {
- if (req.isRequest()) sendUpdatedScoreToFactories(req, null);
+ // As this request was not satisfied on rematch and thus never had any scores sent to
+ // the factories, send null now for each request of type REQUEST.
+ for (final NetworkRequest req : nri.mRequests) {
+ if (req.isRequest()) sendUpdatedScoreToFactories(req, null);
+ }
}
}
@@ -3665,7 +3706,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
return true;
}
- if (!nai.everConnected || nai.isVPN() || nai.isLingering() || numRequests > 0) {
+ if (!nai.everConnected || nai.isVPN() || nai.isInactive() || numRequests > 0) {
return false;
}
@@ -3733,7 +3774,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
private NetworkRequestInfo getNriForAppRequest(
NetworkRequest request, int callingUid, String requestedOperation) {
- final NetworkRequestInfo nri = mNetworkRequests.get(request);
+ // Looking up the app passed param request in mRequests isn't possible since it may return
+ // null for a request managed by a per-app default. Therefore use getNriForAppRequest() to
+ // do the lookup since that will also find per-app default managed requests.
+ // Additionally, this lookup needs to be relatively fast (hence the lookup optimization)
+ // to avoid potential race conditions when validating a package->uid mapping when sending
+ // the callback on the very low-chance that an application shuts down prior to the callback
+ // being sent.
+ final NetworkRequestInfo nri = mNetworkRequests.get(request) != null
+ ? mNetworkRequests.get(request) : getNriForAppRequest(request);
if (nri != null) {
if (Process.SYSTEM_UID != callingUid && Process.NETWORK_STACK_UID != callingUid
@@ -3782,8 +3831,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (nri == null) {
return;
}
- // handleReleaseNetworkRequest() paths don't apply to multilayer requests.
- ensureNotMultilayerRequest(nri, "handleReleaseNetworkRequest");
if (VDBG || (DBG && request.isRequest())) {
log("releasing " + request + " (release request)");
}
@@ -3795,7 +3842,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void handleRemoveNetworkRequest(@NonNull final NetworkRequestInfo nri) {
ensureRunningOnConnectivityServiceThread();
-
nri.unlinkDeathRecipient();
for (final NetworkRequest req : nri.mRequests) {
mNetworkRequests.remove(req);
@@ -3803,6 +3849,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
removeListenRequestFromNetworks(req);
}
}
+ mDefaultNetworkRequests.remove(nri);
mNetworkRequestCounter.decrementCount(nri.mUid);
mNetworkRequestInfoLogs.log("RELEASE " + nri);
@@ -3817,6 +3864,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
cancelNpiRequests(nri);
}
+ private void handleRemoveNetworkRequests(@NonNull final Set<NetworkRequestInfo> nris) {
+ for (final NetworkRequestInfo nri : nris) {
+ if (mDefaultRequest == nri) {
+ // Make sure we never remove the default request.
+ continue;
+ }
+ handleRemoveNetworkRequest(nri);
+ }
+ }
+
private void cancelNpiRequests(@NonNull final NetworkRequestInfo nri) {
for (final NetworkRequest req : nri.mRequests) {
cancelNpiRequest(req);
@@ -4441,6 +4498,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
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;
+ try {
+ handleSetOemNetworkPreference(arg.first, arg.second);
+ } catch (RemoteException e) {
+ loge("handleMessage.EVENT_SET_OEM_NETWORK_PREFERENCE failed", e);
+ }
+ break;
+ case EVENT_REPORT_NETWORK_ACTIVITY:
+ mNetworkActivityTracker.handleReportNetworkActivity();
+ break;
case EVENT_UPDATE_ACTIVE_DATA_SUBID:
handleUpdateActiveDataSubId(msg.arg1);
break;
@@ -4746,183 +4816,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
/**
- * Prepare for a VPN application.
- * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
- * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
- *
- * @param oldPackage Package name of the application which currently controls VPN, which will
- * be replaced. If there is no such application, this should should either be
- * {@code null} or {@link VpnConfig.LEGACY_VPN}.
- * @param newPackage Package name of the application which should gain control of VPN, or
- * {@code null} to disable.
- * @param userId User for whom to prepare the new VPN.
- *
- * @hide
- */
- @Override
- public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
- int userId) {
- enforceCrossUserPermission(userId);
-
- synchronized (mVpns) {
- throwIfLockdownEnabled();
- Vpn vpn = mVpns.get(userId);
- if (vpn != null) {
- return vpn.prepare(oldPackage, newPackage, VpnManager.TYPE_VPN_SERVICE);
- } else {
- return false;
- }
- }
- }
-
- /**
- * Set whether the VPN package has the ability to launch VPNs without user intervention. This
- * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
- * class. If the caller is not {@code userId}, {@link
- * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
- *
- * @param packageName The package for which authorization state should change.
- * @param userId User for whom {@code packageName} is installed.
- * @param authorized {@code true} if this app should be able to start a VPN connection without
- * explicit user approval, {@code false} if not.
- * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
- * permissions should be granted. When unauthorizing an app, {@link
- * VpnManager.TYPE_VPN_NONE} should be used.
- * @hide
- */
- @Override
- public void setVpnPackageAuthorization(
- String packageName, int userId, @VpnManager.VpnType int vpnType) {
- enforceCrossUserPermission(userId);
-
- synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
- if (vpn != null) {
- vpn.setPackageAuthorization(packageName, vpnType);
- }
- }
- }
-
- /**
- * Configure a TUN interface and return its file descriptor. Parameters
- * are encoded and opaque to this class. This method is used by VpnBuilder
- * and not available in ConnectivityManager. Permissions are checked in
- * Vpn class.
- * @hide
- */
- @Override
- public ParcelFileDescriptor establishVpn(VpnConfig config) {
- int user = UserHandle.getUserId(mDeps.getCallingUid());
- synchronized (mVpns) {
- throwIfLockdownEnabled();
- return mVpns.get(user).establish(config);
- }
- }
-
- /**
- * Stores the given VPN profile based on the provisioning package name.
- *
- * <p>If there is already a VPN profile stored for the provisioning package, this call will
- * overwrite the profile.
- *
- * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
- * exclusively by the Settings app, and passed into the platform at startup time.
- *
- * @return {@code true} if user consent has already been granted, {@code false} otherwise.
- * @hide
- */
- @Override
- public boolean provisionVpnProfile(@NonNull VpnProfile profile, @NonNull String packageName) {
- final int user = UserHandle.getUserId(mDeps.getCallingUid());
- synchronized (mVpns) {
- return mVpns.get(user).provisionVpnProfile(packageName, profile, mKeyStore);
- }
- }
-
- /**
- * Deletes the stored VPN profile for the provisioning package
- *
- * <p>If there are no profiles for the given package, this method will silently succeed.
- *
- * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
- * exclusively by the Settings app, and passed into the platform at startup time.
- *
- * @hide
- */
- @Override
- public void deleteVpnProfile(@NonNull String packageName) {
- final int user = UserHandle.getUserId(mDeps.getCallingUid());
- synchronized (mVpns) {
- mVpns.get(user).deleteVpnProfile(packageName, mKeyStore);
- }
- }
-
- /**
- * Starts the VPN based on the stored profile for the given package
- *
- * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
- * exclusively by the Settings app, and passed into the platform at startup time.
- *
- * @throws IllegalArgumentException if no profile was found for the given package name.
- * @hide
- */
- @Override
- public void startVpnProfile(@NonNull String packageName) {
- final int user = UserHandle.getUserId(mDeps.getCallingUid());
- synchronized (mVpns) {
- throwIfLockdownEnabled();
- mVpns.get(user).startVpnProfile(packageName, mKeyStore);
- }
- }
-
- /**
- * Stops the Platform VPN if the provided package is running one.
- *
- * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
- * exclusively by the Settings app, and passed into the platform at startup time.
- *
- * @hide
- */
- @Override
- public void stopVpnProfile(@NonNull String packageName) {
- final int user = UserHandle.getUserId(mDeps.getCallingUid());
- synchronized (mVpns) {
- mVpns.get(user).stopVpnProfile(packageName);
- }
- }
-
- /**
- * Start legacy VPN, controlling native daemons as needed. Creates a
- * secondary thread to perform connection work, returning quickly.
- */
- @Override
- public void startLegacyVpn(VpnProfile profile) {
- int user = UserHandle.getUserId(mDeps.getCallingUid());
- final LinkProperties egress = getActiveLinkProperties();
- if (egress == null) {
- throw new IllegalStateException("Missing active network connection");
- }
- synchronized (mVpns) {
- throwIfLockdownEnabled();
- mVpns.get(user).startLegacyVpn(profile, mKeyStore, null /* underlying */, egress);
- }
- }
-
- /**
- * Return the information of the ongoing legacy VPN. This method is used
- * by VpnSettings and not available in ConnectivityManager. Permissions
- * are checked in Vpn class.
- */
- @Override
- public LegacyVpnInfo getLegacyVpnInfo(int userId) {
- enforceCrossUserPermission(userId);
-
- synchronized (mVpns) {
- return mVpns.get(userId).getLegacyVpnInfo();
- }
- }
-
- /**
* Return the information of all ongoing VPNs.
*
* <p>This method is used to update NetworkStatsService.
@@ -4931,10 +4824,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
private UnderlyingNetworkInfo[] getAllVpnInfo() {
ensureRunningOnConnectivityServiceThread();
- synchronized (mVpns) {
- if (mLockdownEnabled) {
- return new UnderlyingNetworkInfo[0];
- }
+ if (mLockdownEnabled) {
+ return new UnderlyingNetworkInfo[0];
}
List<UnderlyingNetworkInfo> infoList = new ArrayList<>();
for (NetworkAgentInfo nai : mNetworkAgentInfos) {
@@ -4990,25 +4881,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
nai.linkProperties.getInterfaceName(), interfaces);
}
- /**
- * Returns the information of the ongoing VPN for {@code userId}. This method is used by
- * VpnDialogs and not available in ConnectivityManager.
- * Permissions are checked in Vpn class.
- * @hide
- */
- @Override
- public VpnConfig getVpnConfig(int userId) {
- enforceCrossUserPermission(userId);
- synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
- if (vpn != null) {
- return vpn.getVpnConfig();
- } else {
- return null;
- }
- }
- }
-
// TODO This needs to be the default network that applies to the NAI.
private Network[] underlyingNetworksOrDefault(final int ownerUid,
Network[] underlyingNetworks) {
@@ -5096,195 +4968,54 @@ public class ConnectivityService extends IConnectivityManager.Stub
mVpnBlockedUidRanges = newVpnBlockedUidRanges;
}
- private boolean isLockdownVpnEnabled() {
- return mKeyStore.contains(Credentials.LOCKDOWN_VPN);
- }
-
@Override
- public boolean updateLockdownVpn() {
- // Allow the system UID for the system server and for Settings.
- // Also, for unit tests, allow the process that ConnectivityService is running in.
- if (mDeps.getCallingUid() != Process.SYSTEM_UID
- && Binder.getCallingPid() != Process.myPid()) {
- logw("Lockdown VPN only available to system process or AID_SYSTEM");
- return false;
- }
-
- synchronized (mVpns) {
- // Tear down existing lockdown if profile was removed
- mLockdownEnabled = isLockdownVpnEnabled();
- if (mLockdownEnabled) {
- byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
- if (profileTag == null) {
- loge("Lockdown VPN configured but cannot be read from keystore");
- return false;
- }
- String profileName = new String(profileTag);
- final VpnProfile profile = VpnProfile.decode(
- profileName, mKeyStore.get(Credentials.VPN + profileName));
- if (profile == null) {
- loge("Lockdown VPN configured invalid profile " + profileName);
- setLockdownTracker(null);
- return true;
- }
- int user = UserHandle.getUserId(mDeps.getCallingUid());
- Vpn vpn = mVpns.get(user);
- if (vpn == null) {
- logw("VPN for user " + user + " not ready yet. Skipping lockdown");
- return false;
- }
- setLockdownTracker(
- new LockdownVpnTracker(mContext, this, mHandler, mKeyStore, vpn, profile));
- } else {
- setLockdownTracker(null);
- }
- }
-
- return true;
- }
-
- /**
- * Internally set new {@link LockdownVpnTracker}, shutting down any existing
- * {@link LockdownVpnTracker}. Can be {@code null} to disable lockdown.
- */
- @GuardedBy("mVpns")
- private void setLockdownTracker(LockdownVpnTracker tracker) {
- // Shutdown any existing tracker
- final LockdownVpnTracker existing = mLockdownTracker;
- // TODO: Add a trigger when the always-on VPN enable/disable to reevaluate and send the
- // necessary onBlockedStatusChanged callbacks.
- mLockdownTracker = null;
- if (existing != null) {
- existing.shutdown();
- }
-
- if (tracker != null) {
- mLockdownTracker = tracker;
- mLockdownTracker.init();
- }
- }
-
- /**
- * Throws if there is any currently running, always-on Legacy VPN.
- *
- * <p>The LockdownVpnTracker and mLockdownEnabled both track whether an always-on Legacy VPN is
- * running across the entire system. Tracking for app-based VPNs is done on a per-user,
- * per-package basis in Vpn.java
- */
- @GuardedBy("mVpns")
- private void throwIfLockdownEnabled() {
- if (mLockdownEnabled) {
- throw new IllegalStateException("Unavailable in lockdown mode");
- }
+ public void setLegacyLockdownVpnEnabled(boolean enabled) {
+ enforceSettingsPermission();
+ mHandler.post(() -> mLockdownEnabled = enabled);
}
- /**
- * Starts the always-on VPN {@link VpnService} for user {@param userId}, which should perform
- * some setup and then call {@code establish()} to connect.
- *
- * @return {@code true} if the service was started, the service was already connected, or there
- * was no always-on VPN to start. {@code false} otherwise.
- */
- private boolean startAlwaysOnVpn(int userId) {
- synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
- if (vpn == null) {
- // Shouldn't happen as all code paths that point here should have checked the Vpn
- // exists already.
- Log.wtf(TAG, "User " + userId + " has no Vpn configuration");
- return false;
- }
-
- return vpn.startAlwaysOnVpn(mKeyStore);
- }
+ private boolean isLegacyLockdownNai(NetworkAgentInfo nai) {
+ return mLockdownEnabled
+ && getVpnType(nai) == VpnManager.TYPE_VPN_LEGACY
+ && nai.networkCapabilities.appliesToUid(Process.FIRST_APPLICATION_UID);
}
- @Override
- public boolean isAlwaysOnVpnPackageSupported(int userId, String packageName) {
- enforceSettingsPermission();
- enforceCrossUserPermission(userId);
-
- synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
- if (vpn == null) {
- logw("User " + userId + " has no Vpn configuration");
- return false;
- }
- return vpn.isAlwaysOnPackageSupported(packageName, mKeyStore);
+ private NetworkAgentInfo getLegacyLockdownNai() {
+ if (!mLockdownEnabled) {
+ return null;
}
- }
+ // The legacy lockdown VPN always only applies to userId 0.
+ final NetworkAgentInfo nai = getVpnForUid(Process.FIRST_APPLICATION_UID);
+ if (nai == null || !isLegacyLockdownNai(nai)) return null;
- @Override
- public boolean setAlwaysOnVpnPackage(
- int userId, String packageName, boolean lockdown, List<String> lockdownWhitelist) {
- enforceControlAlwaysOnVpnPermission();
- enforceCrossUserPermission(userId);
-
- synchronized (mVpns) {
- // Can't set always-on VPN if legacy VPN is already in lockdown mode.
- if (isLockdownVpnEnabled()) {
- return false;
- }
-
- Vpn vpn = mVpns.get(userId);
- if (vpn == null) {
- logw("User " + userId + " has no Vpn configuration");
- return false;
- }
- if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist, mKeyStore)) {
- return false;
- }
- if (!startAlwaysOnVpn(userId)) {
- vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
- return false;
- }
+ // The legacy lockdown VPN must always have exactly one underlying network.
+ // This code may run on any thread and declaredUnderlyingNetworks may change, so store it in
+ // a local variable. There is no need to make a copy because its contents cannot change.
+ final Network[] underlying = nai.declaredUnderlyingNetworks;
+ if (underlying == null || underlying.length != 1) {
+ return null;
}
- return true;
- }
- @Override
- public String getAlwaysOnVpnPackage(int userId) {
- enforceControlAlwaysOnVpnPermission();
- enforceCrossUserPermission(userId);
-
- synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
- if (vpn == null) {
- logw("User " + userId + " has no Vpn configuration");
- return null;
- }
- return vpn.getAlwaysOnPackage();
+ // 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.
+ final NetworkAgentInfo defaultNetwork = getDefaultNetwork();
+ if (defaultNetwork == null || !defaultNetwork.network.equals(underlying[0])) {
+ return null;
}
- }
- @Override
- public boolean isVpnLockdownEnabled(int userId) {
- enforceControlAlwaysOnVpnPermission();
- enforceCrossUserPermission(userId);
-
- synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
- if (vpn == null) {
- logw("User " + userId + " has no Vpn configuration");
- return false;
- }
- return vpn.getLockdown();
- }
- }
+ return nai;
+ };
- @Override
- public List<String> getVpnLockdownWhitelist(int userId) {
- enforceControlAlwaysOnVpnPermission();
- enforceCrossUserPermission(userId);
-
- synchronized (mVpns) {
- Vpn vpn = mVpns.get(userId);
- if (vpn == null) {
- logw("User " + userId + " has no Vpn configuration");
- return null;
- }
- return vpn.getLockdownAllowlist();
+ private DetailedState getLegacyLockdownState(DetailedState origState) {
+ if (origState != DetailedState.CONNECTED) {
+ return origState;
}
+ return (mLockdownEnabled && getLegacyLockdownNai() == null)
+ ? DetailedState.CONNECTING
+ : DetailedState.CONNECTED;
}
@Override
@@ -5319,111 +5050,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- private void onUserStarted(int userId) {
- synchronized (mVpns) {
- Vpn userVpn = mVpns.get(userId);
- if (userVpn != null) {
- loge("Starting user already has a VPN");
- return;
- }
- userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId, mKeyStore);
- mVpns.put(userId, userVpn);
- if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) {
- updateLockdownVpn();
- }
- }
- }
-
- private void onUserStopped(int userId) {
- synchronized (mVpns) {
- Vpn userVpn = mVpns.get(userId);
- if (userVpn == null) {
- loge("Stopped user has no VPN");
- return;
- }
- userVpn.onUserStopped();
- mVpns.delete(userId);
- }
- }
-
- private void onUserAdded(int userId) {
- mPermissionMonitor.onUserAdded(userId);
- synchronized (mVpns) {
- final int vpnsSize = mVpns.size();
- for (int i = 0; i < vpnsSize; i++) {
- Vpn vpn = mVpns.valueAt(i);
- vpn.onUserAdded(userId);
- }
- }
- }
-
- private void onUserRemoved(int userId) {
- mPermissionMonitor.onUserRemoved(userId);
- synchronized (mVpns) {
- final int vpnsSize = mVpns.size();
- for (int i = 0; i < vpnsSize; i++) {
- Vpn vpn = mVpns.valueAt(i);
- vpn.onUserRemoved(userId);
- }
- }
- }
-
- private void onPackageReplaced(String packageName, int uid) {
- if (TextUtils.isEmpty(packageName) || uid < 0) {
- Log.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid);
- return;
- }
- final int userId = UserHandle.getUserId(uid);
- synchronized (mVpns) {
- final Vpn vpn = mVpns.get(userId);
- if (vpn == null) {
- return;
- }
- // Legacy always-on VPN won't be affected since the package name is not set.
- if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
- log("Restarting always-on VPN package " + packageName + " for user "
- + userId);
- vpn.startAlwaysOnVpn(mKeyStore);
- }
- }
- }
-
- private void onPackageRemoved(String packageName, int uid, boolean isReplacing) {
- if (TextUtils.isEmpty(packageName) || uid < 0) {
- Log.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
- return;
- }
-
- final int userId = UserHandle.getUserId(uid);
- synchronized (mVpns) {
- final Vpn vpn = mVpns.get(userId);
- if (vpn == null) {
- return;
- }
- // Legacy always-on VPN won't be affected since the package name is not set.
- if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
- log("Removing always-on VPN package " + packageName + " for user "
- + userId);
- vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
- }
- }
- }
-
- private void onUserUnlocked(int userId) {
- synchronized (mVpns) {
- // User present may be sent because of an unlock, which might mean an unlocked keystore.
- if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) {
- updateLockdownVpn();
- } else {
- startAlwaysOnVpn(userId);
- }
- }
+ private void onUserAdded(UserHandle user) {
+ mPermissionMonitor.onUserAdded(user);
}
- private void onVpnLockdownReset() {
- synchronized (mVpns) {
- if (mLockdownTracker != null) mLockdownTracker.reset();
- }
+ private void onUserRemoved(UserHandle user) {
+ mPermissionMonitor.onUserRemoved(user);
}
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@@ -5431,53 +5063,24 @@ public class ConnectivityService extends IConnectivityManager.Stub
public void onReceive(Context context, Intent intent) {
ensureRunningOnConnectivityServiceThread();
final String action = intent.getAction();
- final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
- final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
- final Uri packageData = intent.getData();
- final String packageName =
- packageData != null ? packageData.getSchemeSpecificPart() : null;
+ final UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
- if (LockdownVpnTracker.ACTION_LOCKDOWN_RESET.equals(action)) {
- onVpnLockdownReset();
+ // User should be filled for below intents, check the existence.
+ if (user == null) {
+ Log.wtf(TAG, intent.getAction() + " broadcast without EXTRA_USER");
+ return;
}
- // UserId should be filled for below intents, check the existence.
- if (userId == UserHandle.USER_NULL) return;
-
- if (Intent.ACTION_USER_STARTED.equals(action)) {
- onUserStarted(userId);
- } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
- onUserStopped(userId);
- } else if (Intent.ACTION_USER_ADDED.equals(action)) {
- onUserAdded(userId);
+ if (Intent.ACTION_USER_ADDED.equals(action)) {
+ onUserAdded(user);
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
- onUserRemoved(userId);
- } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
- onUserUnlocked(userId);
- } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
- onPackageReplaced(packageName, uid);
- } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
- final boolean isReplacing = intent.getBooleanExtra(
- Intent.EXTRA_REPLACING, false);
- onPackageRemoved(packageName, uid, isReplacing);
- } else {
+ onUserRemoved(user);
+ } else {
Log.wtf(TAG, "received unexpected intent: " + action);
}
}
};
- private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // Try creating lockdown tracker, since user present usually means
- // unlocked keystore.
- updateLockdownVpn();
- // Use the same context that registered receiver before to unregister it. Because use
- // different context to unregister receiver will cause exception.
- context.unregisterReceiver(this);
- }
- };
-
private final HashMap<Messenger, NetworkProviderInfo> mNetworkProviderInfos = new HashMap<>();
private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests = new HashMap<>();
@@ -5576,10 +5179,29 @@ public class ConnectivityService extends IConnectivityManager.Stub
final PendingIntent mPendingIntent;
boolean mPendingIntentSent;
+ @Nullable
+ final Messenger mMessenger;
+ @Nullable
private final IBinder mBinder;
final int mPid;
final int mUid;
- final Messenger messenger;
+ @Nullable
+ final String mCallingAttributionTag;
+ // In order to preserve the mapping of NetworkRequest-to-callback when apps register
+ // callbacks using a returned NetworkRequest, the original NetworkRequest needs to be
+ // maintained for keying off of. This is only a concern when the original nri
+ // mNetworkRequests changes which happens currently for apps that register callbacks to
+ // track the default network. In those cases, the nri is updated to have mNetworkRequests
+ // that match the per-app default nri that currently tracks the calling app's uid so that
+ // callbacks are fired at the appropriate time. When the callbacks fire,
+ // mNetworkRequestForCallback will be used so as to preserve the caller's mapping. When
+ // callbacks are updated to key off of an nri vs NetworkRequest, this stops being an issue.
+ // TODO b/177608132: make sure callbacks are indexed by NRIs and not NetworkRequest objects.
+ @NonNull
+ private final NetworkRequest mNetworkRequestForCallback;
+ NetworkRequest getNetworkRequestForCallback() {
+ return mNetworkRequestForCallback;
+ }
/**
* Get the list of UIDs this nri applies to.
@@ -5593,27 +5215,45 @@ public class ConnectivityService extends IConnectivityManager.Stub
return uids;
}
- NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
+ NetworkRequestInfo(@NonNull final NetworkRequest r, @Nullable final PendingIntent pi,
+ @Nullable String callingAttributionTag) {
+ this(Collections.singletonList(r), r, pi, callingAttributionTag);
+ }
+
+ NetworkRequestInfo(@NonNull final List<NetworkRequest> r,
+ @NonNull final NetworkRequest requestForCallback, @Nullable final PendingIntent pi,
+ @Nullable String callingAttributionTag) {
+ ensureAllNetworkRequestsHaveType(r);
mRequests = initializeRequests(r);
- ensureAllNetworkRequestsHaveType(mRequests);
+ mNetworkRequestForCallback = requestForCallback;
mPendingIntent = pi;
- messenger = null;
+ mMessenger = null;
mBinder = null;
mPid = getCallingPid();
mUid = mDeps.getCallingUid();
mNetworkRequestCounter.incrementCountOrThrow(mUid);
+ 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);
}
- NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder) {
+ NetworkRequestInfo(@NonNull final List<NetworkRequest> r,
+ @NonNull final NetworkRequest requestForCallback, @Nullable final Messenger m,
+ @Nullable final IBinder binder, @Nullable String callingAttributionTag) {
super();
- messenger = m;
+ ensureAllNetworkRequestsHaveType(r);
mRequests = initializeRequests(r);
- ensureAllNetworkRequestsHaveType(mRequests);
+ mNetworkRequestForCallback = requestForCallback;
+ mMessenger = m;
mBinder = binder;
mPid = getCallingPid();
mUid = mDeps.getCallingUid();
mPendingIntent = null;
mNetworkRequestCounter.incrementCountOrThrow(mUid);
+ mCallingAttributionTag = callingAttributionTag;
try {
mBinder.linkToDeath(this, 0);
@@ -5622,8 +5262,26 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- NetworkRequestInfo(NetworkRequest r) {
- this(r, null);
+ NetworkRequestInfo(@NonNull final NetworkRequestInfo nri,
+ @NonNull final List<NetworkRequest> r) {
+ super();
+ ensureAllNetworkRequestsHaveType(r);
+ mRequests = initializeRequests(r);
+ mNetworkRequestForCallback = nri.getNetworkRequestForCallback();
+ mMessenger = nri.mMessenger;
+ mBinder = nri.mBinder;
+ mPid = nri.mPid;
+ mUid = nri.mUid;
+ mPendingIntent = nri.mPendingIntent;
+ mCallingAttributionTag = nri.mCallingAttributionTag;
+ }
+
+ NetworkRequestInfo(@NonNull final NetworkRequest r) {
+ this(Collections.singletonList(r));
+ }
+
+ NetworkRequestInfo(@NonNull final List<NetworkRequest> r) {
+ this(r, r.get(0), null /* pi */, null /* callingAttributionTag */);
}
// True if this NRI is being satisfied. It also accounts for if the nri has its satisifer
@@ -5637,9 +5295,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
return mRequests.size() > 1;
}
- private List<NetworkRequest> initializeRequests(NetworkRequest r) {
- final ArrayList<NetworkRequest> tempRequests = new ArrayList<>();
- tempRequests.add(new NetworkRequest(r));
+ private List<NetworkRequest> initializeRequests(List<NetworkRequest> r) {
+ // Creating a defensive copy to prevent the sender from modifying the list being
+ // reflected in the return value of this method.
+ final List<NetworkRequest> tempRequests = new ArrayList<>(r);
return Collections.unmodifiableList(tempRequests);
}
@@ -5768,6 +5427,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
throw new SecurityException("Insufficient permissions to specify legacy type");
}
}
+ final NetworkCapabilities defaultNc = mDefaultRequest.mRequests.get(0).networkCapabilities;
final int callingUid = mDeps.getCallingUid();
final NetworkRequest.Type reqType;
try {
@@ -5778,11 +5438,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
switch (reqType) {
case TRACK_DEFAULT:
// If the request type is TRACK_DEFAULT, the passed {@code networkCapabilities}
- // is unused and will be replaced by the one from the default network request.
- // This allows callers to keep track of the system default network.
- networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid);
+ // is unused and will be replaced by ones appropriate for the caller.
+ // This allows callers to keep track of the default network for their app.
+ networkCapabilities = copyDefaultNetworkCapabilitiesForUid(
+ defaultNc, callingUid, callingPackageName);
enforceAccessPermission();
break;
+ case TRACK_SYSTEM_DEFAULT:
+ enforceSettingsPermission();
+ networkCapabilities = new NetworkCapabilities(defaultNc);
+ break;
case BACKGROUND_REQUEST:
enforceNetworkStackOrSettingsPermission();
// Fall-through since other checks are the same with normal requests.
@@ -5801,6 +5466,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
ensureRequestableCapabilities(networkCapabilities);
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.
// This will overwrite any allowed UIDs in the requested capabilities. Though there
@@ -5814,11 +5480,23 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
ensureValid(networkCapabilities);
- NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
+ final NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
nextNetworkRequestId(), reqType);
- NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
+ final NetworkRequestInfo nri = getNriToRegister(
+ networkRequest, messenger, binder, callingAttributionTag);
if (DBG) log("requestNetwork for " + nri);
+ // For TRACK_SYSTEM_DEFAULT callbacks, the capabilities have been modified since they were
+ // copied from the default request above. (This is necessary to ensure, for example, that
+ // the callback does not leak sensitive information to unprivileged apps.) Check that the
+ // changes don't alter request matching.
+ if (reqType == NetworkRequest.Type.TRACK_SYSTEM_DEFAULT &&
+ (!networkCapabilities.equalRequestableCapabilities(defaultNc))) {
+ throw new IllegalStateException(
+ "TRACK_SYSTEM_DEFAULT capabilities don't match default request: "
+ + networkCapabilities + " vs. " + defaultNc);
+ }
+
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri));
if (timeoutMs > 0) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NETWORK_REQUEST,
@@ -5827,6 +5505,30 @@ public class ConnectivityService extends IConnectivityManager.Stub
return networkRequest;
}
+ /**
+ * Return the nri to be used when registering a network request. Specifically, this is used with
+ * requests registered to track the default request. If there is currently a per-app default
+ * tracking the app requestor, then we need to create a version of this nri that mirrors that of
+ * the tracking per-app default so that callbacks are sent to the app requestor appropriately.
+ * @param nr the network request for the nri.
+ * @param msgr the messenger for the nri.
+ * @param binder the binder for the nri.
+ * @param callingAttributionTag the calling attribution tag for the nri.
+ * @return the nri to register.
+ */
+ private NetworkRequestInfo getNriToRegister(@NonNull final NetworkRequest nr,
+ @Nullable final Messenger msgr, @Nullable final IBinder binder,
+ @Nullable String callingAttributionTag) {
+ final List<NetworkRequest> requests;
+ if (NetworkRequest.Type.TRACK_DEFAULT == nr.type) {
+ requests = copyDefaultNetworkRequestsForUid(
+ nr.getRequestorUid(), nr.getRequestorPackageName());
+ } else {
+ requests = Collections.singletonList(nr);
+ }
+ return new NetworkRequestInfo(requests, nr, msgr, binder, callingAttributionTag);
+ }
+
private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities,
String callingPackageName, String callingAttributionTag) {
if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) == false) {
@@ -5905,7 +5607,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
- NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
+ NetworkRequestInfo nri =
+ new NetworkRequestInfo(networkRequest, operation, callingAttributionTag);
if (DBG) log("pendingRequest for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT,
nri));
@@ -5949,7 +5652,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
- Messenger messenger, IBinder binder, @NonNull String callingPackageName) {
+ Messenger messenger, IBinder binder, @NonNull String callingPackageName,
+ @Nullable String callingAttributionTag) {
final int callingUid = mDeps.getCallingUid();
if (!hasWifiNetworkListenPermission(networkCapabilities)) {
enforceAccessPermission();
@@ -5969,7 +5673,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
- NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
+ NetworkRequestInfo nri =
+ new NetworkRequestInfo(networkRequest, messenger, binder, callingAttributionTag);
if (VDBG) log("listenForNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -5978,7 +5683,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
- PendingIntent operation, @NonNull String callingPackageName) {
+ PendingIntent operation, @NonNull String callingPackageName,
+ @Nullable String callingAttributionTag) {
Objects.requireNonNull(operation, "PendingIntent cannot be null.");
final int callingUid = mDeps.getCallingUid();
if (!hasWifiNetworkListenPermission(networkCapabilities)) {
@@ -5992,7 +5698,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
- NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
+ NetworkRequestInfo nri =
+ new NetworkRequestInfo(networkRequest, operation, callingAttributionTag);
if (VDBG) log("pendingListenForNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -6095,19 +5802,122 @@ public class ConnectivityService extends IConnectivityManager.Stub
@GuardedBy("mBlockedAppUids")
private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
+ // Current OEM network preferences.
+ @NonNull
+ private OemNetworkPreferences mOemNetworkPreferences =
+ new OemNetworkPreferences.Builder().build();
+
// The always-on request for an Internet-capable network that apps without a specific default
// fall back to.
+ @VisibleForTesting
@NonNull
- private final NetworkRequestInfo mDefaultRequest;
+ final NetworkRequestInfo mDefaultRequest;
// Collection of NetworkRequestInfo's used for default networks.
+ @VisibleForTesting
@NonNull
- private final ArraySet<NetworkRequestInfo> mDefaultNetworkRequests = new ArraySet<>();
+ final ArraySet<NetworkRequestInfo> mDefaultNetworkRequests = new ArraySet<>();
private boolean isPerAppDefaultRequest(@NonNull final NetworkRequestInfo nri) {
return (mDefaultNetworkRequests.contains(nri) && mDefaultRequest != nri);
}
/**
+ * Return the default network request currently tracking the given uid.
+ * @param uid the uid to check.
+ * @return the NetworkRequestInfo tracking the given uid.
+ */
+ @NonNull
+ private NetworkRequestInfo getDefaultRequestTrackingUid(@NonNull final int uid) {
+ for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+ if (nri == mDefaultRequest) {
+ continue;
+ }
+ // Checking the first request is sufficient as only multilayer requests will have more
+ // than one request and for multilayer, all requests will track the same uids.
+ if (nri.mRequests.get(0).networkCapabilities.appliesToUid(uid)) {
+ return nri;
+ }
+ }
+ return mDefaultRequest;
+ }
+
+ /**
+ * Get a copy of the network requests of the default request that is currently tracking the
+ * given uid.
+ * @param requestorUid the uid to check the default for.
+ * @param requestorPackageName the requestor's package name.
+ * @return a copy of the default's NetworkRequest that is tracking the given uid.
+ */
+ @NonNull
+ private List<NetworkRequest> copyDefaultNetworkRequestsForUid(
+ @NonNull final int requestorUid, @NonNull final String requestorPackageName) {
+ return copyNetworkRequestsForUid(
+ getDefaultRequestTrackingUid(requestorUid).mRequests,
+ requestorUid, requestorPackageName);
+ }
+
+ /**
+ * Copy the given nri's NetworkRequest collection.
+ * @param requestsToCopy the NetworkRequest collection to be copied.
+ * @param requestorUid the uid to set on the copied collection.
+ * @param requestorPackageName the package name to set on the copied collection.
+ * @return the copied NetworkRequest collection.
+ */
+ @NonNull
+ private List<NetworkRequest> copyNetworkRequestsForUid(
+ @NonNull final List<NetworkRequest> requestsToCopy, @NonNull final int requestorUid,
+ @NonNull final String requestorPackageName) {
+ final List<NetworkRequest> requests = new ArrayList<>();
+ for (final NetworkRequest nr : requestsToCopy) {
+ requests.add(new NetworkRequest(copyDefaultNetworkCapabilitiesForUid(
+ nr.networkCapabilities, requestorUid, requestorPackageName),
+ nr.legacyType, nextNetworkRequestId(), nr.type));
+ }
+ return requests;
+ }
+
+ @NonNull
+ private NetworkCapabilities copyDefaultNetworkCapabilitiesForUid(
+ @NonNull final NetworkCapabilities netCapToCopy, @NonNull final int requestorUid,
+ @NonNull final String requestorPackageName) {
+ final NetworkCapabilities netCap = new NetworkCapabilities(netCapToCopy);
+ netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
+ netCap.setSingleUid(requestorUid);
+ netCap.setUids(new ArraySet<>());
+ restrictRequestUidsForCallerAndSetRequestorInfo(
+ netCap, requestorUid, requestorPackageName);
+ return netCap;
+ }
+
+ /**
+ * Get the nri that is currently being tracked for callbacks by per-app defaults.
+ * @param nr the network request to check for equality against.
+ * @return the nri if one exists, null otherwise.
+ */
+ @Nullable
+ private NetworkRequestInfo getNriForAppRequest(@NonNull final NetworkRequest nr) {
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.getNetworkRequestForCallback().equals(nr)) {
+ return nri;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Check if an nri is currently being managed by per-app default networking.
+ * @param nri the nri to check.
+ * @return true if this nri is currently being managed by per-app default networking.
+ */
+ private boolean isPerAppTrackedNri(@NonNull final NetworkRequestInfo nri) {
+ // nri.mRequests.get(0) is only different from the original request filed in
+ // nri.getNetworkRequestForCallback() if nri.mRequests was changed by per-app default
+ // functionality therefore if these two don't match, it means this particular nri is
+ // currently being managed by a per-app default.
+ return nri.getNetworkRequestForCallback() != nri.mRequests.get(0);
+ }
+
+ /**
* Determine if an nri is a managed default request that disallows default networking.
* @param nri the request to evaluate
* @return true if device-default networking is disallowed
@@ -6404,20 +6214,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
Math.max(naData.getRefreshTimeMillis(), apiData.getRefreshTimeMillis()));
}
- // Prioritize the user portal URL from the network agent.
- if (apiData.getUserPortalUrl() != null && (naData.getUserPortalUrl() == null
- || TextUtils.isEmpty(naData.getUserPortalUrl().toSafeString()))) {
- captivePortalBuilder.setUserPortalUrl(apiData.getUserPortalUrl());
+ // Prioritize the user portal URL from the network agent if the source is authenticated.
+ if (apiData.getUserPortalUrl() != null && naData.getUserPortalUrlSource()
+ != CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) {
+ captivePortalBuilder.setUserPortalUrl(apiData.getUserPortalUrl(),
+ apiData.getUserPortalUrlSource());
}
- // Prioritize the venue information URL from the network agent.
- if (apiData.getVenueInfoUrl() != null && (naData.getVenueInfoUrl() == null
- || TextUtils.isEmpty(naData.getVenueInfoUrl().toSafeString()))) {
- captivePortalBuilder.setVenueInfoUrl(apiData.getVenueInfoUrl());
-
- // Note that venue friendly name can only come from the network agent because it is not
- // in use in RFC8908. However, if using the Capport venue URL, make sure that the
- // friendly name is not set from the network agent.
- captivePortalBuilder.setVenueFriendlyName(null);
+ // Prioritize the venue information URL from the network agent if the source is
+ // authenticated.
+ if (apiData.getVenueInfoUrl() != null && naData.getVenueInfoUrlSource()
+ != CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) {
+ captivePortalBuilder.setVenueInfoUrl(apiData.getVenueInfoUrl(),
+ apiData.getVenueInfoUrlSource());
}
return captivePortalBuilder.build();
}
@@ -7180,20 +6988,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void callCallbackForRequest(@NonNull final NetworkRequestInfo nri,
@NonNull final NetworkAgentInfo networkAgent, final int notificationType,
final int arg1) {
- if (nri.messenger == null) {
+ if (nri.mMessenger == null) {
// Default request has no msgr. Also prevents callbacks from being invoked for
// NetworkRequestInfos registered with ConnectivityDiagnostics requests. Those callbacks
// are Type.LISTEN, but should not have NetworkCallbacks invoked.
return;
}
Bundle bundle = new Bundle();
- // In the case of multi-layer NRIs, the first request is not necessarily the one that
- // is satisfied. This is vexing, but the ConnectivityManager code that receives this
- // callback is only using the request as a token to identify the callback, so it doesn't
- // matter too much at this point as long as the callback can be found.
// TODO b/177608132: make sure callbacks are indexed by NRIs and not NetworkRequest objects.
// TODO: check if defensive copies of data is needed.
- final NetworkRequest nrForCallback = new NetworkRequest(nri.mRequests.get(0));
+ final NetworkRequest nrForCallback = nri.getNetworkRequestForCallback();
putParcelable(bundle, nrForCallback);
Message msg = Message.obtain();
if (notificationType != ConnectivityManager.CALLBACK_UNAVAIL) {
@@ -7207,7 +7011,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, nri.mUid, nrForCallback.getRequestorPackageName()));
+ nc, nri.mUid, nrForCallback.getRequestorPackageName(),
+ nri.mCallingAttributionTag));
putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
networkAgent.linkProperties, nri.mPid, nri.mUid));
// For this notification, arg1 contains the blocked status.
@@ -7226,7 +7031,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, nri.mUid, nrForCallback.getRequestorPackageName()));
+ netCap, nri.mUid, nrForCallback.getRequestorPackageName(),
+ nri.mCallingAttributionTag));
break;
}
case ConnectivityManager.CALLBACK_IP_CHANGED: {
@@ -7247,7 +7053,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
String notification = ConnectivityManager.getCallbackName(notificationType);
log("sending notification " + notification + " for " + nrForCallback);
}
- nri.messenger.send(msg);
+ nri.mMessenger.send(msg);
} catch (RemoteException e) {
// may occur naturally in the race of binder death.
loge("RemoteException caught trying to send a callback msg for " + nrForCallback);
@@ -7286,7 +7092,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Tear the network down.
teardownUnneededNetwork(oldNetwork);
} else {
- // Put the network in the background.
+ // Put the network in the background if it doesn't satisfy any foreground request.
updateCapabilitiesForNetwork(oldNetwork);
}
}
@@ -7346,7 +7152,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
}
mNetworkActivityTracker.updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork);
- notifyLockdownVpn(newDefaultNetwork);
handleApplyDefaultProxy(null != newDefaultNetwork
? newDefaultNetwork.linkProperties.getHttpProxy() : null);
updateTcpBufferSizes(null != newDefaultNetwork
@@ -7555,6 +7360,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else {
if (VDBG || DDBG) log(" accepting network in place of null");
}
+
+ // To prevent constantly CPU wake up for nascent timer, if a network comes up
+ // and immediately satisfies a request then remove the timer. This will happen for
+ // all networks except in the case of an underlying network for a VCN.
+ if (newSatisfier.isNascent()) {
+ newSatisfier.unlingerRequest(NetworkRequest.REQUEST_ID_NONE);
+ }
+
newSatisfier.unlingerRequest(newRequest.requestId);
if (!newSatisfier.addRequest(newRequest)) {
Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
@@ -7724,19 +7537,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- // Update the linger state before processing listen callbacks, because the background
- // computation depends on whether the network is lingering. Don't send the LOSING callbacks
+ // Update the inactivity state before processing listen callbacks, because the background
+ // computation depends on whether the network is inactive. Don't send the LOSING callbacks
// just yet though, because they have to be sent after the listens are processed to keep
// backward compatibility.
- final ArrayList<NetworkAgentInfo> lingeredNetworks = new ArrayList<>();
+ final ArrayList<NetworkAgentInfo> inactiveNetworks = new ArrayList<>();
for (final NetworkAgentInfo nai : nais) {
- // Rematching may have altered the linger state of some networks, so update all linger
- // timers. updateLingerState reads the state from the network agent and does nothing
- // if the state has not changed : the source of truth is controlled with
- // NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been
- // called while rematching the individual networks above.
+ // Rematching may have altered the inactivity state of some networks, so update all
+ // inactivity timers. updateInactivityState reads the state from the network agent
+ // and does nothing if the state has not changed : the source of truth is controlled
+ // with NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which
+ // have been called while rematching the individual networks above.
if (updateInactivityState(nai, now)) {
- lingeredNetworks.add(nai);
+ inactiveNetworks.add(nai);
}
}
@@ -7773,7 +7586,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- for (final NetworkAgentInfo nai : lingeredNetworks) {
+ for (final NetworkAgentInfo nai : inactiveNetworks) {
+ // For nascent networks, if connecting with no foreground request, skip broadcasting
+ // LOSING for backward compatibility. This is typical when mobile data connected while
+ // wifi connected with mobile data always-on enabled.
+ if (nai.isNascent()) continue;
notifyNetworkLosing(nai, now);
}
@@ -7844,12 +7661,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
mDefaultInetConditionPublished = newDefaultNetwork.lastValidated ? 100 : 0;
mLegacyTypeTracker.add(
newDefaultNetwork.networkInfo.getType(), newDefaultNetwork);
- // If the legacy VPN is connected, notifyLockdownVpn may end up sending a broadcast
- // to reflect the NetworkInfo of this new network. This broadcast has to be sent
- // after the disconnect broadcasts above, but before the broadcasts sent by the
- // legacy type tracker below.
- // TODO : refactor this, it's too complex
- notifyLockdownVpn(newDefaultNetwork);
}
}
@@ -7907,18 +7718,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
sendInetConditionBroadcast(nai.networkInfo);
}
- private void notifyLockdownVpn(NetworkAgentInfo nai) {
- synchronized (mVpns) {
- if (mLockdownTracker != null) {
- if (nai != null && nai.isVPN()) {
- mLockdownTracker.onVpnStateChanged(nai.networkInfo);
- } else {
- mLockdownTracker.onNetworkInfoChanged();
- }
- }
- }
- }
-
@NonNull
private NetworkInfo mixInInfo(@NonNull final NetworkAgentInfo nai, @NonNull NetworkInfo info) {
final NetworkInfo newInfo = new NetworkInfo(info);
@@ -7957,7 +7756,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
oldInfo = networkAgent.networkInfo;
networkAgent.networkInfo = newInfo;
}
- notifyLockdownVpn(networkAgent);
if (DBG) {
log(networkAgent.toShortString() + " EVENT_NETWORK_INFO_CHANGED, going from "
@@ -8014,6 +7812,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
// doing.
updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
+ // Before first rematching networks, put an inactivity timer without any request, this
+ // allows {@code updateInactivityState} to update the state accordingly and prevent
+ // tearing down for any {@code unneeded} evaluation in this period.
+ // Note that the timer will not be rescheduled since the expiry time is
+ // fixed after connection regardless of the network satisfying other requests or not.
+ // But it will be removed as soon as the network satisfies a request for the first time.
+ networkAgent.lingerRequest(NetworkRequest.REQUEST_ID_NONE,
+ SystemClock.elapsedRealtime(), mNascentDelayMs);
+
// Consider network even though it is not yet validated.
rematchAllNetworksAndRequests();
@@ -8249,34 +8056,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
@Override
- public boolean addVpnAddress(String address, int prefixLength) {
- int user = UserHandle.getUserId(mDeps.getCallingUid());
- synchronized (mVpns) {
- throwIfLockdownEnabled();
- return mVpns.get(user).addAddress(address, prefixLength);
- }
- }
-
- @Override
- public boolean removeVpnAddress(String address, int prefixLength) {
- int user = UserHandle.getUserId(mDeps.getCallingUid());
- synchronized (mVpns) {
- throwIfLockdownEnabled();
- return mVpns.get(user).removeAddress(address, prefixLength);
- }
- }
-
- @Override
- public boolean setUnderlyingNetworksForVpn(Network[] networks) {
- int user = UserHandle.getUserId(mDeps.getCallingUid());
- final boolean success;
- synchronized (mVpns) {
- success = mVpns.get(user).setUnderlyingNetworks(networks);
- }
- return success;
- }
-
- @Override
public String getCaptivePortalServerUrl() {
enforceNetworkStackOrSettingsPermission();
String settingUrl = mContext.getResources().getString(
@@ -8355,8 +8134,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
return;
}
- final int userId = UserHandle.getCallingUserId();
-
final long token = Binder.clearCallingIdentity();
try {
final IpMemoryStore ipMemoryStore = IpMemoryStore.getMemoryStore(mContext);
@@ -8368,44 +8145,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Turn airplane mode off
setAirplaneMode(false);
- if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
- // Remove always-on package
- synchronized (mVpns) {
- final String alwaysOnPackage = getAlwaysOnVpnPackage(userId);
- if (alwaysOnPackage != null) {
- setAlwaysOnVpnPackage(userId, null, false, null);
- setVpnPackageAuthorization(alwaysOnPackage, userId, VpnManager.TYPE_VPN_NONE);
- }
-
- // Turn Always-on VPN off
- if (mLockdownEnabled && userId == UserHandle.USER_SYSTEM) {
- final long ident = Binder.clearCallingIdentity();
- try {
- mKeyStore.delete(Credentials.LOCKDOWN_VPN);
- mLockdownEnabled = false;
- setLockdownTracker(null);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- // Turn VPN off
- VpnConfig vpnConfig = getVpnConfig(userId);
- if (vpnConfig != null) {
- if (vpnConfig.legacy) {
- prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
- } else {
- // Prevent this app (packagename = vpnConfig.user) from initiating
- // VPN connections in the future without user intervention.
- setVpnPackageAuthorization(
- vpnConfig.user, userId, VpnManager.TYPE_VPN_NONE);
-
- prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
- }
- }
- }
- }
-
// restore private DNS settings to default mode (opportunistic)
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_PRIVATE_DNS)) {
Settings.Global.putString(mContext.getContentResolver(),
@@ -8497,41 +8236,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- @GuardedBy("mVpns")
- private Vpn getVpnIfOwner() {
- return getVpnIfOwner(mDeps.getCallingUid());
- }
-
- // TODO: stop calling into Vpn.java and get this information from data in this class.
- @GuardedBy("mVpns")
- private Vpn getVpnIfOwner(int uid) {
- final int user = UserHandle.getUserId(uid);
-
- final Vpn vpn = mVpns.get(user);
- if (vpn == null) {
- return null;
- } else {
- final UnderlyingNetworkInfo info = vpn.getUnderlyingNetworkInfo();
- return (info == null || info.ownerUid != uid) ? null : vpn;
- }
- }
-
- /**
- * Caller either needs to be an active VPN, or hold the NETWORK_STACK permission
- * for testing.
- */
- private Vpn enforceActiveVpnOrNetworkStackPermission() {
- if (checkNetworkStackPermission()) {
- return null;
- }
- synchronized (mVpns) {
- Vpn vpn = getVpnIfOwner();
- if (vpn != null) {
- return vpn;
- }
- }
- throw new SecurityException("App must either be an active VPN or have the NETWORK_STACK "
- + "permission");
+ private @VpnManager.VpnType int getVpnType(@Nullable NetworkAgentInfo vpn) {
+ if (vpn == null) return VpnManager.TYPE_VPN_NONE;
+ final TransportInfo ti = vpn.networkCapabilities.getTransportInfo();
+ if (!(ti instanceof VpnTransportInfo)) return VpnManager.TYPE_VPN_NONE;
+ return ((VpnTransportInfo) ti).type;
}
/**
@@ -8541,14 +8250,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
* connection is not found.
*/
public int getConnectionOwnerUid(ConnectionInfo connectionInfo) {
- final Vpn vpn = enforceActiveVpnOrNetworkStackPermission();
-
- // Only VpnService based VPNs should be able to get this information.
- if (vpn != null && vpn.getActiveAppVpnType() != VpnManager.TYPE_VPN_SERVICE) {
- throw new SecurityException(
- "getConnectionOwnerUid() not allowed for non-VpnService VPNs");
- }
-
if (connectionInfo.protocol != IPPROTO_TCP && connectionInfo.protocol != IPPROTO_UDP) {
throw new IllegalArgumentException("Unsupported protocol " + connectionInfo.protocol);
}
@@ -8556,28 +8257,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
final int uid = mDeps.getConnectionOwnerUid(connectionInfo.protocol,
connectionInfo.local, connectionInfo.remote);
- /* Filter out Uids not associated with the VPN. */
- if (vpn != null && !vpn.appliesToUid(uid)) {
- return INVALID_UID;
- }
+ if (uid == INVALID_UID) return uid; // Not found.
- return uid;
- }
+ // Connection owner UIDs are visible only to the network stack and to the VpnService-based
+ // VPN, if any, that applies to the UID that owns the connection.
+ if (checkNetworkStackPermission()) return uid;
- @Override
- public boolean isCallerCurrentAlwaysOnVpnApp() {
- synchronized (mVpns) {
- Vpn vpn = getVpnIfOwner();
- return vpn != null && vpn.getAlwaysOn();
+ final NetworkAgentInfo vpn = getVpnForUid(uid);
+ if (vpn == null || getVpnType(vpn) != VpnManager.TYPE_VPN_SERVICE
+ || vpn.networkCapabilities.getOwnerUid() != Binder.getCallingUid()) {
+ return INVALID_UID;
}
- }
- @Override
- public boolean isCallerCurrentAlwaysOnVpnLockdownApp() {
- synchronized (mVpns) {
- Vpn vpn = getVpnIfOwner();
- return vpn != null && vpn.getLockdown();
- }
+ return uid;
}
/**
@@ -9071,13 +8763,35 @@ public class ConnectivityService extends IConnectivityManager.Stub
* changes.
*/
private static final class LegacyNetworkActivityTracker {
+ private static final int NO_UID = -1;
private final Context mContext;
+ private final INetd mNetd;
private final INetworkManagementService mNMS;
+ private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners =
+ new RemoteCallbackList<>();
+ // Indicate the current system default network activity is active or not.
+ @GuardedBy("mActiveIdleTimers")
+ private boolean mNetworkActive;
+ @GuardedBy("mActiveIdleTimers")
+ private final ArrayMap<String, IdleTimerParams> mActiveIdleTimers = new ArrayMap();
+ private final Handler mHandler;
+
+ private class IdleTimerParams {
+ public final int timeout;
+ public final int transportType;
- LegacyNetworkActivityTracker(@NonNull Context context,
- @NonNull INetworkManagementService nms) {
+ IdleTimerParams(int timeout, int transport) {
+ this.timeout = timeout;
+ this.transportType = transport;
+ }
+ }
+
+ LegacyNetworkActivityTracker(@NonNull Context context, @NonNull Handler handler,
+ @NonNull INetworkManagementService nms, @NonNull INetd netd) {
mContext = context;
mNMS = nms;
+ mNetd = netd;
+ mHandler = handler;
try {
mNMS.registerObserver(mDataActivityObserver);
} catch (RemoteException e) {
@@ -9093,9 +8807,50 @@ public class ConnectivityService extends IConnectivityManager.Stub
long tsNanos, int uid) {
sendDataActivityBroadcast(transportTypeToLegacyType(transportType), active,
tsNanos);
+ synchronized (mActiveIdleTimers) {
+ mNetworkActive = active;
+ // If there are no idle timers, it means that system is not monitoring
+ // activity, so the system default network for those default network
+ // unspecified apps is always considered active.
+ //
+ // TODO: If the mActiveIdleTimers is empty, netd will actually not send
+ // any network activity change event. Whenever this event is received,
+ // the mActiveIdleTimers should be always not empty. The legacy behavior
+ // is no-op. Remove to refer to mNetworkActive only.
+ if (mNetworkActive || mActiveIdleTimers.isEmpty()) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(EVENT_REPORT_NETWORK_ACTIVITY));
+ }
+ }
}
};
+ // The network activity should only be updated from ConnectivityService handler thread
+ // when mActiveIdleTimers lock is held.
+ @GuardedBy("mActiveIdleTimers")
+ private void reportNetworkActive() {
+ final int length = mNetworkActivityListeners.beginBroadcast();
+ if (DDBG) log("reportNetworkActive, notify " + length + " listeners");
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive();
+ } catch (RemoteException | RuntimeException e) {
+ loge("Fail to send network activie to listener " + e);
+ }
+ }
+ } finally {
+ mNetworkActivityListeners.finishBroadcast();
+ }
+ }
+
+ @GuardedBy("mActiveIdleTimers")
+ public void handleReportNetworkActivity() {
+ synchronized (mActiveIdleTimers) {
+ reportNetworkActive();
+ }
+ }
+
// This is deprecated and only to support legacy use cases.
private int transportTypeToLegacyType(int type) {
switch (type) {
@@ -9160,10 +8915,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
return; // do not track any other networks
}
+ updateRadioPowerState(true /* isActive */, type);
+
if (timeout > 0 && iface != null) {
try {
- // TODO: Access INetd directly instead of NMS
- mNMS.addIdleTimer(iface, timeout, type);
+ synchronized (mActiveIdleTimers) {
+ // Networks start up.
+ mNetworkActive = true;
+ mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, type));
+ mNetd.idletimerAddInterface(iface, timeout, Integer.toString(type));
+ reportNetworkActive();
+ }
} catch (Exception e) {
// You shall not crash!
loge("Exception in setupDataActivityTracking " + e);
@@ -9178,16 +8940,28 @@ public class ConnectivityService extends IConnectivityManager.Stub
final String iface = networkAgent.linkProperties.getInterfaceName();
final NetworkCapabilities caps = networkAgent.networkCapabilities;
- if (iface != null && (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
- || caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) {
- try {
- // the call fails silently if no idle timer setup for this interface
- // TODO: Access INetd directly instead of NMS
- mNMS.removeIdleTimer(iface);
- } catch (Exception e) {
- // You shall not crash!
- loge("Exception in removeDataActivityTracking " + e);
+ if (iface == null) return;
+
+ final int type;
+ if (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+ type = NetworkCapabilities.TRANSPORT_CELLULAR;
+ } else if (caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+ type = NetworkCapabilities.TRANSPORT_WIFI;
+ } else {
+ return; // do not track any other networks
+ }
+
+ try {
+ updateRadioPowerState(false /* isActive */, type);
+ synchronized (mActiveIdleTimers) {
+ final IdleTimerParams params = mActiveIdleTimers.remove(iface);
+ // The call fails silently if no idle timer setup for this interface
+ mNetd.idletimerRemoveInterface(iface, params.timeout,
+ Integer.toString(params.transportType));
}
+ } catch (Exception e) {
+ // You shall not crash!
+ loge("Exception in removeDataActivityTracking " + e);
}
}
@@ -9203,6 +8977,53 @@ public class ConnectivityService extends IConnectivityManager.Stub
removeDataActivityTracking(oldNetwork);
}
}
+
+ private void updateRadioPowerState(boolean isActive, int transportType) {
+ final BatteryStatsManager bs = mContext.getSystemService(BatteryStatsManager.class);
+ switch (transportType) {
+ case NetworkCapabilities.TRANSPORT_CELLULAR:
+ bs.reportMobileRadioPowerState(isActive, NO_UID);
+ break;
+ case NetworkCapabilities.TRANSPORT_WIFI:
+ bs.reportWifiRadioPowerState(isActive, NO_UID);
+ break;
+ default:
+ logw("Untracked transport type:" + transportType);
+ }
+ }
+
+ public boolean isDefaultNetworkActive() {
+ synchronized (mActiveIdleTimers) {
+ // If there are no idle timers, it means that system is not monitoring activity,
+ // so the default network is always considered active.
+ //
+ // TODO : Distinguish between the cases where mActiveIdleTimers is empty because
+ // tracking is disabled (negative idle timer value configured), or no active default
+ // network. In the latter case, this reports active but it should report inactive.
+ return mNetworkActive || mActiveIdleTimers.isEmpty();
+ }
+ }
+
+ public void registerNetworkActivityListener(@NonNull INetworkActivityListener l) {
+ mNetworkActivityListeners.register(l);
+ }
+
+ public void unregisterNetworkActivityListener(@NonNull INetworkActivityListener l) {
+ mNetworkActivityListeners.unregister(l);
+ }
+
+ public void dump(IndentingPrintWriter pw) {
+ synchronized (mActiveIdleTimers) {
+ pw.print("mNetworkActive="); pw.println(mNetworkActive);
+ pw.println("Idle timers:");
+ for (HashMap.Entry<String, IdleTimerParams> ent : mActiveIdleTimers.entrySet()) {
+ pw.print(" "); pw.print(ent.getKey()); pw.println(":");
+ final IdleTimerParams params = ent.getValue();
+ pw.print(" timeout="); pw.print(params.timeout);
+ pw.print(" type="); pw.println(params.transportType);
+ }
+ }
+ }
}
/**
@@ -9255,10 +9076,275 @@ public class ConnectivityService extends IConnectivityManager.Stub
mQosCallbackTracker.unregisterCallback(callback);
}
+ private void enforceAutomotiveDevice() {
+ final boolean isAutomotiveDevice =
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ if (!isAutomotiveDevice) {
+ throw new UnsupportedOperationException(
+ "setOemNetworkPreference() is only available on automotive devices.");
+ }
+ }
+
+ /**
+ * Used by automotive devices to set the network preferences used to direct traffic at an
+ * application level as per the given OemNetworkPreferences. An example use-case would be an
+ * automotive OEM wanting to provide connectivity for applications critical to the usage of a
+ * vehicle via a particular network.
+ *
+ * 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
+ * to communicate completion of setOemNetworkPreference();
+ */
@Override
- public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) {
- // TODO http://b/176495594 track multiple default networks with networkPreferences
- if (DBG) log("setOemNetworkPreference() called with: " + preference.toString());
+ public void setOemNetworkPreference(
+ @NonNull final OemNetworkPreferences preference,
+ @Nullable final IOnSetOemNetworkPreferenceListener listener) {
+
+ enforceAutomotiveDevice();
+ enforceOemNetworkPreferencesPermission();
+
+ Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
+ validateOemNetworkPreferences(preference);
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_OEM_NETWORK_PREFERENCE,
+ new Pair<>(preference, listener)));
+ }
+
+ private void validateOemNetworkPreferences(@NonNull OemNetworkPreferences preference) {
+ for (@OemNetworkPreferences.OemNetworkPreference final int pref
+ : preference.getNetworkPreferences().values()) {
+ if (OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED == pref) {
+ final String msg = "OEM_NETWORK_PREFERENCE_UNINITIALIZED is an invalid value.";
+ throw new IllegalArgumentException(msg);
+ }
+ }
+ }
+
+ private void handleSetOemNetworkPreference(
+ @NonNull final OemNetworkPreferences preference,
+ @NonNull final IOnSetOemNetworkPreferenceListener listener) throws RemoteException {
+ Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
+ if (DBG) {
+ log("set OEM network preferences :" + preference.toString());
+ }
+ final ArraySet<NetworkRequestInfo> nris =
+ new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(preference);
+ updateDefaultNetworksForOemNetworkPreference(nris);
+ mOemNetworkPreferences = preference;
+ // TODO http://b/176496396 persist data to shared preferences.
+
+ if (null != listener) {
+ listener.onComplete();
+ }
+ }
+
+ private void updateDefaultNetworksForOemNetworkPreference(
+ @NonNull final Set<NetworkRequestInfo> nris) {
+ // Pass in a defensive copy as this collection will be updated on remove.
+ handleRemoveNetworkRequests(new ArraySet<>(mDefaultNetworkRequests));
+ addPerAppDefaultNetworkRequests(nris);
+ }
+
+ private void addPerAppDefaultNetworkRequests(@NonNull final Set<NetworkRequestInfo> nris) {
+ ensureRunningOnConnectivityServiceThread();
+ mDefaultNetworkRequests.addAll(nris);
+ final ArraySet<NetworkRequestInfo> perAppCallbackRequestsToUpdate =
+ getPerAppCallbackRequestsToUpdate();
+ handleRemoveNetworkRequests(perAppCallbackRequestsToUpdate);
+ final ArraySet<NetworkRequestInfo> nrisToRegister = new ArraySet<>(nris);
+ nrisToRegister.addAll(
+ createPerAppCallbackRequestsToRegister(perAppCallbackRequestsToUpdate));
+ handleRegisterNetworkRequests(nrisToRegister);
+ }
+
+ /**
+ * All current requests that are tracking the default network need to be assessed as to whether
+ * or not the current set of per-application default requests will be changing their default
+ * network. If so, those requests will need to be updated so that they will send callbacks for
+ * default network changes at the appropriate time. Additionally, those requests tracking the
+ * default that were previously updated by this flow will need to be reassessed.
+ * @return the nris which will need to be updated.
+ */
+ private ArraySet<NetworkRequestInfo> getPerAppCallbackRequestsToUpdate() {
+ final ArraySet<NetworkRequestInfo> defaultCallbackRequests = new ArraySet<>();
+ // Get the distinct nris to check since for multilayer requests, it is possible to have the
+ // same nri in the map's values for each of its NetworkRequest objects.
+ final ArraySet<NetworkRequestInfo> nris = new ArraySet<>(mNetworkRequests.values());
+ for (final NetworkRequestInfo nri : nris) {
+ // Include this nri if it is currently being tracked.
+ if (isPerAppTrackedNri(nri)) {
+ defaultCallbackRequests.add(nri);
+ continue;
+ }
+ // We only track callbacks for requests tracking the default.
+ if (NetworkRequest.Type.TRACK_DEFAULT != nri.mRequests.get(0).type) {
+ continue;
+ }
+ // Include this nri if it will be tracked by the new per-app default requests.
+ final boolean isNriGoingToBeTracked =
+ getDefaultRequestTrackingUid(nri.mUid) != mDefaultRequest;
+ if (isNriGoingToBeTracked) {
+ defaultCallbackRequests.add(nri);
+ }
+ }
+ return defaultCallbackRequests;
+ }
+
+ /**
+ * Create nris for those network requests that are currently tracking the default network that
+ * are being controlled by a per-application default.
+ * @param perAppCallbackRequestsForUpdate the baseline network requests to be used as the
+ * foundation when creating the nri. Important items include the calling uid's original
+ * NetworkRequest to be used when mapping callbacks as well as the caller's uid and name. These
+ * requests are assumed to have already been validated as needing to be updated.
+ * @return the Set of nris to use when registering network requests.
+ */
+ private ArraySet<NetworkRequestInfo> createPerAppCallbackRequestsToRegister(
+ @NonNull final ArraySet<NetworkRequestInfo> perAppCallbackRequestsForUpdate) {
+ final ArraySet<NetworkRequestInfo> callbackRequestsToRegister = new ArraySet<>();
+ for (final NetworkRequestInfo callbackRequest : perAppCallbackRequestsForUpdate) {
+ final NetworkRequestInfo trackingNri =
+ getDefaultRequestTrackingUid(callbackRequest.mUid);
+
+ // If this nri is not being tracked, the change it back to an untracked nri.
+ if (trackingNri == mDefaultRequest) {
+ callbackRequestsToRegister.add(new NetworkRequestInfo(
+ callbackRequest,
+ Collections.singletonList(callbackRequest.getNetworkRequestForCallback())));
+ continue;
+ }
+
+ final String requestorPackageName =
+ callbackRequest.mRequests.get(0).getRequestorPackageName();
+ callbackRequestsToRegister.add(new NetworkRequestInfo(
+ callbackRequest,
+ copyNetworkRequestsForUid(
+ trackingNri.mRequests, callbackRequest.mUid, requestorPackageName)));
+ }
+ return callbackRequestsToRegister;
+ }
+
+ /**
+ * Class used to generate {@link NetworkRequestInfo} based off of {@link OemNetworkPreferences}.
+ */
+ @VisibleForTesting
+ final class OemNetworkRequestFactory {
+ ArraySet<NetworkRequestInfo> createNrisFromOemNetworkPreferences(
+ @NonNull final OemNetworkPreferences preference) {
+ final ArraySet<NetworkRequestInfo> nris = new ArraySet<>();
+ final SparseArray<Set<Integer>> uids =
+ createUidsFromOemNetworkPreferences(preference);
+ for (int i = 0; i < uids.size(); i++) {
+ final int key = uids.keyAt(i);
+ final Set<Integer> value = uids.valueAt(i);
+ final NetworkRequestInfo nri = createNriFromOemNetworkPreferences(key, value);
+ // No need to add an nri without any requests.
+ if (0 == nri.mRequests.size()) {
+ continue;
+ }
+ nris.add(nri);
+ }
+
+ return nris;
+ }
+
+ private SparseArray<Set<Integer>> createUidsFromOemNetworkPreferences(
+ @NonNull final OemNetworkPreferences preference) {
+ final SparseArray<Set<Integer>> uids = new SparseArray<>();
+ final PackageManager pm = mContext.getPackageManager();
+ for (final Map.Entry<String, Integer> entry :
+ preference.getNetworkPreferences().entrySet()) {
+ @OemNetworkPreferences.OemNetworkPreference final int pref = entry.getValue();
+ try {
+ final int uid = pm.getApplicationInfo(entry.getKey(), 0).uid;
+ if (!uids.contains(pref)) {
+ uids.put(pref, new ArraySet<>());
+ }
+ uids.get(pref).add(uid);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Although this may seem like an error scenario, it is ok that uninstalled
+ // packages are sent on a network preference as the system will watch for
+ // package installations associated with this network preference and update
+ // accordingly. This is done so as to minimize race conditions on app install.
+ // TODO b/177092163 add app install watching.
+ continue;
+ }
+ }
+ return uids;
+ }
+
+ private NetworkRequestInfo createNriFromOemNetworkPreferences(
+ @OemNetworkPreferences.OemNetworkPreference final int preference,
+ @NonNull final Set<Integer> uids) {
+ final List<NetworkRequest> requests = new ArrayList<>();
+ // Requests will ultimately be evaluated by order of insertion therefore it matters.
+ switch (preference) {
+ case OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID:
+ requests.add(createUnmeteredNetworkRequest());
+ requests.add(createOemPaidNetworkRequest());
+ requests.add(createDefaultRequest());
+ break;
+ case OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK:
+ requests.add(createUnmeteredNetworkRequest());
+ requests.add(createOemPaidNetworkRequest());
+ break;
+ case OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY:
+ requests.add(createOemPaidNetworkRequest());
+ break;
+ case OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY:
+ requests.add(createOemPrivateNetworkRequest());
+ break;
+ default:
+ // This should never happen.
+ throw new IllegalArgumentException("createNriFromOemNetworkPreferences()"
+ + " called with invalid preference of " + preference);
+ }
+
+ setOemNetworkRequestUids(requests, uids);
+ return new NetworkRequestInfo(requests);
+ }
+
+ private NetworkRequest createUnmeteredNetworkRequest() {
+ final NetworkCapabilities netcap = createDefaultPerAppNetCap()
+ .addCapability(NET_CAPABILITY_NOT_METERED)
+ .addCapability(NET_CAPABILITY_VALIDATED);
+ return createNetworkRequest(NetworkRequest.Type.LISTEN, netcap);
+ }
+
+ private NetworkRequest createOemPaidNetworkRequest() {
+ // NET_CAPABILITY_OEM_PAID is a restricted capability.
+ final NetworkCapabilities netcap = createDefaultPerAppNetCap()
+ .addCapability(NET_CAPABILITY_OEM_PAID)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ return createNetworkRequest(NetworkRequest.Type.REQUEST, netcap);
+ }
+
+ private NetworkRequest createOemPrivateNetworkRequest() {
+ // NET_CAPABILITY_OEM_PRIVATE is a restricted capability.
+ final NetworkCapabilities netcap = createDefaultPerAppNetCap()
+ .addCapability(NET_CAPABILITY_OEM_PRIVATE)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ return createNetworkRequest(NetworkRequest.Type.REQUEST, netcap);
+ }
+
+ private NetworkCapabilities createDefaultPerAppNetCap() {
+ final NetworkCapabilities netCap = new NetworkCapabilities();
+ netCap.addCapability(NET_CAPABILITY_INTERNET);
+ netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
+ return netCap;
+ }
+
+ private void setOemNetworkRequestUids(@NonNull final List<NetworkRequest> requests,
+ @NonNull final Set<Integer> uids) {
+ final Set<UidRange> ranges = new ArraySet<>();
+ for (final int uid : uids) {
+ ranges.add(new UidRange(uid, uid));
+ }
+ for (final NetworkRequest req : requests) {
+ req.networkCapabilities.setUids(ranges);
+ }
+ }
}
private boolean isMobileNetwork(NetworkAgentInfo nai) {
diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java
index c56cef2d58dc..a83c981235df 100644
--- a/services/core/java/com/android/server/EntropyMixer.java
+++ b/services/core/java/com/android/server/EntropyMixer.java
@@ -16,12 +16,6 @@
package com.android.server;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -33,10 +27,15 @@ import android.os.Message;
import android.os.SystemProperties;
import android.util.Slog;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+
/**
* A service designed to load and periodically save &quot;randomness&quot;
- * for the Linux kernel RNG and to mix in data from Hardware RNG (if present)
- * into the Linux RNG.
+ * for the Linux kernel RNG.
*
* <p>When a Linux system starts up, the entropy pool associated with
* {@code /dev/random} may be in a fairly predictable state. Applications which
@@ -45,15 +44,8 @@ import android.util.Slog;
* this effect, it's helpful to carry the entropy pool information across
* shutdowns and startups.
*
- * <p>On systems with Hardware RNG (/dev/hw_random), a block of output from HW
- * RNG is mixed into the Linux RNG on EntropyMixer's startup and whenever
- * EntropyMixer periodically runs to save a block of output from Linux RNG on
- * disk. This mixing is done in a way that does not increase the Linux RNG's
- * entropy estimate is not increased. This is to avoid having to trust/verify
- * the quality and authenticity of the &quot;randomness&quot; of the HW RNG.
- *
* <p>This class was modeled after the script in the
- * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">
+ * <a href="https://man7.org/linux/man-pages/man4/random.4.html">
* random(4) manual page</a>.
*/
public class EntropyMixer extends Binder {
@@ -64,7 +56,6 @@ public class EntropyMixer extends Binder {
private static final long START_NANOTIME = System.nanoTime();
private final String randomDevice;
- private final String hwRandomDevice;
private final String entropyFile;
/**
@@ -80,7 +71,6 @@ public class EntropyMixer extends Binder {
Slog.e(TAG, "Will not process invalid message");
return;
}
- addHwRandomEntropy();
writeEntropy();
scheduleEntropyWriter();
}
@@ -94,25 +84,21 @@ public class EntropyMixer extends Binder {
};
public EntropyMixer(Context context) {
- this(context, getSystemDir() + "/entropy.dat", "/dev/urandom", "/dev/hw_random");
+ this(context, getSystemDir() + "/entropy.dat", "/dev/urandom");
}
/** Test only interface, not for public use */
public EntropyMixer(
Context context,
String entropyFile,
- String randomDevice,
- String hwRandomDevice) {
+ String randomDevice) {
if (randomDevice == null) { throw new NullPointerException("randomDevice"); }
- if (hwRandomDevice == null) { throw new NullPointerException("hwRandomDevice"); }
if (entropyFile == null) { throw new NullPointerException("entropyFile"); }
this.randomDevice = randomDevice;
- this.hwRandomDevice = hwRandomDevice;
this.entropyFile = entropyFile;
loadInitialEntropy();
addDeviceSpecificEntropy();
- addHwRandomEntropy();
writeEntropy();
scheduleEntropyWriter();
IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
@@ -192,23 +178,6 @@ public class EntropyMixer extends Binder {
}
}
- /**
- * Mixes in the output from HW RNG (if present) into the Linux RNG.
- */
- private void addHwRandomEntropy() {
- if (!new File(hwRandomDevice).exists()) {
- // HW RNG not present/exposed -- ignore
- return;
- }
-
- try {
- RandomBlock.fromFile(hwRandomDevice).toFile(randomDevice, false);
- Slog.i(TAG, "Added HW RNG output to entropy pool");
- } catch (IOException e) {
- Slog.w(TAG, "Failed to add HW RNG output to entropy pool", e);
- }
- }
-
private static String getSystemDir() {
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index f648c3e146de..b48bc900aa84 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -29,6 +29,7 @@ import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
import android.net.IIpSecService;
import android.net.INetd;
import android.net.InetAddresses;
@@ -41,6 +42,7 @@ import android.net.IpSecTransformResponse;
import android.net.IpSecTunnelInterfaceResponse;
import android.net.IpSecUdpEncapResponse;
import android.net.LinkAddress;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.TrafficStats;
import android.net.util.NetdService;
@@ -797,9 +799,15 @@ public class IpSecService extends IIpSecService.Stub {
}
}
- private final class TunnelInterfaceRecord extends OwnedResourceRecord {
+ /**
+ * Tracks an tunnel interface, and manages cleanup paths.
+ *
+ * <p>This class is not thread-safe, and expects that that users of this class will ensure
+ * synchronization and thread safety by holding the IpSecService.this instance lock
+ */
+ @VisibleForTesting
+ final class TunnelInterfaceRecord extends OwnedResourceRecord {
private final String mInterfaceName;
- private final Network mUnderlyingNetwork;
// outer addresses
private final String mLocalAddress;
@@ -810,6 +818,8 @@ public class IpSecService extends IIpSecService.Stub {
private final int mIfId;
+ private Network mUnderlyingNetwork;
+
TunnelInterfaceRecord(
int resourceId,
String interfaceName,
@@ -870,14 +880,22 @@ public class IpSecService extends IIpSecService.Stub {
releaseNetId(mOkey);
}
- public String getInterfaceName() {
- return mInterfaceName;
+ @GuardedBy("IpSecService.this")
+ public void setUnderlyingNetwork(Network underlyingNetwork) {
+ // When #applyTunnelModeTransform is called, this new underlying network will be used to
+ // update the output mark of the input transform.
+ mUnderlyingNetwork = underlyingNetwork;
}
+ @GuardedBy("IpSecService.this")
public Network getUnderlyingNetwork() {
return mUnderlyingNetwork;
}
+ public String getInterfaceName() {
+ return mInterfaceName;
+ }
+
/** Returns the local, outer address for the tunnelInterface */
public String getLocalAddress() {
return mLocalAddress;
@@ -1429,6 +1447,34 @@ public class IpSecService extends IIpSecService.Stub {
}
}
+ /** Set TunnelInterface to use a specific underlying network. */
+ @Override
+ public synchronized void setNetworkForTunnelInterface(
+ int tunnelResourceId, Network underlyingNetwork, String callingPackage) {
+ enforceTunnelFeatureAndPermissions(callingPackage);
+ Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
+
+ final UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+
+ // Get tunnelInterface record; if no such interface is found, will throw
+ // IllegalArgumentException. userRecord.mTunnelInterfaceRecords is never null
+ final TunnelInterfaceRecord tunnelInterfaceInfo =
+ userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
+
+ final ConnectivityManager connectivityManager =
+ mContext.getSystemService(ConnectivityManager.class);
+ final LinkProperties lp = connectivityManager.getLinkProperties(underlyingNetwork);
+ if (tunnelInterfaceInfo.getInterfaceName().equals(lp.getInterfaceName())) {
+ throw new IllegalArgumentException(
+ "Underlying network cannot be the network being exposed by this tunnel");
+ }
+
+ // It is meaningless to check if the network exists or is valid because the network might
+ // disconnect at any time after it passes the check.
+
+ tunnelInterfaceInfo.setUnderlyingNetwork(underlyingNetwork);
+ }
+
/**
* Delete a TunnelInterface that has been been allocated by and registered with the system
* server
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index d65f911d4a67..dc38067c98a3 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -69,7 +69,6 @@ import android.os.BatteryStats;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
-import android.os.INetworkActivityListener;
import android.os.INetworkManagementService;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -80,7 +79,6 @@ import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
-import android.telephony.DataConnectionRealTimeInfo;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -229,32 +227,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
@GuardedBy("mQuotaLock")
private volatile boolean mDataSaverMode;
- private final Object mIdleTimerLock = new Object();
- /** Set of interfaces with active idle timers. */
- private static class IdleTimerParams {
- public final int timeout;
- public final int type;
- public int networkCount;
-
- IdleTimerParams(int timeout, int type) {
- this.timeout = timeout;
- this.type = type;
- this.networkCount = 1;
- }
- }
- private HashMap<String, IdleTimerParams> mActiveIdleTimers = Maps.newHashMap();
-
private volatile boolean mFirewallEnabled;
private volatile boolean mStrictEnabled;
- private boolean mMobileActivityFromRadio = false;
- private int mLastPowerStateFromRadio = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
- private int mLastPowerStateFromWifi = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
-
- private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners =
- new RemoteCallbackList<>();
- private boolean mNetworkActive;
-
/**
* Constructs a new NetworkManagementService instance
*
@@ -397,55 +372,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
*/
private void notifyInterfaceClassActivity(int type, boolean isActive, long tsNanos,
int uid) {
- final boolean isMobile = ConnectivityManager.isNetworkTypeMobile(type);
- int powerState = isActive
- ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
- : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
- if (isMobile) {
- if (mLastPowerStateFromRadio != powerState) {
- mLastPowerStateFromRadio = powerState;
- try {
- // TODO: The interface changes that comes from netd are handled by BSS itself.
- // There are still events caused by setting or removing idle timer, so keep
- // reporting from here until setting idler timer moved to CS.
- getBatteryStats().noteMobileRadioPowerState(powerState, tsNanos, uid);
- } catch (RemoteException e) {
- }
- }
- }
-
- if (ConnectivityManager.isNetworkTypeWifi(type)) {
- if (mLastPowerStateFromWifi != powerState) {
- mLastPowerStateFromWifi = powerState;
- try {
- // TODO: The interface changes that comes from netd are handled by BSS itself.
- // There are still events caused by setting or removing idle timer, so keep
- // reporting from here until setting idler timer moved to CS.
- getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos, uid);
- } catch (RemoteException e) {
- }
- }
- }
-
- final boolean active = isActive;
invokeForAllObservers(o -> o.interfaceClassDataActivityChanged(
- type, active, tsNanos, uid));
-
- boolean report = false;
- synchronized (mIdleTimerLock) {
- if (mActiveIdleTimers.isEmpty()) {
- // If there are no idle timers, we are not monitoring activity, so we
- // are always considered active.
- isActive = true;
- }
- if (mNetworkActive != isActive) {
- mNetworkActive = isActive;
- report = isActive;
- }
- }
- if (report) {
- reportNetworkActive();
- }
+ type, isActive, tsNanos, uid));
}
@Override
@@ -1122,60 +1050,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
}
@Override
- public void addIdleTimer(String iface, int timeout, final int type) {
- NetworkStack.checkNetworkStackPermission(mContext);
-
- if (DBG) Slog.d(TAG, "Adding idletimer");
-
- synchronized (mIdleTimerLock) {
- IdleTimerParams params = mActiveIdleTimers.get(iface);
- if (params != null) {
- // the interface already has idletimer, update network count
- params.networkCount++;
- return;
- }
-
- try {
- mNetdService.idletimerAddInterface(iface, timeout, Integer.toString(type));
- } catch (RemoteException | ServiceSpecificException e) {
- throw new IllegalStateException(e);
- }
- mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, type));
-
- // Networks start up.
- if (ConnectivityManager.isNetworkTypeMobile(type)) {
- mNetworkActive = false;
- }
- mDaemonHandler.post(() -> notifyInterfaceClassActivity(type, true,
- SystemClock.elapsedRealtimeNanos(), -1));
- }
- }
-
- @Override
- public void removeIdleTimer(String iface) {
- NetworkStack.checkNetworkStackPermission(mContext);
-
- if (DBG) Slog.d(TAG, "Removing idletimer");
-
- synchronized (mIdleTimerLock) {
- final IdleTimerParams params = mActiveIdleTimers.get(iface);
- if (params == null || --(params.networkCount) > 0) {
- return;
- }
-
- try {
- mNetdService.idletimerRemoveInterface(iface,
- params.timeout, Integer.toString(params.type));
- } catch (RemoteException | ServiceSpecificException e) {
- throw new IllegalStateException(e);
- }
- mActiveIdleTimers.remove(iface);
- mDaemonHandler.post(() -> notifyInterfaceClassActivity(params.type, false,
- SystemClock.elapsedRealtimeNanos(), -1));
- }
- }
-
- @Override
public void setInterfaceQuota(String iface, long quotaBytes) {
NetworkStack.checkNetworkStackPermission(mContext);
@@ -1813,44 +1687,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
}
@Override
- public void registerNetworkActivityListener(INetworkActivityListener listener) {
- mNetworkActivityListeners.register(listener);
- }
-
- @Override
- public void unregisterNetworkActivityListener(INetworkActivityListener listener) {
- mNetworkActivityListeners.unregister(listener);
- }
-
- @Override
- public boolean isNetworkActive() {
- synchronized (mNetworkActivityListeners) {
- return mNetworkActive || mActiveIdleTimers.isEmpty();
- }
- }
-
- private void reportNetworkActive() {
- final int length = mNetworkActivityListeners.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive();
- } catch (RemoteException | RuntimeException e) {
- }
- }
- } finally {
- mNetworkActivityListeners.finishBroadcast();
- }
- }
-
- @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- pw.print("mMobileActivityFromRadio="); pw.print(mMobileActivityFromRadio);
- pw.print(" mLastPowerStateFromRadio="); pw.println(mLastPowerStateFromRadio);
- pw.print("mNetworkActive="); pw.println(mNetworkActive);
-
synchronized (mQuotaLock) {
pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
@@ -1882,17 +1721,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
mUidFirewallRestrictedRules);
}
- synchronized (mIdleTimerLock) {
- pw.println("Idle timers:");
- for (HashMap.Entry<String, IdleTimerParams> ent : mActiveIdleTimers.entrySet()) {
- pw.print(" "); pw.print(ent.getKey()); pw.println(":");
- IdleTimerParams params = ent.getValue();
- pw.print(" timeout="); pw.print(params.timeout);
- pw.print(" type="); pw.print(params.type);
- pw.print(" networkCount="); pw.println(params.networkCount);
- }
- }
-
pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
pw.print("Netd service status: " );
if (mNetdService == null) {
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index a6cfae492db9..e12586bfdc06 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,8 +1,8 @@
# Connectivity / Networking
per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkManagementService.java,NsdService.java = file:/services/core/java/com/android/server/net/OWNERS
-# Vibrator / Threads
-per-file VibratorManagerService.java, VibratorService.java, DisplayThread.java = michaelwr@google.com, ogunwale@google.com
+# Threads
+per-file DisplayThread.java = michaelwr@google.com, ogunwale@google.com
# Zram writeback
per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 41903fcd165f..f5c2aacaebb0 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -259,10 +259,7 @@ public class PackageWatchdog {
mIsPackagesReady = true;
mHealthCheckController.setCallbacks(packageName -> onHealthCheckPassed(packageName),
packages -> onSupportedPackages(packages),
- () -> {
- syncRequestsAsync();
- mSyncRequired = true;
- });
+ this::onSyncRequestNotified);
setPropertyChangedListenerLocked();
updateConfigs();
registerConnectivityModuleHealthListener();
@@ -537,6 +534,7 @@ public class PackageWatchdog {
synchronized (mLock) {
mIsHealthCheckEnabled = enabled;
mHealthCheckController.setEnabled(enabled);
+ mSyncRequired = true;
// Prune to update internal state whenever health check is enabled/disabled
syncState("health check state " + (enabled ? "enabled" : "disabled"));
}
@@ -788,6 +786,13 @@ public class PackageWatchdog {
}
}
+ private void onSyncRequestNotified() {
+ synchronized (mLock) {
+ mSyncRequired = true;
+ syncRequestsAsync();
+ }
+ }
+
@GuardedBy("mLock")
private Set<String> getPackagesPendingHealthChecksLocked() {
Set<String> packages = new ArraySet<>();
@@ -902,10 +907,13 @@ public class PackageWatchdog {
if (registeredObserver != null) {
Iterator<MonitoredPackage> it = failedPackages.iterator();
while (it.hasNext()) {
- VersionedPackage versionedPkg = it.next().mPackage;
- Slog.i(TAG, "Explicit health check failed for package " + versionedPkg);
- registeredObserver.execute(versionedPkg,
- PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK, 1);
+ VersionedPackage versionedPkg = getVersionedPackage(it.next().getName());
+ if (versionedPkg != null) {
+ Slog.i(TAG,
+ "Explicit health check failed for package " + versionedPkg);
+ registeredObserver.execute(versionedPkg,
+ PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK, 1);
+ }
}
}
}
@@ -1342,11 +1350,7 @@ public class PackageWatchdog {
MonitoredPackage newMonitoredPackage(String name, long durationMs, long healthCheckDurationMs,
boolean hasPassedHealthCheck, LongArrayQueue mitigationCalls) {
- VersionedPackage pkg = getVersionedPackage(name);
- if (pkg == null) {
- return null;
- }
- return new MonitoredPackage(pkg, durationMs, healthCheckDurationMs,
+ return new MonitoredPackage(name, durationMs, healthCheckDurationMs,
hasPassedHealthCheck, mitigationCalls);
}
@@ -1371,7 +1375,7 @@ public class PackageWatchdog {
* instances of this class.
*/
class MonitoredPackage {
- private final VersionedPackage mPackage;
+ private final String mPackageName;
// Times when package failures happen sorted in ascending order
@GuardedBy("mLock")
private final LongArrayQueue mFailureHistory = new LongArrayQueue();
@@ -1399,10 +1403,10 @@ public class PackageWatchdog {
@GuardedBy("mLock")
private long mHealthCheckDurationMs = Long.MAX_VALUE;
- MonitoredPackage(VersionedPackage pkg, long durationMs,
+ MonitoredPackage(String packageName, long durationMs,
long healthCheckDurationMs, boolean hasPassedHealthCheck,
LongArrayQueue mitigationCalls) {
- mPackage = pkg;
+ mPackageName = packageName;
mDurationMs = durationMs;
mHealthCheckDurationMs = healthCheckDurationMs;
mHasPassedHealthCheck = hasPassedHealthCheck;
@@ -1556,7 +1560,7 @@ public class PackageWatchdog {
/** Returns the monitored package name. */
private String getName() {
- return mPackage.getPackageName();
+ return mPackageName;
}
/**
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 00d8b0f1bed4..d10cf4dd0505 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -30,6 +30,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.service.persistentdata.IPersistentDataBlockService;
import android.service.persistentdata.PersistentDataBlockManager;
+import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.R;
@@ -147,14 +148,15 @@ public class PersistentDataBlockService extends SystemService {
private int getAllowedUid(int userHandle) {
String allowedPackage = mContext.getResources()
.getString(R.string.config_persistentDataPackageName);
- PackageManager pm = mContext.getPackageManager();
int allowedUid = -1;
- try {
- allowedUid = pm.getPackageUidAsUser(allowedPackage,
- PackageManager.MATCH_SYSTEM_ONLY, userHandle);
- } catch (PackageManager.NameNotFoundException e) {
- // not expected
- Slog.e(TAG, "not able to find package " + allowedPackage, e);
+ if (!TextUtils.isEmpty(allowedPackage)) {
+ try {
+ allowedUid = mContext.getPackageManager().getPackageUidAsUser(
+ allowedPackage, PackageManager.MATCH_SYSTEM_ONLY, userHandle);
+ } catch (PackageManager.NameNotFoundException e) {
+ // not expected
+ Slog.e(TAG, "not able to find package " + allowedPackage, e);
+ }
}
return allowedUid;
}
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 0aee78050929..edaf6a90bd4a 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -105,8 +105,6 @@ public final class SensorPrivacyService extends SystemService {
private static final String TAG = "SensorPrivacyService";
- private static final int SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS = 2000;
-
/** Version number indicating compatibility parsing the persisted file */
private static final int CURRENT_PERSISTENCE_VERSION = 1;
/** Version number indicating the persisted data needs upgraded to match new internal data
@@ -568,7 +566,7 @@ public final class SensorPrivacyService extends SystemService {
// User may no longer exist or isn't set
continue;
}
- int sensor = parser.getAttributeIndex(null, XML_ATTRIBUTE_SENSOR);
+ int sensor = parser.getAttributeInt(null, XML_ATTRIBUTE_SENSOR);
boolean isEnabled = parser.getAttributeBoolean(null,
XML_ATTRIBUTE_ENABLED);
SparseBooleanArray userIndividualEnabled = individualEnabled.get(
@@ -756,10 +754,7 @@ public final class SensorPrivacyService extends SystemService {
suppressPackageReminderTokens.add(token);
} else {
- mHandler.postDelayed(PooledLambda.obtainRunnable(
- SensorPrivacyServiceImpl::removeSuppressPackageReminderToken,
- this, key, token),
- SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS);
+ mHandler.removeSuppressPackageReminderToken(key, token);
}
}
}
@@ -1110,6 +1105,13 @@ public final class SensorPrivacyService extends SystemService {
}
listeners.finishBroadcast();
}
+
+ public void removeSuppressPackageReminderToken(Pair<String, UserHandle> key,
+ IBinder token) {
+ sendMessage(PooledLambda.obtainMessage(
+ SensorPrivacyServiceImpl::removeSuppressPackageReminderToken,
+ mSensorPrivacyServiceImpl, key, token));
+ }
}
private final class DeathRecipient implements IBinder.DeathRecipient {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 78fb5552185e..636c9aed4dac 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -891,7 +891,7 @@ class StorageManagerService extends IStorageManager.Stub
ZramWriteback.scheduleZramWriteback(mContext);
}
- updateTranscodeEnabled();
+ configureTranscoding();
}
/**
@@ -923,7 +923,7 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- private void updateTranscodeEnabled() {
+ private void configureTranscoding() {
// See MediaProvider TranscodeHelper#getBooleanProperty for more information
boolean transcodeEnabled = false;
boolean defaultValue = true;
@@ -936,6 +936,18 @@ class StorageManagerService extends IStorageManager.Stub
"transcode_enabled", defaultValue);
}
SystemProperties.set("sys.fuse.transcode_enabled", String.valueOf(transcodeEnabled));
+
+ if (transcodeEnabled) {
+ LocalServices.getService(ActivityManagerInternal.class)
+ .registerAnrController((packageName, uid) -> {
+ try {
+ return mStorageSessionController.getAnrDelayMillis(packageName, uid);
+ } catch (ExternalStorageServiceException e) {
+ Log.e(TAG, "Failed to get ANR delay for " + packageName, e);
+ return 0;
+ }
+ });
+ }
}
/**
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 7f638b9a55a2..915517a4b9ce 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -19,6 +19,7 @@ package com.android.server;
import static android.app.UiModeManager.DEFAULT_PRIORITY;
import static android.app.UiModeManager.MODE_NIGHT_AUTO;
import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
+import static android.app.UiModeManager.MODE_NIGHT_NO;
import static android.app.UiModeManager.MODE_NIGHT_YES;
import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE;
import static android.app.UiModeManager.PROJECTION_TYPE_NONE;
@@ -82,6 +83,7 @@ import com.android.internal.util.DumpUtils;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
@@ -158,6 +160,7 @@ final class UiModeManagerService extends SystemService {
private NotificationManager mNotificationManager;
private StatusBarManager mStatusBarManager;
private WindowManagerInternal mWindowManager;
+ private ActivityTaskManagerInternal mActivityTaskManager;
private AlarmManager mAlarmManager;
private PowerManager mPowerManager;
@@ -366,6 +369,7 @@ final class UiModeManagerService extends SystemService {
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
mWindowManager = LocalServices.getService(WindowManagerInternal.class);
+ mActivityTaskManager = LocalServices.getService(ActivityTaskManagerInternal.class);
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
TwilightManager twilightManager = getLocalService(TwilightManager.class);
if (twilightManager != null) mTwilightManager = twilightManager;
@@ -750,6 +754,39 @@ final class UiModeManagerService extends SystemService {
}
@Override
+ public void setApplicationNightMode(@UiModeManager.NightMode int mode)
+ throws RemoteException {
+ switch (mode) {
+ case UiModeManager.MODE_NIGHT_NO:
+ case UiModeManager.MODE_NIGHT_YES:
+ case UiModeManager.MODE_NIGHT_AUTO:
+ case UiModeManager.MODE_NIGHT_CUSTOM:
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown mode: " + mode);
+ }
+ final int configNightMode;
+ switch (mode) {
+ case MODE_NIGHT_YES:
+ configNightMode = Configuration.UI_MODE_NIGHT_YES;
+ break;
+ case MODE_NIGHT_NO:
+ configNightMode = Configuration.UI_MODE_NIGHT_NO;
+ break;
+ default:
+ configNightMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
+ }
+ try {
+ final ActivityTaskManagerInternal.PackageConfigurationUpdater updater =
+ mActivityTaskManager.createPackageConfigurationUpdater();
+ updater.setNightMode(configNightMode);
+ updater.commit();
+ } catch (RemoteException e) {
+ throw e;
+ }
+ }
+
+ @Override
public boolean isUiModeLocked() {
synchronized (mLock) {
return mUiModeLocked;
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 4dce59f23a79..329ab9983c90 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -22,6 +22,7 @@ import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubsc
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.Context;
import android.net.ConnectivityManager;
@@ -29,8 +30,10 @@ import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.IVcnManagementService;
+import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnManager.VcnErrorCode;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
import android.net.wifi.WifiInfo;
import android.os.Binder;
@@ -54,6 +57,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.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
@@ -124,6 +128,7 @@ import java.util.concurrent.TimeUnit;
*
* @hide
*/
+// TODO(b/180451994): ensure all incoming + outgoing calls have a cleared calling identity
public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull private static final String TAG = VcnManagementService.class.getSimpleName();
@@ -147,6 +152,9 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker;
@NonNull private final VcnContext mVcnContext;
+ /** Can only be assigned when {@link #systemReady()} is called, since it uses AppOpsManager. */
+ @Nullable private LocationPermissionChecker mLocationPermissionChecker;
+
@GuardedBy("mLock")
@NonNull
private final Map<ParcelUuid, VcnConfig> mConfigs = new ArrayMap<>();
@@ -169,6 +177,10 @@ public class VcnManagementService extends IVcnManagementService.Stub {
private final Map<IBinder, PolicyListenerBinderDeath> mRegisteredPolicyListeners =
new ArrayMap<>();
+ @GuardedBy("mLock")
+ @NonNull
+ private final Map<IBinder, VcnStatusCallbackInfo> mRegisteredStatusCallbacks = new ArrayMap<>();
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) {
mContext = requireNonNull(context, "Missing context");
@@ -293,8 +305,8 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot,
- @NonNull VcnSafemodeCallback safemodeCallback) {
- return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safemodeCallback);
+ @NonNull VcnCallback vcnCallback) {
+ return new Vcn(vcnContext, subscriptionGroup, config, snapshot, vcnCallback);
}
/** Gets the subId indicated by the given {@link WifiInfo}. */
@@ -302,6 +314,11 @@ public class VcnManagementService extends IVcnManagementService.Stub {
// TODO(b/178501049): use the subId indicated by WifiInfo#getSubscriptionId
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
+
+ /** Creates a new LocationPermissionChecker for the provided Context. */
+ public LocationPermissionChecker newLocationPermissionChecker(@NonNull Context context) {
+ return new LocationPermissionChecker(context);
+ }
}
/** Notifies the VcnManagementService that external dependencies can be set up. */
@@ -309,6 +326,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
mContext.getSystemService(ConnectivityManager.class)
.registerNetworkProvider(mNetworkProvider);
mTelephonySubscriptionTracker.register();
+ mLocationPermissionChecker = mDeps.newLocationPermissionChecker(mVcnContext.getContext());
}
private void enforcePrimaryUser() {
@@ -440,12 +458,10 @@ public class VcnManagementService extends IVcnManagementService.Stub {
// TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active
// VCN.
- final VcnSafemodeCallbackImpl safemodeCallback =
- new VcnSafemodeCallbackImpl(subscriptionGroup);
+ final VcnCallbackImpl vcnCallback = new VcnCallbackImpl(subscriptionGroup);
final Vcn newInstance =
- mDeps.newVcn(
- mVcnContext, subscriptionGroup, config, mLastSnapshot, safemodeCallback);
+ mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback);
mVcns.put(subscriptionGroup, newInstance);
// Now that a new VCN has started, notify all registered listeners to refresh their
@@ -551,6 +567,14 @@ public class VcnManagementService extends IVcnManagementService.Stub {
}
}
+ /** Get current VcnStatusCallbacks for testing purposes. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public Map<IBinder, VcnStatusCallbackInfo> getAllStatusCallbacks() {
+ synchronized (mLock) {
+ return Collections.unmodifiableMap(mRegisteredStatusCallbacks);
+ }
+ }
+
/** Binder death recipient used to remove a registered policy listener. */
private class PolicyListenerBinderDeath implements Binder.DeathRecipient {
@NonNull private final IVcnUnderlyingNetworkPolicyListener mListener;
@@ -641,40 +665,167 @@ public class VcnManagementService extends IVcnManagementService.Stub {
}
boolean isVcnManagedNetwork = false;
+ boolean isRestrictedCarrierWifi = false;
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
synchronized (mLock) {
ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId);
Vcn vcn = mVcns.get(subGroup);
- if (vcn != null && vcn.isActive()) {
- isVcnManagedNetwork = true;
+ if (vcn != null) {
+ if (vcn.isActive()) {
+ isVcnManagedNetwork = true;
+ }
+
+ if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+ // Carrier WiFi always restricted if VCN exists (even in safe mode).
+ isRestrictedCarrierWifi = true;
+ }
}
}
}
+
if (isVcnManagedNetwork) {
networkCapabilities.removeCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
}
+ if (isRestrictedCarrierWifi) {
+ networkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ }
+
return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
}
- /** Callback for signalling when a Vcn has entered Safemode. */
- public interface VcnSafemodeCallback {
- /** Called by a Vcn to signal that it has entered Safemode. */
- void onEnteredSafemode();
+ /** Binder death recipient used to remove registered VcnStatusCallbacks. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ class VcnStatusCallbackInfo implements Binder.DeathRecipient {
+ @NonNull final ParcelUuid mSubGroup;
+ @NonNull final IVcnStatusCallback mCallback;
+ @NonNull final String mPkgName;
+ final int mUid;
+
+ private VcnStatusCallbackInfo(
+ @NonNull ParcelUuid subGroup,
+ @NonNull IVcnStatusCallback callback,
+ @NonNull String pkgName,
+ int uid) {
+ mSubGroup = subGroup;
+ mCallback = callback;
+ mPkgName = pkgName;
+ mUid = uid;
+ }
+
+ @Override
+ public void binderDied() {
+ Log.e(TAG, "app died without unregistering VcnStatusCallback");
+ unregisterVcnStatusCallback(mCallback);
+ }
}
- /** VcnSafemodeCallback is used by Vcns to notify VcnManagementService on entering Safemode. */
- private class VcnSafemodeCallbackImpl implements VcnSafemodeCallback {
+ /** Registers the provided callback for receiving VCN status updates. */
+ @Override
+ public void registerVcnStatusCallback(
+ @NonNull ParcelUuid subGroup,
+ @NonNull IVcnStatusCallback callback,
+ @NonNull String opPkgName) {
+ final int callingUid = mDeps.getBinderCallingUid();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ requireNonNull(subGroup, "subGroup must not be null");
+ requireNonNull(callback, "callback must not be null");
+ requireNonNull(opPkgName, "opPkgName must not be null");
+
+ mContext.getSystemService(AppOpsManager.class).checkPackage(callingUid, opPkgName);
+
+ final IBinder cbBinder = callback.asBinder();
+ final VcnStatusCallbackInfo cbInfo =
+ new VcnStatusCallbackInfo(
+ subGroup, callback, opPkgName, mDeps.getBinderCallingUid());
+
+ try {
+ cbBinder.linkToDeath(cbInfo, 0 /* flags */);
+ } catch (RemoteException e) {
+ // Remote binder already died - don't add to mRegisteredStatusCallbacks and exit
+ return;
+ }
+
+ synchronized (mLock) {
+ if (mRegisteredStatusCallbacks.containsKey(cbBinder)) {
+ throw new IllegalStateException(
+ "Attempting to register a callback that is already in use");
+ }
+
+ mRegisteredStatusCallbacks.put(cbBinder, cbInfo);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /** Unregisters the provided callback from receiving future VCN status updates. */
+ @Override
+ public void unregisterVcnStatusCallback(@NonNull IVcnStatusCallback callback) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ requireNonNull(callback, "callback must not be null");
+
+ final IBinder cbBinder = callback.asBinder();
+ synchronized (mLock) {
+ VcnStatusCallbackInfo cbInfo = mRegisteredStatusCallbacks.remove(cbBinder);
+
+ if (cbInfo != null) {
+ cbBinder.unlinkToDeath(cbInfo, 0 /* flags */);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ // TODO(b/180452282): Make name more generic and implement directly with VcnManagementService
+ /** Callback for Vcn signals sent up to VcnManagementService. */
+ public interface VcnCallback {
+ /** Called by a Vcn to signal that it has entered safe mode. */
+ void onEnteredSafeMode();
+
+ /** Called by a Vcn to signal that an error occurred. */
+ void onGatewayConnectionError(
+ @NonNull int[] networkCapabilities,
+ @VcnErrorCode int errorCode,
+ @Nullable String exceptionClass,
+ @Nullable String exceptionMessage);
+ }
+
+ /** VcnCallbackImpl for Vcn signals sent up to VcnManagementService. */
+ private class VcnCallbackImpl implements VcnCallback {
@NonNull private final ParcelUuid mSubGroup;
- private VcnSafemodeCallbackImpl(@NonNull final ParcelUuid subGroup) {
+ private VcnCallbackImpl(@NonNull final ParcelUuid subGroup) {
mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
}
+ private boolean isCallbackPermissioned(@NonNull VcnStatusCallbackInfo cbInfo) {
+ if (!mSubGroup.equals(cbInfo.mSubGroup)) {
+ return false;
+ }
+
+ if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup(
+ mSubGroup, cbInfo.mPkgName)) {
+ return false;
+ }
+
+ if (!mLocationPermissionChecker.checkLocationPermission(
+ cbInfo.mPkgName,
+ "VcnStatusCallback" /* featureId */,
+ cbInfo.mUid,
+ null /* message */)) {
+ return false;
+ }
+ return true;
+ }
+
@Override
- public void onEnteredSafemode() {
+ public void onEnteredSafeMode() {
synchronized (mLock) {
// Ignore if this subscription group doesn't exist anymore
if (!mVcns.containsKey(mSubGroup)) {
@@ -682,6 +833,40 @@ public class VcnManagementService extends IVcnManagementService.Stub {
}
notifyAllPolicyListenersLocked();
+
+ // Notify all registered StatusCallbacks for this subGroup
+ for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
+ if (isCallbackPermissioned(cbInfo)) {
+ Binder.withCleanCallingIdentity(() -> cbInfo.mCallback.onEnteredSafeMode());
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onGatewayConnectionError(
+ @NonNull int[] networkCapabilities,
+ @VcnErrorCode int errorCode,
+ @Nullable String exceptionClass,
+ @Nullable String exceptionMessage) {
+ synchronized (mLock) {
+ // Ignore if this subscription group doesn't exist anymore
+ if (!mVcns.containsKey(mSubGroup)) {
+ return;
+ }
+
+ // Notify all registered StatusCallbacks for this subGroup
+ for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
+ if (isCallbackPermissioned(cbInfo)) {
+ Binder.withCleanCallingIdentity(
+ () ->
+ cbInfo.mCallback.onGatewayConnectionError(
+ networkCapabilities,
+ errorCode,
+ exceptionClass,
+ exceptionMessage));
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
deleted file mode 100644
index 026eb630d59a..000000000000
--- a/services/core/java/com/android/server/VibratorService.java
+++ /dev/null
@@ -1,1237 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.hardware.vibrator.IVibrator;
-import android.os.BatteryStats;
-import android.os.Binder;
-import android.os.CombinedVibrationEffect;
-import android.os.ExternalVibration;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IExternalVibratorService;
-import android.os.IVibratorService;
-import android.os.IVibratorStateListener;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.ResultReceiver;
-import android.os.ServiceManager;
-import android.os.ShellCallback;
-import android.os.ShellCommand;
-import android.os.Trace;
-import android.os.VibrationAttributes;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
-import android.os.VibratorInfo;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.util.DumpUtils;
-import com.android.server.vibrator.InputDeviceDelegate;
-import com.android.server.vibrator.Vibration;
-import com.android.server.vibrator.VibrationScaler;
-import com.android.server.vibrator.VibrationSettings;
-import com.android.server.vibrator.VibrationThread;
-import com.android.server.vibrator.VibratorController;
-import com.android.server.vibrator.VibratorController.OnVibrationCompleteListener;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/** System implementation of {@link IVibratorService}. */
-public class VibratorService extends IVibratorService.Stub {
- private static final String TAG = "VibratorService";
- private static final boolean DEBUG = false;
- private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
-
- // Default vibration attributes. Used when vibration is requested without attributes
- private static final VibrationAttributes DEFAULT_ATTRIBUTES =
- new VibrationAttributes.Builder().build();
-
- // Used to generate globally unique vibration ids.
- private final AtomicInteger mNextVibrationId = new AtomicInteger(1); // 0 = no callback
-
- private final LinkedList<Vibration.DebugInfo> mPreviousRingVibrations;
- private final LinkedList<Vibration.DebugInfo> mPreviousNotificationVibrations;
- private final LinkedList<Vibration.DebugInfo> mPreviousAlarmVibrations;
- private final LinkedList<Vibration.DebugInfo> mPreviousExternalVibrations;
- private final LinkedList<Vibration.DebugInfo> mPreviousVibrations;
- private final int mPreviousVibrationsLimit;
- private final Handler mH;
- private final Object mLock = new Object();
- private final VibratorController mVibratorController;
- private final VibrationCallbacks mVibrationCallbacks = new VibrationCallbacks();
-
- private final Context mContext;
- private final PowerManager.WakeLock mWakeLock;
- private final AppOpsManager mAppOps;
- private final IBatteryStats mBatteryStatsService;
- private final String mSystemUiPackage;
- private VibrationSettings mVibrationSettings;
- private VibrationScaler mVibrationScaler;
- private InputDeviceDelegate mInputDeviceDelegate;
-
- @GuardedBy("mLock")
- private VibrationThread mThread;
- @GuardedBy("mLock")
- private VibrationThread mNextVibrationThread;
-
- @GuardedBy("mLock")
- private Vibration mCurrentVibration;
- private int mCurVibUid = -1;
- private ExternalVibrationHolder mCurrentExternalVibration;
-
- /**
- * Implementation of {@link VibrationThread.VibrationCallbacks} that reports finished
- * vibrations.
- */
- private final class VibrationCallbacks implements VibrationThread.VibrationCallbacks {
-
- @Override
- public void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds) {
- }
-
- @Override
- public void triggerSyncedVibration(long vibrationId) {
- }
-
- @Override
- public void onVibrationEnded(long vibrationId, Vibration.Status status) {
- if (DEBUG) {
- Slog.d(TAG, "Vibration thread finished with status " + status);
- }
- synchronized (mLock) {
- if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) {
- mThread = null;
- reportFinishVibrationLocked(status);
- if (mNextVibrationThread != null) {
- startVibrationThreadLocked(mNextVibrationThread);
- mNextVibrationThread = null;
- }
- }
- }
- }
- }
-
- /**
- * Implementation of {@link OnVibrationCompleteListener} with a weak reference to this service.
- */
- private static final class VibrationCompleteListener implements OnVibrationCompleteListener {
- private WeakReference<VibratorService> mServiceRef;
-
- VibrationCompleteListener(VibratorService service) {
- mServiceRef = new WeakReference<>(service);
- }
-
- @Override
- public void onComplete(int vibratorId, long vibrationId) {
- VibratorService service = mServiceRef.get();
- if (service != null) {
- service.onVibrationComplete(vibratorId, vibrationId);
- }
- }
- }
-
- /** Holder for a {@link ExternalVibration}. */
- private final class ExternalVibrationHolder {
-
- public final ExternalVibration externalVibration;
- public int scale;
-
- private final long mStartTimeDebug;
- private long mEndTimeDebug;
- private Vibration.Status mStatus;
-
- private ExternalVibrationHolder(ExternalVibration externalVibration) {
- this.externalVibration = externalVibration;
- this.scale = IExternalVibratorService.SCALE_NONE;
- mStartTimeDebug = System.currentTimeMillis();
- mStatus = Vibration.Status.RUNNING;
- }
-
- public void end(Vibration.Status status) {
- if (mStatus != Vibration.Status.RUNNING) {
- // Vibration already ended, keep first ending status set and ignore this one.
- return;
- }
- mStatus = status;
- mEndTimeDebug = System.currentTimeMillis();
- }
-
- public Vibration.DebugInfo getDebugInfo() {
- return new Vibration.DebugInfo(
- mStartTimeDebug, mEndTimeDebug, /* effect= */ null, /* originalEffect= */ null,
- scale, externalVibration.getVibrationAttributes(),
- externalVibration.getUid(), externalVibration.getPackage(),
- /* reason= */ null, mStatus);
- }
- }
-
- VibratorService(Context context) {
- this(context, new Injector());
- }
-
- @VisibleForTesting
- VibratorService(Context context, Injector injector) {
- mH = injector.createHandler(Looper.myLooper());
- mVibratorController = injector.createVibratorController(
- new VibrationCompleteListener(this));
-
- // Reset the hardware to a default state, in case this is a runtime
- // restart instead of a fresh boot.
- mVibratorController.off();
-
- mContext = context;
- PowerManager pm = context.getSystemService(PowerManager.class);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
- mWakeLock.setReferenceCounted(true);
-
- mAppOps = mContext.getSystemService(AppOpsManager.class);
- mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
- BatteryStats.SERVICE_NAME));
- mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
- .getSystemUiServiceComponent().getPackageName();
-
- mPreviousVibrationsLimit = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_previousVibrationsDumpLimit);
-
- mPreviousRingVibrations = new LinkedList<>();
- mPreviousNotificationVibrations = new LinkedList<>();
- mPreviousAlarmVibrations = new LinkedList<>();
- mPreviousVibrations = new LinkedList<>();
- mPreviousExternalVibrations = new LinkedList<>();
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- context.registerReceiver(mIntentReceiver, filter);
-
- injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
- }
-
- public void systemReady() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorService#systemReady");
- try {
- mVibrationSettings = new VibrationSettings(mContext, mH);
- mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings);
- mInputDeviceDelegate = new InputDeviceDelegate(mContext, mH);
-
- mVibrationSettings.addListener(this::updateVibrators);
-
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- mVibrationSettings.updateSettings();
- }
- }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
-
- updateVibrators();
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- /** Callback for when vibration is complete, to be called by native. */
- @VisibleForTesting
- public void onVibrationComplete(int vibratorId, long vibrationId) {
- synchronized (mLock) {
- if (mCurrentVibration != null && mCurrentVibration.id == vibrationId
- && mThread != null) {
- if (DEBUG) {
- Slog.d(TAG, "Vibration onComplete callback, notifying VibrationThread");
- }
- // Let the thread playing the vibration handle the callback, since it might be
- // expecting the vibrator to turn off multiple times during a single vibration.
- mThread.vibratorComplete(vibratorId);
- }
- }
- }
-
- @Override // Binder call
- public boolean hasVibrator() {
- // For now, we choose to ignore the presence of input devices that have vibrators
- // when reporting whether the device has a vibrator. Applications often use this
- // information to decide whether to enable certain features so they expect the
- // result of hasVibrator() to be constant. For now, just report whether
- // the device has a built-in vibrator.
- return mVibratorController.isAvailable();
- }
-
- @Override // Binder call
- public boolean isVibrating() {
- if (!hasPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)) {
- throw new SecurityException("Requires ACCESS_VIBRATOR_STATE permission");
- }
- return mVibratorController.isVibrating();
- }
-
- @Override // Binder call
- public VibratorInfo getVibratorInfo() {
- return mVibratorController.getVibratorInfo();
- }
-
- @Override // Binder call
- public boolean registerVibratorStateListener(IVibratorStateListener listener) {
- if (!hasPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)) {
- throw new SecurityException("Requires ACCESS_VIBRATOR_STATE permission");
- }
- return mVibratorController.registerVibratorStateListener(listener);
- }
-
- @Override // Binder call
- @GuardedBy("mLock")
- public boolean unregisterVibratorStateListener(IVibratorStateListener listener) {
- if (!hasPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)) {
- throw new SecurityException("Requires ACCESS_VIBRATOR_STATE permission");
- }
- return mVibratorController.unregisterVibratorStateListener(listener);
- }
-
- @Override // Binder call
- public boolean hasAmplitudeControl() {
- // Input device vibrators always support amplitude controls.
- return mInputDeviceDelegate.isAvailable()
- || mVibratorController.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL);
- }
-
- private void verifyIncomingUid(int uid) {
- if (uid == Binder.getCallingUid()) {
- return;
- }
- if (Binder.getCallingPid() == Process.myPid()) {
- return;
- }
- mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
- }
-
- /**
- * Validate the incoming VibrationEffect.
- *
- * We can't throw exceptions here since we might be called from some system_server component,
- * which would bring the whole system down.
- *
- * @return whether the VibrationEffect is valid
- */
- private static boolean verifyVibrationEffect(VibrationEffect effect) {
- if (effect == null) {
- // Effect must not be null.
- Slog.wtf(TAG, "effect must not be null");
- return false;
- }
- try {
- effect.validate();
- } catch (Exception e) {
- Slog.wtf(TAG, "Encountered issue when verifying VibrationEffect.", e);
- return false;
- }
- return true;
- }
-
- private VibrationEffect fixupVibrationEffect(VibrationEffect effect) {
- if (effect instanceof VibrationEffect.Prebaked
- && ((VibrationEffect.Prebaked) effect).shouldFallback()) {
- VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
- VibrationEffect fallback = mVibrationSettings.getFallbackEffect(prebaked.getId());
- return new VibrationEffect.Prebaked(prebaked.getId(), prebaked.getEffectStrength(),
- fallback);
- }
- return effect;
- }
-
- private VibrationAttributes fixupVibrationAttributes(VibrationAttributes attrs) {
- if (attrs == null) {
- attrs = DEFAULT_ATTRIBUTES;
- }
- if (shouldBypassDnd(attrs)) {
- if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- || hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- || hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
- final int flags = attrs.getFlags()
- & ~VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
- attrs = new VibrationAttributes.Builder(attrs)
- .setFlags(flags, attrs.getFlags()).build();
- }
- }
-
- return attrs;
- }
-
- @Override // Binder call
- public void vibrate(int uid, String opPkg, VibrationEffect effect,
- @Nullable VibrationAttributes attrs, String reason, IBinder token) {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
- try {
- if (!hasPermission(android.Manifest.permission.VIBRATE)) {
- throw new SecurityException("Requires VIBRATE permission");
- }
- if (token == null) {
- Slog.e(TAG, "token must not be null");
- return;
- }
- verifyIncomingUid(uid);
- if (!verifyVibrationEffect(effect)) {
- return;
- }
- effect = fixupVibrationEffect(effect);
- attrs = fixupVibrationAttributes(attrs);
- Vibration vib = new Vibration(token, mNextVibrationId.getAndIncrement(),
- CombinedVibrationEffect.createSynced(effect), attrs, uid, opPkg, reason);
-
- // If our current vibration is longer than the new vibration and is the same amplitude,
- // then just let the current one finish.
- synchronized (mLock) {
- VibrationEffect currentEffect =
- mCurrentVibration == null ? null : getEffect(mCurrentVibration);
- if (effect instanceof VibrationEffect.OneShot
- && currentEffect instanceof VibrationEffect.OneShot) {
- VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
- VibrationEffect.OneShot currentOneShot =
- (VibrationEffect.OneShot) currentEffect;
- if (currentOneShot.getDuration() > newOneShot.getDuration()
- && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
- if (DEBUG) {
- Slog.d(TAG,
- "Ignoring incoming vibration in favor of current vibration");
- }
- endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_ONGOING);
- return;
- }
- }
-
-
- // If something has external control of the vibrator, assume that it's more
- // important for now.
- if (mCurrentExternalVibration != null) {
- if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
- }
- endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_EXTERNAL);
- return;
- }
-
- // If the current vibration is repeating and the incoming one is non-repeating,
- // then ignore the non-repeating vibration. This is so that we don't cancel
- // vibrations that are meant to grab the attention of the user, like ringtones and
- // alarms, in favor of one-shot vibrations that are likely quite short.
- if (!isRepeatingVibration(effect)
- && mCurrentVibration != null
- && isRepeatingVibration(currentEffect)) {
- if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
- }
- endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_ALARM);
- return;
- }
-
- if (!mVibrationSettings.shouldVibrateForUid(uid, vib.attrs.getUsage())) {
- Slog.e(TAG, "Ignoring incoming vibration as process with"
- + " uid= " + uid + " is background,"
- + " attrs= " + vib.attrs);
- endVibrationLocked(vib, Vibration.Status.IGNORED_BACKGROUND);
- return;
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- doCancelVibrateLocked(Vibration.Status.CANCELLED);
- startVibrationLocked(vib);
- boolean isNextVibration = mNextVibrationThread != null
- && vib.equals(mNextVibrationThread.getVibration());
-
- if (!vib.hasEnded() && !vib.equals(mCurrentVibration) && !isNextVibration) {
- // Vibration was unexpectedly ignored: add to list for debugging
- endVibrationLocked(vib, Vibration.Status.IGNORED);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- private boolean hasPermission(String permission) {
- return mContext.checkCallingOrSelfPermission(permission)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- private static boolean isRepeatingVibration(VibrationEffect effect) {
- return effect.getDuration() == Long.MAX_VALUE;
- }
-
- private static <T extends VibrationEffect> T getEffect(Vibration vib) {
- return (T) ((CombinedVibrationEffect.Mono) vib.getEffect()).getEffect();
- }
-
- private void endVibrationLocked(Vibration vib, Vibration.Status status) {
- final LinkedList<Vibration.DebugInfo> previousVibrations;
- switch (vib.attrs.getUsage()) {
- case VibrationAttributes.USAGE_NOTIFICATION:
- previousVibrations = mPreviousNotificationVibrations;
- break;
- case VibrationAttributes.USAGE_RINGTONE:
- previousVibrations = mPreviousRingVibrations;
- break;
- case VibrationAttributes.USAGE_ALARM:
- previousVibrations = mPreviousAlarmVibrations;
- break;
- default:
- previousVibrations = mPreviousVibrations;
- }
- if (previousVibrations.size() > mPreviousVibrationsLimit) {
- previousVibrations.removeFirst();
- }
- vib.end(status);
- previousVibrations.addLast(vib.getDebugInfo());
- }
-
- private void endVibrationLocked(ExternalVibrationHolder vib, Vibration.Status status) {
- if (mPreviousExternalVibrations.size() > mPreviousVibrationsLimit) {
- mPreviousExternalVibrations.removeFirst();
- }
- vib.end(status);
- mPreviousExternalVibrations.addLast(vib.getDebugInfo());
- }
-
- @Override // Binder call
- public void cancelVibrate(IBinder token) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.VIBRATE,
- "cancelVibrate");
-
- synchronized (mLock) {
- if (mCurrentVibration != null && mCurrentVibration.token == token) {
- if (DEBUG) {
- Slog.d(TAG, "Canceling vibration.");
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- mNextVibrationThread = null;
- doCancelVibrateLocked(Vibration.Status.CANCELLED);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
- }
-
- @GuardedBy("mLock")
- private void doCancelVibrateLocked(Vibration.Status status) {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked");
- try {
- if (mThread != null) {
- mThread.cancel();
- }
- mInputDeviceDelegate.cancelVibrateIfAvailable();
- if (mCurrentExternalVibration != null) {
- endVibrationLocked(mCurrentExternalVibration, status);
- mCurrentExternalVibration.externalVibration.mute();
- mCurrentExternalVibration = null;
- mVibratorController.setExternalControl(false);
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- @GuardedBy("mLock")
- private void startVibrationLocked(final Vibration vib) {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
- try {
- if (!shouldVibrate(vib)) {
- return;
- }
- applyVibrationIntensityScalingLocked(vib);
- startVibrationInnerLocked(vib);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- @GuardedBy("mLock")
- private void startVibrationInnerLocked(Vibration vib) {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
- try {
- boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable(
- vib.uid, vib.opPkg, vib.getEffect(), vib.reason, vib.attrs);
- if (inputDevicesAvailable) {
- endVibrationLocked(vib, Vibration.Status.FORWARDED_TO_INPUT_DEVICES);
- } else if (mThread == null) {
- startVibrationThreadLocked(new VibrationThread(vib, mVibratorController, mWakeLock,
- mBatteryStatsService, mVibrationCallbacks));
- } else {
- mNextVibrationThread = new VibrationThread(vib, mVibratorController, mWakeLock,
- mBatteryStatsService, mVibrationCallbacks);
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- @GuardedBy("mLock")
- private void startVibrationThreadLocked(VibrationThread thread) {
- Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
- mCurrentVibration = thread.getVibration();
- mThread = thread;
- mThread.start();
- }
-
- /** Scale the vibration effect by the intensity as appropriate based its intent. */
- private void applyVibrationIntensityScalingLocked(Vibration vib) {
- vib.updateEffect(mVibrationScaler.scale(vib.getEffect(), vib.attrs.getUsage()));
- }
-
- private static boolean shouldBypassDnd(VibrationAttributes attrs) {
- return attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY);
- }
-
- private int getAppOpMode(int uid, String packageName, VibrationAttributes attrs) {
- int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
- attrs.getAudioUsage(), uid, packageName);
- if (mode == AppOpsManager.MODE_ALLOWED) {
- mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, uid, packageName);
- }
-
- if (mode == AppOpsManager.MODE_IGNORED && shouldBypassDnd(attrs)) {
- // If we're just ignoring the vibration op then this is set by DND and we should ignore
- // if we're asked to bypass. AppOps won't be able to record this operation, so make
- // sure we at least note it in the logs for debugging.
- Slog.d(TAG, "Bypassing DND for vibrate from uid " + uid);
- mode = AppOpsManager.MODE_ALLOWED;
- }
- return mode;
- }
-
- private boolean shouldVibrate(Vibration vib) {
- if (!mVibrationSettings.shouldVibrateForPowerMode(vib.attrs.getUsage())) {
- endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_POWER);
- return false;
- }
-
- int intensity = mVibrationSettings.getCurrentIntensity(vib.attrs.getUsage());
- if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
- endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_SETTINGS);
- return false;
- }
-
- if (!mVibrationSettings.shouldVibrateForRingerMode(vib.attrs.getUsage())) {
- if (DEBUG) {
- Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
- }
- endVibrationLocked(vib, Vibration.Status.IGNORED_RINGTONE);
- return false;
- }
-
- final int mode = getAppOpMode(vib.uid, vib.opPkg, vib.attrs);
- if (mode != AppOpsManager.MODE_ALLOWED) {
- if (mode == AppOpsManager.MODE_ERRORED) {
- // We might be getting calls from within system_server, so we don't actually
- // want to throw a SecurityException here.
- Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
- endVibrationLocked(vib, Vibration.Status.IGNORED_ERROR_APP_OPS);
- } else {
- endVibrationLocked(vib, Vibration.Status.IGNORED_APP_OPS);
- }
- return false;
- }
-
- return true;
- }
-
- @GuardedBy("mLock")
- private void reportFinishVibrationLocked(Vibration.Status status) {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
- try {
- if (mCurrentVibration != null) {
- endVibrationLocked(mCurrentVibration, status);
- mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
- mCurrentVibration.opPkg);
-
- Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
- mCurrentVibration = null;
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- @VisibleForTesting
- void updateVibrators() {
- synchronized (mLock) {
- boolean inputDevicesChanged = mInputDeviceDelegate.updateInputDeviceVibrators(
- mVibrationSettings.shouldVibrateInputDevices());
-
- if (mCurrentVibration == null) {
- return;
- }
-
- if (inputDevicesChanged || !mVibrationSettings.shouldVibrateForPowerMode(
- mCurrentVibration.attrs.getUsage())) {
- // If the state changes out from under us then just reset.
- doCancelVibrateLocked(Vibration.Status.CANCELLED);
- }
- }
- }
-
- private boolean isSystemHapticFeedback(Vibration vib) {
- if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) {
- return false;
- }
- return vib.uid == Process.SYSTEM_UID || vib.uid == 0 || mSystemUiPackage.equals(vib.opPkg);
- }
-
- private void dumpInternal(PrintWriter pw) {
- pw.println("Vibrator Service:");
- synchronized (mLock) {
- pw.print(" mCurrentVibration=");
- if (mCurrentVibration != null) {
- pw.println(mCurrentVibration.getDebugInfo().toString());
- } else {
- pw.println("null");
- }
- pw.print(" mCurrentExternalVibration=");
- if (mCurrentExternalVibration != null) {
- pw.println(mCurrentExternalVibration.getDebugInfo().toString());
- } else {
- pw.println("null");
- }
- pw.println(" mVibratorController=" + mVibratorController);
- pw.println(" mVibrationSettings=" + mVibrationSettings);
- pw.println();
- pw.println(" Previous ring vibrations:");
- for (Vibration.DebugInfo info : mPreviousRingVibrations) {
- pw.print(" ");
- pw.println(info.toString());
- }
-
- pw.println(" Previous notification vibrations:");
- for (Vibration.DebugInfo info : mPreviousNotificationVibrations) {
- pw.println(" " + info);
- }
-
- pw.println(" Previous alarm vibrations:");
- for (Vibration.DebugInfo info : mPreviousAlarmVibrations) {
- pw.println(" " + info);
- }
-
- pw.println(" Previous vibrations:");
- for (Vibration.DebugInfo info : mPreviousVibrations) {
- pw.println(" " + info);
- }
-
- pw.println(" Previous external vibrations:");
- for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
- pw.println(" " + info);
- }
- }
- }
-
- private void dumpProto(FileDescriptor fd) {
- final ProtoOutputStream proto = new ProtoOutputStream(fd);
-
- synchronized (mLock) {
- if (mCurrentVibration != null) {
- mCurrentVibration.getDebugInfo().dumpProto(proto,
- VibratorServiceDumpProto.CURRENT_VIBRATION);
- }
- if (mCurrentExternalVibration != null) {
- mCurrentExternalVibration.getDebugInfo().dumpProto(proto,
- VibratorServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
- }
- proto.write(VibratorServiceDumpProto.IS_VIBRATING, mVibratorController.isVibrating());
- proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
- mVibratorController.isUnderExternalControl());
- mVibrationSettings.dumpProto(proto);
-
- for (Vibration.DebugInfo info : mPreviousRingVibrations) {
- info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
- }
-
- for (Vibration.DebugInfo info : mPreviousNotificationVibrations) {
- info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS);
- }
-
- for (Vibration.DebugInfo info : mPreviousAlarmVibrations) {
- info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS);
- }
-
- for (Vibration.DebugInfo info : mPreviousVibrations) {
- info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_VIBRATIONS);
- }
-
- for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
- info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
- }
- }
- proto.flush();
- }
-
- /** Point of injection for test dependencies */
- @VisibleForTesting
- static class Injector {
-
- VibratorController createVibratorController(OnVibrationCompleteListener listener) {
- return new VibratorController(/* vibratorId= */ -1, listener);
- }
-
- Handler createHandler(Looper looper) {
- return new Handler(looper);
- }
-
- void addService(String name, IBinder service) {
- ServiceManager.addService(name, service);
- }
- }
-
- BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
- synchronized (mLock) {
- // When the system is entering a non-interactive state, we want
- // to cancel vibrations in case a misbehaving app has abandoned
- // them. However it may happen that the system is currently playing
- // haptic feedback as part of the transition. So we don't cancel
- // system vibrations.
- if (mCurrentVibration != null && !isSystemHapticFeedback(mCurrentVibration)) {
- mNextVibrationThread = null;
- doCancelVibrateLocked(Vibration.Status.CANCELLED);
- }
- }
- }
- }
- };
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-
- final long ident = Binder.clearCallingIdentity();
-
- boolean isDumpProto = false;
- for (String arg : args) {
- if (arg.equals("--proto")) {
- isDumpProto = true;
- }
- }
- try {
- if (isDumpProto) {
- dumpProto(fd);
- } else {
- dumpInternal(pw);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override
- public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
- new VibratorShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver);
- }
-
- final class ExternalVibratorService extends IExternalVibratorService.Stub {
- ExternalVibrationDeathRecipient mCurrentExternalDeathRecipient;
-
- @Override
- public int onExternalVibrationStart(ExternalVibration vib) {
- if (!mVibratorController.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
- return IExternalVibratorService.SCALE_MUTE;
- }
- if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
- vib.getUid(), -1 /*owningUid*/, true /*exported*/)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
- + " tried to play externally controlled vibration"
- + " without VIBRATE permission, ignoring.");
- return IExternalVibratorService.SCALE_MUTE;
- }
-
- int mode = getAppOpMode(vib.getUid(), vib.getPackage(), vib.getVibrationAttributes());
- if (mode != AppOpsManager.MODE_ALLOWED) {
- ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
- vibHolder.scale = SCALE_MUTE;
- if (mode == AppOpsManager.MODE_ERRORED) {
- Slog.w(TAG, "Would be an error: external vibrate from uid " + vib.getUid());
- endVibrationLocked(vibHolder, Vibration.Status.IGNORED_ERROR_APP_OPS);
- } else {
- endVibrationLocked(vibHolder, Vibration.Status.IGNORED_APP_OPS);
- }
- return IExternalVibratorService.SCALE_MUTE;
- }
-
- VibrationThread cancelingVibration = null;
- int scale;
- synchronized (mLock) {
- if (mCurrentExternalVibration != null
- && mCurrentExternalVibration.externalVibration.equals(vib)) {
- // We are already playing this external vibration, so we can return the same
- // scale calculated in the previous call to this method.
- return mCurrentExternalVibration.scale;
- }
- if (mCurrentExternalVibration == null) {
- // If we're not under external control right now, then cancel any normal
- // vibration that may be playing and ready the vibrator for external control.
- mNextVibrationThread = null;
- doCancelVibrateLocked(Vibration.Status.CANCELLED);
- cancelingVibration = mThread;
- } else {
- endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED);
- }
- // At this point we either have an externally controlled vibration playing, or
- // no vibration playing. Since the interface defines that only one externally
- // controlled vibration can play at a time, by returning something other than
- // SCALE_MUTE from this function we can be assured that if we are currently
- // playing vibration, it will be muted in favor of the new vibration.
- //
- // Note that this doesn't support multiple concurrent external controls, as we
- // would need to mute the old one still if it came from a different controller.
- mCurrentExternalVibration = new ExternalVibrationHolder(vib);
- mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
- vib.linkToDeath(mCurrentExternalDeathRecipient);
- mCurrentExternalVibration.scale = mVibrationScaler.getExternalVibrationScale(
- vib.getVibrationAttributes().getUsage());
- scale = mCurrentExternalVibration.scale;
- }
- if (cancelingVibration != null) {
- try {
- cancelingVibration.join();
- } catch (InterruptedException e) {
- Slog.w("Interrupted while waiting current vibration to be cancelled before "
- + "starting external vibration", e);
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "Vibrator going under external control.");
- }
- mVibratorController.setExternalControl(true);
- if (DEBUG) {
- Slog.e(TAG, "Playing external vibration: " + vib);
- }
- return scale;
- }
-
- @Override
- public void onExternalVibrationStop(ExternalVibration vib) {
- synchronized (mLock) {
- if (mCurrentExternalVibration != null
- && mCurrentExternalVibration.externalVibration.equals(vib)) {
- if (DEBUG) {
- Slog.e(TAG, "Stopping external vibration" + vib);
- }
- doCancelExternalVibrateLocked(Vibration.Status.FINISHED);
- }
- }
- }
-
- private void doCancelExternalVibrateLocked(Vibration.Status status) {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelExternalVibrateLocked");
- try {
- if (mCurrentExternalVibration == null) {
- return;
- }
- endVibrationLocked(mCurrentExternalVibration, status);
- mCurrentExternalVibration.externalVibration.unlinkToDeath(
- mCurrentExternalDeathRecipient);
- mCurrentExternalDeathRecipient = null;
- mCurrentExternalVibration = null;
- mVibratorController.setExternalControl(false);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- private class ExternalVibrationDeathRecipient implements IBinder.DeathRecipient {
- public void binderDied() {
- synchronized (mLock) {
- if (mCurrentExternalVibration != null) {
- if (DEBUG) {
- Slog.d(TAG, "External vibration finished because binder died");
- }
- doCancelExternalVibrateLocked(Vibration.Status.CANCELLED);
- }
- }
- }
- }
- }
-
- private final class VibratorShellCommand extends ShellCommand {
-
- private final IBinder mToken;
-
- private final class CommonOptions {
- public boolean force = false;
- public void check(String opt) {
- switch (opt) {
- case "-f":
- force = true;
- break;
- }
- }
- }
-
- private VibratorShellCommand(IBinder token) {
- mToken = token;
- }
-
- @Override
- public int onCommand(String cmd) {
- if ("vibrate".equals(cmd)) {
- return runVibrate();
- } else if ("waveform".equals(cmd)) {
- return runWaveform();
- } else if ("prebaked".equals(cmd)) {
- return runPrebaked();
- } else if ("capabilities".equals(cmd)) {
- return runCapabilities();
- } else if ("cancel".equals(cmd)) {
- cancelVibrate(mToken);
- return 0;
- }
- return handleDefaultCommands(cmd);
- }
-
- private boolean checkDoNotDisturb(CommonOptions opts) {
- if (mVibrationSettings.isInZenMode() && !opts.force) {
- try (PrintWriter pw = getOutPrintWriter();) {
- pw.print("Ignoring because device is on DND mode ");
- return true;
- }
- }
- return false;
- }
-
- private int runVibrate() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runVibrate");
- try {
- CommonOptions commonOptions = new CommonOptions();
-
- String opt;
- while ((opt = getNextOption()) != null) {
- commonOptions.check(opt);
- }
-
- if (checkDoNotDisturb(commonOptions)) {
- return 0;
- }
-
- final long duration = Long.parseLong(getNextArgRequired());
- String description = getNextArg();
- if (description == null) {
- description = "Shell command";
- }
-
- VibrationEffect effect =
- VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
- VibrationAttributes attrs = createVibrationAttributes(commonOptions);
- vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
- mToken);
- return 0;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- private int runWaveform() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runWaveform");
- try {
- String description = "Shell command";
- int repeat = -1;
- ArrayList<Integer> amplitudesList = null;
- CommonOptions commonOptions = new CommonOptions();
-
- String opt;
- while ((opt = getNextOption()) != null) {
- switch (opt) {
- case "-d":
- description = getNextArgRequired();
- break;
- case "-r":
- repeat = Integer.parseInt(getNextArgRequired());
- break;
- case "-a":
- if (amplitudesList == null) {
- amplitudesList = new ArrayList<Integer>();
- }
- break;
- default:
- commonOptions.check(opt);
- break;
- }
- }
-
- if (checkDoNotDisturb(commonOptions)) {
- return 0;
- }
-
- ArrayList<Long> timingsList = new ArrayList<Long>();
-
- String arg;
- while ((arg = getNextArg()) != null) {
- if (amplitudesList != null && amplitudesList.size() < timingsList.size()) {
- amplitudesList.add(Integer.parseInt(arg));
- } else {
- timingsList.add(Long.parseLong(arg));
- }
- }
-
- VibrationEffect effect;
- long[] timings = timingsList.stream().mapToLong(Long::longValue).toArray();
- if (amplitudesList == null) {
- effect = VibrationEffect.createWaveform(timings, repeat);
- } else {
- int[] amplitudes =
- amplitudesList.stream().mapToInt(Integer::intValue).toArray();
- effect = VibrationEffect.createWaveform(timings, amplitudes, repeat);
- }
- VibrationAttributes attrs = createVibrationAttributes(commonOptions);
- vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
- mToken);
- return 0;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- private int runPrebaked() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runPrebaked");
- try {
- CommonOptions commonOptions = new CommonOptions();
- boolean shouldFallback = false;
-
- String opt;
- while ((opt = getNextOption()) != null) {
- if ("-b".equals(opt)) {
- shouldFallback = true;
- } else {
- commonOptions.check(opt);
- }
- }
-
- if (checkDoNotDisturb(commonOptions)) {
- return 0;
- }
-
- final int id = Integer.parseInt(getNextArgRequired());
-
- String description = getNextArg();
- if (description == null) {
- description = "Shell command";
- }
-
- VibrationEffect effect = VibrationEffect.get(id, shouldFallback);
- VibrationAttributes attrs = createVibrationAttributes(commonOptions);
- vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
- mToken);
- return 0;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- private int runCapabilities() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runCapabilities");
- try (PrintWriter pw = getOutPrintWriter();) {
- pw.println("Vibrator capabilities:");
- if (mVibratorController.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
- pw.println(" Always on effects");
- }
- if (mVibratorController.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
- pw.println(" Compose effects");
- }
- if (mVibratorController.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
- pw.println(" Amplitude control");
- }
- if (mVibratorController.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
- pw.println(" External control");
- }
- if (mVibratorController.hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) {
- pw.println(" External amplitude control");
- }
- pw.println("");
- return 0;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
- private VibrationAttributes createVibrationAttributes(CommonOptions commonOptions) {
- final int flags = commonOptions.force
- ? VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
- : 0;
- return new VibrationAttributes.Builder()
- .setFlags(flags, VibrationAttributes.FLAG_ALL_SUPPORTED)
- // Used to apply Settings.System.HAPTIC_FEEDBACK_INTENSITY to scale effects.
- .setUsage(VibrationAttributes.USAGE_TOUCH)
- .build();
- }
-
- @Override
- public void onHelp() {
- try (PrintWriter pw = getOutPrintWriter();) {
- pw.println("Vibrator commands:");
- pw.println(" help");
- pw.println(" Prints this help text.");
- pw.println("");
- pw.println(" vibrate duration [description]");
- pw.println(" Vibrates for duration milliseconds; ignored when device is on ");
- pw.println(" DND (Do Not Disturb) mode; touch feedback strength user setting ");
- pw.println(" will be used to scale amplitude.");
- pw.println(" waveform [-d description] [-r index] [-a] duration [amplitude] ...");
- pw.println(" Vibrates for durations and amplitudes in list; ignored when ");
- pw.println(" device is on DND (Do Not Disturb) mode; touch feedback strength ");
- pw.println(" user setting will be used to scale amplitude.");
- pw.println(" If -r is provided, the waveform loops back to the specified");
- pw.println(" index (e.g. 0 loops from the beginning)");
- pw.println(" If -a is provided, the command accepts duration-amplitude pairs;");
- pw.println(" otherwise, it accepts durations only and alternates off/on");
- pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255.");
- pw.println(" prebaked [-b] effect-id [description]");
- pw.println(" Vibrates with prebaked effect; ignored when device is on DND ");
- pw.println(" (Do Not Disturb) mode; touch feedback strength user setting ");
- pw.println(" will be used to scale amplitude.");
- pw.println(" If -b is provided, the prebaked fallback effect will be played if");
- pw.println(" the device doesn't support the given effect-id.");
- pw.println(" capabilities");
- pw.println(" Prints capabilities of this device.");
- pw.println(" cancel");
- pw.println(" Cancels any active vibration");
- pw.println("Common Options:");
- pw.println(" -f - Force. Ignore Do Not Disturb setting.");
- pw.println("");
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
new file mode 100644
index 000000000000..5d89bf1b1d82
--- /dev/null
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -0,0 +1,918 @@
+/*
+ * 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;
+
+import static android.Manifest.permission.NETWORK_STACK;
+
+import static com.android.net.module.util.PermissionUtils.enforceAnyPermissionOf;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.INetd;
+import android.net.IVpnManager;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkStack;
+import android.net.UnderlyingNetworkInfo;
+import android.net.Uri;
+import android.net.VpnManager;
+import android.net.VpnService;
+import android.net.util.NetdService;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.INetworkManagementService;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.security.Credentials;
+import android.security.KeyStore;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.connectivity.Vpn;
+import com.android.server.net.LockdownVpnTracker;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Service that tracks and manages VPNs, and backs the VpnService and VpnManager APIs.
+ * @hide
+ */
+public class VpnManagerService extends IVpnManager.Stub {
+ private static final String TAG = VpnManagerService.class.getSimpleName();
+
+ @VisibleForTesting
+ protected final HandlerThread mHandlerThread;
+ private final Handler mHandler;
+
+ private final Context mContext;
+ private final Context mUserAllContext;
+
+ private final Dependencies mDeps;
+
+ private final ConnectivityManager mCm;
+ private final KeyStore mKeyStore;
+ private final INetworkManagementService mNMS;
+ private final INetd mNetd;
+ private final UserManager mUserManager;
+
+ @VisibleForTesting
+ @GuardedBy("mVpns")
+ protected final SparseArray<Vpn> mVpns = new SparseArray<>();
+
+ // TODO: investigate if mLockdownEnabled can be removed and replaced everywhere by
+ // a direct call to LockdownVpnTracker.isEnabled().
+ @GuardedBy("mVpns")
+ private boolean mLockdownEnabled;
+ @GuardedBy("mVpns")
+ private LockdownVpnTracker mLockdownTracker;
+
+ /**
+ * Dependencies of VpnManager, for injection in tests.
+ */
+ @VisibleForTesting
+ public static class Dependencies {
+ /** Returns the calling UID of an IPC. */
+ public int getCallingUid() {
+ return Binder.getCallingUid();
+ }
+
+ /** Creates a HandlerThread to be used by this class. */
+ public HandlerThread makeHandlerThread() {
+ return new HandlerThread("VpnManagerService");
+ }
+
+ /** Returns the KeyStore instance to be used by this class. */
+ public KeyStore getKeyStore() {
+ return KeyStore.getInstance();
+ }
+
+ public INetd getNetd() {
+ return NetdService.getInstance();
+ }
+
+ public INetworkManagementService getINetworkManagementService() {
+ return INetworkManagementService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
+ }
+ }
+
+ public VpnManagerService(Context context, Dependencies deps) {
+ mContext = context;
+ mDeps = deps;
+ mHandlerThread = mDeps.makeHandlerThread();
+ mHandlerThread.start();
+ mHandler = mHandlerThread.getThreadHandler();
+ mKeyStore = mDeps.getKeyStore();
+ mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
+ mCm = mContext.getSystemService(ConnectivityManager.class);
+ mNMS = mDeps.getINetworkManagementService();
+ mNetd = mDeps.getNetd();
+ mUserManager = mContext.getSystemService(UserManager.class);
+ registerReceivers();
+ log("VpnManagerService starting up");
+ }
+
+ /** Creates a new VpnManagerService */
+ public static VpnManagerService create(Context context) {
+ return new VpnManagerService(context, new Dependencies());
+ }
+
+ /** Informs the service that the system is ready. */
+ public void systemReady() {
+ // Try bringing up tracker, but KeyStore won't be ready yet for secondary users so wait
+ // for user to unlock device too.
+ updateLockdownVpn();
+ }
+
+ @Override
+ /** Dumps service state. */
+ protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
+ @Nullable String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+ IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
+ pw.println("VPNs:");
+ pw.increaseIndent();
+ synchronized (mVpns) {
+ for (int i = 0; i < mVpns.size(); i++) {
+ pw.println(mVpns.keyAt(i) + ": " + mVpns.valueAt(i).getPackage());
+ }
+ pw.decreaseIndent();
+ }
+ }
+
+ /**
+ * Prepare for a VPN application.
+ * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
+ * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ *
+ * @param oldPackage Package name of the application which currently controls VPN, which will
+ * be replaced. If there is no such application, this should should either be
+ * {@code null} or {@link VpnConfig.LEGACY_VPN}.
+ * @param newPackage Package name of the application which should gain control of VPN, or
+ * {@code null} to disable.
+ * @param userId User for whom to prepare the new VPN.
+ *
+ * @hide
+ */
+ @Override
+ public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
+ int userId) {
+ enforceCrossUserPermission(userId);
+
+ synchronized (mVpns) {
+ throwIfLockdownEnabled();
+ Vpn vpn = mVpns.get(userId);
+ if (vpn != null) {
+ return vpn.prepare(oldPackage, newPackage, VpnManager.TYPE_VPN_SERVICE);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set whether the VPN package has the ability to launch VPNs without user intervention. This
+ * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
+ * class. If the caller is not {@code userId}, {@link
+ * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ *
+ * @param packageName The package for which authorization state should change.
+ * @param userId User for whom {@code packageName} is installed.
+ * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
+ * permissions should be granted. When unauthorizing an app, {@link
+ * VpnManager.TYPE_VPN_NONE} should be used.
+ * @hide
+ */
+ @Override
+ public void setVpnPackageAuthorization(
+ String packageName, int userId, @VpnManager.VpnType int vpnType) {
+ enforceCrossUserPermission(userId);
+
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(userId);
+ if (vpn != null) {
+ vpn.setPackageAuthorization(packageName, vpnType);
+ }
+ }
+ }
+
+ /**
+ * Configure a TUN interface and return its file descriptor. Parameters
+ * are encoded and opaque to this class. This method is used by VpnBuilder
+ * and not available in VpnManager. Permissions are checked in
+ * Vpn class.
+ * @hide
+ */
+ @Override
+ public ParcelFileDescriptor establishVpn(VpnConfig config) {
+ int user = UserHandle.getUserId(mDeps.getCallingUid());
+ synchronized (mVpns) {
+ throwIfLockdownEnabled();
+ return mVpns.get(user).establish(config);
+ }
+ }
+
+ @Override
+ public boolean addVpnAddress(String address, int prefixLength) {
+ int user = UserHandle.getUserId(mDeps.getCallingUid());
+ synchronized (mVpns) {
+ throwIfLockdownEnabled();
+ return mVpns.get(user).addAddress(address, prefixLength);
+ }
+ }
+
+ @Override
+ public boolean removeVpnAddress(String address, int prefixLength) {
+ int user = UserHandle.getUserId(mDeps.getCallingUid());
+ synchronized (mVpns) {
+ throwIfLockdownEnabled();
+ return mVpns.get(user).removeAddress(address, prefixLength);
+ }
+ }
+
+ @Override
+ public boolean setUnderlyingNetworksForVpn(Network[] networks) {
+ int user = UserHandle.getUserId(mDeps.getCallingUid());
+ final boolean success;
+ synchronized (mVpns) {
+ success = mVpns.get(user).setUnderlyingNetworks(networks);
+ }
+ return success;
+ }
+
+ /**
+ * Stores the given VPN profile based on the provisioning package name.
+ *
+ * <p>If there is already a VPN profile stored for the provisioning package, this call will
+ * overwrite the profile.
+ *
+ * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+ * exclusively by the Settings app, and passed into the platform at startup time.
+ *
+ * @return {@code true} if user consent has already been granted, {@code false} otherwise.
+ * @hide
+ */
+ @Override
+ public boolean provisionVpnProfile(@NonNull VpnProfile profile, @NonNull String packageName) {
+ final int user = UserHandle.getUserId(mDeps.getCallingUid());
+ synchronized (mVpns) {
+ return mVpns.get(user).provisionVpnProfile(packageName, profile, mKeyStore);
+ }
+ }
+
+ /**
+ * Deletes the stored VPN profile for the provisioning package
+ *
+ * <p>If there are no profiles for the given package, this method will silently succeed.
+ *
+ * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+ * exclusively by the Settings app, and passed into the platform at startup time.
+ *
+ * @hide
+ */
+ @Override
+ public void deleteVpnProfile(@NonNull String packageName) {
+ final int user = UserHandle.getUserId(mDeps.getCallingUid());
+ synchronized (mVpns) {
+ mVpns.get(user).deleteVpnProfile(packageName, mKeyStore);
+ }
+ }
+
+ /**
+ * Starts the VPN based on the stored profile for the given package
+ *
+ * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+ * exclusively by the Settings app, and passed into the platform at startup time.
+ *
+ * @throws IllegalArgumentException if no profile was found for the given package name.
+ * @hide
+ */
+ @Override
+ public void startVpnProfile(@NonNull String packageName) {
+ final int user = UserHandle.getUserId(mDeps.getCallingUid());
+ synchronized (mVpns) {
+ throwIfLockdownEnabled();
+ mVpns.get(user).startVpnProfile(packageName, mKeyStore);
+ }
+ }
+
+ /**
+ * Stops the Platform VPN if the provided package is running one.
+ *
+ * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+ * exclusively by the Settings app, and passed into the platform at startup time.
+ *
+ * @hide
+ */
+ @Override
+ public void stopVpnProfile(@NonNull String packageName) {
+ final int user = UserHandle.getUserId(mDeps.getCallingUid());
+ synchronized (mVpns) {
+ mVpns.get(user).stopVpnProfile(packageName);
+ }
+ }
+
+ /**
+ * Start legacy VPN, controlling native daemons as needed. Creates a
+ * secondary thread to perform connection work, returning quickly.
+ */
+ @Override
+ public void startLegacyVpn(VpnProfile profile) {
+ int user = UserHandle.getUserId(mDeps.getCallingUid());
+ final LinkProperties egress = mCm.getActiveLinkProperties();
+ if (egress == null) {
+ throw new IllegalStateException("Missing active network connection");
+ }
+ synchronized (mVpns) {
+ throwIfLockdownEnabled();
+ mVpns.get(user).startLegacyVpn(profile, mKeyStore, null /* underlying */, egress);
+ }
+ }
+
+ /**
+ * Return the information of the ongoing legacy VPN. This method is used
+ * by VpnSettings and not available in ConnectivityManager. Permissions
+ * are checked in Vpn class.
+ */
+ @Override
+ public LegacyVpnInfo getLegacyVpnInfo(int userId) {
+ enforceCrossUserPermission(userId);
+
+ synchronized (mVpns) {
+ return mVpns.get(userId).getLegacyVpnInfo();
+ }
+ }
+
+ /**
+ * Returns the information of the ongoing VPN for {@code userId}. This method is used by
+ * VpnDialogs and not available in ConnectivityManager.
+ * Permissions are checked in Vpn class.
+ * @hide
+ */
+ @Override
+ public VpnConfig getVpnConfig(int userId) {
+ enforceCrossUserPermission(userId);
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(userId);
+ if (vpn != null) {
+ return vpn.getVpnConfig();
+ } else {
+ return null;
+ }
+ }
+ }
+
+ private boolean isLockdownVpnEnabled() {
+ return mKeyStore.contains(Credentials.LOCKDOWN_VPN);
+ }
+
+ @Override
+ public boolean updateLockdownVpn() {
+ // Allow the system UID for the system server and for Settings.
+ // Also, for unit tests, allow the process that ConnectivityService is running in.
+ if (mDeps.getCallingUid() != Process.SYSTEM_UID
+ && Binder.getCallingPid() != Process.myPid()) {
+ logw("Lockdown VPN only available to system process or AID_SYSTEM");
+ return false;
+ }
+
+ synchronized (mVpns) {
+ // Tear down existing lockdown if profile was removed
+ mLockdownEnabled = isLockdownVpnEnabled();
+ if (!mLockdownEnabled) {
+ setLockdownTracker(null);
+ return true;
+ }
+
+ byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
+ if (profileTag == null) {
+ loge("Lockdown VPN configured but cannot be read from keystore");
+ return false;
+ }
+ String profileName = new String(profileTag);
+ final VpnProfile profile = VpnProfile.decode(
+ profileName, mKeyStore.get(Credentials.VPN + profileName));
+ if (profile == null) {
+ loge("Lockdown VPN configured invalid profile " + profileName);
+ setLockdownTracker(null);
+ return true;
+ }
+ int user = UserHandle.getUserId(mDeps.getCallingUid());
+ Vpn vpn = mVpns.get(user);
+ if (vpn == null) {
+ logw("VPN for user " + user + " not ready yet. Skipping lockdown");
+ return false;
+ }
+ setLockdownTracker(
+ new LockdownVpnTracker(mContext, mHandler, mKeyStore, vpn, profile));
+ }
+
+ return true;
+ }
+
+ /**
+ * Internally set new {@link LockdownVpnTracker}, shutting down any existing
+ * {@link LockdownVpnTracker}. Can be {@code null} to disable lockdown.
+ */
+ @GuardedBy("mVpns")
+ private void setLockdownTracker(LockdownVpnTracker tracker) {
+ // Shutdown any existing tracker
+ final LockdownVpnTracker existing = mLockdownTracker;
+ // TODO: Add a trigger when the always-on VPN enable/disable to reevaluate and send the
+ // necessary onBlockedStatusChanged callbacks.
+ mLockdownTracker = null;
+ if (existing != null) {
+ existing.shutdown();
+ }
+
+ if (tracker != null) {
+ mLockdownTracker = tracker;
+ mLockdownTracker.init();
+ }
+ }
+
+ /**
+ * Throws if there is any currently running, always-on Legacy VPN.
+ *
+ * <p>The LockdownVpnTracker and mLockdownEnabled both track whether an always-on Legacy VPN is
+ * running across the entire system. Tracking for app-based VPNs is done on a per-user,
+ * per-package basis in Vpn.java
+ */
+ @GuardedBy("mVpns")
+ private void throwIfLockdownEnabled() {
+ if (mLockdownEnabled) {
+ throw new IllegalStateException("Unavailable in lockdown mode");
+ }
+ }
+
+ /**
+ * Starts the always-on VPN {@link VpnService} for user {@param userId}, which should perform
+ * some setup and then call {@code establish()} to connect.
+ *
+ * @return {@code true} if the service was started, the service was already connected, or there
+ * was no always-on VPN to start. {@code false} otherwise.
+ */
+ private boolean startAlwaysOnVpn(int userId) {
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ // Shouldn't happen as all code paths that point here should have checked the Vpn
+ // exists already.
+ Log.wtf(TAG, "User " + userId + " has no Vpn configuration");
+ return false;
+ }
+
+ return vpn.startAlwaysOnVpn(mKeyStore);
+ }
+ }
+
+ @Override
+ public boolean isAlwaysOnVpnPackageSupported(int userId, String packageName) {
+ enforceSettingsPermission();
+ enforceCrossUserPermission(userId);
+
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ logw("User " + userId + " has no Vpn configuration");
+ return false;
+ }
+ return vpn.isAlwaysOnPackageSupported(packageName, mKeyStore);
+ }
+ }
+
+ @Override
+ public boolean setAlwaysOnVpnPackage(
+ int userId, String packageName, boolean lockdown, List<String> lockdownAllowlist) {
+ enforceControlAlwaysOnVpnPermission();
+ enforceCrossUserPermission(userId);
+
+ synchronized (mVpns) {
+ // Can't set always-on VPN if legacy VPN is already in lockdown mode.
+ if (isLockdownVpnEnabled()) {
+ return false;
+ }
+
+ Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ logw("User " + userId + " has no Vpn configuration");
+ return false;
+ }
+ if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownAllowlist, mKeyStore)) {
+ return false;
+ }
+ if (!startAlwaysOnVpn(userId)) {
+ vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String getAlwaysOnVpnPackage(int userId) {
+ enforceControlAlwaysOnVpnPermission();
+ enforceCrossUserPermission(userId);
+
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ logw("User " + userId + " has no Vpn configuration");
+ return null;
+ }
+ return vpn.getAlwaysOnPackage();
+ }
+ }
+
+ @Override
+ public boolean isVpnLockdownEnabled(int userId) {
+ enforceControlAlwaysOnVpnPermission();
+ enforceCrossUserPermission(userId);
+
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ logw("User " + userId + " has no Vpn configuration");
+ return false;
+ }
+ return vpn.getLockdown();
+ }
+ }
+
+ @Override
+ public List<String> getVpnLockdownAllowlist(int userId) {
+ enforceControlAlwaysOnVpnPermission();
+ enforceCrossUserPermission(userId);
+
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ logw("User " + userId + " has no Vpn configuration");
+ return null;
+ }
+ return vpn.getLockdownAllowlist();
+ }
+ }
+
+ @GuardedBy("mVpns")
+ private Vpn getVpnIfOwner() {
+ return getVpnIfOwner(mDeps.getCallingUid());
+ }
+
+ // TODO: stop calling into Vpn.java and get this information from data in this class.
+ @GuardedBy("mVpns")
+ private Vpn getVpnIfOwner(int uid) {
+ final int user = UserHandle.getUserId(uid);
+
+ final Vpn vpn = mVpns.get(user);
+ if (vpn == null) {
+ return null;
+ } else {
+ final UnderlyingNetworkInfo info = vpn.getUnderlyingNetworkInfo();
+ return (info == null || info.ownerUid != uid) ? null : vpn;
+ }
+ }
+
+ private void registerReceivers() {
+ // Set up the listener for user state for creating user VPNs.
+ // Should run on mHandler to avoid any races.
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_USER_STARTED);
+ intentFilter.addAction(Intent.ACTION_USER_STOPPED);
+ intentFilter.addAction(Intent.ACTION_USER_ADDED);
+ intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+ intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
+
+ mUserAllContext.registerReceiver(
+ mIntentReceiver,
+ intentFilter,
+ null /* broadcastPermission */,
+ mHandler);
+ mContext.createContextAsUser(UserHandle.SYSTEM, 0 /* flags */).registerReceiver(
+ mUserPresentReceiver,
+ new IntentFilter(Intent.ACTION_USER_PRESENT),
+ null /* broadcastPermission */,
+ mHandler /* scheduler */);
+
+ // Listen to package add and removal events for all users.
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ intentFilter.addDataScheme("package");
+ mUserAllContext.registerReceiver(
+ mIntentReceiver,
+ intentFilter,
+ null /* broadcastPermission */,
+ mHandler);
+
+ // Listen to lockdown VPN reset.
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(LockdownVpnTracker.ACTION_LOCKDOWN_RESET);
+ mUserAllContext.registerReceiver(
+ mIntentReceiver, intentFilter, NETWORK_STACK, mHandler);
+ }
+
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ ensureRunningOnHandlerThread();
+ final String action = intent.getAction();
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ final Uri packageData = intent.getData();
+ final String packageName =
+ packageData != null ? packageData.getSchemeSpecificPart() : null;
+
+ if (LockdownVpnTracker.ACTION_LOCKDOWN_RESET.equals(action)) {
+ onVpnLockdownReset();
+ }
+
+ // UserId should be filled for below intents, check the existence.
+ if (userId == UserHandle.USER_NULL) return;
+
+ if (Intent.ACTION_USER_STARTED.equals(action)) {
+ onUserStarted(userId);
+ } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+ onUserStopped(userId);
+ } else if (Intent.ACTION_USER_ADDED.equals(action)) {
+ onUserAdded(userId);
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ onUserRemoved(userId);
+ } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
+ onUserUnlocked(userId);
+ } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+ onPackageReplaced(packageName, uid);
+ } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ final boolean isReplacing = intent.getBooleanExtra(
+ Intent.EXTRA_REPLACING, false);
+ onPackageRemoved(packageName, uid, isReplacing);
+ } else {
+ Log.wtf(TAG, "received unexpected intent: " + action);
+ }
+ }
+ };
+
+ private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ ensureRunningOnHandlerThread();
+ // Try creating lockdown tracker, since user present usually means
+ // unlocked keystore.
+ updateLockdownVpn();
+ // Use the same context that registered receiver before to unregister it. Because use
+ // different context to unregister receiver will cause exception.
+ context.unregisterReceiver(this);
+ }
+ };
+
+ private void onUserStarted(int userId) {
+ synchronized (mVpns) {
+ Vpn userVpn = mVpns.get(userId);
+ if (userVpn != null) {
+ loge("Starting user already has a VPN");
+ return;
+ }
+ userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId, mKeyStore);
+ mVpns.put(userId, userVpn);
+ if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) {
+ updateLockdownVpn();
+ }
+ }
+ }
+
+ private void onUserStopped(int userId) {
+ synchronized (mVpns) {
+ Vpn userVpn = mVpns.get(userId);
+ if (userVpn == null) {
+ loge("Stopped user has no VPN");
+ return;
+ }
+ userVpn.onUserStopped();
+ mVpns.delete(userId);
+ }
+ }
+
+ @Override
+ public boolean isCallerCurrentAlwaysOnVpnApp() {
+ synchronized (mVpns) {
+ Vpn vpn = getVpnIfOwner();
+ return vpn != null && vpn.getAlwaysOn();
+ }
+ }
+
+ @Override
+ public boolean isCallerCurrentAlwaysOnVpnLockdownApp() {
+ synchronized (mVpns) {
+ Vpn vpn = getVpnIfOwner();
+ return vpn != null && vpn.getLockdown();
+ }
+ }
+
+
+ private void onUserAdded(int userId) {
+ synchronized (mVpns) {
+ final int vpnsSize = mVpns.size();
+ for (int i = 0; i < vpnsSize; i++) {
+ Vpn vpn = mVpns.valueAt(i);
+ vpn.onUserAdded(userId);
+ }
+ }
+ }
+
+ private void onUserRemoved(int userId) {
+ synchronized (mVpns) {
+ final int vpnsSize = mVpns.size();
+ for (int i = 0; i < vpnsSize; i++) {
+ Vpn vpn = mVpns.valueAt(i);
+ vpn.onUserRemoved(userId);
+ }
+ }
+ }
+
+ private void onPackageReplaced(String packageName, int uid) {
+ if (TextUtils.isEmpty(packageName) || uid < 0) {
+ Log.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid);
+ return;
+ }
+ final int userId = UserHandle.getUserId(uid);
+ synchronized (mVpns) {
+ final Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ return;
+ }
+ // Legacy always-on VPN won't be affected since the package name is not set.
+ if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
+ log("Restarting always-on VPN package " + packageName + " for user "
+ + userId);
+ vpn.startAlwaysOnVpn(mKeyStore);
+ }
+ }
+ }
+
+ private void onPackageRemoved(String packageName, int uid, boolean isReplacing) {
+ if (TextUtils.isEmpty(packageName) || uid < 0) {
+ Log.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
+ return;
+ }
+
+ final int userId = UserHandle.getUserId(uid);
+ synchronized (mVpns) {
+ final Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ return;
+ }
+ // Legacy always-on VPN won't be affected since the package name is not set.
+ if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
+ log("Removing always-on VPN package " + packageName + " for user "
+ + userId);
+ vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
+ }
+ }
+ }
+
+ private void onUserUnlocked(int userId) {
+ synchronized (mVpns) {
+ // User present may be sent because of an unlock, which might mean an unlocked keystore.
+ if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) {
+ updateLockdownVpn();
+ } else {
+ startAlwaysOnVpn(userId);
+ }
+ }
+ }
+
+ private void onVpnLockdownReset() {
+ synchronized (mVpns) {
+ if (mLockdownTracker != null) mLockdownTracker.reset();
+ }
+ }
+
+
+ @Override
+ public void factoryReset() {
+ enforceSettingsPermission();
+
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)
+ || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
+ return;
+ }
+
+ // Remove always-on package
+ final int userId = UserHandle.getCallingUserId();
+ synchronized (mVpns) {
+ final String alwaysOnPackage = getAlwaysOnVpnPackage(userId);
+ if (alwaysOnPackage != null) {
+ setAlwaysOnVpnPackage(userId, null, false, null);
+ setVpnPackageAuthorization(alwaysOnPackage, userId, VpnManager.TYPE_VPN_NONE);
+ }
+
+ // Turn Always-on VPN off
+ if (mLockdownEnabled && userId == UserHandle.USER_SYSTEM) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mKeyStore.delete(Credentials.LOCKDOWN_VPN);
+ mLockdownEnabled = false;
+ setLockdownTracker(null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ // Turn VPN off
+ VpnConfig vpnConfig = getVpnConfig(userId);
+ if (vpnConfig != null) {
+ if (vpnConfig.legacy) {
+ prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, userId);
+ } else {
+ // Prevent this app (packagename = vpnConfig.user) from initiating
+ // VPN connections in the future without user intervention.
+ setVpnPackageAuthorization(
+ vpnConfig.user, userId, VpnManager.TYPE_VPN_NONE);
+
+ prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
+ }
+ }
+ }
+ }
+
+ private void ensureRunningOnHandlerThread() {
+ if (mHandler.getLooper().getThread() != Thread.currentThread()) {
+ throw new IllegalStateException(
+ "Not running on VpnManagerService thread: "
+ + Thread.currentThread().getName());
+ }
+ }
+
+ private void enforceControlAlwaysOnVpnPermission() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CONTROL_ALWAYS_ON_VPN,
+ "VpnManagerService");
+ }
+
+ /**
+ * Require that the caller is either in the same user or has appropriate permission to interact
+ * across users.
+ *
+ * @param userId Target user for whatever operation the current IPC is supposed to perform.
+ */
+ private void enforceCrossUserPermission(int userId) {
+ if (userId == UserHandle.getCallingUserId()) {
+ // Not a cross-user call.
+ return;
+ }
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "VpnManagerService");
+ }
+
+ private void enforceSettingsPermission() {
+ enforceAnyPermissionOf(mContext,
+ android.Manifest.permission.NETWORK_SETTINGS,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
+ private static void log(String s) {
+ Log.d(TAG, s);
+ }
+
+ private static void logw(String s) {
+ Log.w(TAG, s);
+ }
+
+ private static void loge(String s) {
+ Log.e(TAG, s);
+ }
+}
diff --git a/services/core/java/com/android/server/accounts/OWNERS b/services/core/java/com/android/server/accounts/OWNERS
index ea5fd36702f9..8dcc04a27af6 100644
--- a/services/core/java/com/android/server/accounts/OWNERS
+++ b/services/core/java/com/android/server/accounts/OWNERS
@@ -3,7 +3,6 @@ dementyev@google.com
sandrakwan@google.com
hackbod@google.com
svetoslavganov@google.com
-moltmann@google.com
fkupolov@google.com
yamasani@google.com
omakoto@google.com
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4f445296f2af..104c7024f7eb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -130,6 +130,7 @@ import static com.android.server.wm.ActivityTaskManagerService.DUMP_LASTANR_TRAC
import static com.android.server.wm.ActivityTaskManagerService.DUMP_RECENTS_CMD;
import static com.android.server.wm.ActivityTaskManagerService.DUMP_RECENTS_SHORT_CMD;
import static com.android.server.wm.ActivityTaskManagerService.DUMP_STARTER_CMD;
+import static com.android.server.wm.ActivityTaskManagerService.DUMP_TOP_RESUMED_ACTIVITY;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import static com.android.server.wm.ActivityTaskManagerService.relaunchReasonToString;
@@ -145,6 +146,7 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManagerInternal;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.ActivityThread;
+import android.app.AnrController;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal.CheckOpsDelegate;
@@ -185,6 +187,7 @@ import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
+import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -354,6 +357,7 @@ import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.firewall.IntentFirewall;
import com.android.server.graphics.fonts.FontManagerInternal;
import com.android.server.job.JobSchedulerInternal;
+import com.android.server.os.NativeTombstoneManager;
import com.android.server.pm.Installer;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.uri.GrantUri;
@@ -1511,7 +1515,9 @@ public class ActivityManagerService extends IActivityManager.Stub
private static final int INDEX_TOTAL_SWAP_PSS = 10;
private static final int INDEX_TOTAL_RSS = 11;
private static final int INDEX_TOTAL_NATIVE_PSS = 12;
- private static final int INDEX_LAST = 13;
+ private static final int INDEX_TOTAL_MEMTRACK_GRAPHICS = 13;
+ private static final int INDEX_TOTAL_MEMTRACK_GL = 14;
+ private static final int INDEX_LAST = 15;
final class UiHandler extends Handler {
public UiHandler() {
@@ -5699,6 +5705,23 @@ public class ActivityManagerService extends IActivityManager.Stub
? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
}
+ @Override
+ public int[] checkUriPermissions(@NonNull List<Uri> uris, int pid, int uid,
+ final int modeFlags, IBinder callerToken) {
+ final int size = uris.size();
+ int[] res = new int[size];
+ // Default value DENIED.
+ Arrays.fill(res, PackageManager.PERMISSION_DENIED);
+
+ for (int i = 0; i < size; i++) {
+ final Uri uri = uris.get(i);
+ final int userId = ContentProvider.getUserIdFromUri(uri, mContext.getUserId());
+ res[i] = checkUriPermission(ContentProvider.getUriWithoutUserId(uri), pid, uid,
+ modeFlags, userId, callerToken);
+ }
+ return res;
+ }
+
/**
* @param uri This uri must NOT contain an embedded userId.
* @param userId The userId in which the uri is to be resolved.
@@ -8328,6 +8351,9 @@ public class ActivityManagerService extends IActivityManager.Stub
mUserController.handleIncomingUser(callingPid, callingUid, userId, true, ALLOW_NON_FULL,
"getHistoricalProcessExitReasons", null);
+ NativeTombstoneManager tombstoneService = LocalServices.getService(
+ NativeTombstoneManager.class);
+
final ArrayList<ApplicationExitInfo> results = new ArrayList<ApplicationExitInfo>();
if (!TextUtils.isEmpty(packageName)) {
final int uid = enforceDumpPermissionForPackage(packageName, userId, callingUid,
@@ -8335,11 +8361,13 @@ public class ActivityManagerService extends IActivityManager.Stub
if (uid != Process.INVALID_UID) {
mProcessList.mAppExitInfoTracker.getExitInfo(
packageName, uid, pid, maxNum, results);
+ tombstoneService.collectTombstones(results, uid, pid, maxNum);
}
} else {
// If no package name is given, use the caller's uid as the filter uid.
mProcessList.mAppExitInfoTracker.getExitInfo(
packageName, callingUid, pid, maxNum, results);
+ tombstoneService.collectTombstones(results, callingUid, pid, maxNum);
}
return new ParceledListSlice<ApplicationExitInfo>(results);
@@ -8723,7 +8751,8 @@ public class ActivityManagerService extends IActivityManager.Stub
if (DUMP_ACTIVITIES_CMD.equals(cmd) || DUMP_ACTIVITIES_SHORT_CMD.equals(cmd)
|| DUMP_LASTANR_CMD.equals(cmd) || DUMP_LASTANR_TRACES_CMD.equals(cmd)
|| DUMP_STARTER_CMD.equals(cmd) || DUMP_CONTAINERS_CMD.equals(cmd)
- || DUMP_RECENTS_CMD.equals(cmd) || DUMP_RECENTS_SHORT_CMD.equals(cmd)) {
+ || DUMP_RECENTS_CMD.equals(cmd) || DUMP_RECENTS_SHORT_CMD.equals(cmd)
+ || DUMP_TOP_RESUMED_ACTIVITY.equals(cmd)) {
mAtmInternal.dump(
cmd, fd, pw, args, opti, true /* dumpAll */, dumpClient, dumpPackage);
} else if ("binder-proxies".equals(cmd)) {
@@ -10391,6 +10420,7 @@ public class ActivityManagerService extends IActivityManager.Stub
long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
long[] miscSwapPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
long[] miscRss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
+ long[] memtrackTmp = new long[4];
long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length];
long oomSwapPss[] = new long[DUMP_MEM_OOM_LABEL.length];
@@ -10424,6 +10454,8 @@ public class ActivityManagerService extends IActivityManager.Stub
final int reportType;
final long startTime;
final long endTime;
+ long memtrackGraphics = 0;
+ long memtrackGl = 0;
if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
reportType = ProcessStats.ADD_PSS_EXTERNAL_SLOW;
startTime = SystemClock.currentThreadTimeMillis();
@@ -10435,7 +10467,7 @@ public class ActivityManagerService extends IActivityManager.Stub
} else {
reportType = ProcessStats.ADD_PSS_EXTERNAL;
startTime = SystemClock.currentThreadTimeMillis();
- long pss = Debug.getPss(pid, tmpLong, null);
+ long pss = Debug.getPss(pid, tmpLong, memtrackTmp);
if (pss == 0) {
continue;
}
@@ -10443,6 +10475,8 @@ public class ActivityManagerService extends IActivityManager.Stub
endTime = SystemClock.currentThreadTimeMillis();
mi.dalvikPrivateDirty = (int) tmpLong[0];
mi.dalvikRss = (int) tmpLong[2];
+ memtrackGraphics = memtrackTmp[1];
+ memtrackGl = memtrackTmp[2];
}
if (!opts.isCheckinRequest && opts.dumpDetails) {
pw.println("\n** MEMINFO in pid " + pid + " [" + r.processName + "] **");
@@ -10506,6 +10540,8 @@ public class ActivityManagerService extends IActivityManager.Stub
ss[INDEX_TOTAL_PSS] += myTotalPss;
ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss;
ss[INDEX_TOTAL_RSS] += myTotalRss;
+ ss[INDEX_TOTAL_MEMTRACK_GRAPHICS] += memtrackGraphics;
+ ss[INDEX_TOTAL_MEMTRACK_GL] += memtrackGl;
MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
(hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
myTotalSwapPss, myTotalRss, pid, hasActivities);
@@ -10569,6 +10605,8 @@ public class ActivityManagerService extends IActivityManager.Stub
final Debug.MemoryInfo[] memInfos = new Debug.MemoryInfo[1];
mAppProfiler.forAllCpuStats((st) -> {
if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) {
+ long memtrackGraphics = 0;
+ long memtrackGl = 0;
if (memInfos[0] == null) {
memInfos[0] = new Debug.MemoryInfo();
}
@@ -10578,13 +10616,15 @@ public class ActivityManagerService extends IActivityManager.Stub
return;
}
} else {
- long pss = Debug.getPss(st.pid, tmpLong, null);
+ long pss = Debug.getPss(st.pid, tmpLong, memtrackTmp);
if (pss == 0) {
return;
}
info.nativePss = (int) pss;
info.nativePrivateDirty = (int) tmpLong[0];
info.nativeRss = (int) tmpLong[2];
+ memtrackGraphics = memtrackTmp[1];
+ memtrackGl = memtrackTmp[2];
}
final long myTotalPss = info.getTotalPss();
@@ -10594,6 +10634,8 @@ public class ActivityManagerService extends IActivityManager.Stub
ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss;
ss[INDEX_TOTAL_RSS] += myTotalRss;
ss[INDEX_TOTAL_NATIVE_PSS] += myTotalPss;
+ ss[INDEX_TOTAL_MEMTRACK_GRAPHICS] += memtrackGraphics;
+ ss[INDEX_TOTAL_MEMTRACK_GL] += memtrackGl;
MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
st.name, myTotalPss, info.getSummaryTotalSwapPss(), myTotalRss,
@@ -10775,13 +10817,13 @@ public class ActivityManagerService extends IActivityManager.Stub
long kernelUsed = memInfo.getKernelUsedSizeKb();
final long ionHeap = Debug.getIonHeapsSizeKb();
final long ionPool = Debug.getIonPoolsSizeKb();
+ final long dmabufMapped = Debug.getDmabufMappedSizeKb();
if (ionHeap >= 0 && ionPool >= 0) {
- final long ionMapped = Debug.getIonMappedSizeKb();
- final long ionUnmapped = ionHeap - ionMapped;
+ final long ionUnmapped = ionHeap - dmabufMapped;
pw.print(" ION: ");
pw.print(stringifyKBSize(ionHeap + ionPool));
pw.print(" (");
- pw.print(stringifyKBSize(ionMapped));
+ pw.print(stringifyKBSize(dmabufMapped));
pw.print(" mapped + ");
pw.print(stringifyKBSize(ionUnmapped));
pw.print(" unmapped + ");
@@ -10790,11 +10832,61 @@ public class ActivityManagerService extends IActivityManager.Stub
// Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
// set on ION VMAs, therefore consider the entire ION heap as used kernel memory
kernelUsed += ionHeap;
+ } else {
+ final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb();
+ if (totalExportedDmabuf >= 0) {
+ final long dmabufUnmapped = totalExportedDmabuf - dmabufMapped;
+ pw.print("DMA-BUF: ");
+ pw.print(stringifyKBSize(totalExportedDmabuf));
+ pw.print(" (");
+ pw.print(stringifyKBSize(dmabufMapped));
+ pw.print(" mapped + ");
+ pw.print(stringifyKBSize(dmabufUnmapped));
+ pw.println(" unmapped)");
+ // Account unmapped dmabufs as part of kernel memory allocations
+ kernelUsed += dmabufUnmapped;
+ // Replace memtrack HAL reported Graphics category with mapped dmabufs
+ ss[INDEX_TOTAL_PSS] -= ss[INDEX_TOTAL_MEMTRACK_GRAPHICS];
+ ss[INDEX_TOTAL_PSS] += dmabufMapped;
+ }
+
+ // totalDmabufHeapExported is included in totalExportedDmabuf above and hence do not
+ // need to be added to kernelUsed.
+ final long totalDmabufHeapExported = Debug.getDmabufHeapTotalExportedKb();
+ if (totalDmabufHeapExported >= 0) {
+ pw.print("DMA-BUF Heaps: ");
+ pw.println(stringifyKBSize(totalDmabufHeapExported));
+ }
+
+ final long totalDmabufHeapPool = Debug.getDmabufHeapPoolsSizeKb();
+ if (totalDmabufHeapPool >= 0) {
+ pw.print("DMA-BUF Heaps pool: ");
+ pw.println(stringifyKBSize(totalDmabufHeapPool));
+ }
}
final long gpuUsage = Debug.getGpuTotalUsageKb();
if (gpuUsage >= 0) {
- pw.print(" GPU: "); pw.println(stringifyKBSize(gpuUsage));
+ final long gpuDmaBufUsage = Debug.getGpuDmaBufUsageKb();
+ if (gpuDmaBufUsage >= 0) {
+ final long gpuPrivateUsage = gpuUsage - gpuDmaBufUsage;
+ pw.print(" GPU: ");
+ pw.print(stringifyKBSize(gpuUsage));
+ pw.print(" (");
+ pw.print(stringifyKBSize(gpuDmaBufUsage));
+ pw.print(" dmabuf + ");
+ pw.print(stringifyKBSize(gpuPrivateUsage));
+ pw.println(" private)");
+ // Replace memtrack HAL reported GL category with private GPU allocations and
+ // account it as part of kernel memory allocations
+ ss[INDEX_TOTAL_PSS] -= ss[INDEX_TOTAL_MEMTRACK_GL];
+ kernelUsed += gpuPrivateUsage;
+ } else {
+ pw.print(" GPU: "); pw.println(stringifyKBSize(gpuUsage));
+ }
}
+
+ // Note: ION/DMA-BUF heap pools are reclaimable and hence, they are included as part of
+ // memInfo.getCachedSizeKb().
final long lostRAM = memInfo.getTotalSizeKb()
- (ss[INDEX_TOTAL_PSS] - ss[INDEX_TOTAL_SWAP_PSS])
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
@@ -15964,6 +16056,16 @@ public class ActivityManagerService extends IActivityManager.Stub
// PackageManagerService.
return mConstants.mBootTimeTempAllowlistDuration;
}
+
+ @Override
+ public void registerAnrController(AnrController controller) {
+ mActivityTaskManager.registerAnrController(controller);
+ }
+
+ @Override
+ public void unregisterAnrController(AnrController controller) {
+ mActivityTaskManager.unregisterAnrController(controller);
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index fe71fbf79157..c971bd2ab6d5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2984,7 +2984,13 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println("Reset all changes for " + packageName + " to default value.");
return 0;
}
- if (platformCompat.clearOverride(changeId, packageName)) {
+ boolean existed;
+ if (killPackage) {
+ existed = platformCompat.clearOverride(changeId, packageName);
+ } else {
+ existed = platformCompat.clearOverrideForTest(changeId, packageName);
+ }
+ if (existed) {
pw.println("Reset change " + changeId + " for " + packageName
+ " to default value.");
} else {
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 17be2100414f..b85d7292e738 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -63,8 +63,10 @@ import com.android.internal.app.ProcessMap;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.IoThread;
+import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemServiceManager;
+import com.android.server.os.NativeTombstoneManager;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -78,6 +80,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
@@ -762,6 +765,10 @@ public final class AppExitInfoTracker {
* Helper function for shell command
*/
void clearHistoryProcessExitInfo(String packageName, int userId) {
+ NativeTombstoneManager tombstoneService = LocalServices.getService(
+ NativeTombstoneManager.class);
+ Optional<Integer> appId = Optional.empty();
+
if (TextUtils.isEmpty(packageName)) {
synchronized (mLock) {
removeByUserIdLocked(userId);
@@ -769,10 +776,13 @@ public final class AppExitInfoTracker {
} else {
final int uid = mService.mPackageManagerInt.getPackageUid(packageName,
PackageManager.MATCH_ALL, userId);
+ appId = Optional.of(UserHandle.getAppId(uid));
synchronized (mLock) {
removePackageLocked(packageName, uid, true, userId);
}
}
+
+ tombstoneService.purge(Optional.of(userId), appId);
schedulePersistProcessExitInfo(true);
}
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 8741c25472be..490b15e7c176 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1327,7 +1327,7 @@ public class AppProfiler {
infoMap.put(mi.pid, mi);
}
updateCpuStatsNow();
- long[] memtrackTmp = new long[1];
+ long[] memtrackTmp = new long[4];
long[] swaptrackTmp = new long[2];
// Get a list of Stats that have vsize > 0
final List<ProcessCpuTracker.Stats> stats = getCpuStats(st -> st.vsize > 0);
@@ -1350,6 +1350,8 @@ public class AppProfiler {
long totalPss = 0;
long totalSwapPss = 0;
long totalMemtrack = 0;
+ long totalMemtrackGraphics = 0;
+ long totalMemtrackGl = 0;
for (int i = 0, size = memInfos.size(); i < size; i++) {
ProcessMemInfo mi = memInfos.get(i);
if (mi.pss == 0) {
@@ -1360,6 +1362,8 @@ public class AppProfiler {
totalPss += mi.pss;
totalSwapPss += mi.swapPss;
totalMemtrack += mi.memtrack;
+ totalMemtrackGraphics += memtrackTmp[1];
+ totalMemtrackGl += memtrackTmp[2];
}
Collections.sort(memInfos, new Comparator<ProcessMemInfo>() {
@Override public int compare(ProcessMemInfo lhs, ProcessMemInfo rhs) {
@@ -1517,25 +1521,73 @@ public class AppProfiler {
final long ionHeap = Debug.getIonHeapsSizeKb();
final long ionPool = Debug.getIonPoolsSizeKb();
if (ionHeap >= 0 && ionPool >= 0) {
- final long ionMapped = Debug.getIonMappedSizeKb();
- final long ionUnmapped = ionHeap - ionMapped;
memInfoBuilder.append(" ION: ");
memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool));
memInfoBuilder.append("\n");
// Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
// set on ION VMAs, therefore consider the entire ION heap as used kernel memory
kernelUsed += ionHeap;
+ } else {
+ final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb();
+ if (totalExportedDmabuf >= 0) {
+ final long dmabufMapped = Debug.getDmabufMappedSizeKb();
+ final long dmabufUnmapped = totalExportedDmabuf - dmabufMapped;
+ memInfoBuilder.append("DMA-BUF: ");
+ memInfoBuilder.append(stringifyKBSize(totalExportedDmabuf));
+ memInfoBuilder.append("\n");
+ // Account unmapped dmabufs as part of kernel memory allocations
+ kernelUsed += dmabufUnmapped;
+ // Replace memtrack HAL reported Graphics category with mapped dmabufs
+ totalPss -= totalMemtrackGraphics;
+ totalPss += dmabufMapped;
+ }
+ // These are included in the totalExportedDmabuf above and hence do not need to be added
+ // to kernelUsed.
+ final long totalExportedDmabufHeap = Debug.getDmabufHeapTotalExportedKb();
+ if (totalExportedDmabufHeap >= 0) {
+ memInfoBuilder.append("DMA-BUF Heap: ");
+ memInfoBuilder.append(stringifyKBSize(totalExportedDmabufHeap));
+ memInfoBuilder.append("\n");
+ }
+
+ final long totalDmabufHeapPool = Debug.getDmabufHeapPoolsSizeKb();
+ if (totalDmabufHeapPool >= 0) {
+ memInfoBuilder.append("DMA-BUF Heaps pool: ");
+ memInfoBuilder.append(stringifyKBSize(totalDmabufHeapPool));
+ memInfoBuilder.append("\n");
+ }
}
+
final long gpuUsage = Debug.getGpuTotalUsageKb();
if (gpuUsage >= 0) {
- memInfoBuilder.append(" GPU: ");
- memInfoBuilder.append(stringifyKBSize(gpuUsage));
- memInfoBuilder.append("\n");
+ final long gpuDmaBufUsage = Debug.getGpuDmaBufUsageKb();
+ if (gpuDmaBufUsage >= 0) {
+ final long gpuPrivateUsage = gpuUsage - gpuDmaBufUsage;
+ memInfoBuilder.append(" GPU: ");
+ memInfoBuilder.append(stringifyKBSize(gpuUsage));
+ memInfoBuilder.append(" (");
+ memInfoBuilder.append(stringifyKBSize(gpuDmaBufUsage));
+ memInfoBuilder.append(" dmabuf + ");
+ memInfoBuilder.append(stringifyKBSize(gpuPrivateUsage));
+ memInfoBuilder.append(" private)\n");
+ // Replace memtrack HAL reported GL category with private GPU allocations and
+ // account it as part of kernel memory allocations
+ totalPss -= totalMemtrackGl;
+ kernelUsed += gpuPrivateUsage;
+ } else {
+ memInfoBuilder.append(" GPU: ");
+ memInfoBuilder.append(stringifyKBSize(gpuUsage));
+ memInfoBuilder.append("\n");
+ }
+
}
memInfoBuilder.append(" Used RAM: ");
memInfoBuilder.append(stringifyKBSize(
totalPss - cachedPss + kernelUsed));
memInfoBuilder.append("\n");
+
+ // Note: ION/DMA-BUF heap pools are reclaimable and hence, they are included as part of
+ // memInfo.getCachedSizeKb().
memInfoBuilder.append(" Lost RAM: ");
memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb()
- (totalPss - totalSwapPss) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index c6947c2d9525..fc28bfbea710 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -15,8 +15,6 @@
*/
package com.android.server.am;
-import static com.android.internal.power.MeasuredEnergyArray.SUBSYSTEM_DISPLAY;
-
import android.annotation.Nullable;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
@@ -39,13 +37,12 @@ import android.telephony.ModemActivityInfo;
import android.telephony.TelephonyManager;
import android.util.IntArray;
import android.util.Slog;
-import android.util.SparseIntArray;
+import android.util.SparseArray;
import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BatteryStatsImpl;
-import com.android.internal.power.MeasuredEnergyArray;
import com.android.internal.power.MeasuredEnergyStats;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -53,8 +50,6 @@ import com.android.server.LocalServices;
import libcore.util.EmptyArray;
-import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@@ -148,13 +143,12 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
private WifiActivityEnergyInfo mLastWifiInfo =
new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0);
- /** Maps the EnergyConsumer id to it's corresponding {@link MeasuredEnergySubsystem} */
- @GuardedBy("mWorkerLock")
- private @Nullable SparseIntArray mEnergyConsumerToSubsystemMap = null;
-
- /** Maps a {@link MeasuredEnergySubsystem} to it's corresponding EnergyConsumer id */
+ /**
+ * Maps an {@link EnergyConsumerType} to it's corresponding {@link EnergyConsumer#id}s,
+ * unless it is of {@link EnergyConsumer#type}=={@link EnergyConsumerType#OTHER}
+ */
@GuardedBy("mWorkerLock")
- private @Nullable SparseIntArray mSubsystemToEnergyConsumerMap = null;
+ private @Nullable SparseArray<int[]> mEnergyConsumerTypeToIdMap = null;
/** Snapshot of measured energies, or null if no measured energies are supported. */
@GuardedBy("mWorkerLock")
@@ -204,18 +198,37 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
mWifiManager = wm;
mTelephony = tm;
mPowerStatsInternal = psi;
+
+ boolean[] supportedStdBuckets = null;
+ int numCustomBuckets = 0;
if (mPowerStatsInternal != null) {
- populateEnergyConsumerSubsystemMapsLocked();
- final MeasuredEnergyArray initialMeasuredEnergies = getEnergyConsumptionData();
- mMeasuredEnergySnapshot = initialMeasuredEnergies == null
- ? null : new MeasuredEnergySnapshot(initialMeasuredEnergies);
- final boolean[] supportedStdBuckets
- = getSupportedEnergyBuckets(initialMeasuredEnergies);
- final int numCustomBuckets = 0; // TODO: Get this from initialMeasuredEnergies
- synchronized (mStats) {
- mStats.initMeasuredEnergyStatsLocked(supportedStdBuckets, numCustomBuckets);
+ final SparseArray<EnergyConsumer> idToConsumer
+ = populateEnergyConsumerSubsystemMapsLocked();
+ if (idToConsumer != null) {
+ mMeasuredEnergySnapshot = new MeasuredEnergySnapshot(idToConsumer);
+ try {
+ final EnergyConsumerResult[] initialEcrs = getEnergyConsumptionData().get(
+ EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ // According to spec, initialEcrs will include 0s for consumers that haven't
+ // used any energy yet, as long as they are supported; however,
+ // attributed uid energies will be absent if their energy is 0.
+ mMeasuredEnergySnapshot.updateAndGetDelta(initialEcrs);
+ } catch (TimeoutException | InterruptedException e) {
+ Slog.w(TAG, "timeout or interrupt reading initial getEnergyConsumedAsync: "
+ + e);
+ // Continue running, later attempts to query may be successful.
+ } catch (ExecutionException e) {
+ Slog.wtf(TAG, "exception reading initial getEnergyConsumedAsync: "
+ + e.getCause());
+ // Continue running, later attempts to query may be successful.
+ }
+ numCustomBuckets = mMeasuredEnergySnapshot.getNumOtherOrdinals();
+ supportedStdBuckets = getSupportedEnergyBuckets(idToConsumer);
}
}
+ synchronized (mStats) {
+ mStats.initMeasuredEnergyStatsLocked(supportedStdBuckets, numCustomBuckets);
+ }
}
}
@@ -493,6 +506,8 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
CompletableFuture<ModemActivityInfo> modemFuture = CompletableFuture.completedFuture(null);
boolean railUpdated = false;
+ CompletableFuture<EnergyConsumerResult[]> futureECRs = getMeasuredEnergyLocked(updateFlags);
+
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
// We were asked to fetch WiFi data.
// Only fetch WiFi power data if it is supported.
@@ -568,14 +583,31 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
} catch (ExecutionException e) {
Slog.w(TAG, "exception reading modem stats: " + e.getCause());
}
- final SparseLongArray energyDeltas = mMeasuredEnergySnapshot == null ? null :
- mMeasuredEnergySnapshot.updateAndGetDelta(getMeasuredEnergyLocked(updateFlags));
+
+ final MeasuredEnergySnapshot.MeasuredEnergyDeltaData measuredEnergyDeltas;
+ if (mMeasuredEnergySnapshot == null || futureECRs == null) {
+ measuredEnergyDeltas = null;
+ } else {
+ EnergyConsumerResult[] ecrs;
+ try {
+ ecrs = futureECRs.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException | InterruptedException e) {
+ // TODO (b/180519623): Invalidate the MeasuredEnergy derived data until next reset.
+ Slog.w(TAG, "timeout or interrupt reading getEnergyConsumedAsync: " + e);
+ ecrs = null;
+ } catch (ExecutionException e) {
+ Slog.wtf(TAG, "exception reading getEnergyConsumedAsync: " + e.getCause());
+ ecrs = null;
+ }
+ measuredEnergyDeltas = mMeasuredEnergySnapshot.updateAndGetDelta(ecrs);
+ }
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
final long elapsedRealtimeUs = elapsedRealtime * 1000;
final long uptimeUs = uptime * 1000;
+ // Now that we have finally received all the data, we can tell mStats about it.
synchronized (mStats) {
mStats.addHistoryEventLocked(
elapsedRealtime,
@@ -601,10 +633,21 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
}
// Inform mStats about each applicable measured energy.
- if (energyDeltas != null) {
- final long displayEnergy = energyDeltas.get(SUBSYSTEM_DISPLAY, 0L);
- // Always pass in what BatteryExternalStatsWorker thinks screenState is.
- mStats.updateDisplayEnergyLocked(displayEnergy, screenState, elapsedRealtime);
+ if (measuredEnergyDeltas != null) {
+ final long displayEnergy = measuredEnergyDeltas.displayEnergyUJ;
+ if (displayEnergy != MeasuredEnergySnapshot.UNAVAILABLE) {
+ // If updating, pass in what BatteryExternalStatsWorker thinks screenState is.
+ mStats.updateDisplayEnergyLocked(displayEnergy, screenState, elapsedRealtime);
+ }
+ }
+ // Inform mStats about each applicable custom energy bucket.
+ if (measuredEnergyDeltas != null && measuredEnergyDeltas.otherTotalEnergyUJ != null) {
+ // Iterate over the custom (EnergyConsumerType.OTHER) ordinals.
+ for (int ord = 0; ord < measuredEnergyDeltas.otherTotalEnergyUJ.length; ord++) {
+ long totalEnergy = measuredEnergyDeltas.otherTotalEnergyUJ[ord];
+ SparseLongArray uidEnergies = measuredEnergyDeltas.otherUidEnergiesUJ[ord];
+ mStats.updateCustomMeasuredEnergyDataLocked(ord, totalEnergy, uidEnergies);
+ }
}
if (bluetoothInfo != null) {
@@ -621,7 +664,8 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
if (wifiInfo != null) {
if (wifiInfo.isValid()) {
- // TODO: wifiEnergyDelta = energyDeltas.get(MeasuredEnergyArray.SUBSYSTEM_WIFI, 0L);
+ // TODO: wifiEnergyDelta = measuredEnergyDeltas.consumerTypeEnergyUJ
+ // .get(EnergyConsumerType.WIFI, MeasuredEnergySnapshot.UNAVAILABLE)
mStats.updateWifiState(extractDeltaLocked(wifiInfo)
/*, TODO: wifiEnergyDelta */, elapsedRealtime, uptime);
} else {
@@ -740,21 +784,23 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
}
/**
- * Map the {@link MeasuredEnergyArray.MeasuredEnergySubsystem}s in the given energyArray to
+ * Map the {@link EnergyConsumerType}s in the given energyArray to
* their corresponding {@link MeasuredEnergyStats.StandardEnergyBucket}s.
* Does not include custom energy buckets (which are always, by definition, supported).
*
* @return array with true for index i if standard energy bucket i is supported.
*/
- private static @Nullable boolean[] getSupportedEnergyBuckets(MeasuredEnergyArray energyArray) {
- if (energyArray == null) {
+ private static @Nullable boolean[] getSupportedEnergyBuckets(
+ SparseArray<EnergyConsumer> idToConsumer) {
+ if (idToConsumer == null) {
return null;
}
final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_ENERGY_BUCKETS];
- final int size = energyArray.size();
- for (int energyIdx = 0; energyIdx < size; energyIdx++) {
- switch (energyArray.getSubsystem(energyIdx)) {
- case MeasuredEnergyArray.SUBSYSTEM_DISPLAY:
+ final int size = idToConsumer.size();
+ for (int idx = 0; idx < size; idx++) {
+ final EnergyConsumer consumer = idToConsumer.valueAt(idx);
+ switch (consumer.type) {
+ case EnergyConsumerType.DISPLAY:
buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON] = true;
buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE] = true;
buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_OTHER] = true;
@@ -764,71 +810,29 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
return buckets;
}
+ /** Get all {@link EnergyConsumerResult}s with the latest energy usage since boot. */
+ @GuardedBy("mWorkerLock")
+ @Nullable
+ private CompletableFuture<EnergyConsumerResult[]> getEnergyConsumptionData() {
+ return getEnergyConsumptionData(new int[0]);
+ }
+
/**
- * Get a {@link MeasuredEnergyArray} with the latest
- * {@link MeasuredEnergyArray.MeasuredEnergySubsystem} energy usage since boot.
- *
- * TODO(b/176988041): Replace {@link MeasuredEnergyArray} usage with {@link
- * EnergyConsumerResult}[]
+ * Get {@link EnergyConsumerResult}s of the specified {@link EnergyConsumer} ids with the latest
+ * energy usage since boot.
*/
@GuardedBy("mWorkerLock")
- @VisibleForTesting
- public @Nullable MeasuredEnergyArray getEnergyConsumptionData() {
- final EnergyConsumerResult[] results;
- try {
- results = mPowerStatsInternal.getEnergyConsumedAsync(new int[0])
- .get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (Exception e) {
- Slog.e(TAG, "Failed to getEnergyConsumedAsync", e);
- return null;
- }
- if (results == null) return null;
- final int size = results.length;
- final int[] subsystems = new int[size];
- final long[] energyUJ = new long[size];
-
- int count = 0;
- for (int i = 0; i < size; i++) {
- final EnergyConsumerResult consumer = results[i];
- final int subsystem = mEnergyConsumerToSubsystemMap.get(consumer.id,
- MeasuredEnergyArray.SUBSYSTEM_UNKNOWN);
- if (subsystem == MeasuredEnergyArray.SUBSYSTEM_UNKNOWN) continue;
- subsystems[count] = subsystem;
- energyUJ[count] = consumer.energyUWs;
- count++;
- }
- final int arraySize = count;
- return new MeasuredEnergyArray() {
- @Override
- public int getSubsystem(int index) {
- if (index >= size()) {
- throw new IllegalArgumentException(
- "Out of bounds subsystem index! index : " + index + ", size : "
- + size());
- }
- return subsystems[index];
- }
-
- @Override
- public long getEnergy(int index) {
- if (index >= size()) {
- throw new IllegalArgumentException(
- "Out of bounds subsystem index! index : " + index + ", size : "
- + size());
- }
- return energyUJ[index];
- }
-
- @Override
- public int size() {
- return arraySize;
- }
- };
+ @Nullable
+ private CompletableFuture<EnergyConsumerResult[]> getEnergyConsumptionData(int[] consumerIds) {
+ return mPowerStatsInternal.getEnergyConsumedAsync(consumerIds);
}
- /** Fetch MeasuredEnergyArray for supported subsystems based on the given updateFlags. */
+ /** Fetch EnergyConsumerResult[] for supported subsystems based on the given updateFlags. */
+ @VisibleForTesting
@GuardedBy("mWorkerLock")
- private @Nullable MeasuredEnergyArray getMeasuredEnergyLocked(@ExternalUpdateFlag int flags) {
+ @Nullable
+ public CompletableFuture<EnergyConsumerResult[]> getMeasuredEnergyLocked(
+ @ExternalUpdateFlag int flags) {
if (mMeasuredEnergySnapshot == null || mPowerStatsInternal == null) return null;
if (flags == UPDATE_ALL) {
@@ -836,72 +840,77 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
return getEnergyConsumptionData();
}
- final List<Integer> energySubsystems = new ArrayList<>();
+ final IntArray energyConsumerIds = new IntArray();
+ if ((flags & UPDATE_CPU) != 0) {
+ addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.CPU_CLUSTER);
+ }
if ((flags & UPDATE_DISPLAY) != 0) {
- addEnergyConsumerIdLocked(energySubsystems, SUBSYSTEM_DISPLAY);
+ addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.DISPLAY);
}
// TODO: Wifi, Bluetooth, etc., go here
- if (energySubsystems.isEmpty()) {
+
+ if (energyConsumerIds.size() == 0) {
return null;
}
- // TODO: Query *specific* subsystems from HAL based on energyConsumerIds.toArray()
- return getEnergyConsumptionData();
+ return getEnergyConsumptionData(energyConsumerIds.toArray());
}
@GuardedBy("mWorkerLock")
- private void addEnergyConsumerIdLocked(List<Integer> energyConsumerIds,
- @MeasuredEnergyArray.MeasuredEnergySubsystem int consumerId) {
- if (mMeasuredEnergySnapshot.hasSubsystem(consumerId)) {
- energyConsumerIds.add(consumerId);
- }
+ private void addEnergyConsumerIdLocked(
+ IntArray energyConsumerIds, @EnergyConsumerType int type) {
+ final int[] consumerIds = mEnergyConsumerTypeToIdMap.get(type);
+ if (consumerIds == null) return;
+ energyConsumerIds.addAll(consumerIds);
}
+ /** Populates the cached type->ids map, and returns the (inverse) id->EnergyConsumer map. */
@GuardedBy("mWorkerLock")
- private void populateEnergyConsumerSubsystemMapsLocked() {
+ private @Nullable SparseArray<EnergyConsumer> populateEnergyConsumerSubsystemMapsLocked() {
if (mPowerStatsInternal == null) {
- // PowerStatsInternal unavailable, don't bother populating maps.
- mEnergyConsumerToSubsystemMap = null;
- mSubsystemToEnergyConsumerMap = null;
- return;
+ return null;
}
final EnergyConsumer[] energyConsumers = mPowerStatsInternal.getEnergyConsumerInfo();
- if (energyConsumers == null) {
- // EnergyConsumer data unavailable, don't bother populating maps.
- mEnergyConsumerToSubsystemMap = null;
- mSubsystemToEnergyConsumerMap = null;
- return;
- }
-
- final int length = energyConsumers.length;
- if (length == 0) {
- // EnergyConsumer array empty, don't bother populating maps.
- mEnergyConsumerToSubsystemMap = null;
- mSubsystemToEnergyConsumerMap = null;
- return;
- } else {
- mEnergyConsumerToSubsystemMap = new SparseIntArray(length);
- mSubsystemToEnergyConsumerMap = new SparseIntArray(length);
+ if (energyConsumers == null || energyConsumers.length == 0) {
+ return null;
}
+ // Maps id -> EnergyConsumer (1:1 map)
+ final SparseArray<EnergyConsumer> idToConsumer = new SparseArray<>(energyConsumers.length);
+ // Maps type -> {ids} (1:n map, since multiple ids might have the same type)
+ final SparseArray<IntArray> tempTypeToId = new SparseArray<>();
+
// Add all expected EnergyConsumers to the maps
- for (int i = 0; i < length; i++) {
- final EnergyConsumer consumer = energyConsumers[i];
- switch (consumer.type) {
- case EnergyConsumerType.DISPLAY:
- if (consumer.ordinal == 0) {
- mEnergyConsumerToSubsystemMap.put(consumer.id,
- MeasuredEnergyArray.SUBSYSTEM_DISPLAY);
- mSubsystemToEnergyConsumerMap.put(MeasuredEnergyArray.SUBSYSTEM_DISPLAY,
- consumer.id);
- } else {
- Slog.w(TAG, "Unexpected ordinal (" + consumer.ordinal
- + ") for EnergyConsumerType.DISPLAY");
- }
- break;
- default:
- Slog.w(TAG, "Unexpected EnergyConsumerType (" + consumer.type + ")");
+ for (final EnergyConsumer consumer : energyConsumers) {
+ // Check for inappropriate ordinals
+ if (consumer.ordinal != 0) {
+ switch (consumer.type) {
+ case EnergyConsumerType.OTHER:
+ case EnergyConsumerType.CPU_CLUSTER:
+ break;
+ default:
+ Slog.w(TAG, "EnergyConsumer '" + consumer.name + "' has unexpected ordinal "
+ + consumer.ordinal + " for type " + consumer.type);
+ continue; // Ignore this consumer
+ }
}
+ idToConsumer.put(consumer.id, consumer);
+ IntArray ids = tempTypeToId.get(consumer.type);
+ if (ids == null) {
+ ids = new IntArray();
+ tempTypeToId.put(consumer.type, ids);
+ }
+ ids.add(consumer.id);
+ }
+
+ mEnergyConsumerTypeToIdMap = new SparseArray<>(tempTypeToId.size());
+ // Populate mEnergyConsumerTypeToIdMap with EnergyConsumer type to ids mappings
+ final int size = tempTypeToId.size();
+ for (int i = 0; i < size; i++) {
+ final int consumerType = tempTypeToId.keyAt(i);
+ final int[] consumerIds = tempTypeToId.valueAt(i).toArray();
+ mEnergyConsumerTypeToIdMap.put(consumerType, consumerIds);
}
+ return idToConsumer;
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 773e3137c247..6500f6d1b6d3 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -72,6 +72,7 @@ import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.RailStats;
import com.android.internal.os.RpmStats;
+import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.ParseUtils;
@@ -126,15 +127,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.replaceWith("?");
- private static final int MAX_LOW_POWER_STATS_SIZE = 4096;
+ private static final int MAX_LOW_POWER_STATS_SIZE = 8192;
private static final int POWER_STATS_QUERY_TIMEOUT_MILLIS = 2000;
+ private static final String EMPTY = "Empty";
private final HandlerThread mHandlerThread;
private final Handler mHandler;
private final Object mLock = new Object();
+ private final Object mPowerStatsLock = new Object();
+ @GuardedBy("mPowerStatsLock")
private PowerStatsInternal mPowerStatsInternal = null;
+ @GuardedBy("mPowerStatsLock")
private Map<Integer, String> mEntityNames = new HashMap();
+ @GuardedBy("mPowerStatsLock")
private Map<Integer, Map<Integer, String>> mStateNames = new HashMap();
@GuardedBy("mStats")
@@ -172,13 +178,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub
};
private void populatePowerEntityMaps() {
- if (mPowerStatsInternal == null) {
- // PowerStatsInternal unavailable, don't bother populating maps.
- mEntityNames = null;
- mStateNames = null;
- return;
- }
-
PowerEntity[] entities = mPowerStatsInternal.getPowerEntityInfo();
if (entities == null) {
return;
@@ -202,6 +201,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub
*/
@Override
public void fillLowPowerStats(RpmStats rpmStats) {
+ synchronized (mPowerStatsLock) {
+ if (mPowerStatsInternal == null || mEntityNames.isEmpty() || mStateNames.isEmpty()) {
+ return;
+ }
+ }
+
final StateResidencyResult[] results;
try {
results = mPowerStatsInternal.getStateResidencyAsync(new int[0])
@@ -237,16 +242,22 @@ public final class BatteryStatsService extends IBatteryStats.Stub
@Override
public String getSubsystemLowPowerStats() {
+ synchronized (mPowerStatsLock) {
+ if (mPowerStatsInternal == null || mEntityNames.isEmpty() || mStateNames.isEmpty()) {
+ return EMPTY;
+ }
+ }
+
final StateResidencyResult[] results;
try {
results = mPowerStatsInternal.getStateResidencyAsync(new int[0])
.get(POWER_STATS_QUERY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Slog.e(TAG, "Failed to getStateResidencyAsync", e);
- return "Empty";
+ return EMPTY;
}
- if (results.length == 0) return "Empty";
+ if (results.length == 0) return EMPTY;
int charsLeft = MAX_LOW_POWER_STATS_SIZE;
StringBuilder builder = new StringBuilder("SubsystemPowerState");
@@ -322,9 +333,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub
} catch (RemoteException e) {
Slog.e(TAG, "Could not register INetworkManagement event observer " + e);
}
- mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class);
- if (mPowerStatsInternal != null) {
- populatePowerEntityMaps();
+
+ synchronized (mPowerStatsLock) {
+ mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class);
+ if (mPowerStatsInternal != null) {
+ populatePowerEntityMaps();
+ } else {
+ Slog.e(TAG, "Could not register PowerStatsInternal");
+ }
}
Watchdog.getInstance().addMonitor(this);
@@ -342,6 +358,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
@Override
+ public SystemServiceCpuThreadTimes getSystemServiceCpuThreadTimes() {
+ return mStats.getSystemServiceCpuThreadTimes();
+ }
+
+ @Override
public void noteJobsDeferred(int uid, int numDeferred, long sinceLast) {
if (DBG) Slog.d(TAG, "Jobs deferred " + uid + ": " + numDeferred + " " + sinceLast);
BatteryStatsService.this.noteJobsDeferred(uid, numDeferred, sinceLast);
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 7e65434b8189..34ff77494f94 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -825,7 +825,8 @@ public final class BroadcastQueue {
r.resultExtras, r.ordered, r.initialSticky, r.userId);
// parallel broadcasts are fire-and-forget, not bookended by a call to
// finishReceiverLocked(), so we manage their activity-start token here
- if (r.allowBackgroundActivityStarts && !r.ordered) {
+ if (filter.receiverList.app != null
+ && r.allowBackgroundActivityStarts && !r.ordered) {
postActivityStartTokenRemoval(filter.receiverList.app, r);
}
}
@@ -967,6 +968,12 @@ public final class BroadcastQueue {
}
}
+ static String broadcastDescription(BroadcastRecord r, ComponentName component) {
+ return r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ") to " + component.flattenToShortString();
+ }
+
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
BroadcastRecord r;
@@ -1349,14 +1356,18 @@ public final class BroadcastQueue {
< brOptions.getMinManifestReceiverApiLevel() ||
info.activityInfo.applicationInfo.targetSdkVersion
> brOptions.getMaxManifestReceiverApiLevel())) {
+ Slog.w(TAG, "Target SDK mismatch: receiver " + info.activityInfo
+ + " targets " + info.activityInfo.applicationInfo.targetSdkVersion
+ + " but delivery restricted to ["
+ + brOptions.getMinManifestReceiverApiLevel() + ", "
+ + brOptions.getMaxManifestReceiverApiLevel()
+ + "] broadcasting " + broadcastDescription(r, component));
skip = true;
}
if (!skip && !mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
component.getPackageName(), info.activityInfo.applicationInfo.uid)) {
Slog.w(TAG, "Association not allowed: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ") to " + component.flattenToShortString());
+ + broadcastDescription(r, component));
skip = true;
}
if (!skip) {
@@ -1364,9 +1375,7 @@ public final class BroadcastQueue {
r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
if (skip) {
Slog.w(TAG, "Firewall blocked: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ") to " + component.flattenToShortString());
+ + broadcastDescription(r, component));
}
}
int perm = mService.checkComponentPermission(info.activityInfo.permission,
@@ -1375,18 +1384,12 @@ public final class BroadcastQueue {
if (!skip && perm != PackageManager.PERMISSION_GRANTED) {
if (!info.activityInfo.exported) {
Slog.w(TAG, "Permission Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ")"
- + " is not exported from uid " + info.activityInfo.applicationInfo.uid
- + " due to receiver " + component.flattenToShortString());
+ + broadcastDescription(r, component)
+ + " is not exported from uid " + info.activityInfo.applicationInfo.uid);
} else {
Slog.w(TAG, "Permission Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ")"
- + " requires " + info.activityInfo.permission
- + " due to receiver " + component.flattenToShortString());
+ + broadcastDescription(r, component)
+ + " requires " + info.activityInfo.permission);
}
skip = true;
} else if (!skip && info.activityInfo.permission != null) {
@@ -1396,13 +1399,9 @@ public final class BroadcastQueue {
"Broadcast delivered to " + info.activityInfo.name)
!= AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid="
- + r.callingPid + ", uid=" + r.callingUid + ")"
+ + broadcastDescription(r, component)
+ " requires appop " + AppOpsManager.permissionToOp(
- info.activityInfo.permission)
- + " due to registered receiver "
- + component.flattenToShortString());
+ info.activityInfo.permission));
skip = true;
}
}
@@ -1520,7 +1519,7 @@ public final class BroadcastQueue {
+ info.activityInfo.packageName, e);
}
if (!isAvailable) {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ Slog.w(TAG_BROADCAST,
"Skipping delivery to " + info.activityInfo.packageName + " / "
+ info.activityInfo.applicationInfo.uid
+ " : package no longer available");
@@ -1536,6 +1535,9 @@ public final class BroadcastQueue {
if (!requestStartTargetPermissionsReviewIfNeededLocked(r,
info.activityInfo.packageName, UserHandle.getUserId(
info.activityInfo.applicationInfo.uid))) {
+ Slog.w(TAG_BROADCAST,
+ "Skipping delivery: permission review required for "
+ + broadcastDescription(r, component));
skip = true;
}
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index cc02f8a70e94..64ad76bb42bd 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -618,6 +618,12 @@ public final class CachedAppOptimizer {
private static native int getBinderFreezeInfo(int pid);
/**
+ * Returns the path to be checked to verify whether the freezer is supported by this system.
+ * @return absolute path to the file
+ */
+ private static native String getFreezerCheckPath();
+
+ /**
* Determines whether the freezer is supported by this system
*/
public static boolean isFreezerSupported() {
@@ -625,11 +631,15 @@ public final class CachedAppOptimizer {
FileReader fr = null;
try {
- fr = new FileReader("/sys/fs/cgroup/uid_0/cgroup.freeze");
+ fr = new FileReader(getFreezerCheckPath());
char state = (char) fr.read();
if (state == '1' || state == '0') {
supported = true;
+ // This is a workaround after reverting the cgroup v2 uid/pid hierarchy due to
+ // http://b/179006802.
+ // TODO: remove once the uid/pid hierarchy is restored
+ enableFreezerInternal(true);
} else {
Slog.e(TAG_AM, "unexpected value in cgroup.freeze");
}
@@ -1229,7 +1239,7 @@ public final class CachedAppOptimizer {
}
return;
}
- } catch (IOException e) {
+ } catch (Exception e) {
Slog.e(TAG_AM, "Not freezing. Unable to check file locks for " + name + "(" + pid
+ "): " + e);
return;
@@ -1324,7 +1334,7 @@ public final class CachedAppOptimizer {
}
}
}
- } catch (IOException e) {
+ } catch (Exception e) {
Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e);
synchronized (mAm) {
synchronized (mProcLock) {
diff --git a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
index b915c0ce44f3..9e0aa32a0b9e 100644
--- a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
+++ b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
@@ -17,132 +17,260 @@
package com.android.server.am;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerAttribution;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseLongArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.power.MeasuredEnergyArray;
-import com.android.internal.power.MeasuredEnergyArray.MeasuredEnergySubsystem;
import java.io.PrintWriter;
-import java.util.Arrays;
/**
- * Keeps snapshots of data from previously pulled MeasuredEnergyArrays.
+ * Keeps snapshots of data from previously pulled EnergyConsumerResults.
*/
@VisibleForTesting
public class MeasuredEnergySnapshot {
private static final String TAG = "MeasuredEnergySnapshot";
- private static final long UNAVAILABLE = -1;
+ public static final long UNAVAILABLE = -1L;
+
+ /** Map of {@link EnergyConsumer#id} to its corresponding {@link EnergyConsumer}. */
+ private final SparseArray<EnergyConsumer> mEnergyConsumers;
+
+ /** Number of ordinals for {@link EnergyConsumerType#OTHER}. */
+ private final int mNumOtherOrdinals;
/**
- * Energy snapshots from the last time each {@link MeasuredEnergySubsystem} was updated.
+ * Energy snapshots, mapping {@link EnergyConsumer#id} to energy (UJ) from the last time
+ * each {@link EnergyConsumer} was updated.
*
- * Note that the snapshots for different subsystems may have been taken at different times.
+ * Note that the snapshots for different ids may have been taken at different times.
+ * Note that energies for all existing ids are stored here, including each ordinal of type
+ * {@link EnergyConsumerType#OTHER} (tracking their total energy usage).
*
- * A snapshot is {@link #UNAVAILABLE} if the subsystem has never been updated (ie. unsupported).
+ * If an id is not present yet, it is treated as uninitialized (energy {@link #UNAVAILABLE}).
*/
- private final long[] mMeasuredEnergySnapshots;
+ private final SparseLongArray mMeasuredEnergySnapshots;
/**
- * Constructor that initializes to the given energyArray;
- * all subsystems not mentioned in initialEnergyArray are set to UNAVAILABLE.
+ * Energy snapshots <b>per uid</b> from the last time each {@link EnergyConsumer} of type
+ * {@link EnergyConsumerType#OTHER} was updated.
+ * It maps each OTHER {@link EnergyConsumer#id} to a SparseLongArray, which itself maps each
+ * uid to an energy (UJ). That is,
+ * mAttributionSnapshots.get(consumerId).get(uid) = energy used by uid for this consumer.
+ *
+ * If an id is not present yet, it is treated as uninitialized (i.e. each uid is unavailable).
+ * If an id is present but a uid is not present, that uid's energy is 0.
*/
- public MeasuredEnergySnapshot(MeasuredEnergyArray initialEnergyArray) {
- this(MeasuredEnergyArray.NUMBER_SUBSYSTEMS, initialEnergyArray);
- }
+ private final SparseArray<SparseLongArray> mAttributionSnapshots;
/**
- * Constructor (for testing) that initializes to the given energyArray and numSubsystems;
- * all subsystems not mentioned in initialEnergyArray are set to UNAVAILABLE.
+ * Constructor that initializes to the given id->EnergyConsumer map, indicating which consumers
+ * exist and what their details are.
*/
- @VisibleForTesting
- MeasuredEnergySnapshot(int numSubsystems, MeasuredEnergyArray initialEnergyArray) {
- if (initialEnergyArray.size() > numSubsystems) {
- throw new IllegalArgumentException("Energy array contains " + initialEnergyArray.size()
- + " subsystems, which exceeds the maximum allowed of " + numSubsystems);
- }
- mMeasuredEnergySnapshots = new long[numSubsystems];
- Arrays.fill(mMeasuredEnergySnapshots, UNAVAILABLE);
- fillGivenSubsystems(initialEnergyArray);
+ MeasuredEnergySnapshot(@NonNull SparseArray<EnergyConsumer> idToConsumerMap) {
+ mEnergyConsumers = idToConsumerMap;
+ mMeasuredEnergySnapshots = new SparseLongArray(mEnergyConsumers.size());
+
+ mNumOtherOrdinals = calculateNumOtherOrdinals(idToConsumerMap);
+ mAttributionSnapshots = new SparseArray<>(mNumOtherOrdinals);
}
/**
- * For the subsystems present in energyArray, overwrites mMeasuredEnergySnapshots with their
- * energy values from energyArray.
+ * Returns the number of ordinals for {@link EnergyConsumerType#OTHER}, i.e. the number of
+ * custom energy buckets supported by the device.
*/
- private void fillGivenSubsystems(MeasuredEnergyArray energyArray) {
- final int size = energyArray.size();
- for (int i = 0; i < size; i++) {
- final int subsystem = energyArray.getSubsystem(i);
- mMeasuredEnergySnapshots[subsystem] = energyArray.getEnergy(i);
- }
+ public int getNumOtherOrdinals() {
+ return mNumOtherOrdinals;
+ }
+
+ /** Class for returning measured energy delta data. */
+ static class MeasuredEnergyDeltaData {
+ /** The energyUJ for {@link EnergyConsumerType#DISPLAY}. */
+ public long displayEnergyUJ = UNAVAILABLE;
+
+ /** Map of {@link EnergyConsumerType#OTHER} ordinals to their total energyUJ. */
+ public @Nullable long[] otherTotalEnergyUJ = null;
+
+ /** Map of {@link EnergyConsumerType#OTHER} ordinals to their {uid->energyUJ} maps. */
+ public @Nullable SparseLongArray[] otherUidEnergiesUJ = null;
}
/**
* Update with the some freshly measured energies and return the difference (delta)
* between the previously stored values and the passed-in values.
*
- * @param energyArray measured energy array for some (possibly not all) subsystems.
+ * @param ecrs EnergyConsumerResults for some (possibly not all) {@link EnergyConsumer}s.
+ * Consumers that are not present are ignored (they are *not* treated as 0).
*
- * @return a map from the updated subsystems to their corresponding energy deltas.
- * Subsystems not present in energyArray will not appear.
- * Subsystems with no difference in energy will not appear.
- * Returns null, if energyArray is null.
+ * @return a MeasuredEnergyDeltaData, containing maps from the updated consumers to
+ * their corresponding energy deltas.
+ * Fields with no interesting data (consumers not present in ecrs or with no energy
+ * difference) will generally be left as their default values.
+ * otherTotalEnergyUJ and otherUidEnergiesUJ are always either both null or both of
+ * length {@link #getNumOtherOrdinals()}.
+ * Returns null, if ecrs is null or empty.
*/
- public @Nullable SparseLongArray updateAndGetDelta(MeasuredEnergyArray energyArray) {
- if (energyArray == null) {
+ public @Nullable MeasuredEnergyDeltaData updateAndGetDelta(EnergyConsumerResult[] ecrs) {
+ if (ecrs == null || ecrs.length == 0) {
return null;
}
- final SparseLongArray delta = new SparseLongArray();
- final int size = energyArray.size();
- for (int i = 0; i < size; i++) {
- final int updatedSubsystem = energyArray.getSubsystem(i);
- final long newEnergyUJ = energyArray.getEnergy(i);
- final long oldEnergyUJ = mMeasuredEnergySnapshots[updatedSubsystem];
-
- // If this is the first valid energy, there is no delta to take.
- if (oldEnergyUJ < 0) continue;
+ final MeasuredEnergyDeltaData output = new MeasuredEnergyDeltaData();
+
+ for (final EnergyConsumerResult ecr : ecrs) {
+ // Extract the new energy data for the current consumer.
+ final int consumerId = ecr.id;
+ final long newEnergyUJ = ecr.energyUWs;
+ final EnergyConsumerAttribution[] newAttributions = ecr.attribution;
+
+ // Look up the static information about this consumer.
+ final EnergyConsumer consumer = mEnergyConsumers.get(consumerId, null);
+ if (consumer == null) {
+ Slog.e(TAG, "updateAndGetDelta given invalid consumerId " + consumerId);
+ continue;
+ }
+ final int type = consumer.type;
+ final int ordinal = consumer.ordinal;
+
+ // Look up, and update, the old energy information about this consumer.
+ final long oldEnergyUJ = mMeasuredEnergySnapshots.get(consumerId, UNAVAILABLE);
+ mMeasuredEnergySnapshots.put(consumerId, newEnergyUJ);
+ final SparseLongArray otherUidEnergies
+ = updateAndGetDeltaForTypeOther(consumer, newAttributions);
+
+ // Everything is fully done being updated. We now calculate the delta for returning.
+
+ // NB: Since sum(attribution.energyUWs)<=energyUWs we assume that if deltaEnergy==0
+ // there's no attribution either. Technically that isn't enforced at the HAL, but we
+ // can't really trust data like that anyway.
+
+ if (oldEnergyUJ < 0) continue; // Generally happens only on initialization.
+ if (newEnergyUJ == oldEnergyUJ) continue;
final long deltaUJ = newEnergyUJ - oldEnergyUJ;
- if (deltaUJ == 0) continue;
if (deltaUJ < 0) {
- Slog.e(TAG, "For subsystem " + updatedSubsystem + ", new energy (" + newEnergyUJ
- + ") is less than old energy (" + oldEnergyUJ + "). Skipping. ");
+ Slog.e(TAG, "EnergyConsumer " + consumer.name + ": new energy (" + newEnergyUJ
+ + ") < old energy (" + oldEnergyUJ + "). Skipping. ");
continue;
}
- delta.put(updatedSubsystem, deltaUJ);
- }
- fillGivenSubsystems(energyArray);
+ switch (type) {
+ case EnergyConsumerType.DISPLAY:
+ output.displayEnergyUJ = deltaUJ;
+ break;
+ case EnergyConsumerType.OTHER:
+ if (output.otherTotalEnergyUJ == null) {
+ output.otherTotalEnergyUJ = new long[getNumOtherOrdinals()];
+ output.otherUidEnergiesUJ = new SparseLongArray[getNumOtherOrdinals()];
+ }
+ output.otherTotalEnergyUJ[ordinal] = deltaUJ;
+ output.otherUidEnergiesUJ[ordinal] = otherUidEnergies;
+ break;
+ default:
+ Slog.w(TAG, "Ignoring consumer " + consumer.name + " of unknown type " + type);
- return delta;
+ }
+ }
+ return output;
}
/**
- * Check if a subsystem's measured energy is available.
- * @param subsystem which subsystem.
- * @return true if subsystem is available.
+ * For a consumer of type {@link EnergyConsumerType#OTHER}, updates
+ * {@link #mAttributionSnapshots} with freshly measured energies (per uid) and returns the
+ * difference (delta) between the previously stored values and the passed-in values.
+ *
+ * @param consumerInfo a consumer of type {@link EnergyConsumerType#OTHER}.
+ * @param newAttributions Record of uids and their new energyUJ values.
+ * Any uid not present is treated as having energy 0.
+ * If null or empty, all uids are treated as having energy 0.
+ * @return A map (in the sense of {@link MeasuredEnergyDeltaData#otherUidEnergiesUJ} for this
+ * consumer) of uid -> energyDelta, with all uids that have a non-zero energyDelta.
+ * Returns null if no delta available to calculate.
*/
- public boolean hasSubsystem(@MeasuredEnergySubsystem int subsystem) {
- return mMeasuredEnergySnapshots[subsystem] != UNAVAILABLE;
+ private @Nullable SparseLongArray updateAndGetDeltaForTypeOther(
+ @NonNull EnergyConsumer consumerInfo,
+ @Nullable EnergyConsumerAttribution[] newAttributions) {
+
+ if (consumerInfo.type != EnergyConsumerType.OTHER) {
+ return null;
+ }
+ if (newAttributions == null) {
+ // Treat null as empty (i.e. all uids have 0 energy).
+ newAttributions = new EnergyConsumerAttribution[0];
+ }
+
+ // SparseLongArray mapping uid -> energyUJ (for this particular consumerId)
+ SparseLongArray uidOldEnergyMap = mAttributionSnapshots.get(consumerInfo.id, null);
+
+ // If uidOldEnergyMap wasn't present, each uid was UNAVAILABLE, so update data and return.
+ if (uidOldEnergyMap == null) {
+ uidOldEnergyMap = new SparseLongArray(newAttributions.length);
+ mAttributionSnapshots.put(consumerInfo.id, uidOldEnergyMap);
+ for (EnergyConsumerAttribution newAttribution : newAttributions) {
+ uidOldEnergyMap.put(newAttribution.uid, newAttribution.energyUWs);
+ }
+ return null;
+ }
+
+ // Map uid -> energyDelta. No initial capacity since many deltas might be 0.
+ final SparseLongArray uidEnergyDeltas = new SparseLongArray();
+
+ for (EnergyConsumerAttribution newAttribution : newAttributions) {
+ final int uid = newAttribution.uid;
+ final long newEnergyUJ = newAttribution.energyUWs;
+ // uidOldEnergyMap was present. So any particular uid that wasn't present, had 0 energy.
+ final long oldEnergyUJ = uidOldEnergyMap.get(uid, 0L);
+ uidOldEnergyMap.put(uid, newEnergyUJ);
+
+ // Everything is fully done being updated. We now calculate the delta for returning.
+ if (oldEnergyUJ < 0) continue;
+ if (newEnergyUJ == oldEnergyUJ) continue;
+ final long deltaUJ = newEnergyUJ - oldEnergyUJ;
+ if (deltaUJ < 0) {
+ Slog.e(TAG, "EnergyConsumer " + consumerInfo.name + ": new energy (" + newEnergyUJ
+ + ") but old energy (" + oldEnergyUJ + "). Skipping. ");
+ continue;
+ }
+ uidEnergyDeltas.put(uid, deltaUJ);
+ }
+ return uidEnergyDeltas;
}
/** Dump debug data. */
public void dump(PrintWriter pw) {
- pw.println("Measured energy snapshot (microjoules):");
- pw.print(" ");
- for (int i = 0; i < MeasuredEnergyArray.NUMBER_SUBSYSTEMS; i++) {
- final long energyUJ = mMeasuredEnergySnapshots[i];
- if (energyUJ == UNAVAILABLE) continue;
- pw.print(MeasuredEnergyArray.SUBSYSTEM_NAMES[i]);
- pw.print(" : ");
- pw.print(energyUJ);
- if (i != MeasuredEnergyArray.NUMBER_SUBSYSTEMS - 1) {
- pw.print(", ");
- }
+ pw.println("Measured energy snapshot");
+ pw.println("List of EnergyConsumers:");
+ for (int i = 0; i < mEnergyConsumers.size(); i++) {
+ final int id = mEnergyConsumers.keyAt(i);
+ final EnergyConsumer consumer = mEnergyConsumers.valueAt(i);
+ pw.println(String.format(" Consumer %d is {id=%d, ordinal=%d, type=%d, name=%s}", id,
+ consumer.id, consumer.ordinal, consumer.type, consumer.name));
+ }
+ pw.println("Map of consumerIds to energy (in microjoules):");
+ for (int i = 0; i < mMeasuredEnergySnapshots.size(); i++) {
+ final int id = mMeasuredEnergySnapshots.keyAt(i);
+ final long energyUJ = mMeasuredEnergySnapshots.valueAt(i);
+ pw.println(String.format(" Consumer %d has energy %d uJ}", id, energyUJ));
}
+ pw.println("List of the " + mNumOtherOrdinals + " OTHER EnergyConsumers:");
+ pw.println(" " + mAttributionSnapshots);
pw.println();
}
+
+ /** Determines the number of ordinals for {@link EnergyConsumerType#OTHER}. */
+ private static int calculateNumOtherOrdinals(SparseArray<EnergyConsumer> idToConsumer) {
+ if (idToConsumer == null) return 0;
+ int numOtherOrdinals = 0;
+ final int size = idToConsumer.size();
+ for (int idx = 0; idx < size; idx++) {
+ final EnergyConsumer consumer = idToConsumer.valueAt(idx);
+ if (consumer.type == EnergyConsumerType.OTHER) numOtherOrdinals++;
+ }
+ return numOtherOrdinals;
+ }
}
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 68449d9fad8d..b8a4dc75a8a4 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -415,6 +415,14 @@ class ProcessErrorStateRecord {
return;
}
+ // Retrieve max ANR delay from AnrControllers without the mService lock since the
+ // controllers might in turn call into apps
+ long anrDialogDelayMs = mService.mActivityTaskManager.getMaxAnrDelayMillis(aInfo);
+ if (aInfo != null && aInfo.packageName != null && anrDialogDelayMs > 0) {
+ Slog.i(TAG, "Delaying ANR dialog for " + aInfo.packageName + " for " + anrDialogDelayMs
+ + "ms");
+ }
+
synchronized (mService) {
// mBatteryStatsService can be null if the AMS is constructed with injector only. This
// will only happen in tests.
@@ -446,7 +454,7 @@ class ProcessErrorStateRecord {
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = new AppNotRespondingDialog.Data(mApp, aInfo, aboveSystem);
- mService.mUiHandler.sendMessage(msg);
+ mService.mUiHandler.sendMessageDelayed(msg, anrDialogDelayMs);
}
}
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 520e196b81a6..ed1d7a476177 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2413,6 +2413,11 @@ public final class ProcessList {
app.getDisabledCompatChanges(), pkgDataInfoMap, allowlistedAppDataInfoMap,
false, false,
new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
+
+ if (Process.createProcessGroup(uid, startResult.pid) < 0) {
+ Slog.e(ActivityManagerService.TAG, "Unable to create process group for "
+ + app.processName + " (" + startResult.pid + ")");
+ }
} else {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
diff --git a/services/core/java/com/android/server/am/ProcessProfileRecord.java b/services/core/java/com/android/server/am/ProcessProfileRecord.java
index e533cc3512d3..47573f31440b 100644
--- a/services/core/java/com/android/server/am/ProcessProfileRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProfileRecord.java
@@ -607,7 +607,8 @@ final class ProcessProfileRecord {
@GuardedBy("mService")
void dumpPss(PrintWriter pw, String prefix, long nowUptime) {
synchronized (mProfilerLock) {
- pw.print(" lastPssTime=");
+ pw.print(prefix);
+ pw.print("lastPssTime=");
TimeUtils.formatDuration(mLastPssTime, nowUptime, pw);
pw.print(" pssProcState=");
pw.print(mPssProcState);
@@ -629,9 +630,8 @@ final class ProcessProfileRecord {
DebugUtils.printSizeValue(pw, mLastRss * 1024);
pw.println();
pw.print(prefix);
- pw.print(" trimMemoryLevel=");
+ pw.print("trimMemoryLevel=");
pw.println(mTrimMemoryLevel);
- pw.println();
pw.print(prefix); pw.print("procStateMemTracker: ");
mProcStateMemTracker.dumpLine(pw);
pw.print(prefix);
@@ -653,5 +653,6 @@ final class ProcessProfileRecord {
pw.print(" timeUsed=");
TimeUtils.formatDuration(mCurCpuTime.get() - lastCpuTime, pw);
}
+ pw.println();
}
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index a330bf9d90b7..cbc3d37849c1 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -426,9 +426,10 @@ class ProcessRecord implements WindowProcessListener {
pw.print(prefix); pw.print("mInstr="); pw.println(mInstr);
}
pw.print(prefix); pw.print("thread="); pw.println(mThread);
- pw.print(prefix); pw.print("pid="); pw.print(mPid);
+ pw.print(prefix); pw.print("pid="); pw.println(mPid);
pw.print(prefix); pw.print("lastActivityTime=");
TimeUtils.formatDuration(mLastActivityTime, nowUptime, pw);
+ pw.println();
if (mPersistent || mRemoved) {
pw.print(prefix); pw.print("persistent="); pw.print(mPersistent);
pw.print(" removed="); pw.println(mRemoved);
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index e1a153dfa920..499fbcb0642d 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -1277,7 +1277,7 @@ final class ProcessStateRecord {
pw.print(" set="); pw.println(mSetAdj);
pw.print(prefix); pw.print("mCurSchedGroup="); pw.print(mCurSchedGroup);
pw.print(" setSchedGroup="); pw.print(mSetSchedGroup);
- pw.print(" systemNoUi="); pw.print(mSystemNoUi);
+ pw.print(" systemNoUi="); pw.println(mSystemNoUi);
pw.print(prefix); pw.print("curProcState="); pw.print(getCurProcState());
pw.print(" mRepProcState="); pw.print(mRepProcState);
pw.print(" setProcState="); pw.print(mSetProcState);
@@ -1297,7 +1297,7 @@ final class ProcessStateRecord {
}
if (mHasShownUi || mApp.mProfile.hasPendingUiClean()) {
pw.print(prefix); pw.print("hasShownUi="); pw.print(mHasShownUi);
- pw.print(" pendingUiClean="); pw.print(mApp.mProfile.hasPendingUiClean());
+ pw.print(" pendingUiClean="); pw.println(mApp.mProfile.hasPendingUiClean());
}
pw.print(prefix); pw.print("cached="); pw.print(mCached);
pw.print(" empty="); pw.println(mEmpty);
@@ -1316,12 +1316,12 @@ final class ProcessStateRecord {
}
if (mHasForegroundActivities || mRepForegroundActivities) {
pw.print(prefix);
- pw.print(" foregroundActivities="); pw.print(mHasForegroundActivities);
+ pw.print("foregroundActivities="); pw.print(mHasForegroundActivities);
pw.print(" (rep="); pw.print(mRepForegroundActivities); pw.println(")");
}
if (mSetProcState > ActivityManager.PROCESS_STATE_SERVICE) {
pw.print(prefix);
- pw.print(" whenUnimportant=");
+ pw.print("whenUnimportant=");
TimeUtils.formatDuration(mWhenUnimportant - nowUptime, pw);
pw.println();
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 7299e814b020..e022e977e02f 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -81,6 +81,7 @@ public class SettingsToPropertiesMapper {
static final String[] sDeviceConfigScopes = new String[] {
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
DeviceConfig.NAMESPACE_CONFIGURATION,
+ DeviceConfig.NAMESPACE_CONNECTIVITY,
DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
DeviceConfig.NAMESPACE_MEDIA_NATIVE,
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 3acad49496c5..26ce0d77e0af 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -16,17 +16,23 @@
package com.android.server.app;
+import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.app.ActivityManager;
import android.app.GameManager;
import android.app.GameManager.GameMode;
import android.app.IGameManagerService;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -55,7 +61,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
private final Object mLock = new Object();
private final Handler mHandler;
@GuardedBy("mLock")
- private final ArrayMap<Integer, Settings> mSettings = new ArrayMap<>();
+ private final ArrayMap<Integer, GameManagerSettings> mSettings = new ArrayMap<>();
public GameManagerService(Context context) {
this(context, createServiceThread().getLooper());
@@ -81,11 +87,19 @@ public final class GameManagerService extends IGameManagerService.Stub {
switch (msg.what) {
case WRITE_SETTINGS: {
final int userId = (int) msg.obj;
+ if (userId < 0) {
+ Slog.wtf(TAG, "Attempt to write settings for invalid user: " + userId);
+ synchronized (mLock) {
+ removeMessages(WRITE_SETTINGS, msg.obj);
+ }
+ break;
+ }
+
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mLock) {
removeMessages(WRITE_SETTINGS, msg.obj);
if (mSettings.containsKey(userId)) {
- Settings userSettings = mSettings.get(userId);
+ GameManagerSettings userSettings = mSettings.get(userId);
userSettings.writePersistentDataLocked();
}
}
@@ -94,13 +108,22 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
case REMOVE_SETTINGS: {
final int userId = (int) msg.obj;
+ if (userId < 0) {
+ Slog.wtf(TAG, "Attempt to write settings for invalid user: " + userId);
+ synchronized (mLock) {
+ removeMessages(WRITE_SETTINGS, msg.obj);
+ removeMessages(REMOVE_SETTINGS, msg.obj);
+ }
+ break;
+ }
+
synchronized (mLock) {
// Since the user was removed, ignore previous write message
// and do write here.
removeMessages(WRITE_SETTINGS, msg.obj);
removeMessages(REMOVE_SETTINGS, msg.obj);
if (mSettings.containsKey(userId)) {
- final Settings userSettings = mSettings.get(userId);
+ final GameManagerSettings userSettings = mSettings.get(userId);
mSettings.remove(userId);
userSettings.writePersistentDataLocked();
}
@@ -146,26 +169,49 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
- //TODO(b/178111358) Add proper permission check and multi-user handling
+ private boolean hasPermission(String permission) {
+ return mContext.checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
@Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public @GameMode int getGameMode(String packageName, int userId) {
+ if (!hasPermission(Manifest.permission.MANAGE_GAME_MODE)) {
+ Log.w(TAG, String.format("Caller or self does not have permission.MANAGE_GAME_MODE"));
+ return GameManager.GAME_MODE_UNSUPPORTED;
+ }
+
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "getGameMode",
+ "com.android.server.app.GameManagerService");
+
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
return GameManager.GAME_MODE_UNSUPPORTED;
}
- Settings userSettings = mSettings.get(userId);
+ GameManagerSettings userSettings = mSettings.get(userId);
return userSettings.getGameModeLocked(packageName);
}
}
- //TODO(b/178111358) Add proper permission check and multi-user handling
@Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public void setGameMode(String packageName, @GameMode int gameMode, int userId) {
+ if (!hasPermission(Manifest.permission.MANAGE_GAME_MODE)) {
+ Log.w(TAG, String.format("Caller or self does not have permission.MANAGE_GAME_MODE"));
+ return;
+ }
+
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "setGameMode",
+ "com.android.server.app.GameManagerService");
+
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
return;
}
- Settings userSettings = mSettings.get(userId);
+ GameManagerSettings userSettings = mSettings.get(userId);
userSettings.setGameModeLocked(packageName, gameMode);
final Message msg = mHandler.obtainMessage(WRITE_SETTINGS);
msg.obj = userId;
@@ -189,7 +235,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
return;
}
- Settings userSettings = new Settings(Environment.getDataSystemDeDirectory(userId));
+ GameManagerSettings userSettings =
+ new GameManagerSettings(Environment.getDataSystemDeDirectory(userId));
mSettings.put(userId, userSettings);
userSettings.readPersistentDataLocked();
}
diff --git a/services/core/java/com/android/server/app/Settings.java b/services/core/java/com/android/server/app/GameManagerSettings.java
index ab367fb923ac..3e32380b60a9 100644
--- a/services/core/java/com/android/server/app/Settings.java
+++ b/services/core/java/com/android/server/app/GameManagerSettings.java
@@ -41,7 +41,7 @@ import java.util.Map;
* Persists all GameService related settings.
* @hide
*/
-public class Settings {
+public class GameManagerSettings {
// The XML file follows the below format:
// <?xml>
@@ -63,7 +63,7 @@ public class Settings {
// PackageName -> GameMode
private final ArrayMap<String, Integer> mGameModes = new ArrayMap<>();
- Settings(File dataDir) {
+ GameManagerSettings(File dataDir) {
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
@@ -144,6 +144,7 @@ public class Settings {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
+ // Do nothing
}
if (type != XmlPullParser.START_TAG) {
Slog.wtf(GameManagerService.TAG,
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 10fe1e1d0684..c92abd48fdc2 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3131,11 +3131,6 @@ public class AppOpsService extends IAppOpsService.Stub {
return AppOpsManager.MODE_ERRORED;
}
final Op op = getOpLocked(ops, code, uid, true);
- if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
- scheduleOpNotedIfNeededLocked(code, uid, packageName, flags,
- AppOpsManager.MODE_IGNORED);
- return AppOpsManager.MODE_IGNORED;
- }
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
if (attributedOp.isRunning()) {
Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code "
@@ -3145,6 +3140,12 @@ public class AppOpsService extends IAppOpsService.Stub {
final int switchCode = AppOpsManager.opToSwitch(code);
final UidState uidState = ops.uidState;
+ if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
+ attributedOp.rejected(uidState.state, flags);
+ scheduleOpNotedIfNeededLocked(code, uid, packageName, flags,
+ AppOpsManager.MODE_IGNORED);
+ return AppOpsManager.MODE_IGNORED;
+ }
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index c29d29e1f0b4..a26fa5d6ebf5 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -774,10 +774,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
return mAudioService.getVssVolumeForDevice(streamType, device);
}
- /*package*/ int getModeOwnerPid() {
- return mModeOwnerPid;
- }
-
/*package*/ int getDeviceForStream(int streamType) {
return mAudioService.getDeviceForStream(streamType);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index bb662fb1268b..5145ff891391 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -95,6 +95,7 @@ import android.media.IVolumeController;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMetrics;
+import android.media.MediaRecorder.AudioSource;
import android.media.PlayerBase;
import android.media.VolumePolicy;
import android.media.audiofx.AudioEffect;
@@ -161,9 +162,11 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -175,6 +178,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BooleanSupplier;
import java.util.stream.Collectors;
@@ -304,6 +308,9 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_UPDATE_VOLUME_STATES_FOR_DEVICE = 33;
private static final int MSG_REINIT_VOLUMES = 34;
private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35;
+ private static final int MSG_UPDATE_AUDIO_MODE = 36;
+ private static final int MSG_RECORDING_CONFIG_CHANGE = 37;
+
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -327,7 +334,8 @@ public class AudioService extends IAudioService.Stub
private SettingsObserver mSettingsObserver;
- private int mMode = AudioSystem.MODE_NORMAL;
+ private AtomicInteger mMode = new AtomicInteger(AudioSystem.MODE_NORMAL);
+
// protects mRingerMode
private final Object mSettingsLock = new Object();
@@ -944,6 +952,7 @@ public class AudioService extends IAudioService.Stub
mDeviceBroker = new AudioDeviceBroker(mContext, this);
mRecordMonitor = new RecordingActivityMonitor(mContext);
+ mRecordMonitor.registerRecordingCallback(mVoiceRecordingActivityMonitor, true);
// must be called before readPersistedSettings() which needs a valid mStreamVolumeAlias[]
// array initialized by updateStreamVolumeAlias()
@@ -953,6 +962,8 @@ public class AudioService extends IAudioService.Stub
mPlaybackMonitor =
new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]);
+ mPlaybackMonitor.registerPlaybackCallback(mVoicePlaybackActivityMonitor, true);
+
mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
readAndSetLowRamDevice();
@@ -1201,12 +1212,8 @@ public class AudioService extends IAudioService.Stub
// Restore call state
synchronized (mDeviceBroker.mSetModeLock) {
- if (mAudioSystem.setPhoneState(mMode, getModeOwnerUid())
- == AudioSystem.AUDIO_STATUS_OK) {
- mModeLogger.log(new AudioEventLogger.StringEvent(
- "onAudioServerDied causes setPhoneState(" + AudioSystem.modeToString(mMode)
- + ", uid=" + getModeOwnerUid() + ")"));
- }
+ onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(),
+ mContext.getPackageName());
}
final int forSys;
synchronized (mSettingsLock) {
@@ -2999,7 +3006,7 @@ public class AudioService extends IAudioService.Stub
}
/*package*/ int getHearingAidStreamType() {
- return getHearingAidStreamType(mMode);
+ return getHearingAidStreamType(mMode.get());
}
private int getHearingAidStreamType(int mode) {
@@ -3012,15 +3019,15 @@ public class AudioService extends IAudioService.Stub
// other conditions will influence the stream type choice, read on...
break;
}
- if (mVoiceActive.get()) {
+ if (mVoicePlaybackActive.get()) {
return AudioSystem.STREAM_VOICE_CALL;
}
return AudioSystem.STREAM_MUSIC;
}
- private AtomicBoolean mVoiceActive = new AtomicBoolean(false);
+ private AtomicBoolean mVoicePlaybackActive = new AtomicBoolean(false);
- private final IPlaybackConfigDispatcher mVoiceActivityMonitor =
+ private final IPlaybackConfigDispatcher mVoicePlaybackActivityMonitor =
new IPlaybackConfigDispatcher.Stub() {
@Override
public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
@@ -3042,16 +3049,127 @@ public class AudioService extends IAudioService.Stub
break;
}
}
- if (mVoiceActive.getAndSet(voiceActive) != voiceActive) {
+ if (mVoicePlaybackActive.getAndSet(voiceActive) != voiceActive) {
updateHearingAidVolumeOnVoiceActivityUpdate();
}
+
+ // Update playback active state for all apps in audio mode stack.
+ // When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE
+ // and request an audio mode update immediately. Upon any other change, queue the message
+ // and request an audio mode update after a grace period.
+ synchronized (mDeviceBroker.mSetModeLock) {
+ boolean updateAudioMode = false;
+ int existingMsgPolicy = SENDMSG_QUEUE;
+ int delay = CHECK_MODE_FOR_UID_PERIOD_MS;
+ for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+ boolean wasActive = h.isActive();
+ h.setPlaybackActive(false);
+ for (AudioPlaybackConfiguration config : configs) {
+ final int usage = config.getAudioAttributes().getUsage();
+ if (config.getClientUid() == h.getUid()
+ && (usage == AudioAttributes.USAGE_VOICE_COMMUNICATION
+ || usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
+ && config.getPlayerState()
+ == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+ h.setPlaybackActive(true);
+ break;
+ }
+ }
+ if (wasActive != h.isActive()) {
+ updateAudioMode = true;
+ if (h.isActive() && h == getAudioModeOwnerHandler()) {
+ existingMsgPolicy = SENDMSG_REPLACE;
+ delay = 0;
+ }
+ }
+ }
+ if (updateAudioMode) {
+ sendMsg(mAudioHandler,
+ MSG_UPDATE_AUDIO_MODE,
+ existingMsgPolicy,
+ AudioSystem.MODE_CURRENT,
+ android.os.Process.myPid(),
+ mContext.getPackageName(),
+ delay);
+ }
+ }
+ }
+
+ private final IRecordingConfigDispatcher mVoiceRecordingActivityMonitor =
+ new IRecordingConfigDispatcher.Stub() {
+ @Override
+ public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
+ sendMsg(mAudioHandler, MSG_RECORDING_CONFIG_CHANGE, SENDMSG_REPLACE,
+ 0 /*arg1 ignored*/, 0 /*arg2 ignored*/,
+ configs /*obj*/, 0 /*delay*/);
+ }
+ };
+
+ private void onRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
+ // Update recording active state for all apps in audio mode stack.
+ // When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE
+ // and request an audio mode update immediately. Upon any other change, queue the message
+ // and request an audio mode update after a grace period.
+ synchronized (mDeviceBroker.mSetModeLock) {
+ boolean updateAudioMode = false;
+ int existingMsgPolicy = SENDMSG_QUEUE;
+ int delay = CHECK_MODE_FOR_UID_PERIOD_MS;
+ for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+ boolean wasActive = h.isActive();
+ h.setRecordingActive(false);
+ for (AudioRecordingConfiguration config : configs) {
+ if (config.getClientUid() == h.getUid()
+ && config.getAudioSource() == AudioSource.VOICE_COMMUNICATION) {
+ h.setRecordingActive(true);
+ break;
+ }
+ }
+ if (wasActive != h.isActive()) {
+ updateAudioMode = true;
+ if (h.isActive() && h == getAudioModeOwnerHandler()) {
+ existingMsgPolicy = SENDMSG_REPLACE;
+ delay = 0;
+ }
+ }
+ }
+ if (updateAudioMode) {
+ sendMsg(mAudioHandler,
+ MSG_UPDATE_AUDIO_MODE,
+ existingMsgPolicy,
+ AudioSystem.MODE_CURRENT,
+ android.os.Process.myPid(),
+ mContext.getPackageName(),
+ delay);
+ }
+ }
+ }
+
+ private void dumpAudioMode(PrintWriter pw) {
+ pw.println("\nAudio mode: ");
+ pw.println("- Requested mode = " + AudioSystem.modeToString(getMode()));
+ pw.println("- Actual mode = " + AudioSystem.modeToString(mMode.get()));
+ pw.println("- Mode owner: ");
+ SetModeDeathHandler hdlr = getAudioModeOwnerHandler();
+ if (hdlr != null) {
+ hdlr.dump(pw, -1);
+ } else {
+ pw.println(" None");
+ }
+ pw.println("- Mode owner stack: ");
+ if (mSetModeDeathHandlers.isEmpty()) {
+ pw.println(" Empty");
+ } else {
+ for (int i = 0; i < mSetModeDeathHandlers.size(); i++) {
+ mSetModeDeathHandlers.get(i).dump(pw, i);
+ }
+ }
}
private void updateHearingAidVolumeOnVoiceActivityUpdate() {
final int streamType = getHearingAidStreamType();
final int index = getStreamVolume(streamType);
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID,
- mVoiceActive.get(), streamType, index));
+ mVoicePlaybackActive.get(), streamType, index));
mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
}
@@ -4069,66 +4187,46 @@ public class AudioService extends IAudioService.Stub
}
- /**
- * Return the pid of the current audio mode owner
- * @return 0 if nobody owns the mode
- */
- /*package*/ int getModeOwnerPid() {
- int modeOwnerPid = 0;
- try {
- modeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
- } catch (Exception e) {
- // nothing to do, modeOwnerPid is not modified
- }
- return modeOwnerPid;
- }
-
- /**
- * Return the uid of the current audio mode owner
- * @return 0 if nobody owns the mode
- */
- /*package*/ int getModeOwnerUid() {
- int modeOwnerUid = 0;
- try {
- modeOwnerUid = mSetModeDeathHandlers.get(0).getUid();
- } catch (Exception e) {
- // nothing to do, modeOwnerUid is not modified
- }
- return modeOwnerUid;
- }
-
private class SetModeDeathHandler implements IBinder.DeathRecipient {
private final IBinder mCb; // To be notified of client's death
private final int mPid;
private final int mUid;
private final boolean mIsPrivileged;
private final String mPackage;
- private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
+ private int mMode;
+ private long mUpdateTime;
+ private boolean mPlaybackActive = false;
+ private boolean mRecordingActive = false;
- SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged, String caller) {
+ SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged,
+ String caller, int mode) {
+ mMode = mode;
mCb = cb;
mPid = pid;
mUid = uid;
- mIsPrivileged = isPrivileged;
mPackage = caller;
+ mIsPrivileged = isPrivileged;
+ mUpdateTime = java.lang.System.currentTimeMillis();
}
public void binderDied() {
- int newModeOwnerPid = 0;
synchronized (mDeviceBroker.mSetModeLock) {
- Log.w(TAG, "setMode() client died");
+ Log.w(TAG, "SetModeDeathHandler client died");
int index = mSetModeDeathHandlers.indexOf(this);
if (index < 0) {
- Log.w(TAG, "unregistered setMode() client died");
+ Log.w(TAG, "unregistered SetModeDeathHandler client died");
} else {
- newModeOwnerPid = setModeInt(
- AudioSystem.MODE_NORMAL, mCb, mPid, mUid, mIsPrivileged, TAG);
+ SetModeDeathHandler h = mSetModeDeathHandlers.get(index);
+ mSetModeDeathHandlers.remove(index);
+ sendMsg(mAudioHandler,
+ MSG_UPDATE_AUDIO_MODE,
+ SENDMSG_QUEUE,
+ AudioSystem.MODE_CURRENT,
+ android.os.Process.myPid(),
+ mContext.getPackageName(),
+ 0);
}
}
- // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
- // SCO connections not started by the application changing the mode when pid changes
- Log.i(TAG, "In binderDied(), calling setModeOwnerPid()");
- mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, AudioService.this.getMode());
}
public int getPid() {
@@ -4137,6 +4235,7 @@ public class AudioService extends IAudioService.Stub
public void setMode(int mode) {
mMode = mode;
+ mUpdateTime = java.lang.System.currentTimeMillis();
}
public int getMode() {
@@ -4158,199 +4257,283 @@ public class AudioService extends IAudioService.Stub
public boolean isPrivileged() {
return mIsPrivileged;
}
+
+ public long getUpdateTime() {
+ return mUpdateTime;
+ }
+
+ public void setPlaybackActive(boolean active) {
+ mPlaybackActive = active;
+ }
+
+ public void setRecordingActive(boolean active) {
+ mRecordingActive = active;
+ }
+
+ /**
+ * An app is considered active if:
+ * - It is privileged (has MODIFY_PHONE_STATE permission)
+ * or
+ * - It requests mode MODE_IN_COMMUNICATION, and it is either playing
+ * or recording for VOICE_COMMUNICATION.
+ * or
+ * - It requests a mode different from MODE_IN_COMMUNICATION or MODE_NORMAL
+ */
+ public boolean isActive() {
+ return mIsPrivileged
+ || ((mMode == AudioSystem.MODE_IN_COMMUNICATION)
+ && (mRecordingActive || mPlaybackActive))
+ || mMode == AudioSystem.MODE_IN_CALL
+ || mMode == AudioSystem.MODE_RINGTONE
+ || mMode == AudioSystem.MODE_CALL_SCREENING;
+ }
+
+ public void dump(PrintWriter pw, int index) {
+ SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss:SSS");
+
+ if (index >= 0) {
+ pw.println(" Requester # " + (index + 1) + ":");
+ }
+ pw.println(" - Mode: " + AudioSystem.modeToString(mMode));
+ pw.println(" - Binder: " + mCb);
+ pw.println(" - Pid: " + mPid);
+ pw.println(" - Uid: " + mUid);
+ pw.println(" - Package: " + mPackage);
+ pw.println(" - Privileged: " + mIsPrivileged);
+ pw.println(" - Active: " + isActive());
+ pw.println(" Playback active: " + mPlaybackActive);
+ pw.println(" Recording active: " + mRecordingActive);
+ pw.println(" - update time: " + format.format(new Date(mUpdateTime)));
+ }
+ }
+
+ @GuardedBy("mDeviceBroker.mSetModeLock")
+ private SetModeDeathHandler getAudioModeOwnerHandler() {
+ // The Audio mode owner is:
+ // 1) the most recent privileged app in the stack
+ // 2) the most recent active app in the tack
+ SetModeDeathHandler modeOwner = null;
+ SetModeDeathHandler privilegedModeOwner = null;
+ for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+ if (h.isActive()) {
+ // privileged apps are always active
+ if (h.isPrivileged()) {
+ if (privilegedModeOwner == null
+ || h.getUpdateTime() > privilegedModeOwner.getUpdateTime()) {
+ privilegedModeOwner = h;
+ }
+ } else {
+ if (modeOwner == null
+ || h.getUpdateTime() > modeOwner.getUpdateTime()) {
+ modeOwner = h;
+ }
+ }
+ }
+ }
+ return privilegedModeOwner != null ? privilegedModeOwner : modeOwner;
+ }
+
+ /**
+ * Return the pid of the current audio mode owner
+ * @return 0 if nobody owns the mode
+ */
+ @GuardedBy("mDeviceBroker.mSetModeLock")
+ /*package*/ int getModeOwnerPid() {
+ SetModeDeathHandler hdlr = getAudioModeOwnerHandler();
+ if (hdlr != null) {
+ return hdlr.getPid();
+ }
+ return 0;
+ }
+
+ /**
+ * Return the uid of the current audio mode owner
+ * @return 0 if nobody owns the mode
+ */
+ @GuardedBy("mDeviceBroker.mSetModeLock")
+ /*package*/ int getModeOwnerUid() {
+ SetModeDeathHandler hdlr = getAudioModeOwnerHandler();
+ if (hdlr != null) {
+ return hdlr.getUid();
+ }
+ return 0;
}
/** @see AudioManager#setMode(int) */
public void setMode(int mode, IBinder cb, String callingPackage) {
- Log.i(TAG, "setMode(mode = " + mode + ", callingPackage = " +
- callingPackage + ", Process ID: " + Binder.getCallingPid());
+ int pid = Binder.getCallingPid();
+ int uid = Binder.getCallingUid();
if (DEBUG_MODE) {
- Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")");
+ Log.v(TAG, "setMode(mode=" + mode + ", pid=" + pid
+ + ", uid=" + uid + ", caller=" + callingPackage + ")");
}
if (!checkAudioSettingsPermission("setMode()")) {
return;
}
- final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE)
- == PackageManager.PERMISSION_GRANTED;
- final int callingPid = Binder.getCallingPid();
- if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) {
- Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
- + callingPid + ", uid=" + Binder.getCallingUid());
+ if (cb == null) {
+ Log.e(TAG, "setMode() called with null binder");
+ return;
+ }
+ if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
+ Log.w(TAG, "setMode() invalid mode: " + mode);
return;
}
+ if (mode == AudioSystem.MODE_CURRENT) {
+ mode = getMode();
+ }
+
if (mode == AudioSystem.MODE_CALL_SCREENING && !mIsCallScreeningModeSupported) {
Log.w(TAG, "setMode(MODE_CALL_SCREENING) not permitted "
+ "when call screening is not supported");
return;
}
- if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
+ final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE)
+ == PackageManager.PERMISSION_GRANTED;
+ if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) {
+ Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
+ + pid + ", uid=" + Binder.getCallingUid());
return;
}
- int newModeOwnerPid;
+ SetModeDeathHandler currentModeHandler = null;
synchronized (mDeviceBroker.mSetModeLock) {
- if (mode == AudioSystem.MODE_CURRENT) {
- mode = mMode;
- }
- int oldModeOwnerPid = getModeOwnerPid();
- // Do not allow changing mode if a call is active and the requester
- // does not have permission to modify phone state or is not the mode owner,
- // unless returning to NORMAL mode (will not change current mode owner) or
- // not changing mode in which case the mode owner will reflect the last
- // requester of current mode
- if (!((mode == mMode) || (mode == AudioSystem.MODE_NORMAL))
- && ((mMode == AudioSystem.MODE_IN_CALL)
- || (mMode == AudioSystem.MODE_IN_COMMUNICATION))
- && !(hasModifyPhoneStatePermission || (oldModeOwnerPid == callingPid))) {
- Log.w(TAG, "setMode(" + mode + ") from pid=" + callingPid
- + ", uid=" + Binder.getCallingUid()
- + ", cannot change mode from " + mMode
- + " without permission or being mode owner");
- return;
- }
- newModeOwnerPid = setModeInt(mode, cb, callingPid, Binder.getCallingUid(),
- hasModifyPhoneStatePermission, callingPackage);
- }
- // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
- // SCO connections not started by the application changing the mode when pid changes
- mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, getMode());
- }
-
- // setModeInt() returns a valid PID if the audio mode was successfully set to
- // any mode other than NORMAL.
- @GuardedBy("mDeviceBroker.mSetModeLock")
- private int setModeInt(
- int mode, IBinder cb, int pid, int uid, boolean isPrivileged, String caller) {
- if (DEBUG_MODE) {
- Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid
- + ", uid=" + uid + ", caller=" + caller + ")");
- }
- int newModeOwnerPid = 0;
- if (cb == null) {
- Log.e(TAG, "setModeInt() called with null binder");
- return newModeOwnerPid;
- }
-
- SetModeDeathHandler hdlr = null;
- Iterator iter = mSetModeDeathHandlers.iterator();
- while (iter.hasNext()) {
- SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
- if (h.getPid() == pid) {
- hdlr = h;
- // Remove from client list so that it is re-inserted at top of list
- iter.remove();
- if (hdlr.getMode() == AudioSystem.MODE_IN_COMMUNICATION) {
- mAudioHandler.removeEqualMessages(MSG_CHECK_MODE_FOR_UID, hdlr);
- }
- try {
- hdlr.getBinder().unlinkToDeath(hdlr, 0);
- if (cb != hdlr.getBinder()){
- hdlr = null;
- }
- } catch (NoSuchElementException e) {
- hdlr = null;
- Log.w(TAG, "link does not exist ...");
+ for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+ if (h.getPid() == pid) {
+ currentModeHandler = h;
+ break;
}
- break;
}
- }
- final int oldMode = mMode;
- int status = AudioSystem.AUDIO_STATUS_OK;
- int actualMode;
- do {
- actualMode = mode;
+
if (mode == AudioSystem.MODE_NORMAL) {
- // get new mode from client at top the list if any
- if (!mSetModeDeathHandlers.isEmpty()) {
- hdlr = mSetModeDeathHandlers.get(0);
- cb = hdlr.getBinder();
- actualMode = hdlr.getMode();
+ if (currentModeHandler != null) {
+ if (!currentModeHandler.isPrivileged()
+ && currentModeHandler.getMode() == AudioSystem.MODE_IN_COMMUNICATION) {
+ mAudioHandler.removeEqualMessages(
+ MSG_CHECK_MODE_FOR_UID, currentModeHandler);
+ }
+ mSetModeDeathHandlers.remove(currentModeHandler);
if (DEBUG_MODE) {
- Log.w(TAG, " using mode=" + mode + " instead due to death hdlr at pid="
- + hdlr.mPid);
+ Log.v(TAG, "setMode(" + mode + ") removing hldr for pid: " + pid);
+ }
+ try {
+ currentModeHandler.getBinder().unlinkToDeath(currentModeHandler, 0);
+ } catch (NoSuchElementException e) {
+ Log.w(TAG, "setMode link does not exist ...");
}
}
} else {
- if (hdlr == null) {
- hdlr = new SetModeDeathHandler(cb, pid, uid, isPrivileged, caller);
- }
- // Register for client death notification
- try {
- cb.linkToDeath(hdlr, 0);
- } catch (RemoteException e) {
- // Client has died!
- Log.w(TAG, "setMode() could not link to "+cb+" binder death");
- }
-
- // Last client to call setMode() is always at top of client list
- // as required by SetModeDeathHandler.binderDied()
- mSetModeDeathHandlers.add(0, hdlr);
- hdlr.setMode(mode);
- }
-
- if (actualMode != mMode) {
- final long identity = Binder.clearCallingIdentity();
- status = mAudioSystem.setPhoneState(actualMode, getModeOwnerUid());
- Binder.restoreCallingIdentity(identity);
- if (status == AudioSystem.AUDIO_STATUS_OK) {
- if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); }
- mMode = actualMode;
+ if (currentModeHandler != null) {
+ currentModeHandler.setMode(mode);
+ if (DEBUG_MODE) {
+ Log.v(TAG, "setMode(" + mode + ") updating hldr for pid: " + pid);
+ }
} else {
- if (hdlr != null) {
- mSetModeDeathHandlers.remove(hdlr);
- cb.unlinkToDeath(hdlr, 0);
+ currentModeHandler = new SetModeDeathHandler(cb, pid, uid,
+ hasModifyPhoneStatePermission, callingPackage, mode);
+ // Register for client death notification
+ try {
+ cb.linkToDeath(currentModeHandler, 0);
+ } catch (RemoteException e) {
+ // Client has died!
+ Log.w(TAG, "setMode() could not link to " + cb + " binder death");
+ return;
+ }
+ mSetModeDeathHandlers.add(currentModeHandler);
+ if (DEBUG_MODE) {
+ Log.v(TAG, "setMode(" + mode + ") adding handler for pid=" + pid);
+ }
+ }
+ if (mode == AudioSystem.MODE_IN_COMMUNICATION) {
+ // Force active state when entering/updating the stack to avoid glitches when
+ // an app starts playing/recording after settng the audio mode,
+ // and send a reminder to check activity after a grace period.
+ if (!currentModeHandler.isPrivileged()) {
+ currentModeHandler.setPlaybackActive(true);
+ currentModeHandler.setRecordingActive(true);
+ sendMsg(mAudioHandler,
+ MSG_CHECK_MODE_FOR_UID,
+ SENDMSG_QUEUE,
+ 0,
+ 0,
+ currentModeHandler,
+ CHECK_MODE_FOR_UID_PERIOD_MS);
}
- // force reading new top of mSetModeDeathHandlers stack
- if (DEBUG_MODE) { Log.w(TAG, " mode set to MODE_NORMAL after phoneState pb"); }
- mode = AudioSystem.MODE_NORMAL;
}
- } else {
- status = AudioSystem.AUDIO_STATUS_OK;
}
- } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
- if (status == AudioSystem.AUDIO_STATUS_OK) {
- if (actualMode != AudioSystem.MODE_NORMAL) {
- newModeOwnerPid = getModeOwnerPid();
- if (newModeOwnerPid == 0) {
- Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
+ sendMsg(mAudioHandler,
+ MSG_UPDATE_AUDIO_MODE,
+ SENDMSG_REPLACE,
+ mode,
+ pid,
+ callingPackage,
+ 0);
+ }
+ }
+
+ @GuardedBy("mDeviceBroker.mSetModeLock")
+ void onUpdateAudioMode(int requestedMode, int requesterPid, String requesterPackage) {
+ if (requestedMode == AudioSystem.MODE_CURRENT) {
+ requestedMode = getMode();
+ }
+ int mode = AudioSystem.MODE_NORMAL;
+ int uid = 0;
+ int pid = 0;
+ SetModeDeathHandler currentModeHandler = getAudioModeOwnerHandler();
+ if (currentModeHandler != null) {
+ mode = currentModeHandler.getMode();
+ uid = currentModeHandler.getUid();
+ pid = currentModeHandler.getPid();
+ }
+ if (DEBUG_MODE) {
+ Log.v(TAG, "onUpdateAudioMode() new mode: " + mode + ", current mode: "
+ + mMode.get() + " requested mode: " + requestedMode);
+ }
+ if (mode != mMode.get()) {
+ final long identity = Binder.clearCallingIdentity();
+ int status = mAudioSystem.setPhoneState(mode, uid);
+ Binder.restoreCallingIdentity(identity);
+ if (status == AudioSystem.AUDIO_STATUS_OK) {
+ if (DEBUG_MODE) {
+ Log.v(TAG, "onUpdateAudioMode: mode successfully set to " + mode);
}
- }
- // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
- mModeLogger.log(
- new PhoneStateEvent(caller, pid, mode, newModeOwnerPid, actualMode));
+ int previousMode = mMode.getAndSet(mode);
+ // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
+ mModeLogger.log(new PhoneStateEvent(requesterPackage, requesterPid,
+ requestedMode, pid, mode));
- int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
- int device = getDeviceForStream(streamType);
- int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
- setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, caller,
- true /*hasModifyAudioSettings*/);
+ int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
+ int device = getDeviceForStream(streamType);
+ int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
+ setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true,
+ requesterPackage, true /*hasModifyAudioSettings*/);
- updateStreamVolumeAlias(true /*updateVolumes*/, caller);
+ updateStreamVolumeAlias(true /*updateVolumes*/, requesterPackage);
- // change of mode may require volume to be re-applied on some devices
- updateAbsVolumeMultiModeDevices(oldMode, actualMode);
+ // change of mode may require volume to be re-applied on some devices
+ updateAbsVolumeMultiModeDevices(previousMode, mode);
- if (actualMode == AudioSystem.MODE_IN_COMMUNICATION
- && !hdlr.isPrivileged()) {
- sendMsg(mAudioHandler,
- MSG_CHECK_MODE_FOR_UID,
- SENDMSG_QUEUE,
- 0,
- 0,
- hdlr,
- CHECK_MODE_FOR_UID_PERIOD_MS);
+ // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO
+ // connections not started by the application changing the mode when pid changes
+ mDeviceBroker.postSetModeOwnerPid(pid, mode);
+ } else {
+ Log.w(TAG, "onUpdateAudioMode: failed to set audio mode to: " + mode);
}
}
- return newModeOwnerPid;
}
/** @see AudioManager#getMode() */
public int getMode() {
- return mMode;
+ synchronized (mDeviceBroker.mSetModeLock) {
+ SetModeDeathHandler currentModeHandler = getAudioModeOwnerHandler();
+ if (currentModeHandler != null) {
+ return currentModeHandler.getMode();
+ }
+ return AudioSystem.MODE_NORMAL;
+ }
}
/** cached value read from audiopolicy manager after initialization. */
@@ -5258,8 +5441,10 @@ public class AudioService extends IAudioService.Stub
IsInCall = telecomManager.isInCall();
Binder.restoreCallingIdentity(ident);
- return (IsInCall || getMode() == AudioManager.MODE_IN_COMMUNICATION ||
- getMode() == AudioManager.MODE_IN_CALL);
+ int mode = mMode.get();
+ return (IsInCall
+ || mode == AudioManager.MODE_IN_COMMUNICATION
+ || mode == AudioManager.MODE_IN_CALL);
}
/**
@@ -5714,11 +5899,6 @@ public class AudioService extends IAudioService.Stub
throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
+ " (dis)connection, got " + state);
}
- if (state == BluetoothProfile.STATE_CONNECTED) {
- mPlaybackMonitor.registerPlaybackCallback(mVoiceActivityMonitor, true);
- } else {
- mPlaybackMonitor.unregisterPlaybackCallback(mVoiceActivityMonitor);
- }
mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
device, state, suppressNoisyIntent, musicDevice, "AudioService");
}
@@ -7084,6 +7264,9 @@ public class AudioService extends IAudioService.Stub
case MSG_PLAYBACK_CONFIG_CHANGE:
onPlaybackConfigChange((List<AudioPlaybackConfiguration>) msg.obj);
break;
+ case MSG_RECORDING_CONFIG_CHANGE:
+ onRecordingConfigChange((List<AudioRecordingConfiguration>) msg.obj);
+ break;
case MSG_BROADCAST_MICROPHONE_MUTE:
mSystemServer.sendMicrophoneMuteChangedIntent();
@@ -7094,30 +7277,19 @@ public class AudioService extends IAudioService.Stub
if (msg.obj == null) {
break;
}
- // If no other app is currently owning the audio mode and
- // the app corresponding to this mode death handler object is still in the
- // mode owner stack but not capturing or playing audio after 3 seconds,
- // remove it from the stack.
- // Otherwise, check again in 3 seconds.
+ // Update active playback/recording for apps requesting IN_COMMUNICATION
+ // mode after a grace period following the mode change
SetModeDeathHandler h = (SetModeDeathHandler) msg.obj;
if (mSetModeDeathHandlers.indexOf(h) < 0) {
break;
}
- if (getModeOwnerUid() != h.getUid()
- || mRecordMonitor.isRecordingActiveForUid(h.getUid())
- || mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())) {
- sendMsg(mAudioHandler,
- MSG_CHECK_MODE_FOR_UID,
- SENDMSG_QUEUE,
- 0,
- 0,
- h,
- CHECK_MODE_FOR_UID_PERIOD_MS);
- break;
+ boolean wasActive = h.isActive();
+ h.setPlaybackActive(mPlaybackMonitor.isPlaybackActiveForUid(h.getUid()));
+ h.setRecordingActive(mRecordMonitor.isRecordingActiveForUid(h.getUid()));
+ if (wasActive != h.isActive()) {
+ onUpdateAudioMode(AudioSystem.MODE_CURRENT,
+ android.os.Process.myPid(), mContext.getPackageName());
}
- setModeInt(AudioSystem.MODE_NORMAL, h.getBinder(), h.getPid(), h.getUid(),
- h.isPrivileged(), "MSG_CHECK_MODE_FOR_UID");
- mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid()));
}
break;
@@ -7134,10 +7306,16 @@ public class AudioService extends IAudioService.Stub
case MSG_REINIT_VOLUMES:
onReinitVolumes((String) msg.obj);
break;
+
case MSG_UPDATE_A11Y_SERVICE_UIDS:
onUpdateAccessibilityServiceUids();
break;
+ case MSG_UPDATE_AUDIO_MODE:
+ synchronized (mDeviceBroker.mSetModeLock) {
+ onUpdateAudioMode(msg.arg1, msg.arg2, (String) msg.obj);
+ }
+ break;
}
}
}
@@ -8167,6 +8345,7 @@ public class AudioService extends IAudioService.Stub
dumpStreamStates(pw);
dumpVolumeGroups(pw);
dumpRingerMode(pw);
+ dumpAudioMode(pw);
pw.println("\nAudio routes:");
pw.print(" mMainType=0x"); pw.println(Integer.toHexString(
mDeviceBroker.getCurAudioRoutes().mainType));
@@ -9138,6 +9317,18 @@ public class AudioService extends IAudioService.Stub
}
/**
+ * Update player session ID
+ * @param piid Player id to update
+ * @param sessionId The new audio session ID
+ */
+ public void playerSessionId(int piid, int sessionId) {
+ if (sessionId <= AudioSystem.AUDIO_SESSION_ALLOCATE) {
+ throw new IllegalArgumentException("invalid session Id " + sessionId);
+ }
+ mPlaybackMonitor.playerSessionId(piid, sessionId, Binder.getCallingUid());
+ }
+
+ /**
* Update player event
* @param piid Player id to update
* @param event The new player event
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 36c67cdbac4b..8af1b5be1517 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -197,6 +197,28 @@ public final class PlaybackActivityMonitor
}
}
+ /**
+ * Update player session ID
+ * @param piid Player id to update
+ * @param sessionId The new audio session ID
+ * @param binderUid Calling binder uid
+ */
+ public void playerSessionId(int piid, int sessionId, int binderUid) {
+ final boolean change;
+ synchronized (mPlayerLock) {
+ final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
+ if (checkConfigurationCaller(piid, apc, binderUid)) {
+ change = apc.handleSessionIdEvent(sessionId);
+ } else {
+ Log.e(TAG, "Error updating audio session");
+ change = false;
+ }
+ }
+ if (change) {
+ dispatchPlaybackChange(false);
+ }
+ }
+
private static final int FLAGS_FOR_SILENCE_OVERRIDE =
AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
AudioAttributes.FLAG_BYPASS_MUTE;
@@ -242,8 +264,8 @@ public final class PlaybackActivityMonitor
*/
public void playerEvent(int piid, int event, int deviceId, int binderUid) {
if (DEBUG) {
- Log.v(TAG, String.format("playerEvent(piid=%d, deviceId=%d, event=%d)",
- piid, deviceId, event));
+ Log.v(TAG, String.format("playerEvent(piid=%d, deviceId=%d, event=%s)",
+ piid, deviceId, AudioPlaybackConfiguration.playerStateToString(event)));
}
final boolean change;
synchronized(mPlayerLock) {
@@ -921,6 +943,7 @@ public final class PlaybackActivityMonitor
private final int mClientUid;
private final int mClientPid;
private final AudioAttributes mPlayerAttr;
+ private final int mSessionId;
NewPlayerEvent(AudioPlaybackConfiguration apc) {
mPlayerIId = apc.getPlayerInterfaceId();
@@ -928,6 +951,7 @@ public final class PlaybackActivityMonitor
mClientUid = apc.getClientUid();
mClientPid = apc.getClientPid();
mPlayerAttr = apc.getAudioAttributes();
+ mSessionId = apc.getSessionId();
}
@Override
@@ -935,7 +959,8 @@ public final class PlaybackActivityMonitor
return new String("new player piid:" + mPlayerIId + " uid/pid:" + mClientUid + "/"
+ mClientPid + " type:"
+ AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType)
- + " attr:" + mPlayerAttr);
+ + " attr:" + mPlayerAttr
+ + " session:" + mSessionId);
}
}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index b15a8869b22a..e19745e5c578 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -40,6 +40,7 @@ import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.face.IFaceService;
@@ -144,13 +145,14 @@ public class AuthService extends SystemService {
private final class AuthServiceImpl extends IAuthService.Stub {
@Override
- public ITestSession createTestSession(int sensorId, @NonNull String opPackageName)
- throws RemoteException {
+ public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
+ @NonNull String opPackageName) throws RemoteException {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
final long identity = Binder.clearCallingIdentity();
try {
- return mInjector.getBiometricService().createTestSession(sensorId, opPackageName);
+ return mInjector.getBiometricService()
+ .createTestSession(sensorId, callback, opPackageName);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 14292d9c5f8d..f88820083768 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -257,11 +257,15 @@ public final class AuthSession implements IBinder.DeathRecipient {
mUserId,
mOpPackageName,
mOperationId);
+ mState = STATE_AUTH_STARTED;
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
+ } else {
+ // The UI was already showing :)
+ mState = STATE_AUTH_STARTED_UI_SHOWING;
}
- mState = STATE_AUTH_STARTED;
+
}
}
@@ -686,7 +690,8 @@ public final class AuthSession implements IBinder.DeathRecipient {
* @return true if this AuthSession is finished, e.g. should be set to null
*/
boolean onCancelAuthSession(boolean force) {
- final boolean authStarted = mState == STATE_AUTH_STARTED
+ final boolean authStarted = mState == STATE_AUTH_CALLED
+ || mState == STATE_AUTH_STARTED
|| mState == STATE_AUTH_STARTED_UI_SHOWING;
if (authStarted && !force) {
@@ -793,7 +798,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
case BiometricAuthenticator.TYPE_FINGERPRINT:
return FingerprintManager.getAcquiredString(mContext, acquiredInfo, vendorCode);
case BiometricAuthenticator.TYPE_FACE:
- return FaceManager.getAcquiredString(mContext, acquiredInfo, vendorCode);
+ return FaceManager.getAuthHelpMessage(mContext, acquiredInfo, vendorCode);
default:
return null;
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 614c5f1b65bf..00a4e43f347d 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -44,6 +44,7 @@ import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintManager;
@@ -570,13 +571,13 @@ public class BiometricService extends SystemService {
*/
private final class BiometricServiceWrapper extends IBiometricService.Stub {
@Override // Binder call
- public ITestSession createTestSession(int sensorId, @NonNull String opPackageName)
- throws RemoteException {
+ public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
+ @NonNull String opPackageName) throws RemoteException {
checkInternalPermission();
for (BiometricSensor sensor : mSensors) {
if (sensor.id == sensorId) {
- return sensor.impl.createTestSession(opPackageName);
+ return sensor.impl.createTestSession(callback, opPackageName);
}
}
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 0536e78e58f6..b31a54b8b15e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -311,4 +311,9 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
public int getProtoEnum() {
return BiometricsProto.CM_AUTHENTICATE;
}
+
+ @Override
+ public boolean interruptsPrecedingClients() {
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index 8fa3bbbf615a..81ce2d535237 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -82,12 +82,18 @@ public abstract class BaseClientMonitor extends LoggableMonitor
@NonNull protected Callback mCallback;
/**
- * Returns a ClientMonitorEnum constant defined in biometrics.proto
- * @return
+ * @return A ClientMonitorEnum constant defined in biometrics.proto
*/
public abstract int getProtoEnum();
/**
+ * @return True if the ClientMonitor should cancel any current and pending interruptable clients
+ */
+ public boolean interruptsPrecedingClients() {
+ return false;
+ }
+
+ /**
* @param context system_server context
* @param token a unique token for the client
* @param listener recipient of related events (e.g. authentication)
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index c5237ab8c8e7..20c25c35535a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -475,14 +475,17 @@ public class BiometricScheduler {
*/
public void scheduleClientMonitor(@NonNull BaseClientMonitor clientMonitor,
@Nullable BaseClientMonitor.Callback clientCallback) {
- // Mark any interruptable pending clients as canceling. Once they reach the head of the
- // queue, the scheduler will send ERROR_CANCELED and skip the operation.
- for (Operation operation : mPendingOperations) {
- if (operation.mClientMonitor instanceof Interruptable
- && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
- Slog.d(getTag(), "New client incoming, marking pending client as canceling: "
- + operation.mClientMonitor);
- operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+ // If the incoming operation should interrupt preceding clients, mark any interruptable
+ // pending clients as canceling. Once they reach the head of the queue, the scheduler will
+ // send ERROR_CANCELED and skip the operation.
+ if (clientMonitor.interruptsPrecedingClients()) {
+ for (Operation operation : mPendingOperations) {
+ if (operation.mClientMonitor instanceof Interruptable
+ && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
+ Slog.d(getTag(), "New client incoming, marking pending client as canceling: "
+ + operation.mClientMonitor);
+ operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+ }
}
}
@@ -490,8 +493,11 @@ public class BiometricScheduler {
Slog.d(getTag(), "[Added] " + clientMonitor
+ ", new queue size: " + mPendingOperations.size());
- // If the current operation is cancellable, start the cancellation process.
- if (mCurrentOperation != null && mCurrentOperation.mClientMonitor instanceof Interruptable
+ // If the new operation should interrupt preceding clients, and if the current operation is
+ // cancellable, start the cancellation process.
+ if (clientMonitor.interruptsPrecedingClients()
+ && mCurrentOperation != null
+ && mCurrentOperation.mClientMonitor instanceof Interruptable
&& mCurrentOperation.mState == Operation.STATE_STARTED) {
Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation);
cancelInternal(mCurrentOperation);
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index da76af800d3d..25b7add0a7d8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -16,9 +16,12 @@
package com.android.server.biometrics.sensors;
+import android.annotation.NonNull;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.face.Face;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceEnrollFrame;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -174,4 +177,33 @@ public class ClientMonitorCallbackConverter {
mFingerprintServiceReceiver.onUdfpsPointerUp(sensorId);
}
}
+
+ // Face-specific callbacks for FaceManager only
+
+ /**
+ * Called each time a new frame is received during face authentication.
+ *
+ * @param frame Information about the current frame.
+ *
+ * @throws RemoteException If the binder call to {@link IFaceServiceReceiver} fails.
+ */
+ public void onAuthenticationFrame(@NonNull FaceAuthenticationFrame frame)
+ throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+ mFaceServiceReceiver.onAuthenticationFrame(frame);
+ }
+ }
+
+ /**
+ * Called each time a new frame is received during face enrollment.
+ *
+ * @param frame Information about the current frame.
+ *
+ * @throws RemoteException If the binder call to {@link IFaceServiceReceiver} fails.
+ */
+ public void onEnrollmentFrame(@NonNull FaceEnrollFrame frame) throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+ mFaceServiceReceiver.onEnrollmentFrame(frame);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 8d81016dab59..e1320d8e1a4f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -113,4 +113,9 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> {
public int getProtoEnum() {
return BiometricsProto.CM_ENROLL;
}
+
+ @Override
+ public boolean interruptsPrecedingClients() {
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index ce24e5efdc07..de571863dbd4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -69,6 +69,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
final List<BiometricAuthenticator.Identifier> unknownHALTemplates =
((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates();
+ Slog.d(TAG, "Enumerate onClientFinished: " + clientMonitor + ", success: " + success);
+
if (!unknownHALTemplates.isEmpty()) {
Slog.w(TAG, "Adding " + unknownHALTemplates.size() + " templates for deletion");
}
@@ -90,6 +92,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
private final Callback mRemoveCallback = new Callback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
+ Slog.d(TAG, "Remove onClientFinished: " + clientMonitor + ", success: " + success);
mCallback.onClientFinished(InternalCleanupClient.this, success);
}
};
@@ -115,6 +118,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
}
private void startCleanupUnknownHalTemplates() {
+ Slog.d(TAG, "startCleanupUnknownHalTemplates, size: " + mUnknownHALTemplates.size());
+
UserTemplate template = mUnknownHALTemplates.get(0);
mUnknownHALTemplates.remove(template);
mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(),
@@ -138,6 +143,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
// Start enumeration. Removal will start if necessary, when enumeration is completed.
mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(),
getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId());
+
+ Slog.d(TAG, "Starting enumerate: " + mCurrentTask);
mCurrentTask.start(mEnumerateCallback);
}
@@ -165,6 +172,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
+ mCurrentTask.getClass().getSimpleName());
return;
}
+ Slog.d(TAG, "onEnumerated, remaining: " + remaining);
((EnumerateConsumer) mCurrentTask).onEnumerationResult(identifier, remaining);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 9d19fdf4868d..e3feb74248ec 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -82,6 +82,7 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T>
private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) {
if (identifier == null) {
+ Slog.d(TAG, "Null identifier");
return;
}
Slog.v(TAG, "handleEnumeratedTemplate: " + identifier.getBiometricId());
@@ -103,6 +104,7 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T>
private void doTemplateCleanup() {
if (mEnrolledList == null) {
+ Slog.d(TAG, "Null enrolledList");
return;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index e0626952fac3..8197edc97a05 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -37,18 +37,16 @@ public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier,
private static final String TAG = "Biometrics/RemovalClient";
- protected final int mBiometricId;
private final BiometricUtils<S> mBiometricUtils;
private final Map<Integer, Long> mAuthenticatorIds;
public RemovalClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
- int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<S> utils,
- int sensorId, @NonNull Map<Integer, Long> authenticatorIds, int statsModality) {
+ int userId, @NonNull String owner, @NonNull BiometricUtils<S> utils, int sensorId,
+ @NonNull Map<Integer, Long> authenticatorIds, int statsModality) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
statsModality, BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN);
- mBiometricId = biometricId;
mBiometricUtils = utils;
mAuthenticatorIds = authenticatorIds;
}
@@ -68,6 +66,7 @@ public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier,
@Override
public void onRemoved(@Nullable BiometricAuthenticator.Identifier identifier, int remaining) {
+ Slog.d(TAG, "onRemoved: " + identifier.getBiometricId() + " remaining: " + remaining);
if (identifier != null) {
mBiometricUtils.removeBiometricForUser(getContext(), getTargetUserId(),
identifier.getBiometricId());
@@ -97,4 +96,9 @@ public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier,
public int getProtoEnum() {
return BiometricsProto.CM_REMOVE;
}
+
+ @Override
+ public boolean interruptsPrecedingClients() {
+ return true;
+ }
}
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 f37cf18a1320..06b049be4501 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
@@ -21,6 +21,7 @@ import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.face.IFaceService;
import android.os.IBinder;
@@ -41,8 +42,9 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub {
}
@Override
- public ITestSession createTestSession(@NonNull String opPackageName) throws RemoteException {
- return mFaceService.createTestSession(mSensorId, opPackageName);
+ public ITestSession createTestSession(@NonNull ITestSessionCallback callback,
+ @NonNull String opPackageName) throws RemoteException {
+ return mFaceService.createTestSession(mSensorId, callback, opPackageName);
}
@Override
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 825392762c8c..6dbd590df851 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
@@ -31,6 +31,7 @@ import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
import android.hardware.face.Face;
@@ -133,7 +134,8 @@ public class FaceService extends SystemService implements BiometricServiceCallba
*/
private final class FaceServiceWrapper extends IFaceService.Stub {
@Override
- public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
+ public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
+ @NonNull String opPackageName) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -143,7 +145,7 @@ public class FaceService extends SystemService implements BiometricServiceCallba
return null;
}
- return provider.createTestSession(sensorId, opPackageName);
+ return provider.createTestSession(sensorId, callback, opPackageName);
}
@Override
@@ -386,7 +388,22 @@ public class FaceService extends SystemService implements BiometricServiceCallba
opPackageName);
}
- @Override
+ @Override // Binder call
+ public void removeAll(final IBinder token, final int userId,
+ 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;
+ }
+
+ provider.second.scheduleRemoveAll(provider.first, token, userId, receiver,
+ opPackageName);
+ }
+
+ @Override // Binder call
public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback,
final String opPackageName) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index cc24b8960e75..88edfbf12df1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.face.Face;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -28,6 +29,7 @@ import android.os.IBinder;
import android.os.NativeHandle;
import android.util.proto.ProtoOutputStream;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -109,6 +111,9 @@ public interface ServiceProvider {
void scheduleRemove(int sensorId, @NonNull IBinder token, int faceId, int userId,
@NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName);
+ void scheduleRemoveAll(int sensorId, @NonNull IBinder token, int userId,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName);
+
void scheduleResetLockout(int sensorId, int userId, @NonNull byte[] hardwareAuthToken);
void scheduleSetFeature(int sensorId, @NonNull IBinder token, int userId, int feature,
@@ -120,7 +125,8 @@ public interface ServiceProvider {
void startPreparedClient(int sensorId, int cookie);
- void scheduleInternalCleanup(int sensorId, int userId);
+ void scheduleInternalCleanup(int sensorId, int userId,
+ @Nullable BaseClientMonitor.Callback callback);
void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
boolean clearSchedulerBuffer);
@@ -130,7 +136,8 @@ public interface ServiceProvider {
void dumpInternal(int sensorId, @NonNull PrintWriter pw);
@NonNull
- ITestSession createTestSession(int sensorId, @NonNull String opPackageName);
+ ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
+ @NonNull String opPackageName);
void dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index d2673d2969c9..a5e6ddb81669 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -21,15 +21,20 @@ import static android.Manifest.permission.TEST_BIOMETRIC;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.face.AuthenticationFrame;
import android.hardware.biometrics.face.BaseFrame;
import android.hardware.face.Face;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceEnrollFrame;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
+import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.face.FaceUtils;
import java.util.HashSet;
@@ -47,6 +52,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
@NonNull private final Context mContext;
private final int mSensorId;
+ @NonNull private final ITestSessionCallback mCallback;
@NonNull private final FaceProvider mProvider;
@NonNull private final Sensor mSensor;
@NonNull private final Set<Integer> mEnrollmentIds;
@@ -117,12 +123,24 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
public void onChallengeInterruptFinished(int sensorId) {
}
+
+ @Override
+ public void onAuthenticationFrame(FaceAuthenticationFrame frame) {
+
+ }
+
+ @Override
+ public void onEnrollmentFrame(FaceEnrollFrame frame) {
+
+ }
};
BiometricTestSessionImpl(@NonNull Context context, int sensorId,
+ @NonNull ITestSessionCallback callback,
@NonNull FaceProvider provider, @NonNull Sensor sensor) {
mContext = context;
mSensorId = sensorId;
+ mCallback = callback;
mProvider = provider;
mSensor = sensor;
mEnrollmentIds = new HashSet<>();
@@ -183,7 +201,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFailed();
}
- // TODO(b/174619156): replace with notifyAuthenticationFrame and notifyEnrollmentFrame.
+ // TODO(b/178414967): replace with notifyAuthenticationFrame and notifyEnrollmentFrame.
@Override
public void notifyAcquired(int userId, int acquireInfo) {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
@@ -194,7 +212,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
AuthenticationFrame authenticationFrame = new AuthenticationFrame();
authenticationFrame.data = data;
- // TODO(b/174619156): Currently onAuthenticationFrame and onEnrollmentFrame are the same.
+ // TODO(b/178414967): Currently onAuthenticationFrame and onEnrollmentFrame are the same.
// This will need to call the correct callback once the onAcquired callback is removed.
mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFrame(
authenticationFrame);
@@ -212,6 +230,25 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
public void cleanupInternalState(int userId) {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
- mProvider.scheduleInternalCleanup(mSensorId, userId);
+ mProvider.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ try {
+ mCallback.onCleanupStarted(clientMonitor.getTargetUserId());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ try {
+ mCallback.onCleanupFinished(clientMonitor.getTargetUserId());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+ });
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 30577667e5e4..8f554028ebfd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -29,7 +29,6 @@ import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.FaceAuthenticationFrame;
-import android.hardware.face.FaceDataFrame;
import android.hardware.face.FaceManager;
import android.os.IBinder;
import android.os.RemoteException;
@@ -179,19 +178,16 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
return isBiometricPrompt() ? mBiometricPromptIgnoreListVendor : mKeyguardIgnoreListVendor;
}
- private boolean shouldSend(int acquireInfo, int vendorCode) {
- if (acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR) {
- return !Utils.listContains(getAcquireVendorIgnorelist(), vendorCode);
- } else {
- return !Utils.listContains(getAcquireIgnorelist(), acquireInfo);
- }
+ private boolean shouldSendAcquiredMessage(int acquireInfo, int vendorCode) {
+ return acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR
+ ? !Utils.listContains(getAcquireVendorIgnorelist(), vendorCode)
+ : !Utils.listContains(getAcquireIgnorelist(), acquireInfo);
}
@Override
public void onAcquired(int acquireInfo, int vendorCode) {
mLastAcquire = acquireInfo;
-
- final boolean shouldSend = shouldSend(acquireInfo, vendorCode);
+ final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode);
onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
}
@@ -201,9 +197,21 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
* @param frame Information about the current frame.
*/
public void onAuthenticationFrame(@NonNull FaceAuthenticationFrame frame) {
- // TODO(b/178414967): Send additional frame data to the client callback.
- final FaceDataFrame data = frame.getData();
- onAcquired(data.getAcquiredInfo(), data.getVendorCode());
+ // Log acquisition but don't send it to the client yet, since that's handled below.
+ final int acquireInfo = frame.getData().getAcquiredInfo();
+ final int vendorCode = frame.getData().getVendorCode();
+ mLastAcquire = acquireInfo;
+ onAcquiredInternal(acquireInfo, vendorCode, false /* shouldSend */);
+
+ final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode);
+ if (shouldSend && getListener() != null) {
+ try {
+ getListener().onAuthenticationFrame(frame);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to send authentication frame", e);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
}
@Override public void onLockoutTimed(long durationMillis) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index da657b96afd5..898d81b0c8c4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -27,7 +27,6 @@ import android.hardware.biometrics.face.Feature;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
-import android.hardware.face.FaceDataFrame;
import android.hardware.face.FaceEnrollFrame;
import android.hardware.face.FaceManager;
import android.os.IBinder;
@@ -101,14 +100,15 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
getTargetUserId()).size() >= mMaxTemplatesPerUser;
}
+ private boolean shouldSendAcquiredMessage(int acquireInfo, int vendorCode) {
+ return acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR
+ ? !Utils.listContains(mEnrollIgnoreListVendor, vendorCode)
+ : !Utils.listContains(mEnrollIgnoreList, acquireInfo);
+ }
+
@Override
public void onAcquired(int acquireInfo, int vendorCode) {
- final boolean shouldSend;
- if (acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR) {
- shouldSend = !Utils.listContains(mEnrollIgnoreListVendor, vendorCode);
- } else {
- shouldSend = !Utils.listContains(mEnrollIgnoreList, acquireInfo);
- }
+ final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode);
onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
}
@@ -118,9 +118,20 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
* @param frame Information about the current frame.
*/
public void onEnrollmentFrame(@NonNull FaceEnrollFrame frame) {
- // TODO(b/178414967): Send additional frame data to the client callback.
- final FaceDataFrame data = frame.getData();
- onAcquired(data.getAcquiredInfo(), data.getVendorCode());
+ // Log acquisition but don't send it to the client yet, since that's handled below.
+ final int acquireInfo = frame.getData().getAcquiredInfo();
+ final int vendorCode = frame.getData().getVendorCode();
+ onAcquiredInternal(acquireInfo, vendorCode, false /* shouldSend */);
+
+ final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode);
+ if (shouldSend && getListener() != null) {
+ try {
+ getListener().onEnrollmentFrame(frame);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to send enrollment frame", e);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
index 9680e4e1841e..c6696aed6520 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
@@ -61,7 +61,7 @@ class FaceInternalCleanupClient extends InternalCleanupClient<Face, ISession> {
// Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
// is all done internally.
return new FaceRemovalClient(context, lazyDaemon, token,
- null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
- sensorId, authenticatorIds);
+ null /* ClientMonitorCallbackConverter */, new int[] {biometricId}, userId, owner,
+ utils, sensorId, authenticatorIds);
}
}
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 e685ee2899af..1d8f210b394e 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
@@ -25,6 +25,7 @@ import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
import android.hardware.face.Face;
@@ -177,7 +178,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
for (int i = 0; i < mSensors.size(); i++) {
final int sensorId = mSensors.keyAt(i);
scheduleLoadAuthenticatorIds(sensorId);
- scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser());
+ scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser(),
+ null /* callback */);
}
return mDaemon;
@@ -468,6 +470,25 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@Override
public void scheduleRemove(int sensorId, @NonNull IBinder token, int faceId, int userId,
@NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+ scheduleRemoveSpecifiedIds(sensorId, token, new int[] {faceId}, userId, receiver,
+ opPackageName);
+ }
+
+ @Override
+ public void scheduleRemoveAll(int sensorId, @NonNull IBinder token, int userId,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+ final List<Face> faces = FaceUtils.getInstance(sensorId)
+ .getBiometricsForUser(mContext, userId);
+ final int[] faceIds = new int[faces.size()];
+ for (int i = 0; i < faces.size(); i++) {
+ faceIds[i] = faces.get(i).getBiometricId();
+ }
+
+ scheduleRemoveSpecifiedIds(sensorId, token, faceIds, userId, receiver, opPackageName);
+ }
+
+ private void scheduleRemoveSpecifiedIds(int sensorId, @NonNull IBinder token, int[] faceIds,
+ int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
final IFace daemon = getHalInstance();
if (daemon == null) {
@@ -485,7 +506,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final FaceRemovalClient client = new FaceRemovalClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
- new ClientMonitorCallbackConverter(receiver), faceId, userId,
+ new ClientMonitorCallbackConverter(receiver), faceIds, userId,
opPackageName, FaceUtils.getInstance(sensorId), sensorId,
mSensors.get(sensorId).getAuthenticatorIds());
@@ -543,7 +564,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
@Override
- public void scheduleInternalCleanup(int sensorId, int userId) {
+ public void scheduleInternalCleanup(int sensorId, int userId,
+ @Nullable BaseClientMonitor.Callback callback) {
mHandler.post(() -> {
final IFace daemon = getHalInstance();
if (daemon == null) {
@@ -564,7 +586,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
FaceUtils.getInstance(sensorId),
mSensors.get(sensorId).getAuthenticatorIds());
- mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+ mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client, callback);
} catch (RemoteException e) {
Slog.e(getTag(), "Remote exception when scheduling internal cleanup", e);
}
@@ -627,8 +649,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@NonNull
@Override
- public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
- return mSensors.get(sensorId).createTestSession();
+ public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
+ @NonNull String opPackageName) {
+ return mSensors.get(sensorId).createTestSession(callback);
}
@Override
@@ -643,8 +666,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final Sensor sensor = mSensors.valueAt(i);
final int sensorId = mSensors.keyAt(i);
PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount();
- sensor.getScheduler().recordCrashState();
- sensor.getScheduler().reset();
+ sensor.onBinderDied();
}
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
index 1cb5031374ec..48796c173dd8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
@@ -38,19 +38,22 @@ import java.util.Map;
class FaceRemovalClient extends RemovalClient<Face, ISession> {
private static final String TAG = "FaceRemovalClient";
+ final int[] mBiometricIds;
+
FaceRemovalClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
- int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<Face> utils,
- int sensorId, @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
+ int[] biometricIds, int userId, @NonNull String owner,
+ @NonNull BiometricUtils<Face> utils, int sensorId,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
authenticatorIds, BiometricsProtoEnums.MODALITY_FACE);
+ mBiometricIds = biometricIds;
}
@Override
protected void startHalOperation() {
try {
- final int[] ids = new int[]{mBiometricId};
- getFreshDaemon().removeEnrollments(mSequentialId, ids);
+ getFreshDaemon().removeEnrollments(mSequentialId, mBiometricIds);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting remove", e);
mCallback.onClientFinished(this, false /* success */);
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 640838c6ee04..3434acbf73cc 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
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.face.AuthenticationFrame;
import android.hardware.biometrics.face.EnrollmentFrame;
import android.hardware.biometrics.face.Error;
@@ -33,7 +34,6 @@ import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.Handler;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserManager;
import android.util.Slog;
@@ -63,7 +63,7 @@ import java.util.Map;
/**
* Maintains the state of a single sensor within an instance of the {@link IFace} HAL.
*/
-public class Sensor implements IBinder.DeathRecipient {
+public class Sensor {
private boolean mTestHalEnabled;
@@ -436,13 +436,7 @@ public class Sensor implements IBinder.DeathRecipient {
mScheduler = new BiometricScheduler(tag, null /* gestureAvailabilityDispatcher */);
mLockoutCache = new LockoutCache();
mAuthenticatorIds = new HashMap<>();
- mLazySession = () -> {
- if (mTestHalEnabled) {
- return new TestSession(mCurrentSession.mHalSessionCallback);
- } else {
- return mCurrentSession != null ? mCurrentSession.mSession : null;
- }
- };
+ mLazySession = () -> mCurrentSession != null ? mCurrentSession.mSession : null;
}
@NonNull HalClientMonitor.LazyDaemon<ISession> getLazySession() {
@@ -466,8 +460,9 @@ public class Sensor implements IBinder.DeathRecipient {
}
}
- @NonNull ITestSession createTestSession() {
- return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, mProvider, this);
+ @NonNull ITestSession createTestSession(@NonNull ITestSessionCallback callback) {
+ return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, callback,
+ mProvider, this);
}
void createNewSession(@NonNull IFace daemon, int sensorId, int userId)
@@ -481,7 +476,6 @@ public class Sensor implements IBinder.DeathRecipient {
mTag, mScheduler, sensorId, userId, callback);
final ISession newSession = daemon.createSession(sensorId, userId, resultController);
- newSession.asBinder().linkToDeath(this, 0 /* flags */);
mCurrentSession = new Session(mTag, newSession, userId, resultController);
}
@@ -498,6 +492,11 @@ public class Sensor implements IBinder.DeathRecipient {
}
void setTestHalEnabled(boolean enabled) {
+ Slog.w(mTag, "setTestHalEnabled: " + enabled);
+ if (enabled != mTestHalEnabled) {
+ // The framework should retrieve a new session from the HAL.
+ mCurrentSession = null;
+ }
mTestHalEnabled = enabled;
}
@@ -523,24 +522,21 @@ public class Sensor implements IBinder.DeathRecipient {
proto.end(sensorToken);
}
- @Override
- public void binderDied() {
- Slog.e(mTag, "Binder died");
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (client instanceof Interruptable) {
- Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
- final Interruptable interruptable = (Interruptable) client;
- interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
-
- mScheduler.recordCrashState();
+ public void onBinderDied() {
+ final BaseClientMonitor client = mScheduler.getCurrentClient();
+ if (client instanceof Interruptable) {
+ Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+ BiometricsProtoEnums.MODALITY_FACE,
+ BiometricsProtoEnums.ISSUE_HAL_DEATH);
+ }
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
- BiometricsProtoEnums.MODALITY_FACE,
- BiometricsProtoEnums.ISSUE_HAL_DEATH);
- mCurrentSession = null;
- }
- });
+ mScheduler.recordCrashState();
+ mScheduler.reset();
+ mCurrentSession = null;
}
}
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 1d57073d6516..afa5bd22e081 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
@@ -17,93 +17,130 @@
package com.android.server.biometrics.sensors.face.aidl;
import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.face.Error;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.biometrics.face.ISessionCallback;
import android.hardware.biometrics.face.SensorProps;
+import android.hardware.biometrics.face.SessionState;
import android.hardware.common.NativeHandle;
import android.hardware.keymaster.HardwareAuthToken;
-import android.os.Binder;
-import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
/**
* Test HAL that provides only no-ops.
*/
public class TestHal extends IFace.Stub {
+ private static final String TAG = "face.aidl.TestHal";
@Override
public SensorProps[] getSensorProps() {
+ Slog.w(TAG, "getSensorProps");
return new SensorProps[0];
}
@Override
public ISession createSession(int sensorId, int userId, ISessionCallback cb) {
- return new ISession() {
- @Override
- public void generateChallenge(int cookie, int timeoutSec) {
+ Slog.w(TAG, "createSession, sensorId: " + sensorId + " userId: " + userId);
+ return new ISession.Stub() {
+ @Override
+ public void generateChallenge(int cookie, int timeoutSec) throws RemoteException {
+ Slog.w(TAG, "generateChallenge, cookie: " + cookie);
+ cb.onChallengeGenerated(0L);
}
@Override
- public void revokeChallenge(int cookie, long challenge) {
-
+ public void revokeChallenge(int cookie, long challenge) throws RemoteException {
+ Slog.w(TAG, "revokeChallenge: " + challenge + ", cookie: " + cookie);
+ cb.onChallengeRevoked(challenge);
}
@Override
public ICancellationSignal enroll(int cookie, HardwareAuthToken hat,
byte enrollmentType, byte[] features, NativeHandle previewSurface) {
- return null;
+ Slog.w(TAG, "enroll, cookie: " + cookie);
+ return new ICancellationSignal.Stub() {
+ @Override
+ public void cancel() throws RemoteException {
+ cb.onError(Error.CANCELED, 0 /* vendorCode */);
+ }
+ };
}
@Override
public ICancellationSignal authenticate(int cookie, long operationId) {
- return null;
+ Slog.w(TAG, "authenticate, cookie: " + cookie);
+ return new ICancellationSignal.Stub() {
+ @Override
+ public void cancel() throws RemoteException {
+ cb.onError(Error.CANCELED, 0 /* vendorCode */);
+ }
+ };
}
@Override
public ICancellationSignal detectInteraction(int cookie) {
- return null;
+ Slog.w(TAG, "detectInteraction, cookie: " + cookie);
+ return new ICancellationSignal.Stub() {
+ @Override
+ public void cancel() throws RemoteException {
+ cb.onError(Error.CANCELED, 0 /* vendorCode */);
+ }
+ };
}
@Override
- public void enumerateEnrollments(int cookie) {
-
+ public void enumerateEnrollments(int cookie) throws RemoteException {
+ Slog.w(TAG, "enumerateEnrollments, cookie: " + cookie);
+ cb.onEnrollmentsEnumerated(new int[0]);
}
@Override
- public void removeEnrollments(int cookie, int[] enrollmentIds) {
-
+ public void removeEnrollments(int cookie, int[] enrollmentIds) throws RemoteException {
+ Slog.w(TAG, "removeEnrollments, cookie: " + cookie);
+ cb.onEnrollmentsRemoved(enrollmentIds);
}
@Override
- public void getFeatures(int cookie, int enrollmentId) {
-
+ public void getFeatures(int cookie, int enrollmentId) throws RemoteException {
+ Slog.w(TAG, "getFeatures, cookie: " + cookie);
+ cb.onFeaturesRetrieved(new byte[0], enrollmentId);
}
@Override
public void setFeature(int cookie, HardwareAuthToken hat, int enrollmentId,
- byte feature, boolean enabled) {
-
+ byte feature, boolean enabled) throws RemoteException {
+ Slog.w(TAG, "setFeature, cookie: " + cookie);
+ cb.onFeatureSet(enrollmentId, feature);
}
@Override
- public void getAuthenticatorId(int cookie) {
-
+ public void getAuthenticatorId(int cookie) throws RemoteException {
+ Slog.w(TAG, "getAuthenticatorId, cookie: " + cookie);
+ cb.onAuthenticatorIdRetrieved(0L);
}
@Override
- public void invalidateAuthenticatorId(int cookie) {
-
+ public void invalidateAuthenticatorId(int cookie) throws RemoteException {
+ Slog.w(TAG, "invalidateAuthenticatorId, cookie: " + cookie);
+ cb.onAuthenticatorIdInvalidated(0L);
}
@Override
- public void resetLockout(int cookie, HardwareAuthToken hat) {
-
+ public void resetLockout(int cookie, HardwareAuthToken hat) throws RemoteException {
+ Slog.w(TAG, "resetLockout, cookie: " + cookie);
+ cb.onLockoutCleared();
}
@Override
- public IBinder asBinder() {
- return new Binder();
+ public void close(int cookie) throws RemoteException {
+ cb.onStateChanged(cookie, SessionState.CLOSED);
}
};
}
+
+ @Override
+ public void reset() {
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java
deleted file mode 100644
index 23e69885841a..000000000000
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java
+++ /dev/null
@@ -1,117 +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.server.biometrics.sensors.face.aidl;
-
-import android.annotation.NonNull;
-import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.face.Error;
-import android.hardware.biometrics.face.ISession;
-import android.hardware.common.NativeHandle;
-import android.hardware.keymaster.HardwareAuthToken;
-import android.os.Binder;
-import android.os.IBinder;
-import android.util.Slog;
-
-/**
- * Test session that provides mostly no-ops.
- */
-public class TestSession extends ISession.Stub {
- private static final String TAG = "FaceTestSession";
-
- @NonNull
- private final Sensor.HalSessionCallback mHalSessionCallback;
-
- TestSession(@NonNull Sensor.HalSessionCallback halSessionCallback) {
- mHalSessionCallback = halSessionCallback;
- }
-
- @Override
- public void generateChallenge(int cookie, int timeoutSec) {
- mHalSessionCallback.onChallengeGenerated(0 /* challenge */);
- }
-
- @Override
- public void revokeChallenge(int cookie, long challenge) {
- mHalSessionCallback.onChallengeRevoked(challenge);
- }
-
- @Override
- public ICancellationSignal enroll(int cookie, HardwareAuthToken hat, byte enrollmentType,
- byte[] features, NativeHandle previewSurface) {
- return null;
- }
-
- @Override
- public ICancellationSignal authenticate(int cookie, long operationId) {
- return new ICancellationSignal() {
- @Override
- public void cancel() {
- mHalSessionCallback.onError(Error.CANCELED, 0 /* vendorCode */);
- }
-
- @Override
- public IBinder asBinder() {
- return new Binder();
- }
- };
- }
-
- @Override
- public ICancellationSignal detectInteraction(int cookie) {
- return null;
- }
-
- @Override
- public void enumerateEnrollments(int cookie) {
-
- }
-
- @Override
- public void removeEnrollments(int cookie, int[] enrollmentIds) {
-
- }
-
- @Override
- public void getFeatures(int cookie, int enrollmentId) {
-
- }
-
- @Override
- public void setFeature(int cookie, HardwareAuthToken hat, int enrollmentId, byte feature,
- boolean enabled) {
-
- }
-
- @Override
- public void getAuthenticatorId(int cookie) {
- Slog.d(TAG, "getAuthenticatorId");
- // Immediately return a value so the framework can continue with subsequent requests.
- mHalSessionCallback.onAuthenticatorIdRetrieved(0);
- }
-
- @Override
- public void invalidateAuthenticatorId(int cookie) {
- Slog.d(TAG, "invalidateAuthenticatorId");
- // Immediately return a value so the framework can continue with subsequent requests.
- mHalSessionCallback.onAuthenticatorIdInvalidated(0);
- }
-
- @Override
- public void resetLockout(int cookie, HardwareAuthToken hat) {
- mHalSessionCallback.onLockoutCleared();
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
index 4142a52c9253..e8668ed1b6c5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
@@ -21,12 +21,17 @@ import static android.Manifest.permission.TEST_BIOMETRIC;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.face.Face;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceEnrollFrame;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
+import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.face.FaceUtils;
import java.util.ArrayList;
@@ -41,6 +46,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
@NonNull private final Context mContext;
private final int mSensorId;
+ @NonNull private final ITestSessionCallback mCallback;
@NonNull private final Face10 mFace10;
@NonNull private final Face10.HalResultController mHalResultController;
@NonNull private final Set<Integer> mEnrollmentIds;
@@ -106,12 +112,24 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
public void onChallengeInterruptFinished(int sensorId) {
}
+
+ @Override
+ public void onAuthenticationFrame(FaceAuthenticationFrame frame) {
+
+ }
+
+ @Override
+ public void onEnrollmentFrame(FaceEnrollFrame frame) {
+
+ }
};
- BiometricTestSessionImpl(@NonNull Context context, int sensorId, @NonNull Face10 face10,
+ BiometricTestSessionImpl(@NonNull Context context, int sensorId,
+ @NonNull ITestSessionCallback callback, @NonNull Face10 face10,
@NonNull Face10.HalResultController halResultController) {
mContext = context;
mSensorId = sensorId;
+ mCallback = callback;
mFace10 = face10;
mHalResultController = halResultController;
mEnrollmentIds = new HashSet<>();
@@ -189,6 +207,25 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
public void cleanupInternalState(int userId) {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
- mFace10.scheduleInternalCleanup(mSensorId, userId);
+ mFace10.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ try {
+ mCallback.onCleanupStarted(clientMonitor.getTargetUserId());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ try {
+ mCallback.onCleanupFinished(clientMonitor.getTargetUserId());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+ });
}
}
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 e46661a5e985..ee8823e041bc 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
@@ -29,6 +29,7 @@ import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
import android.hardware.face.Face;
@@ -123,7 +124,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
@Override
public void onUserSwitching(int newUserId) {
- scheduleInternalCleanup(newUserId);
+ scheduleInternalCleanup(newUserId, null /* callback */);
scheduleGetFeature(mSensorId, new Binder(), newUserId,
BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION,
null, mContext.getOpPackageName());
@@ -437,7 +438,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
Slog.d(TAG, "Face HAL ready, HAL ID: " + halId);
if (halId != 0) {
scheduleLoadAuthenticatorIds();
- scheduleInternalCleanup(ActivityManager.getCurrentUser());
+ scheduleInternalCleanup(ActivityManager.getCurrentUser(), null /* callback */);
scheduleGetFeature(mSensorId, new Binder(),
ActivityManager.getCurrentUser(),
BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION, null,
@@ -671,6 +672,20 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
});
}
+ @Override
+ public void scheduleRemoveAll(int sensorId, @NonNull IBinder token, int userId,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+ mHandler.post(() -> {
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ // For IBiometricsFace@1.0, remove(0) means remove all enrollments
+ final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token,
+ new ClientMonitorCallbackConverter(receiver), 0 /* faceId */, userId,
+ opPackageName,
+ FaceUtils.getLegacyInstance(mSensorId), mSensorId, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
@Override
public void scheduleResetLockout(int sensorId, int userId, @NonNull byte[] hardwareAuthToken) {
@@ -742,7 +757,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
});
}
- private void scheduleInternalCleanup(int userId) {
+ private void scheduleInternalCleanup(int userId,
+ @Nullable BaseClientMonitor.Callback callback) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -750,13 +766,14 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext,
mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, enrolledList,
FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client);
+ mScheduler.scheduleClientMonitor(client, callback);
});
}
@Override
- public void scheduleInternalCleanup(int sensorId, int userId) {
- scheduleInternalCleanup(userId);
+ public void scheduleInternalCleanup(int sensorId, int userId,
+ @Nullable BaseClientMonitor.Callback callback) {
+ scheduleInternalCleanup(userId, callback);
}
@Override
@@ -930,7 +947,9 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
@NonNull
@Override
- public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
- return new BiometricTestSessionImpl(mContext, mSensorId, this, mHalResultController);
+ public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
+ @NonNull String opPackageName) {
+ return new BiometricTestSessionImpl(mContext, mSensorId, callback, this,
+ mHalResultController);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 0b78dd01f855..a4b3ac57a4df 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -46,7 +46,7 @@ import java.util.ArrayList;
/**
* Face-specific authentication client supporting the {@link android.hardware.biometrics.face.V1_0}
- * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
+ * HIDL interface.
*/
class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
index 1a7544fc7f01..fc1200a4b42a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
@@ -41,7 +41,7 @@ import java.util.Arrays;
/**
* Face-specific enroll client supporting the {@link android.hardware.biometrics.face.V1_0}
- * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
+ * HIDL interface.
*/
public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
@@ -103,18 +103,9 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
disabledFeatures.add(disabledFeature);
}
- android.hardware.biometrics.face.V1_1.IBiometricsFace daemon11 =
- android.hardware.biometrics.face.V1_1.IBiometricsFace.castFrom(getFreshDaemon());
try {
- final int status;
- if (daemon11 != null) {
- status = daemon11.enroll_1_1(token, mTimeoutSec, disabledFeatures, mSurfaceHandle);
- } else if (mSurfaceHandle == null) {
- status = getFreshDaemon().enroll(token, mTimeoutSec, disabledFeatures);
- } else {
- Slog.e(TAG, "enroll(): surface is only supported in @1.1 HAL");
- status = BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS;
- }
+ final int status = getFreshDaemon().enroll(token, mTimeoutSec, disabledFeatures);
+
if (status != Status.OK) {
onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
mCallback.onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
index c3d54c2b7fbb..72c5ee5e78c4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
@@ -29,8 +29,7 @@ import com.android.server.biometrics.sensors.GenerateChallengeClient;
/**
* Face-specific generateChallenge client supporting the
- * {@link android.hardware.biometrics.face.V1_0} and {@link android.hardware.biometrics.face.V1_1}
- * HIDL interfaces.
+ * {@link android.hardware.biometrics.face.V1_0} HIDL interface.
*/
public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiometricsFace> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
index 722a3b843e12..b1083d410fec 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
@@ -33,7 +33,7 @@ import com.android.server.biometrics.sensors.HalClientMonitor;
/**
* Face-specific getFeature client supporting the {@link android.hardware.biometrics.face.V1_0}
- * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
+ * HIDL interface.
*/
public class FaceGetFeatureClient extends HalClientMonitor<IBiometricsFace> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java
index abfda499cf0f..1e3b92dcbf61 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java
@@ -33,8 +33,7 @@ import java.util.Map;
/**
* Face-specific internal cleanup client supporting the
- * {@link android.hardware.biometrics.face.V1_0} and {@link android.hardware.biometrics.face.V1_1}
- * HIDL interfaces.
+ * {@link android.hardware.biometrics.face.V1_0} HIDL interface.
*/
class FaceInternalCleanupClient extends InternalCleanupClient<Face, IBiometricsFace> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java
index 9a0974b472cb..f2a9afce1ff7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java
@@ -32,8 +32,7 @@ import java.util.List;
/**
* Face-specific internal enumerate client supporting the
- * {@link android.hardware.biometrics.face.V1_0} and {@link android.hardware.biometrics.face.V1_1}
- * HIDL interfaces.
+ * {@link android.hardware.biometrics.face.V1_0} HIDL interface.
*/
class FaceInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFace> {
private static final String TAG = "FaceInternalEnumerateClient";
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java
index acae89928460..3ae201134deb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java
@@ -33,17 +33,20 @@ import java.util.Map;
/**
* Face-specific removal client supporting the {@link android.hardware.biometrics.face.V1_0}
- * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
+ * HIDL interface.
*/
class FaceRemovalClient extends RemovalClient<Face, IBiometricsFace> {
private static final String TAG = "FaceRemovalClient";
+ private final int mBiometricId;
+
FaceRemovalClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<Face> utils,
int sensorId, @NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
+ super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
authenticatorIds, BiometricsProtoEnums.MODALITY_FACE);
+ mBiometricId = biometricId;
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
index 14a46481ddc6..9d977d60e705 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
@@ -30,7 +30,7 @@ import java.util.ArrayList;
/**
* Face-specific resetLockout client supporting the {@link android.hardware.biometrics.face.V1_0}
- * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
+ * HIDL interface.
*/
public class FaceResetLockoutClient extends HalClientMonitor<IBiometricsFace> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
index e5edfafcef61..28580dece284 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
@@ -27,7 +27,7 @@ import com.android.server.biometrics.sensors.RevokeChallengeClient;
/**
* Face-specific revokeChallenge client supporting the {@link android.hardware.biometrics.face.V1_0}
- * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
+ * HIDL interface.
*/
public class FaceRevokeChallengeClient extends RevokeChallengeClient<IBiometricsFace> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
index 6290e001f65b..cc3d8f0e28ba 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
@@ -33,7 +33,7 @@ import java.util.ArrayList;
/**
* Face-specific setFeature client supporting the {@link android.hardware.biometrics.face.V1_0}
- * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
+ * HIDL interface.
*/
public class FaceSetFeatureClient extends HalClientMonitor<IBiometricsFace> {
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 bab1114dc70d..4cdb68df70af 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
@@ -18,17 +18,19 @@ package com.android.server.biometrics.sensors.face.hidl;
import android.annotation.Nullable;
import android.hardware.biometrics.face.V1_0.FaceError;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
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.hardware.biometrics.face.V1_1.IBiometricsFace;
import android.os.NativeHandle;
import android.os.RemoteException;
+import android.util.Slog;
import java.util.ArrayList;
public class TestHal extends IBiometricsFace.Stub {
+ private static final String TAG = "face.hidl.TestHal";
@Nullable
private IBiometricsFaceClientCallback mCallback;
@@ -47,6 +49,7 @@ public class TestHal extends IBiometricsFace.Stub {
@Override
public OptionalUint64 generateChallenge(int challengeTimeoutSec) {
+ Slog.w(TAG, "generateChallenge");
final OptionalUint64 result = new OptionalUint64();
result.status = Status.OK;
result.value = 0;
@@ -55,6 +58,7 @@ public class TestHal extends IBiometricsFace.Stub {
@Override
public int enroll(ArrayList<Byte> hat, int timeoutSec, ArrayList<Integer> disabledFeatures) {
+ Slog.w(TAG, "enroll");
return 0;
}
@@ -94,17 +98,23 @@ public class TestHal extends IBiometricsFace.Stub {
}
@Override
- public int enumerate() {
+ public int enumerate() throws RemoteException {
+ Slog.w(TAG, "enumerate");
+ if (mCallback != null) {
+ mCallback.onEnumerate(0 /* deviceId */, new ArrayList<>(), 0 /* userId */);
+ }
return 0;
}
@Override
public int remove(int faceId) {
+ Slog.w(TAG, "remove");
return 0;
}
@Override
public int authenticate(long operationId) {
+ Slog.w(TAG, "authenticate");
return 0;
}
@@ -115,18 +125,8 @@ public class TestHal extends IBiometricsFace.Stub {
@Override
public int resetLockout(ArrayList<Byte> hat) {
+ Slog.w(TAG, "resetLockout");
return 0;
}
- @Override
- public int enrollRemotely(ArrayList<Byte> hat, int timeoutSec,
- ArrayList<Integer> disabledFeatures) {
- return 0;
- }
-
- @Override
- public int enroll_1_1(ArrayList<Byte> hat, int timeoutSec, ArrayList<Integer> disabledFeatures,
- NativeHandle nativeHandle) {
- 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 34a909908b5a..32e9409de4b2 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
@@ -21,6 +21,7 @@ import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintService;
import android.os.IBinder;
@@ -42,8 +43,9 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub
}
@Override
- public ITestSession createTestSession(@NonNull String opPackageName) throws RemoteException {
- return mFingerprintService.createTestSession(mSensorId, opPackageName);
+ public ITestSession createTestSession(@NonNull ITestSessionCallback callback,
+ @NonNull String opPackageName) throws RemoteException {
+ return mFingerprintService.createTestSession(mSensorId, callback, opPackageName);
}
@Override
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 b0e42cd137eb..396dd5f42d4d 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
@@ -43,6 +43,7 @@ import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.Fingerprint;
@@ -109,7 +110,8 @@ public class FingerprintService extends SystemService implements BiometricServic
*/
private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
@Override
- public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
+ public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
+ @NonNull String opPackageName) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -119,7 +121,7 @@ public class FingerprintService extends SystemService implements BiometricServic
return null;
}
- return provider.createTestSession(sensorId, opPackageName);
+ return provider.createTestSession(sensorId, callback, opPackageName);
}
@Override
@@ -499,7 +501,21 @@ public class FingerprintService extends SystemService implements BiometricServic
opPackageName);
}
- @Override
+ @Override // Binder call
+ public void removeAll(final IBinder token, final int userId,
+ 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;
+ }
+ provider.second.scheduleRemoveAll(provider.first, token, receiver, userId,
+ opPackageName);
+ }
+
+ @Override // Binder call
public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback,
final String opPackageName) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
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 f672ae56e020..dfec2e3e308f 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
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -28,6 +29,7 @@ import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.util.proto.ProtoOutputStream;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -98,7 +100,12 @@ public interface ServiceProvider {
@NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
@NonNull String opPackageName);
- void scheduleInternalCleanup(int sensorId, int userId);
+ void scheduleRemoveAll(int sensorId, @NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, int userId,
+ @NonNull String opPackageName);
+
+ void scheduleInternalCleanup(int sensorId, int userId,
+ @Nullable BaseClientMonitor.Callback callback);
boolean isHardwareDetected(int sensorId);
@@ -133,5 +140,6 @@ public interface ServiceProvider {
void dumpInternal(int sensorId, @NonNull PrintWriter pw);
@NonNull
- ITestSession createTestSession(int sensorId, @NonNull String opPackageName);
+ ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
+ @NonNull String opPackageName);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index ea9c709ec79f..20b32543f7a0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -21,14 +21,17 @@ import static android.Manifest.permission.TEST_BIOMETRIC;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
+import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import java.util.HashSet;
@@ -46,6 +49,7 @@ class BiometricTestSessionImpl extends ITestSession.Stub {
@NonNull private final Context mContext;
private final int mSensorId;
+ @NonNull private final ITestSessionCallback mCallback;
@NonNull private final FingerprintProvider mProvider;
@NonNull private final Sensor mSensor;
@NonNull private final Set<Integer> mEnrollmentIds;
@@ -110,9 +114,11 @@ class BiometricTestSessionImpl extends ITestSession.Stub {
};
BiometricTestSessionImpl(@NonNull Context context, int sensorId,
- @NonNull FingerprintProvider provider, @NonNull Sensor sensor) {
+ @NonNull ITestSessionCallback callback, @NonNull FingerprintProvider provider,
+ @NonNull Sensor sensor) {
mContext = context;
mSensorId = sensorId;
+ mCallback = callback;
mProvider = provider;
mSensor = sensor;
mEnrollmentIds = new HashSet<>();
@@ -192,6 +198,25 @@ class BiometricTestSessionImpl extends ITestSession.Stub {
public void cleanupInternalState(int userId) {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
- mProvider.scheduleInternalCleanup(mSensorId, userId);
+ mProvider.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ try {
+ mCallback.onCleanupStarted(clientMonitor.getTargetUserId());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ try {
+ mCallback.onCleanupFinished(clientMonitor.getTargetUserId());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+ });
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 9611192fa13e..bcd1b8bc9976 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -98,4 +98,9 @@ class FingerprintDetectClient extends AcquisitionClient<ISession> {
public int getProtoEnum() {
return BiometricsProto.CM_DETECT_INTERACTION;
}
+
+ @Override
+ public boolean interruptsPrecedingClients() {
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
index 2a0e984e5933..0de3f4f8cce2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
@@ -60,7 +60,7 @@ class FingerprintInternalCleanupClient extends InternalCleanupClient<Fingerprint
String owner, BiometricUtils<Fingerprint> utils, int sensorId,
Map<Integer, Long> authenticatorIds) {
return new FingerprintRemovalClient(context, lazyDaemon, token,
- null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
- sensorId, authenticatorIds);
+ null /* ClientMonitorCallbackConverter */, new int[] {biometricId}, userId, owner,
+ utils, sensorId, authenticatorIds);
}
}
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 ced46e140c0a..598cc8992c2d 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
@@ -25,6 +25,7 @@ import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.Fingerprint;
@@ -185,7 +186,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
for (int i = 0; i < mSensors.size(); i++) {
final int sensorId = mSensors.keyAt(i);
scheduleLoadAuthenticatorIds(sensorId);
- scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser());
+ scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser(),
+ null /* callback */);
}
return mDaemon;
@@ -490,6 +492,27 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
public void scheduleRemove(int sensorId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
@NonNull String opPackageName) {
+ scheduleRemoveSpecifiedIds(sensorId, token, new int[] {fingerId}, userId, receiver,
+ opPackageName);
+ }
+
+ @Override
+ public void scheduleRemoveAll(int sensorId, @NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, int userId,
+ @NonNull String opPackageName) {
+ final List<Fingerprint> fingers = FingerprintUtils.getInstance(sensorId)
+ .getBiometricsForUser(mContext, userId);
+ final int[] fingerIds = new int[fingers.size()];
+ for (int i = 0; i < fingers.size(); i++) {
+ fingerIds[i] = fingers.get(i).getBiometricId();
+ }
+
+ scheduleRemoveSpecifiedIds(sensorId, token, fingerIds, userId, receiver, opPackageName);
+ }
+
+ private void scheduleRemoveSpecifiedIds(int sensorId, @NonNull IBinder token,
+ int[] fingerprintIds, int userId, @NonNull IFingerprintServiceReceiver receiver,
+ @NonNull String opPackageName) {
mHandler.post(() -> {
final IFingerprint daemon = getHalInstance();
if (daemon == null) {
@@ -507,7 +530,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
- new ClientMonitorCallbackConverter(receiver), fingerId, userId,
+ new ClientMonitorCallbackConverter(receiver), fingerprintIds, userId,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
mSensors.get(sensorId).getAuthenticatorIds());
mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
@@ -518,7 +541,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
@Override
- public void scheduleInternalCleanup(int sensorId, int userId) {
+ public void scheduleInternalCleanup(int sensorId, int userId,
+ @Nullable BaseClientMonitor.Callback callback) {
mHandler.post(() -> {
final IFingerprint daemon = getHalInstance();
if (daemon == null) {
@@ -538,7 +562,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mContext.getOpPackageName(), sensorId, enrolledList,
FingerprintUtils.getInstance(sensorId),
mSensors.get(sensorId).getAuthenticatorIds());
- mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+ mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client, callback);
} catch (RemoteException e) {
Slog.e(getTag(), "Remote exception when scheduling internal cleanup", e);
}
@@ -683,8 +707,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@NonNull
@Override
- public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
- return mSensors.get(sensorId).createTestSession();
+ public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
+ @NonNull String opPackageName) {
+ return mSensors.get(sensorId).createTestSession(callback);
}
@Override
@@ -697,8 +722,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final Sensor sensor = mSensors.valueAt(i);
final int sensorId = mSensors.keyAt(i);
PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount();
- sensor.getScheduler().recordCrashState();
- sensor.getScheduler().reset();
+ sensor.onBinderDied();
}
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
index 4a99a7b29638..c622208262e0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
@@ -39,20 +39,22 @@ import java.util.Map;
class FingerprintRemovalClient extends RemovalClient<Fingerprint, ISession> {
private static final String TAG = "FingerprintRemovalClient";
+ private final int[] mBiometricIds;
+
FingerprintRemovalClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
- @Nullable ClientMonitorCallbackConverter listener, int biometricId, int userId,
+ @Nullable ClientMonitorCallbackConverter listener, int[] biometricIds, int userId,
@NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
+ super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ mBiometricIds = biometricIds;
}
@Override
protected void startHalOperation() {
try {
- final int[] ids = new int[] {mBiometricId};
- getFreshDaemon().removeEnrollments(mSequentialId, ids);
+ getFreshDaemon().removeEnrollments(mSequentialId, mBiometricIds);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting remove", e);
mCallback.onClientFinished(this, false /* success */);
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 f0e7e1cf5d25..a98e7db43f79 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
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.fingerprint.Error;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.ISession;
@@ -31,7 +32,6 @@ import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.Handler;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserManager;
import android.util.Slog;
@@ -65,7 +65,7 @@ import java.util.Map;
* {@link android.hardware.biometrics.fingerprint.IFingerprint} HAL.
*/
@SuppressWarnings("deprecation")
-class Sensor implements IBinder.DeathRecipient {
+class Sensor {
private boolean mTestHalEnabled;
@@ -416,13 +416,7 @@ class Sensor implements IBinder.DeathRecipient {
mScheduler = new BiometricScheduler(tag, gestureAvailabilityDispatcher);
mLockoutCache = new LockoutCache();
mAuthenticatorIds = new HashMap<>();
- mLazySession = () -> {
- if (mTestHalEnabled) {
- return new TestSession(mCurrentSession.mHalSessionCallback);
- } else {
- return mCurrentSession != null ? mCurrentSession.mSession : null;
- }
- };
+ mLazySession = () -> mCurrentSession != null ? mCurrentSession.mSession : null;
}
@NonNull HalClientMonitor.LazyDaemon<ISession> getLazySession() {
@@ -446,8 +440,9 @@ class Sensor implements IBinder.DeathRecipient {
}
}
- @NonNull ITestSession createTestSession() {
- return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, mProvider, this);
+ @NonNull ITestSession createTestSession(@NonNull ITestSessionCallback callback) {
+ return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, callback,
+ mProvider, this);
}
void createNewSession(@NonNull IFingerprint daemon, int sensorId, int userId)
@@ -461,7 +456,6 @@ class Sensor implements IBinder.DeathRecipient {
mTag, mScheduler, sensorId, userId, callback);
final ISession newSession = daemon.createSession(sensorId, userId, resultController);
- newSession.asBinder().linkToDeath(this, 0 /* flags */);
mCurrentSession = new Session(mTag, newSession, userId, resultController);
}
@@ -478,6 +472,11 @@ class Sensor implements IBinder.DeathRecipient {
}
void setTestHalEnabled(boolean enabled) {
+ Slog.w(mTag, "setTestHalEnabled: " + enabled);
+ if (enabled != mTestHalEnabled) {
+ // The framework should retrieve a new session from the HAL.
+ mCurrentSession = null;
+ }
mTestHalEnabled = enabled;
}
@@ -503,24 +502,21 @@ class Sensor implements IBinder.DeathRecipient {
proto.end(sensorToken);
}
- @Override
- public void binderDied() {
- Slog.e(mTag, "Binder died");
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (client instanceof Interruptable) {
- Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
- final Interruptable interruptable = (Interruptable) client;
- interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
-
- mScheduler.recordCrashState();
+ public void onBinderDied() {
+ final BaseClientMonitor client = mScheduler.getCurrentClient();
+ if (client instanceof Interruptable) {
+ Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT,
+ BiometricsProtoEnums.ISSUE_HAL_DEATH);
+ }
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
- BiometricsProtoEnums.MODALITY_FINGERPRINT,
- BiometricsProtoEnums.ISSUE_HAL_DEATH);
- mCurrentSession = null;
- }
- });
+ mScheduler.recordCrashState();
+ mScheduler.reset();
+ mCurrentSession = null;
}
}
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 a31bcdcaf4e6..9db2fcfd1b5b 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
@@ -17,95 +17,132 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.fingerprint.Error;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.ISessionCallback;
import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.biometrics.fingerprint.SessionState;
import android.hardware.keymaster.HardwareAuthToken;
-import android.os.Binder;
-import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
/**
* Test HAL that provides only provides no-ops.
*/
public class TestHal extends IFingerprint.Stub {
+ private static final String TAG = "fingerprint.aidl.TestHal";
+
@Override
public SensorProps[] getSensorProps() {
+ Slog.w(TAG, "getSensorProps");
return new SensorProps[0];
}
@Override
public ISession createSession(int sensorId, int userId, ISessionCallback cb) {
- return new ISession() {
- @Override
- public void generateChallenge(int cookie, int timeoutSec) {
+ Slog.w(TAG, "createSession, sensorId: " + sensorId + " userId: " + userId);
+ return new ISession.Stub() {
+ @Override
+ public void generateChallenge(int cookie, int timeoutSec) throws RemoteException {
+ Slog.w(TAG, "generateChallenge, cookie: " + cookie);
+ cb.onChallengeGenerated(0L);
}
@Override
- public void revokeChallenge(int cookie, long challenge) {
-
+ public void revokeChallenge(int cookie, long challenge) throws RemoteException {
+ Slog.w(TAG, "revokeChallenge: " + challenge + ", cookie: " + cookie);
+ cb.onChallengeRevoked(challenge);
}
@Override
public ICancellationSignal enroll(int cookie, HardwareAuthToken hat) {
- return null;
+ Slog.w(TAG, "enroll, cookie: " + cookie);
+ return new ICancellationSignal.Stub() {
+ @Override
+ public void cancel() throws RemoteException {
+ cb.onError(Error.CANCELED, 0 /* vendorCode */);
+ }
+ };
}
@Override
public ICancellationSignal authenticate(int cookie, long operationId) {
- return null;
+ Slog.w(TAG, "authenticate, cookie: " + cookie);
+ return new ICancellationSignal.Stub() {
+ @Override
+ public void cancel() throws RemoteException {
+ cb.onError(Error.CANCELED, 0 /* vendorCode */);
+ }
+ };
}
@Override
public ICancellationSignal detectInteraction(int cookie) {
- return null;
+ Slog.w(TAG, "detectInteraction, cookie: " + cookie);
+ return new ICancellationSignal.Stub() {
+ @Override
+ public void cancel() throws RemoteException {
+ cb.onError(Error.CANCELED, 0 /* vendorCode */);
+ }
+ };
}
@Override
- public void enumerateEnrollments(int cookie) {
-
+ public void enumerateEnrollments(int cookie) throws RemoteException {
+ Slog.w(TAG, "enumerateEnrollments, cookie: " + cookie);
+ cb.onEnrollmentsEnumerated(new int[0]);
}
@Override
- public void removeEnrollments(int cookie, int[] enrollmentIds) {
-
+ public void removeEnrollments(int cookie, int[] enrollmentIds) throws RemoteException {
+ Slog.w(TAG, "removeEnrollments, cookie: " + cookie);
+ cb.onEnrollmentsRemoved(enrollmentIds);
}
@Override
- public void getAuthenticatorId(int cookie) {
-
+ public void getAuthenticatorId(int cookie) throws RemoteException {
+ Slog.w(TAG, "getAuthenticatorId, cookie: " + cookie);
+ cb.onAuthenticatorIdRetrieved(0L);
}
@Override
- public void invalidateAuthenticatorId(int cookie) {
-
+ public void invalidateAuthenticatorId(int cookie) throws RemoteException {
+ Slog.w(TAG, "invalidateAuthenticatorId, cookie: " + cookie);
+ cb.onAuthenticatorIdInvalidated(0L);
}
@Override
- public void resetLockout(int cookie, HardwareAuthToken hat) {
+ public void resetLockout(int cookie, HardwareAuthToken hat) throws RemoteException {
+ Slog.w(TAG, "resetLockout, cookie: " + cookie);
+ cb.onLockoutCleared();
+ }
+ @Override
+ public void close(int cookie) throws RemoteException {
+ cb.onStateChanged(cookie, SessionState.CLOSED);
}
@Override
public void onPointerDown(int pointerId, int x, int y, float minor, float major) {
-
+ Slog.w(TAG, "onPointerDown");
}
@Override
public void onPointerUp(int pointerId) {
-
+ Slog.w(TAG, "onPointerUp");
}
@Override
public void onUiReady() {
-
- }
-
- @Override
- public IBinder asBinder() {
- return new Binder();
+ Slog.w(TAG, "onUiReady");
}
};
}
+
+ @Override
+ public void reset() {
+ }
}
+
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java
deleted file mode 100644
index ac4f6651613d..000000000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java
+++ /dev/null
@@ -1,119 +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.server.biometrics.sensors.fingerprint.aidl;
-
-import android.annotation.NonNull;
-import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.face.Error;
-import android.hardware.biometrics.fingerprint.ISession;
-import android.hardware.keymaster.HardwareAuthToken;
-import android.os.Binder;
-import android.os.IBinder;
-import android.util.Slog;
-
-/**
- * Test session that provides mostly no-ops.
- */
-class TestSession extends ISession.Stub {
-
- private static final String TAG = "FingerprintTestSession";
-
- @NonNull private final Sensor.HalSessionCallback mHalSessionCallback;
-
- TestSession(@NonNull Sensor.HalSessionCallback halSessionCallback) {
- mHalSessionCallback = halSessionCallback;
- }
-
- @Override
- public void generateChallenge(int cookie, int timeoutSec) {
- mHalSessionCallback.onChallengeGenerated(0 /* challenge */);
- }
-
- @Override
- public void revokeChallenge(int cookie, long challenge) {
- mHalSessionCallback.onChallengeRevoked(challenge);
- }
-
- @Override
- public ICancellationSignal enroll(int cookie, HardwareAuthToken hat) {
- return null;
- }
-
- @Override
- public ICancellationSignal authenticate(int cookie, long operationId) {
- return new ICancellationSignal() {
- @Override
- public void cancel() {
- mHalSessionCallback.onError(Error.CANCELED, 0 /* vendorCode */);
- }
-
- @Override
- public IBinder asBinder() {
- return new Binder();
- }
- };
- }
-
- @Override
- public ICancellationSignal detectInteraction(int cookie) {
- return null;
- }
-
- @Override
- public void enumerateEnrollments(int cookie) {
- Slog.d(TAG, "enumerate");
- }
-
- @Override
- public void removeEnrollments(int cookie, int[] enrollmentIds) {
- Slog.d(TAG, "remove");
- }
-
- @Override
- public void getAuthenticatorId(int cookie) {
- Slog.d(TAG, "getAuthenticatorId");
- // Immediately return a value so the framework can continue with subsequent requests.
- mHalSessionCallback.onAuthenticatorIdRetrieved(0);
- }
-
- @Override
- public void invalidateAuthenticatorId(int cookie) {
- Slog.d(TAG, "invalidateAuthenticatorId");
- // Immediately return a value so the framework can continue with subsequent requests.
- mHalSessionCallback.onAuthenticatorIdInvalidated(0);
- }
-
- @Override
- public void resetLockout(int cookie, HardwareAuthToken hat) {
- mHalSessionCallback.onLockoutCleared();
- }
-
- @Override
- public void onPointerDown(int pointerId, int x, int y, float minor, float major) {
-
- }
-
- @Override
- public void onPointerUp(int pointerId) {
-
- }
-
- @Override
- public void onUiReady() {
-
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index 312ee0a267ac..766a8829e968 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -21,13 +21,16 @@ import static android.Manifest.permission.TEST_BIOMETRIC;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
+import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import java.util.ArrayList;
@@ -47,6 +50,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
@NonNull private final Context mContext;
private final int mSensorId;
+ @NonNull private final ITestSessionCallback mCallback;
@NonNull private final Fingerprint21 mFingerprint21;
@NonNull private final Fingerprint21.HalResultController mHalResultController;
@NonNull private final Set<Integer> mEnrollmentIds;
@@ -111,10 +115,12 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
};
BiometricTestSessionImpl(@NonNull Context context, int sensorId,
+ @NonNull ITestSessionCallback callback,
@NonNull Fingerprint21 fingerprint21,
@NonNull Fingerprint21.HalResultController halResultController) {
mContext = context;
mSensorId = sensorId;
+ mCallback = callback;
mFingerprint21 = fingerprint21;
mHalResultController = halResultController;
mEnrollmentIds = new HashSet<>();
@@ -191,6 +197,25 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
public void cleanupInternalState(int userId) {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
- mFingerprint21.scheduleInternalCleanup(mSensorId, userId);
+ mFingerprint21.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ try {
+ mCallback.onCleanupStarted(clientMonitor.getTargetUserId());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ try {
+ mCallback.onCleanupFinished(clientMonitor.getTargetUserId());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+ });
}
}
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 7a74c6a39aa1..6e22a797b435 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
@@ -30,6 +30,7 @@ import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
import android.hardware.fingerprint.Fingerprint;
@@ -158,7 +159,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
@Override
public void onUserSwitching(int newUserId) {
- scheduleInternalCleanup(newUserId);
+ scheduleInternalCleanup(newUserId, null /* callback */);
}
};
@@ -437,7 +438,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
Slog.d(TAG, "Fingerprint HAL ready, HAL ID: " + halId);
if (halId != 0) {
scheduleLoadAuthenticatorIds();
- scheduleInternalCleanup(ActivityManager.getCurrentUser());
+ scheduleInternalCleanup(ActivityManager.getCurrentUser(), null /* callback */);
} else {
Slog.e(TAG, "Unable to set callback");
mDaemon = null;
@@ -463,26 +464,33 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
final int targetUserId = user.id;
if (!mAuthenticatorIds.containsKey(targetUserId)) {
- scheduleUpdateActiveUserWithoutHandler(targetUserId);
+ scheduleUpdateActiveUserWithoutHandler(targetUserId, true /* force */);
}
}
});
}
+ private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) {
+ scheduleUpdateActiveUserWithoutHandler(targetUserId, false /* force */);
+ }
+
/**
* Schedules the {@link FingerprintUpdateActiveUserClient} without posting the work onto the
* handler. Many/most APIs are user-specific. However, the HAL requires explicit "setActiveUser"
* invocation prior to authenticate/enroll/etc. Thus, internally we usually want to schedule
* this operation on the same lambda/runnable as those operations so that the ordering is
* correct.
+ *
+ * @param targetUserId Switch to this user, and update their authenticatorId
+ * @param force Always retrieve the authenticatorId, even if we are already the targetUserId
*/
- private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) {
+ private void scheduleUpdateActiveUserWithoutHandler(int targetUserId, boolean force) {
final boolean hasEnrolled =
!getEnrolledFingerprints(mSensorProperties.sensorId, targetUserId).isEmpty();
final FingerprintUpdateActiveUserClient client =
new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
mContext.getOpPackageName(), mSensorProperties.sensorId, mCurrentUserId,
- hasEnrolled, mAuthenticatorIds);
+ hasEnrolled, mAuthenticatorIds, force);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
@@ -563,7 +571,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
boolean success) {
if (success) {
// Update authenticatorIds
- scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId());
+ scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId(),
+ true /* force */);
}
}
});
@@ -636,7 +645,25 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
});
}
- private void scheduleInternalCleanup(int userId) {
+ @Override
+ public void scheduleRemoveAll(int sensorId, @NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, int userId,
+ @NonNull String opPackageName) {
+ mHandler.post(() -> {
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ // For IBiometricsFingerprint@2.1, remove(0) means remove all enrollments
+ final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver),
+ 0 /* fingerprintId */, userId, opPackageName,
+ FingerprintUtils.getLegacyInstance(mSensorId),
+ mSensorProperties.sensorId, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ private void scheduleInternalCleanup(int userId,
+ @Nullable BaseClientMonitor.Callback callback) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -646,13 +673,14 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
mSensorProperties.sensorId, enrolledList,
FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client);
+ mScheduler.scheduleClientMonitor(client, callback);
});
}
@Override
- public void scheduleInternalCleanup(int sensorId, int userId) {
- scheduleInternalCleanup(userId);
+ public void scheduleInternalCleanup(int sensorId, int userId,
+ @Nullable BaseClientMonitor.Callback callback) {
+ scheduleInternalCleanup(userId, callback);
}
@Override
@@ -832,8 +860,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
@NonNull
@Override
- public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
- return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, this,
+ public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
+ @NonNull String opPackageName) {
+ return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, callback, this,
mHalResultController);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 7989dca3e06f..8acb284667c7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -130,4 +130,9 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
public int getProtoEnum() {
return BiometricsProto.CM_DETECT_INTERACTION;
}
+
+ @Override
+ public boolean interruptsPrecedingClients() {
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
index f6a22f581f1a..2f360f31a325 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
@@ -39,13 +39,16 @@ import java.util.Map;
class FingerprintRemovalClient extends RemovalClient<Fingerprint, IBiometricsFingerprint> {
private static final String TAG = "FingerprintRemovalClient";
+ private final int mBiometricId;
+
FingerprintRemovalClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
@NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@NonNull Map<Integer, Long> authenticatorIds) {
- super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
+ super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId,
authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ mBiometricId = biometricId;
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
index db7f4bcbec9d..6776810c2e52 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
@@ -41,6 +41,7 @@ public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometr
private static final String FP_DATA_DIR = "fpdata";
private final int mCurrentUserId;
+ private final boolean mForceUpdateAuthenticatorId;
private final boolean mHasEnrolledBiometrics;
private final Map<Integer, Long> mAuthenticatorIds;
private File mDirectory;
@@ -48,11 +49,12 @@ public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometr
FingerprintUpdateActiveUserClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId,
@NonNull String owner, int sensorId, int currentUserId, boolean hasEnrolledBiometrics,
- @NonNull Map<Integer, Long> authenticatorIds) {
+ @NonNull Map<Integer, Long> authenticatorIds, boolean forceUpdateAuthenticatorId) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
mCurrentUserId = currentUserId;
+ mForceUpdateAuthenticatorId = forceUpdateAuthenticatorId;
mHasEnrolledBiometrics = hasEnrolledBiometrics;
mAuthenticatorIds = authenticatorIds;
}
@@ -61,7 +63,7 @@ public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometr
public void start(@NonNull Callback callback) {
super.start(callback);
- if (mCurrentUserId == getTargetUserId()) {
+ if (mCurrentUserId == getTargetUserId() && !mForceUpdateAuthenticatorId) {
Slog.d(TAG, "Already user: " + mCurrentUserId + ", returning");
callback.onClientFinished(this, true /* success */);
return;
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 b7aec0ed2731..14fdb507b0b1 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
@@ -21,11 +21,14 @@ import android.hardware.biometrics.fingerprint.V2_1.FingerprintError;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
import android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint;
import android.os.RemoteException;
+import android.util.Slog;
/**
* Test HAL that provides only provides no-ops.
*/
public class TestHal extends IBiometricsFingerprint.Stub {
+ private static final String TAG = "fingerprint.hidl.TestHal";
+
@Nullable
private IBiometricsFingerprintClientCallback mCallback;
@@ -57,6 +60,7 @@ public class TestHal extends IBiometricsFingerprint.Stub {
@Override
public int enroll(byte[] hat, int gid, int timeoutSec) {
+ Slog.w(TAG, "enroll");
return 0;
}
@@ -79,12 +83,18 @@ public class TestHal extends IBiometricsFingerprint.Stub {
}
@Override
- public int enumerate() {
+ public int enumerate() throws RemoteException {
+ Slog.w(TAG, "Enumerate");
+ if (mCallback != null) {
+ mCallback.onEnumerate(0 /* deviceId */, 0 /* fingerId */, 0 /* groupId */,
+ 0 /* remaining */);
+ }
return 0;
}
@Override
public int remove(int gid, int fid) {
+ Slog.w(TAG, "Remove");
return 0;
}
@@ -95,6 +105,7 @@ public class TestHal extends IBiometricsFingerprint.Stub {
@Override
public int authenticate(long operationId, int gid) {
+ Slog.w(TAG, "Authenticate");
return 0;
}
} \ No newline at end of file
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 8e84613b2d64..f44e0691bb9d 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
@@ -21,6 +21,7 @@ import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
+import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.iris.IIrisService;
import android.os.IBinder;
@@ -39,7 +40,8 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub {
}
@Override
- public ITestSession createTestSession(@NonNull String opPackageName) throws RemoteException {
+ public ITestSession createTestSession(@NonNull ITestSessionCallback callback,
+ @NonNull String opPackageName) throws RemoteException {
return null;
}
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index e3757dfc6a59..df83df9a73fb 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -16,15 +16,24 @@
package com.android.server.compat;
+import static android.app.compat.PackageOverride.VALUE_DISABLED;
+import static android.app.compat.PackageOverride.VALUE_ENABLED;
+import static android.app.compat.PackageOverride.VALUE_UNDEFINED;
+
import android.annotation.Nullable;
+import android.app.compat.PackageOverride;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import com.android.internal.compat.CompatibilityChangeInfo;
+import com.android.internal.compat.OverrideAllowedState;
import com.android.server.compat.config.Change;
import com.android.server.compat.overrides.ChangeOverrides;
import com.android.server.compat.overrides.OverrideValue;
+import com.android.server.compat.overrides.RawOverrideValue;
import java.util.HashMap;
import java.util.List;
@@ -36,7 +45,7 @@ import java.util.Map;
* <p>A compatibility change has a default setting, determined by the {@code enableAfterTargetSdk}
* and {@code disabled} constructor parameters. If a change is {@code disabled}, this overrides any
* target SDK criteria set. These settings can be overridden for a specific package using
- * {@link #addPackageOverride(String, boolean)}.
+ * {@link #addPackageOverrideInternal(String, boolean)}.
*
* <p>Note, this class is not thread safe so callers must ensure thread safety.
*/
@@ -63,8 +72,8 @@ public final class CompatChange extends CompatibilityChangeInfo {
ChangeListener mListener = null;
- private Map<String, Boolean> mPackageOverrides;
- private Map<String, Boolean> mDeferredOverrides;
+ private Map<String, Boolean> mEvaluatedOverrides;
+ private Map<String, PackageOverride> mRawOverrides;
public CompatChange(long changeId) {
this(changeId, null, -1, -1, false, false, null, false);
@@ -113,18 +122,26 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @param pname Package name to enable the change for.
* @param enabled Whether or not to enable the change.
*/
- void addPackageOverride(String pname, boolean enabled) {
+ private void addPackageOverrideInternal(String pname, boolean enabled) {
if (getLoggingOnly()) {
throw new IllegalArgumentException(
"Can't add overrides for a logging only change " + toString());
}
- if (mPackageOverrides == null) {
- mPackageOverrides = new HashMap<>();
+ if (mEvaluatedOverrides == null) {
+ mEvaluatedOverrides = new HashMap<>();
}
- mPackageOverrides.put(pname, enabled);
+ mEvaluatedOverrides.put(pname, enabled);
notifyListener(pname);
}
+ private void removePackageOverrideInternal(String pname) {
+ if (mEvaluatedOverrides != null) {
+ if (mEvaluatedOverrides.remove(pname) != null) {
+ notifyListener(pname);
+ }
+ }
+ }
+
/**
* Tentatively set the state of this change for a given package name.
* The override will only take effect after that package is installed, if applicable.
@@ -132,17 +149,19 @@ public final class CompatChange extends CompatibilityChangeInfo {
* <p>Note, this method is not thread safe so callers must ensure thread safety.
*
* @param packageName Package name to tentatively enable the change for.
- * @param enabled Whether or not to enable the change.
+ * @param override The package override to be set
*/
- void addPackageDeferredOverride(String packageName, boolean enabled) {
+ void addPackageOverride(String packageName, PackageOverride override,
+ OverrideAllowedState allowedState, Context context) {
if (getLoggingOnly()) {
throw new IllegalArgumentException(
"Can't add overrides for a logging only change " + toString());
}
- if (mDeferredOverrides == null) {
- mDeferredOverrides = new HashMap<>();
+ if (mRawOverrides == null) {
+ mRawOverrides = new HashMap<>();
}
- mDeferredOverrides.put(packageName, enabled);
+ mRawOverrides.put(packageName, override);
+ recheckOverride(packageName, allowedState, context);
}
/**
@@ -157,24 +176,44 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @return {@code true} if the recheck yielded a result that requires invalidating caches
* (a deferred override was consolidated or a regular override was removed).
*/
- boolean recheckOverride(String packageName, boolean allowed) {
- // A deferred override now is allowed by the policy, so promote it to a regular override.
- if (hasDeferredOverride(packageName) && allowed) {
- boolean overrideValue = mDeferredOverrides.remove(packageName);
- addPackageOverride(packageName, overrideValue);
- return true;
+ boolean recheckOverride(String packageName, OverrideAllowedState allowedState,
+ Context context) {
+ boolean allowed = (allowedState.state == OverrideAllowedState.ALLOWED);
+
+ Long version = null;
+ try {
+ ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(
+ packageName, 0);
+ version = applicationInfo.longVersionCode;
+ } catch (PackageManager.NameNotFoundException e) {
+ // Do nothing
}
- // A previously set override is no longer allowed by the policy, so make it deferred.
- if (hasOverride(packageName) && !allowed) {
- boolean overrideValue = mPackageOverrides.remove(packageName);
- addPackageDeferredOverride(packageName, overrideValue);
- // Notify because the override was removed.
- notifyListener(packageName);
- return true;
+
+ // If the app is not installed or no longer has raw overrides, evaluate to false
+ if (version == null || !hasRawOverride(packageName) || !allowed) {
+ removePackageOverrideInternal(packageName);
+ return false;
}
- return false;
+
+ // Evaluate the override based on its version
+ int overrideValue = mRawOverrides.get(packageName).evaluate(version);
+ switch (overrideValue) {
+ case VALUE_UNDEFINED:
+ removePackageOverrideInternal(packageName);
+ break;
+ case VALUE_ENABLED:
+ addPackageOverrideInternal(packageName, true);
+ break;
+ case VALUE_DISABLED:
+ addPackageOverrideInternal(packageName, false);
+ break;
+ }
+ return true;
}
+ boolean hasPackageOverride(String pname) {
+ return mRawOverrides != null && mRawOverrides.containsKey(pname);
+ }
/**
* Remove any package override for the given package name, restoring the default behaviour.
*
@@ -182,15 +221,13 @@ public final class CompatChange extends CompatibilityChangeInfo {
*
* @param pname Package name to reset to defaults for.
*/
- void removePackageOverride(String pname) {
- if (mPackageOverrides != null) {
- if (mPackageOverrides.remove(pname) != null) {
- notifyListener(pname);
- }
- }
- if (mDeferredOverrides != null) {
- mDeferredOverrides.remove(pname);
+ boolean removePackageOverride(String pname, OverrideAllowedState allowedState,
+ Context context) {
+ if (mRawOverrides != null && (mRawOverrides.remove(pname) != null)) {
+ recheckOverride(pname, allowedState, context);
+ return true;
}
+ return false;
}
/**
@@ -204,8 +241,8 @@ public final class CompatChange extends CompatibilityChangeInfo {
if (app == null) {
return defaultValue();
}
- if (mPackageOverrides != null && mPackageOverrides.containsKey(app.packageName)) {
- return mPackageOverrides.get(app.packageName);
+ if (mEvaluatedOverrides != null && mEvaluatedOverrides.containsKey(app.packageName)) {
+ return mEvaluatedOverrides.get(app.packageName);
}
if (getDisabled()) {
return false;
@@ -223,8 +260,16 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @return {@code true} if the change should be enabled for the package.
*/
boolean willBeEnabled(String packageName) {
- if (hasDeferredOverride(packageName)) {
- return mDeferredOverrides.get(packageName);
+ if (hasRawOverride(packageName)) {
+ int eval = mRawOverrides.get(packageName).evaluateForAllVersions();
+ switch (eval) {
+ case VALUE_ENABLED:
+ return true;
+ case VALUE_DISABLED:
+ return false;
+ case VALUE_UNDEFINED:
+ return defaultValue();
+ }
}
return defaultValue();
}
@@ -243,8 +288,8 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @param packageName name of the package
* @return true if there is such override
*/
- boolean hasOverride(String packageName) {
- return mPackageOverrides != null && mPackageOverrides.containsKey(packageName);
+ private boolean hasOverride(String packageName) {
+ return mEvaluatedOverrides != null && mEvaluatedOverrides.containsKey(packageName);
}
/**
@@ -252,65 +297,77 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @param packageName name of the package
* @return true if there is such a deferred override
*/
- boolean hasDeferredOverride(String packageName) {
- return mDeferredOverrides != null && mDeferredOverrides.containsKey(packageName);
- }
-
- /**
- * Checks whether a change has any package overrides.
- * @return true if the change has at least one deferred override
- */
- boolean hasAnyPackageOverride() {
- return mDeferredOverrides != null && !mDeferredOverrides.isEmpty();
- }
-
- /**
- * Checks whether a change has any deferred overrides.
- * @return true if the change has at least one deferred override
- */
- boolean hasAnyDeferredOverride() {
- return mPackageOverrides != null && !mPackageOverrides.isEmpty();
+ private boolean hasRawOverride(String packageName) {
+ return mRawOverrides != null && mRawOverrides.containsKey(packageName);
}
void loadOverrides(ChangeOverrides changeOverrides) {
- if (mDeferredOverrides == null) {
- mDeferredOverrides = new HashMap<>();
+ if (mRawOverrides == null) {
+ mRawOverrides = new HashMap<>();
}
- mDeferredOverrides.clear();
- for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) {
- mDeferredOverrides.put(override.getPackageName(), override.getEnabled());
+ mRawOverrides.clear();
+
+ if (mEvaluatedOverrides == null) {
+ mEvaluatedOverrides = new HashMap<>();
}
+ mEvaluatedOverrides.clear();
- if (mPackageOverrides == null) {
- mPackageOverrides = new HashMap<>();
+ // Load deferred overrides for backwards compatibility
+ if (changeOverrides.getDeferred() != null) {
+ for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) {
+ mRawOverrides.put(override.getPackageName(),
+ new PackageOverride.Builder().setEnabled(
+ override.getEnabled()).build());
+ }
+ }
+
+ // Load validated overrides. For backwards compatibility, we also add them to raw overrides.
+ if (changeOverrides.getValidated() != null) {
+ for (OverrideValue override : changeOverrides.getValidated().getOverrideValue()) {
+ mEvaluatedOverrides.put(override.getPackageName(), override.getEnabled());
+ mRawOverrides.put(override.getPackageName(),
+ new PackageOverride.Builder().setEnabled(
+ override.getEnabled()).build());
+ }
}
- mPackageOverrides.clear();
- for (OverrideValue override : changeOverrides.getValidated().getOverrideValue()) {
- mPackageOverrides.put(override.getPackageName(), override.getEnabled());
+
+ // Load raw overrides
+ if (changeOverrides.getRaw() != null) {
+ for (RawOverrideValue override : changeOverrides.getRaw().getRawOverrideValue()) {
+ PackageOverride packageOverride = new PackageOverride.Builder()
+ .setMinVersionCode(override.getMinVersionCode())
+ .setMaxVersionCode(override.getMaxVersionCode())
+ .setEnabled(override.getEnabled())
+ .build();
+ mRawOverrides.put(override.getPackageName(), packageOverride);
+ }
}
}
ChangeOverrides saveOverrides() {
- if (!hasAnyDeferredOverride() && !hasAnyPackageOverride()) {
+ if (mRawOverrides == null || mRawOverrides.isEmpty()) {
return null;
}
ChangeOverrides changeOverrides = new ChangeOverrides();
changeOverrides.setChangeId(getId());
- ChangeOverrides.Deferred deferredOverrides = new ChangeOverrides.Deferred();
- List<OverrideValue> deferredList = deferredOverrides.getOverrideValue();
- if (mDeferredOverrides != null) {
- for (Map.Entry<String, Boolean> entry : mDeferredOverrides.entrySet()) {
- OverrideValue override = new OverrideValue();
+ ChangeOverrides.Raw rawOverrides = new ChangeOverrides.Raw();
+ List<RawOverrideValue> rawList = rawOverrides.getRawOverrideValue();
+ if (mRawOverrides != null) {
+ for (Map.Entry<String, PackageOverride> entry : mRawOverrides.entrySet()) {
+ RawOverrideValue override = new RawOverrideValue();
override.setPackageName(entry.getKey());
- override.setEnabled(entry.getValue());
- deferredList.add(override);
+ override.setMinVersionCode(entry.getValue().getMinVersionCode());
+ override.setMaxVersionCode(entry.getValue().getMaxVersionCode());
+ override.setEnabled(entry.getValue().getEnabled());
+ rawList.add(override);
}
}
- changeOverrides.setDeferred(deferredOverrides);
+ changeOverrides.setRaw(rawOverrides);
+
ChangeOverrides.Validated validatedOverrides = new ChangeOverrides.Validated();
List<OverrideValue> validatedList = validatedOverrides.getOverrideValue();
- if (mPackageOverrides != null) {
- for (Map.Entry<String, Boolean> entry : mPackageOverrides.entrySet()) {
+ if (mEvaluatedOverrides != null) {
+ for (Map.Entry<String, Boolean> entry : mEvaluatedOverrides.entrySet()) {
OverrideValue override = new OverrideValue();
override.setPackageName(entry.getKey());
override.setEnabled(entry.getValue());
@@ -337,11 +394,11 @@ public final class CompatChange extends CompatibilityChangeInfo {
if (getLoggingOnly()) {
sb.append("; loggingOnly");
}
- if (mPackageOverrides != null && mPackageOverrides.size() > 0) {
- sb.append("; packageOverrides=").append(mPackageOverrides);
+ if (mEvaluatedOverrides != null && mEvaluatedOverrides.size() > 0) {
+ sb.append("; packageOverrides=").append(mEvaluatedOverrides);
}
- if (mDeferredOverrides != null && mDeferredOverrides.size() > 0) {
- sb.append("; deferredOverrides=").append(mDeferredOverrides);
+ if (mRawOverrides != null && mRawOverrides.size() > 0) {
+ sb.append("; rawOverrides=").append(mRawOverrides);
}
if (getOverridable()) {
sb.append("; overridable");
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 6b77b9d4ce39..422991e082a9 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -17,6 +17,7 @@
package com.android.server.compat;
import android.app.compat.ChangeIdStateCache;
+import android.app.compat.PackageOverride;
import android.compat.Compatibility.ChangeConfig;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -31,6 +32,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.compat.CompatibilityChangeInfo;
+import com.android.internal.compat.CompatibilityOverrideConfig;
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.OverrideAllowedState;
import com.android.server.compat.config.Change;
@@ -70,11 +72,13 @@ final class CompatConfig {
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
private final OverrideValidatorImpl mOverrideValidator;
+ private Context mContext;
private File mOverridesFile;
@VisibleForTesting
CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this);
+ mContext = context;
}
static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) {
@@ -210,17 +214,33 @@ final class CompatConfig {
* @throws IllegalStateException if overriding is not allowed
*/
boolean addOverride(long changeId, String packageName, boolean enabled) {
- boolean alreadyKnown = addOverrideUnsafe(changeId, packageName, enabled);
+ boolean alreadyKnown = addOverrideUnsafe(changeId, packageName,
+ new PackageOverride.Builder().setEnabled(enabled).build());
saveOverrides();
invalidateCache();
return alreadyKnown;
}
/**
- * Unsafe version of {@link #addOverride(long, String, boolean)}.
- * It does not invalidate the cache nor save the overrides.
+ * Overrides the enabled state for a given change and app.
+ *
+ * <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
+ *
+ * @param overrides list of overrides to default changes config.
+ * @param packageName app for which the overrides will be applied.
*/
- private boolean addOverrideUnsafe(long changeId, String packageName, boolean enabled) {
+ void addOverrides(CompatibilityOverrideConfig overrides, String packageName) {
+ synchronized (mChanges) {
+ for (Long changeId : overrides.overrides.keySet()) {
+ addOverrideUnsafe(changeId, packageName, overrides.overrides.get(changeId));
+ }
+ saveOverrides();
+ invalidateCache();
+ }
+ }
+
+ private boolean addOverrideUnsafe(long changeId, String packageName,
+ PackageOverride overrides) {
boolean alreadyKnown = true;
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
@@ -232,17 +252,8 @@ final class CompatConfig {
c = new CompatChange(changeId);
addChange(c);
}
- switch (allowedState.state) {
- case OverrideAllowedState.ALLOWED:
- c.addPackageOverride(packageName, enabled);
- break;
- case OverrideAllowedState.DEFERRED_VERIFICATION:
- c.addPackageDeferredOverride(packageName, enabled);
- break;
- default:
- throw new IllegalStateException("Should only be able to override changes that "
- + "are allowed or can be deferred.");
- }
+ c.addPackageOverride(packageName, overrides, allowedState, mContext);
+ invalidateCache();
}
return alreadyKnown;
}
@@ -311,47 +322,20 @@ final class CompatConfig {
* It does not invalidate the cache nor save the overrides.
*/
private boolean removeOverrideUnsafe(long changeId, String packageName) {
- boolean overrideExists = false;
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
if (c != null) {
- // Always allow removing a deferred override.
- if (c.hasDeferredOverride(packageName)) {
- c.removePackageOverride(packageName);
- overrideExists = true;
- } else if (c.hasOverride(packageName)) {
- // Regular overrides need to pass the policy.
- overrideExists = true;
- OverrideAllowedState allowedState =
- mOverrideValidator.getOverrideAllowedState(changeId, packageName);
+ OverrideAllowedState allowedState =
+ mOverrideValidator.getOverrideAllowedState(changeId, packageName);
+ if (c.hasPackageOverride(packageName)) {
allowedState.enforce(changeId, packageName);
- c.removePackageOverride(packageName);
+ c.removePackageOverride(packageName, allowedState, mContext);
+ invalidateCache();
+ return true;
}
}
}
- return overrideExists;
- }
-
- /**
- * Overrides the enabled state for a given change and app.
- *
- * <p>Note: package overrides are not persistent and will be lost on system or runtime restart.
- *
- * @param overrides list of overrides to default changes config
- * @param packageName app for which the overrides will be applied
- */
- void addOverrides(CompatibilityChangeConfig overrides, String packageName) {
- synchronized (mChanges) {
- for (Long changeId : overrides.enabledChanges()) {
- addOverrideUnsafe(changeId, packageName, true);
- }
- for (Long changeId : overrides.disabledChanges()) {
- addOverrideUnsafe(changeId, packageName, false);
-
- }
- saveOverrides();
- invalidateCache();
- }
+ return false;
}
/**
@@ -402,7 +386,8 @@ final class CompatConfig {
int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
for (long changeId : changes) {
- addOverrideUnsafe(changeId, packageName, true);
+ addOverrideUnsafe(changeId, packageName,
+ new PackageOverride.Builder().setEnabled(true).build());
}
saveOverrides();
invalidateCache();
@@ -418,7 +403,8 @@ final class CompatConfig {
int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
for (long changeId : changes) {
- addOverrideUnsafe(changeId, packageName, false);
+ addOverrideUnsafe(changeId, packageName,
+ new PackageOverride.Builder().setEnabled(false).build());
}
saveOverrides();
invalidateCache();
@@ -615,8 +601,7 @@ final class CompatConfig {
CompatChange c = mChanges.valueAt(idx);
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(c.getId(), packageName);
- boolean allowedOverride = (allowedState.state == OverrideAllowedState.ALLOWED);
- shouldInvalidateCache |= c.recheckOverride(packageName, allowedOverride);
+ shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, mContext);
}
if (shouldInvalidateCache) {
invalidateCache();
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 6b2a1c950e38..d17753fe81bd 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -25,6 +25,7 @@ import static android.os.Process.SYSTEM_UID;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.IActivityManager;
+import android.app.compat.PackageOverride;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -43,6 +44,7 @@ import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.ChangeReporter;
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.compat.CompatibilityChangeInfo;
+import com.android.internal.compat.CompatibilityOverrideConfig;
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.util.DumpUtils;
@@ -51,6 +53,8 @@ import com.android.server.LocalServices;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
/**
* System server internal API for gating and reporting compatibility changes.
@@ -161,6 +165,22 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@Override
public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
checkCompatChangeOverridePermission();
+ Map<Long, PackageOverride> overridesMap = new HashMap<>();
+ for (long change : overrides.enabledChanges()) {
+ overridesMap.put(change, new PackageOverride.Builder().setEnabled(true).build());
+ }
+ for (long change : overrides.disabledChanges()) {
+ overridesMap.put(change, new PackageOverride.Builder().setEnabled(false)
+ .build());
+ }
+ mCompatConfig.addOverrides(new CompatibilityOverrideConfig(overridesMap), packageName);
+ killPackage(packageName);
+ }
+
+ @Override
+ public void setOverridesFromInstaller(CompatibilityOverrideConfig overrides,
+ String packageName) {
+ checkCompatChangeOverridePermission();
mCompatConfig.addOverrides(overrides, packageName);
killPackage(packageName);
}
@@ -168,7 +188,15 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@Override
public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) {
checkCompatChangeOverridePermission();
- mCompatConfig.addOverrides(overrides, packageName);
+ Map<Long, PackageOverride> overridesMap = new HashMap<>();
+ for (long change : overrides.enabledChanges()) {
+ overridesMap.put(change, new PackageOverride.Builder().setEnabled(true).build());
+ }
+ for (long change : overrides.disabledChanges()) {
+ overridesMap.put(change, new PackageOverride.Builder().setEnabled(false)
+ .build());
+ }
+ mCompatConfig.addOverrides(new CompatibilityOverrideConfig(overridesMap), packageName);
}
@Override
@@ -211,9 +239,9 @@ public class PlatformCompat extends IPlatformCompat.Stub {
}
@Override
- public void clearOverrideForTest(long changeId, String packageName) {
+ public boolean clearOverrideForTest(long changeId, String packageName) {
checkCompatChangeOverridePermission();
- mCompatConfig.removeOverride(changeId, packageName);
+ return mCompatConfig.removeOverride(changeId, packageName);
}
@Override
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index 397af7ba2991..61b11a5851a9 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -16,7 +16,6 @@
package com.android.server.connectivity;
-import static android.net.NetworkCapabilities.MAX_TRANSPORT;
import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -25,15 +24,14 @@ import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
-import android.net.ConnectivityManager;
import android.net.ConnectivityMetricsEvent;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfStats;
+import android.net.metrics.ConnectStats;
import android.net.metrics.DefaultNetworkEvent;
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpErrorEvent;
import android.net.metrics.DnsEvent;
-import android.net.metrics.ConnectStats;
import android.net.metrics.IpManagerEvent;
import android.net.metrics.IpReachabilityEvent;
import android.net.metrics.NetworkEvent;
@@ -41,12 +39,13 @@ import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
import android.net.metrics.WakeupStats;
import android.os.Parcelable;
-import android.util.SparseArray;
import android.util.SparseIntArray;
+
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.Pair;
+
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -361,29 +360,22 @@ final public class IpConnectivityEventBuilder {
return IpConnectivityLogClass.UNKNOWN;
case 1:
int t = Long.numberOfTrailingZeros(transports);
- return transportToLinkLayer(t);
+ return TRANSPORT_LINKLAYER_MAP.get(t, IpConnectivityLogClass.UNKNOWN);
default:
return IpConnectivityLogClass.MULTIPLE;
}
}
- private static int transportToLinkLayer(int transport) {
- if (0 <= transport && transport < TRANSPORT_LINKLAYER_MAP.length) {
- return TRANSPORT_LINKLAYER_MAP[transport];
- }
- return IpConnectivityLogClass.UNKNOWN;
- }
-
- private static final int[] TRANSPORT_LINKLAYER_MAP = new int[MAX_TRANSPORT + 1];
+ private static final SparseIntArray TRANSPORT_LINKLAYER_MAP = new SparseIntArray();
static {
- TRANSPORT_LINKLAYER_MAP[TRANSPORT_CELLULAR] = IpConnectivityLogClass.CELLULAR;
- TRANSPORT_LINKLAYER_MAP[TRANSPORT_WIFI] = IpConnectivityLogClass.WIFI;
- TRANSPORT_LINKLAYER_MAP[TRANSPORT_BLUETOOTH] = IpConnectivityLogClass.BLUETOOTH;
- TRANSPORT_LINKLAYER_MAP[TRANSPORT_ETHERNET] = IpConnectivityLogClass.ETHERNET;
- TRANSPORT_LINKLAYER_MAP[TRANSPORT_VPN] = IpConnectivityLogClass.UNKNOWN;
- TRANSPORT_LINKLAYER_MAP[TRANSPORT_WIFI_AWARE] = IpConnectivityLogClass.WIFI_NAN;
- TRANSPORT_LINKLAYER_MAP[TRANSPORT_LOWPAN] = IpConnectivityLogClass.LOWPAN;
- };
+ TRANSPORT_LINKLAYER_MAP.append(TRANSPORT_CELLULAR, IpConnectivityLogClass.CELLULAR);
+ TRANSPORT_LINKLAYER_MAP.append(TRANSPORT_WIFI, IpConnectivityLogClass.WIFI);
+ TRANSPORT_LINKLAYER_MAP.append(TRANSPORT_BLUETOOTH, IpConnectivityLogClass.BLUETOOTH);
+ TRANSPORT_LINKLAYER_MAP.append(TRANSPORT_ETHERNET, IpConnectivityLogClass.ETHERNET);
+ TRANSPORT_LINKLAYER_MAP.append(TRANSPORT_VPN, IpConnectivityLogClass.UNKNOWN);
+ TRANSPORT_LINKLAYER_MAP.append(TRANSPORT_WIFI_AWARE, IpConnectivityLogClass.WIFI_NAN);
+ TRANSPORT_LINKLAYER_MAP.append(TRANSPORT_LOWPAN, IpConnectivityLogClass.LOWPAN);
+ }
private static int ifnameToLinkLayer(String ifname) {
// Do not try to catch all interface names with regexes, instead only catch patterns that
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 6f953cc263c3..dacdc0013464 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -124,6 +124,13 @@ import java.util.TreeSet;
//
// When ConnectivityService disconnects a network:
// -----------------------------------------------
+// If a network is just connected, ConnectivityService will think it will be used soon, but might
+// not be used. Thus, a 5s timer will be held to prevent the network being torn down immediately.
+// This "nascent" state is implemented by the "lingering" logic below without relating to any
+// request, and is used in some cases where network requests race with network establishment. The
+// nascent state ends when the 5-second timer fires, or as soon as the network satisfies a
+// request, whichever is earlier. In this state, the network is considered in the background.
+//
// If a network has no chance of satisfying any requests (even if it were to become validated
// and enter state #5), ConnectivityService will disconnect the NetworkAgent's AsyncChannel.
//
@@ -273,7 +280,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
// All inactivity timers for this network, sorted by expiry time. A timer is added whenever
// a request is moved to a network with a better score, regardless of whether the network is or
- // was lingering or not.
+ // was lingering or not. An inactivity timer is also added when a network connects
+ // without immediately satisfying any requests.
// TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g.,
// SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire.
private final SortedSet<InactivityTimer> mInactivityTimers = new TreeSet<>();
@@ -711,8 +719,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
mNumBackgroundNetworkRequests += delta;
break;
- case TRACK_DEFAULT:
case LISTEN:
+ case TRACK_DEFAULT:
+ case TRACK_SYSTEM_DEFAULT:
break;
case NONE:
@@ -892,13 +901,18 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
? networkAgentConfig.subscriberId : null;
return new NetworkState(new NetworkInfo(networkInfo),
new LinkProperties(linkProperties),
- new NetworkCapabilities(networkCapabilities), network, subscriberId, null);
+ new NetworkCapabilities(networkCapabilities), network, subscriberId);
}
}
/**
* Sets the specified requestId to linger on this network for the specified time. Called by
- * ConnectivityService when the request is moved to another network with a higher score.
+ * ConnectivityService when the request is moved to another network with a higher score, or
+ * when a network is newly created.
+ *
+ * @param requestId The requestId of the request that no longer need to be served by this
+ * network. Or {@link NetworkRequest.REQUEST_ID_NONE} if this is the
+ * {@code LingerTimer} for a newly created network.
*/
public void lingerRequest(int requestId, long now, long duration) {
if (mInactivityTimerForRequest.get(requestId) != null) {
@@ -980,10 +994,23 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
mInactive = false;
}
- public boolean isLingering() {
+ public boolean isInactive() {
return mInactive;
}
+ public boolean isLingering() {
+ return mInactive && !isNascent();
+ }
+
+ /**
+ * Return whether the network is just connected and about to be torn down because of not
+ * satisfying any request.
+ */
+ public boolean isNascent() {
+ return mInactive && mInactivityTimers.size() == 1
+ && mInactivityTimers.first().requestId == NetworkRequest.REQUEST_ID_NONE;
+ }
+
public void clearInactivityState() {
if (mInactivityMessage != null) {
mInactivityMessage.cancel();
@@ -1033,7 +1060,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
+ "network{" + network + "} handle{" + network.getNetworkHandle() + "} ni{"
+ networkInfo.toShortString() + "} "
+ " Score{" + getCurrentScore() + "} "
- + (isLingering() ? " lingering" : "")
+ + (isNascent() ? " nascent" : (isLingering() ? " lingering" : ""))
+ (everValidated ? " everValidated" : "")
+ (lastValidated ? " lastValidated" : "")
+ (partialConnectivity ? " partialConnectivity" : "")
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index d5edf2c728d3..f121fed6d7a1 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -80,7 +80,6 @@ public class NetworkNotificationManager {
// server.
public static final String NOTIFICATION_CHANNEL_NETWORK_STATUS = "NETWORK_STATUS";
public static final String NOTIFICATION_CHANNEL_NETWORK_ALERTS = "NETWORK_ALERTS";
- public static final String NOTIFICATION_CHANNEL_VPN = "VPN";
// The context is for the current user (system server)
private final Context mContext;
@@ -162,13 +161,20 @@ public class NetworkNotificationManager {
if (nai != null) {
transportType = approximateTransportType(nai);
final String extraInfo = nai.networkInfo.getExtraInfo();
- name = TextUtils.isEmpty(extraInfo) ? nai.networkCapabilities.getSsid() : extraInfo;
+ if (nai.linkProperties != null && nai.linkProperties.getCaptivePortalData() != null
+ && !TextUtils.isEmpty(nai.linkProperties.getCaptivePortalData()
+ .getVenueFriendlyName())) {
+ name = nai.linkProperties.getCaptivePortalData().getVenueFriendlyName();
+ } else {
+ name = TextUtils.isEmpty(extraInfo)
+ ? WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()) : extraInfo;
+ }
// Only notify for Internet-capable networks.
if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) return;
} else {
// Legacy notifications.
transportType = TRANSPORT_CELLULAR;
- name = null;
+ name = "";
}
// Clear any previous notification with lower priority, otherwise return. http://b/63676954.
@@ -194,35 +200,30 @@ public class NetworkNotificationManager {
final CharSequence details;
int icon = getIcon(transportType);
if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
- title = r.getString(R.string.wifi_no_internet,
- WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
+ title = r.getString(R.string.wifi_no_internet, name);
details = r.getString(R.string.wifi_no_internet_detailed);
} else if (notifyType == NotificationType.PRIVATE_DNS_BROKEN) {
if (transportType == TRANSPORT_CELLULAR) {
title = r.getString(R.string.mobile_no_internet);
} else if (transportType == TRANSPORT_WIFI) {
- title = r.getString(R.string.wifi_no_internet,
- WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
+ title = r.getString(R.string.wifi_no_internet, name);
} else {
title = r.getString(R.string.other_networks_no_internet);
}
details = r.getString(R.string.private_dns_broken_detailed);
} else if (notifyType == NotificationType.PARTIAL_CONNECTIVITY
&& transportType == TRANSPORT_WIFI) {
- title = r.getString(R.string.network_partial_connectivity,
- WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
+ title = r.getString(R.string.network_partial_connectivity, name);
details = r.getString(R.string.network_partial_connectivity_detailed);
} else if (notifyType == NotificationType.LOST_INTERNET &&
transportType == TRANSPORT_WIFI) {
- title = r.getString(R.string.wifi_no_internet,
- WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
+ title = r.getString(R.string.wifi_no_internet, name);
details = r.getString(R.string.wifi_no_internet_detailed);
} else if (notifyType == NotificationType.SIGN_IN) {
switch (transportType) {
case TRANSPORT_WIFI:
title = r.getString(R.string.wifi_available_sign_in, 0);
- details = r.getString(R.string.network_available_sign_in_detailed,
- WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
+ details = r.getString(R.string.network_available_sign_in_detailed, name);
break;
case TRANSPORT_CELLULAR:
title = r.getString(R.string.network_available_sign_in, 0);
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index d507b5f82bd0..8bf188696c27 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -83,9 +83,8 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
private final INetd mNetd;
private final Dependencies mDeps;
- // Values are User IDs.
@GuardedBy("this")
- private final Set<Integer> mUsers = new HashSet<>();
+ private final Set<UserHandle> mUsers = new HashSet<>();
// Keys are app uids. Values are true for SYSTEM permission and false for NETWORK permission.
@GuardedBy("this")
@@ -173,10 +172,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
netdPermsUids.put(uid, netdPermsUids.get(uid) | otherNetdPerms);
}
- final List<UserHandle> users = mUserManager.getUserHandles(true /* excludeDying */);
- for (UserHandle user : users) {
- mUsers.add(user.getIdentifier());
- }
+ mUsers.addAll(mUserManager.getUserHandles(true /* excludeDying */));
final SparseArray<ArraySet<String>> systemPermission =
SystemConfig.getInstance().getSystemPermissions();
@@ -259,12 +255,14 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
return array;
}
- private void update(Set<Integer> users, Map<Integer, Boolean> apps, boolean add) {
+ private void update(Set<UserHandle> users, Map<Integer, Boolean> apps, boolean add) {
List<Integer> network = new ArrayList<>();
List<Integer> system = new ArrayList<>();
for (Entry<Integer, Boolean> app : apps.entrySet()) {
List<Integer> list = app.getValue() ? system : network;
- for (int user : users) {
+ for (UserHandle user : users) {
+ if (user == null) continue;
+
list.add(UserHandle.getUid(user, app.getKey()));
}
}
@@ -288,14 +286,10 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
*
* @hide
*/
- public synchronized void onUserAdded(int user) {
- if (user < 0) {
- loge("Invalid user in onUserAdded: " + user);
- return;
- }
+ public synchronized void onUserAdded(@NonNull UserHandle user) {
mUsers.add(user);
- Set<Integer> users = new HashSet<>();
+ Set<UserHandle> users = new HashSet<>();
users.add(user);
update(users, mApps, true);
}
@@ -307,14 +301,10 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
*
* @hide
*/
- public synchronized void onUserRemoved(int user) {
- if (user < 0) {
- loge("Invalid user in onUserRemoved: " + user);
- return;
- }
+ public synchronized void onUserRemoved(@NonNull UserHandle user) {
mUsers.remove(user);
- Set<Integer> users = new HashSet<>();
+ Set<UserHandle> users = new HashSet<>();
users.add(user);
update(users, mApps, false);
}
@@ -550,7 +540,10 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse
for (UidRange range : ranges) {
for (int userId = range.getStartUser(); userId <= range.getEndUser(); userId++) {
for (int appId : appIds) {
- final int uid = UserHandle.getUid(userId, appId);
+ final UserHandle handle = UserHandle.of(userId);
+ if (handle == null) continue;
+
+ final int uid = UserHandle.getUid(handle, appId);
if (range.contains(uid)) {
result.add(uid);
}
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
index 87b4c162a2cc..7ef315c469ae 100644
--- a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
+++ b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
@@ -27,7 +27,7 @@ import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.telephony.data.EpsBearerQosSessionAttributes;
-import android.util.Slog;
+import android.util.Log;
import com.android.internal.util.CollectionUtils;
import com.android.server.ConnectivityService;
@@ -260,18 +260,18 @@ public class QosCallbackTracker {
}
private static void log(final String msg) {
- Slog.d(TAG, msg);
+ Log.d(TAG, msg);
}
private static void logw(final String msg) {
- Slog.w(TAG, msg);
+ Log.w(TAG, msg);
}
private static void loge(final String msg) {
- Slog.e(TAG, msg);
+ Log.e(TAG, msg);
}
private static void logwtf(final String msg) {
- Slog.wtf(TAG, msg);
+ Log.wtf(TAG, msg);
}
}
diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
index b5f20d70db7f..c480594b8c60 100644
--- a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
+++ b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
@@ -41,7 +41,6 @@ import android.os.Handler;
import android.os.MessageQueue;
import android.os.Messenger;
import android.system.ErrnoException;
-import android.system.Int32Ref;
import android.system.Os;
import android.util.Log;
import android.util.SparseArray;
@@ -306,9 +305,8 @@ public class TcpKeepaliveController {
private static boolean isReceiveQueueEmpty(FileDescriptor fd)
throws ErrnoException {
- Int32Ref result = new Int32Ref(-1);
- Os.ioctlInt(fd, SIOCINQ, result);
- if (result.value != 0) {
+ final int result = Os.ioctlInt(fd, SIOCINQ);
+ if (result != 0) {
Log.e(TAG, "Read queue has data");
return false;
}
@@ -317,9 +315,8 @@ public class TcpKeepaliveController {
private static boolean isSendQueueEmpty(FileDescriptor fd)
throws ErrnoException {
- Int32Ref result = new Int32Ref(-1);
- Os.ioctlInt(fd, SIOCOUTQ, result);
- if (result.value != 0) {
+ final int result = Os.ioctlInt(fd, SIOCOUTQ);
+ if (result != 0) {
Log.e(TAG, "Write queue has data");
return false;
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index fc2c7e01efde..a769e88f77d7 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -21,10 +21,10 @@ import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
+import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.server.connectivity.NetworkNotificationManager.NOTIFICATION_CHANNEL_VPN;
import android.Manifest;
import android.annotation.NonNull;
@@ -74,6 +74,7 @@ import android.net.UidRangeParcel;
import android.net.UnderlyingNetworkInfo;
import android.net.VpnManager;
import android.net.VpnService;
+import android.net.VpnTransportInfo;
import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.ChildSessionConfiguration;
import android.net.ipsec.ike.ChildSessionParams;
@@ -171,6 +172,12 @@ public class Vpn {
*/
@VisibleForTesting static final int MAX_VPN_PROFILE_SIZE_BYTES = 1 << 17; // 128kB
+ /**
+ * Network score that VPNs will announce to ConnectivityService.
+ * TODO: remove when the network scoring refactor lands.
+ */
+ private static final int VPN_DEFAULT_SCORE = 101;
+
// TODO: create separate trackers for each unique VPN to support
// automated reconnection
@@ -435,6 +442,7 @@ public class Vpn {
mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE));
loadAlwaysOnPackage(keyStore);
}
@@ -494,6 +502,11 @@ public class Vpn {
updateAlwaysOnNotification(detailedState);
}
+ private void resetNetworkCapabilities() {
+ mNetworkCapabilities.setUids(null);
+ mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE));
+ }
+
/**
* Chooses whether to force all connections to go though VPN.
*
@@ -518,6 +531,11 @@ public class Vpn {
}
}
+ /** Returns the package name that is currently prepared. */
+ public String getPackage() {
+ return mPackage;
+ }
+
/**
* Check whether to prevent all traffic outside of a VPN even when the VPN is not connected.
*
@@ -928,7 +946,7 @@ public class Vpn {
agentDisconnect();
jniReset(mInterface);
mInterface = null;
- mNetworkCapabilities.setUids(null);
+ resetNetworkCapabilities();
}
// Revoke the connection or stop the VpnRunner.
@@ -999,6 +1017,8 @@ public class Vpn {
case VpnManager.TYPE_VPN_SERVICE:
toChange = new String[] {AppOpsManager.OPSTR_ACTIVATE_VPN};
break;
+ case VpnManager.TYPE_VPN_LEGACY:
+ return false;
default:
Log.wtf(TAG, "Unrecognized VPN type while granting authorization");
return false;
@@ -1029,6 +1049,8 @@ public class Vpn {
return isVpnServicePreConsented(context, packageName);
case VpnManager.TYPE_VPN_PLATFORM:
return isVpnProfilePreConsented(context, packageName);
+ case VpnManager.TYPE_VPN_LEGACY:
+ return VpnConfig.LEGACY_VPN.equals(packageName);
default:
return false;
}
@@ -1211,6 +1233,8 @@ public class Vpn {
mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserId,
mConfig.allowedApplications, mConfig.disallowedApplications));
+ mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType()));
+
// Only apps targeting Q and above can explicitly declare themselves as metered.
// These VPNs are assumed metered unless they state otherwise.
if (mIsPackageTargetingAtLeastQ && mConfig.isMetered) {
@@ -1220,8 +1244,7 @@ public class Vpn {
}
mNetworkAgent = new NetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */,
- mNetworkCapabilities, lp,
- ConnectivityConstants.VPN_DEFAULT_SCORE, networkAgentConfig, mNetworkProvider) {
+ mNetworkCapabilities, lp, VPN_DEFAULT_SCORE, networkAgentConfig, mNetworkProvider) {
@Override
public void unwanted() {
// We are user controlled, not driven by NetworkRequest.
@@ -1735,7 +1758,7 @@ public class Vpn {
private void cleanupVpnStateLocked() {
mStatusIntent = null;
- mNetworkCapabilities.setUids(null);
+ resetNetworkCapabilities();
mConfig = null;
mInterface = null;
@@ -1846,22 +1869,18 @@ public class Vpn {
}
/**
- * Gets the currently running App-based VPN type
+ * Gets the currently running VPN type
*
- * @return the {@link VpnManager.VpnType}. {@link VpnManager.TYPE_VPN_NONE} if not running an
- * app-based VPN. While VpnService-based VPNs are always app VPNs and LegacyVpn is always
+ * @return the {@link VpnManager.VpnType}. {@link VpnManager.TYPE_VPN_NONE} if not running a
+ * VPN. While VpnService-based VPNs are always app VPNs and LegacyVpn is always
* Settings-based, the Platform VPNs can be initiated by both apps and Settings.
*/
- public synchronized int getActiveAppVpnType() {
- if (VpnConfig.LEGACY_VPN.equals(mPackage)) {
- return VpnManager.TYPE_VPN_NONE;
- }
-
- if (mVpnRunner != null && mVpnRunner instanceof IkeV2VpnRunner) {
- return VpnManager.TYPE_VPN_PLATFORM;
- } else {
- return VpnManager.TYPE_VPN_SERVICE;
- }
+ public synchronized int getActiveVpnType() {
+ if (!mNetworkInfo.isConnectedOrConnecting()) return VpnManager.TYPE_VPN_NONE;
+ if (mVpnRunner == null) return VpnManager.TYPE_VPN_SERVICE;
+ return mVpnRunner instanceof IkeV2VpnRunner
+ ? VpnManager.TYPE_VPN_PLATFORM
+ : VpnManager.TYPE_VPN_LEGACY;
}
private void updateAlwaysOnNotification(DetailedState networkState) {
diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java
index e496d77deaf5..e693bcc93f8f 100644
--- a/services/core/java/com/android/server/devicestate/DeviceState.java
+++ b/services/core/java/com/android/server/devicestate/DeviceState.java
@@ -16,9 +16,14 @@
package com.android.server.devicestate;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+
import android.annotation.IntRange;
import android.annotation.NonNull;
+import com.android.internal.util.Preconditions;
+
import java.util.Objects;
/**
@@ -35,24 +40,25 @@ import java.util.Objects;
*/
public final class DeviceState {
/** Unique identifier for the device state. */
- @IntRange(from = 0)
+ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE)
private final int mIdentifier;
/** String description of the device state. */
@NonNull
private final String mName;
- public DeviceState(@IntRange(from = 0) int identifier,
+ public DeviceState(
+ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
@NonNull String name) {
- if (identifier < 0) {
- throw new IllegalArgumentException("Identifier must be greater than or equal to zero.");
- }
+ Preconditions.checkArgumentInRange(identifier, MINIMUM_DEVICE_STATE, MAXIMUM_DEVICE_STATE,
+ "identifier");
+
mIdentifier = identifier;
mName = name;
}
/** Returns the unique identifier for the device state. */
- @IntRange(from = 0)
+ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE)
public int getIdentifier() {
return mIdentifier;
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 984a17694e07..b3a6f263953f 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -17,6 +17,8 @@
package com.android.server.devicestate;
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES;
import android.annotation.IntRange;
@@ -89,7 +91,7 @@ public final class DeviceStateManagerService extends SystemService {
// the current state after the initial callback from the DeviceStateProvider.
@GuardedBy("mLock")
@NonNull
- private DeviceState mCommittedState = new DeviceState(0, "UNSET");
+ private DeviceState mCommittedState = new DeviceState(MINIMUM_DEVICE_STATE, "UNSET");
// The device state that is currently awaiting callback from the policy to be committed.
@GuardedBy("mLock")
@NonNull
@@ -598,8 +600,9 @@ public final class DeviceStateManagerService extends SystemService {
}
@Override
- public void onStateChanged(@IntRange(from = 0) int identifier) {
- if (identifier < 0) {
+ public void onStateChanged(
+ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier) {
+ if (identifier < MINIMUM_DEVICE_STATE || identifier > MAXIMUM_DEVICE_STATE) {
throw new IllegalArgumentException("Invalid identifier: " + identifier);
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
index 2d4377f820fd..109bf6358e8b 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
@@ -16,6 +16,9 @@
package com.android.server.devicestate;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+
import android.annotation.IntRange;
/**
@@ -65,8 +68,10 @@ public interface DeviceStateProvider {
*
* @param identifier the identifier of the new device state.
*
- * @throws IllegalArgumentException if the state is less than 0.
+ * @throws IllegalArgumentException if the state is less than {@link MINIMUM_DEVICE_STATE}
+ * or greater than {@link MAXIMUM_DEVICE_STATE}.
*/
- void onStateChanged(@IntRange(from = 0) int identifier);
+ void onStateChanged(
+ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier);
}
}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index fa063b223250..225da7ad87b3 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -42,8 +42,8 @@ import android.util.MathUtils;
import android.util.Slog;
import android.util.TimeUtils;
-import com.android.internal.BrightnessSynchronizer;
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;
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 4832e46be8a8..a62f67a743ad 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -28,8 +28,8 @@ import android.util.Pair;
import android.util.Slog;
import android.util.Spline;
-import com.android.internal.BrightnessSynchronizer;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.Preconditions;
import com.android.server.display.utils.Plog;
diff --git a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
new file mode 100644
index 000000000000..d4556ed5f9fa
--- /dev/null
+++ b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
@@ -0,0 +1,121 @@
+/*
+ * 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.display;
+
+import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
+import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.DisplayAddress;
+
+import com.android.server.display.layout.Layout;
+
+import java.util.Arrays;
+
+/**
+ * Mapping from device states into {@link Layout}s. This allows us to map device
+ * states into specific layouts for the connected displays; particularly useful for
+ * foldable and multi-display devices where the default display and which displays are ON can
+ * change depending on the state of the device.
+ */
+class DeviceStateToLayoutMap {
+ private static final String TAG = "DeviceStateToLayoutMap";
+
+ public static final int STATE_DEFAULT = DeviceStateManager.INVALID_DEVICE_STATE;
+
+ // TODO - b/168208162 - Remove these when we check in static definitions for layouts
+ public static final int STATE_FOLDED = 100;
+ public static final int STATE_UNFOLDED = 101;
+
+ private final SparseArray<Layout> mLayoutMap = new SparseArray<>();
+
+ DeviceStateToLayoutMap(Context context) {
+ mLayoutMap.append(STATE_DEFAULT, new Layout());
+ loadFoldedDisplayConfig(context);
+ }
+
+ public void dumpLocked(IndentingPrintWriter ipw) {
+ ipw.println("DeviceStateToLayoutMap:");
+ ipw.increaseIndent();
+
+ ipw.println("Registered Layouts:");
+ for (int i = 0; i < mLayoutMap.size(); i++) {
+ ipw.println("state(" + mLayoutMap.keyAt(i) + "): " + mLayoutMap.valueAt(i));
+ }
+ }
+
+ Layout get(int state) {
+ Layout layout = mLayoutMap.get(state);
+ if (layout == null) {
+ layout = mLayoutMap.get(STATE_DEFAULT);
+ }
+ return layout;
+ }
+
+ private Layout create(int state) {
+ if (mLayoutMap.contains(state)) {
+ Slog.e(TAG, "Attempted to create a second layout for state " + state);
+ return null;
+ }
+
+ final Layout layout = new Layout();
+ mLayoutMap.append(state, layout);
+ return layout;
+ }
+
+ private void loadFoldedDisplayConfig(Context context) {
+ final String[] strDisplayIds = context.getResources().getStringArray(
+ com.android.internal.R.array.config_internalFoldedPhysicalDisplayIds);
+
+ if (strDisplayIds.length != 2 || TextUtils.isEmpty(strDisplayIds[0])
+ || TextUtils.isEmpty(strDisplayIds[1])) {
+ Slog.w(TAG, "Folded display configuration invalid: [" + Arrays.toString(strDisplayIds)
+ + "]");
+ return;
+ }
+
+ final long[] displayIds;
+ try {
+ displayIds = new long[] {
+ Long.parseLong(strDisplayIds[0]),
+ Long.parseLong(strDisplayIds[1])
+ };
+ } catch (NumberFormatException nfe) {
+ Slog.w(TAG, "Folded display config non numerical: " + Arrays.toString(strDisplayIds));
+ return;
+ }
+
+ final int[] foldedDeviceStates = context.getResources().getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates);
+ // Only add folded states if folded state config is not empty
+ if (foldedDeviceStates.length == 0) {
+ return;
+ }
+
+ // Create the folded state layout
+ final Layout foldedLayout = create(STATE_FOLDED);
+ foldedLayout.createDisplayLocked(
+ DisplayAddress.fromPhysicalDisplayId(displayIds[0]), true /*isDefault*/);
+
+ // Create the unfolded state layout
+ final Layout unfoldedLayout = create(STATE_UNFOLDED);
+ unfoldedLayout.createDisplayLocked(
+ DisplayAddress.fromPhysicalDisplayId(displayIds[1]), true /*isDefault*/);
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index d6872217eab6..1b25427adf71 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -23,8 +23,8 @@ import android.os.PowerManager;
import android.util.Slog;
import android.view.DisplayAddress;
-import com.android.internal.BrightnessSynchronizer;
import com.android.internal.R;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.server.display.config.DisplayConfiguration;
import com.android.server.display.config.DisplayQuirks;
import com.android.server.display.config.HbmTiming;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index bf16a6d5efb9..501533d535d3 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -26,7 +26,7 @@ import android.view.DisplayEventReceiver;
import android.view.RoundedCorners;
import android.view.Surface;
-import com.android.internal.BrightnessSynchronizer;
+import com.android.internal.display.BrightnessSynchronizer;
import java.util.Arrays;
import java.util.Objects;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
index 5c0fceb9b795..57f44864d2c0 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
@@ -19,6 +19,7 @@ package com.android.server.display;
import android.annotation.NonNull;
import android.util.Slog;
import android.view.Display;
+import android.view.DisplayAddress;
import com.android.internal.annotations.GuardedBy;
import com.android.server.display.DisplayManagerService.SyncRoot;
@@ -112,12 +113,11 @@ class DisplayDeviceRepository implements DisplayAdapter.Listener {
}
}
- public DisplayDevice getByIdLocked(@NonNull String uniqueId) {
- final int count = mDisplayDevices.size();
- for (int i = 0; i < count; i++) {
- final DisplayDevice d = mDisplayDevices.get(i);
- if (uniqueId.equals(d.getUniqueId())) {
- return d;
+ public DisplayDevice getByAddressLocked(@NonNull DisplayAddress address) {
+ for (int i = mDisplayDevices.size() - 1; i >= 0; i--) {
+ final DisplayDevice device = mDisplayDevices.get(i);
+ if (address.equals(device.getDisplayDeviceInfoLocked().address)) {
+ return device;
}
}
return null;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 1cd7dbd9ac80..5fa950e00e08 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -107,9 +107,9 @@ import android.view.DisplayAddress;
import android.view.Surface;
import android.view.SurfaceControl;
-import com.android.internal.BrightnessSynchronizer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.AnimationThread;
@@ -1197,6 +1197,7 @@ public final class DisplayManagerService extends SystemService {
final LogicalDisplay display = mLogicalDisplayMapper.getLocked(device);
final int state;
final int displayId = display.getDisplayIdLocked();
+
if (display.isEnabled()) {
state = mDisplayStates.get(displayId);
} else {
@@ -1573,12 +1574,6 @@ public final class DisplayManagerService extends SystemService {
}
}
- void setFoldOverride(Boolean isFolded) {
- synchronized (mSyncRoot) {
- mLogicalDisplayMapper.setFoldOverrideLocked(isFolded);
- }
- }
-
private void clearViewportsLocked() {
mViewports.clear();
}
@@ -2599,14 +2594,14 @@ public final class DisplayManagerService extends SystemService {
}
@Override // Binder call
- public void setTemporaryBrightness(float brightness) {
+ public void setTemporaryBrightness(int displayId, float brightness) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
"Permission required to set the display's brightness");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
- mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
+ mDisplayPowerControllers.get(displayId)
.setTemporaryBrightness(brightness);
}
} finally {
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index aaea15a27e01..d1d04966bdec 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -60,8 +60,6 @@ class DisplayManagerShellCommand extends ShellCommand {
return setDisplayModeDirectorLoggingEnabled(false);
case "dwb-set-cct":
return setAmbientColorTemperatureOverride();
- case "set-fold":
- return setFoldOverride();
default:
return handleDefaultCommands(cmd);
}
@@ -92,8 +90,6 @@ class DisplayManagerShellCommand extends ShellCommand {
pw.println(" Disable display mode director logging.");
pw.println(" dwb-set-cct CCT");
pw.println(" Sets the ambient color temperature override to CCT (use -1 to disable).");
- pw.println(" set-fold [fold|unfold|reset]");
- pw.println(" Simulates the 'fold' state of a device. 'reset' for default behavior.");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
@@ -165,26 +161,4 @@ class DisplayManagerShellCommand extends ShellCommand {
mService.setAmbientColorTemperatureOverride(cct);
return 0;
}
-
- private int setFoldOverride() {
- String state = getNextArg();
- if (state == null) {
- getErrPrintWriter().println("Error: no parameter specified for set-fold");
- return 1;
- }
- final Boolean isFolded;
- if ("fold".equals(state)) {
- isFolded = true;
- } else if ("unfold".equals(state)) {
- isFolded = false;
- } else if ("reset".equals(state)) {
- isFolded = null;
- } else {
- getErrPrintWriter().println("Error: Invalid fold state request: " + state);
- return 1;
- }
-
- mService.setFoldOverride(isFolded);
- return 0;
- }
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 2df336528939..9320f5027ce0 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -53,8 +53,8 @@ import android.util.Slog;
import android.util.TimeUtils;
import android.view.Display;
-import com.android.internal.BrightnessSynchronizer;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.LocalServices;
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 173adce00cd9..1d20d878fb81 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -26,7 +26,7 @@ import android.util.Slog;
import android.view.Choreographer;
import android.view.Display;
-import com.android.internal.BrightnessSynchronizer;
+import com.android.internal.display.BrightnessSynchronizer;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index c0d4ac301570..99bf8cb3cdab 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -38,7 +38,7 @@ import android.view.DisplayEventReceiver;
import android.view.RoundedCorners;
import android.view.SurfaceControl;
-import com.android.internal.BrightnessSynchronizer;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
@@ -105,17 +105,18 @@ final class LocalDisplayAdapter extends DisplayAdapter {
Slog.w(TAG, "No valid info found for display device " + physicalDisplayId);
return;
}
- SurfaceControl.DisplayConfig[] configs = SurfaceControl.getDisplayConfigs(displayToken);
- if (configs == null) {
- // There are no valid configs for this device, so we can't use it
- Slog.w(TAG, "No valid configs found for display device " + physicalDisplayId);
+ SurfaceControl.DisplayMode[] displayModes =
+ SurfaceControl.getDisplayModes(displayToken);
+ if (displayModes == null) {
+ // There are no valid modes for this device, so we can't use it
+ Slog.w(TAG, "No valid modes found for display device " + physicalDisplayId);
return;
}
- int activeConfig = SurfaceControl.getActiveConfig(displayToken);
- if (activeConfig < 0) {
- // There is no active config, and for now we don't have the
+ int activeDisplayMode = SurfaceControl.getActiveDisplayMode(displayToken);
+ if (activeDisplayMode < 0) {
+ // There is no active mode, and for now we don't have the
// policy to set one.
- Slog.w(TAG, "No active config found for display device " + physicalDisplayId);
+ Slog.w(TAG, "No active mode found for display device " + physicalDisplayId);
return;
}
int activeColorMode = SurfaceControl.getActiveColorMode(displayToken);
@@ -127,8 +128,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
physicalDisplayId);
activeColorMode = Display.COLOR_MODE_INVALID;
}
- SurfaceControl.DesiredDisplayConfigSpecs configSpecs =
- SurfaceControl.getDesiredDisplayConfigSpecs(displayToken);
+ SurfaceControl.DesiredDisplayModeSpecs modeSpecsSpecs =
+ SurfaceControl.getDesiredDisplayModeSpecs(displayToken);
int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
Display.HdrCapabilities hdrCapabilities =
SurfaceControl.getHdrCapabilities(displayToken);
@@ -136,13 +137,13 @@ final class LocalDisplayAdapter extends DisplayAdapter {
if (device == null) {
// Display was added.
final boolean isDefaultDisplay = mDevices.size() == 0;
- device = new LocalDisplayDevice(displayToken, physicalDisplayId, info,
- configs, activeConfig, configSpecs, colorModes, activeColorMode,
+ device = new LocalDisplayDevice(displayToken, physicalDisplayId, info, displayModes,
+ activeDisplayMode, modeSpecsSpecs, colorModes, activeColorMode,
hdrCapabilities, isDefaultDisplay);
mDevices.put(physicalDisplayId, device);
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
- } else if (device.updateDisplayPropertiesLocked(info, configs, activeConfig,
- configSpecs, colorModes, activeColorMode, hdrCapabilities)) {
+ } else if (device.updateDisplayPropertiesLocked(info, displayModes, activeDisplayMode,
+ modeSpecsSpecs, colorModes, activeColorMode, hdrCapabilities)) {
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
}
} else {
@@ -189,12 +190,12 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// This is only set in the runnable returned from requestDisplayStateLocked.
private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
private int mDefaultModeId;
- private int mDefaultConfigGroup;
+ private int mDefaultModeGroup;
private int mActiveModeId;
private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
new DisplayModeDirector.DesiredDisplayModeSpecs();
private boolean mDisplayModeSpecsInvalid;
- private int mActiveConfigId;
+ private int mActiveDisplayModeId;
private int mActiveColorMode;
private Display.HdrCapabilities mHdrCapabilities;
private boolean mAllmSupported;
@@ -204,7 +205,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private boolean mSidekickActive;
private SidekickInternal mSidekickInternal;
private SurfaceControl.DisplayInfo mDisplayInfo;
- private SurfaceControl.DisplayConfig[] mDisplayConfigs;
+ private SurfaceControl.DisplayMode[] mDisplayModes;
private Spline mSystemBrightnessToNits;
private Spline mNitsToHalBrightness;
private DisplayDeviceConfig mDisplayDeviceConfig;
@@ -213,15 +214,15 @@ final class LocalDisplayAdapter extends DisplayAdapter {
new DisplayEventReceiver.FrameRateOverride[0];
LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
- SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs,
- int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
+ SurfaceControl.DisplayInfo info, SurfaceControl.DisplayMode[] displayModes,
+ int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs,
int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities,
boolean isDefaultDisplay) {
super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
mPhysicalDisplayId = physicalDisplayId;
mIsDefaultDisplay = isDefaultDisplay;
- updateDisplayPropertiesLocked(info, configs, activeConfigId, configSpecs, colorModes,
- activeColorMode, hdrCapabilities);
+ updateDisplayPropertiesLocked(info, displayModes, activeDisplayModeId, modeSpecs,
+ colorModes, activeColorMode, hdrCapabilities);
mSidekickInternal = LocalServices.getService(SidekickInternal.class);
mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay);
mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken);
@@ -241,10 +242,11 @@ final class LocalDisplayAdapter extends DisplayAdapter {
* Returns true if there is a change.
**/
public boolean updateDisplayPropertiesLocked(SurfaceControl.DisplayInfo info,
- SurfaceControl.DisplayConfig[] configs,
- int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
+ SurfaceControl.DisplayMode[] displayModes,
+ int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs,
int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities) {
- boolean changed = updateDisplayConfigsLocked(configs, activeConfigId, configSpecs);
+ boolean changed = updateDisplayModesLocked(
+ displayModes, activeDisplayModeId, modeSpecs);
changed |= updateDisplayInfo(info);
changed |= updateColorModesLocked(colorModes, activeColorMode);
changed |= updateHdrCapabilitiesLocked(hdrCapabilities);
@@ -255,35 +257,35 @@ final class LocalDisplayAdapter extends DisplayAdapter {
return changed;
}
- public boolean updateDisplayConfigsLocked(
- SurfaceControl.DisplayConfig[] configs, int activeConfigId,
- SurfaceControl.DesiredDisplayConfigSpecs configSpecs) {
- mDisplayConfigs = Arrays.copyOf(configs, configs.length);
- mActiveConfigId = activeConfigId;
+ public boolean updateDisplayModesLocked(
+ SurfaceControl.DisplayMode[] displayModes, int activeDisplayModeId,
+ SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
+ mDisplayModes = Arrays.copyOf(displayModes, displayModes.length);
+ mActiveDisplayModeId = activeDisplayModeId;
// Build an updated list of all existing modes.
ArrayList<DisplayModeRecord> records = new ArrayList<>();
boolean modesAdded = false;
- for (int i = 0; i < configs.length; i++) {
- SurfaceControl.DisplayConfig config = configs[i];
+ for (int i = 0; i < displayModes.length; i++) {
+ SurfaceControl.DisplayMode mode = displayModes[i];
List<Float> alternativeRefreshRates = new ArrayList<>();
- for (int j = 0; j < configs.length; j++) {
- SurfaceControl.DisplayConfig other = configs[j];
- boolean isAlternative = j != i && other.width == config.width
- && other.height == config.height
- && other.refreshRate != config.refreshRate
- && other.configGroup == config.configGroup;
+ for (int j = 0; j < displayModes.length; j++) {
+ SurfaceControl.DisplayMode other = displayModes[j];
+ boolean isAlternative = j != i && other.width == mode.width
+ && other.height == mode.height
+ && other.refreshRate != mode.refreshRate
+ && other.group == mode.group;
if (isAlternative) {
- alternativeRefreshRates.add(configs[j].refreshRate);
+ alternativeRefreshRates.add(displayModes[j].refreshRate);
}
}
Collections.sort(alternativeRefreshRates);
// First, check to see if we've already added a matching mode. Since not all
// configuration options are exposed via Display.Mode, it's possible that we have
- // multiple DisplayConfigs that would generate the same Display.Mode.
+ // multiple DisplayModess that would generate the same Display.Mode.
boolean existingMode = false;
for (DisplayModeRecord record : records) {
- if (record.hasMatchingMode(config)
+ if (record.hasMatchingMode(mode)
&& refreshRatesEquals(alternativeRefreshRates,
record.mMode.getAlternativeRefreshRates())) {
existingMode = true;
@@ -296,13 +298,13 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// If we haven't already added a mode for this configuration to the new set of
// supported modes then check to see if we have one in the prior set of supported
// modes to reuse.
- DisplayModeRecord record = findDisplayModeRecord(config, alternativeRefreshRates);
+ DisplayModeRecord record = findDisplayModeRecord(mode, alternativeRefreshRates);
if (record == null) {
float[] alternativeRates = new float[alternativeRefreshRates.size()];
for (int j = 0; j < alternativeRates.length; j++) {
alternativeRates[j] = alternativeRefreshRates.get(j);
}
- record = new DisplayModeRecord(config, alternativeRates);
+ record = new DisplayModeRecord(mode, alternativeRates);
modesAdded = true;
}
records.add(record);
@@ -312,7 +314,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
DisplayModeRecord activeRecord = null;
for (int i = 0; i < records.size(); i++) {
DisplayModeRecord record = records.get(i);
- if (record.hasMatchingMode(configs[activeConfigId])) {
+ if (record.hasMatchingMode(displayModes[activeDisplayModeId])) {
activeRecord = record;
break;
}
@@ -334,20 +336,20 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// Check whether surface flinger spontaneously changed display config specs out from
// under us. If so, schedule a traversal to reapply our display config specs.
if (mDisplayModeSpecs.baseModeId != NO_DISPLAY_MODE_ID) {
- int activeBaseMode = findMatchingModeIdLocked(configSpecs.defaultConfig);
- // If we can't map the defaultConfig index to a mode, then the physical display
- // configs must have changed, and the code below for handling changes to the
- // list of available modes will take care of updating display config specs.
+ int activeBaseMode = findMatchingModeIdLocked(modeSpecs.defaultMode);
+ // If we can't map the defaultMode index to a mode, then the physical display
+ // modes must have changed, and the code below for handling changes to the
+ // list of available modes will take care of updating display mode specs.
if (activeBaseMode != NO_DISPLAY_MODE_ID) {
if (mDisplayModeSpecs.baseModeId != activeBaseMode
|| mDisplayModeSpecs.primaryRefreshRateRange.min
- != configSpecs.primaryRefreshRateMin
+ != modeSpecs.primaryRefreshRateMin
|| mDisplayModeSpecs.primaryRefreshRateRange.max
- != configSpecs.primaryRefreshRateMax
+ != modeSpecs.primaryRefreshRateMax
|| mDisplayModeSpecs.appRequestRefreshRateRange.min
- != configSpecs.appRequestRefreshRateMin
+ != modeSpecs.appRequestRefreshRateMin
|| mDisplayModeSpecs.appRequestRefreshRateRange.max
- != configSpecs.appRequestRefreshRateMax) {
+ != modeSpecs.appRequestRefreshRateMax) {
mDisplayModeSpecsInvalid = true;
sendTraversalRequestLocked();
}
@@ -368,17 +370,17 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// For a new display, we need to initialize the default mode ID.
if (mDefaultModeId == NO_DISPLAY_MODE_ID) {
mDefaultModeId = activeRecord.mMode.getModeId();
- mDefaultConfigGroup = configs[activeConfigId].configGroup;
+ mDefaultModeGroup = displayModes[activeDisplayModeId].group;
} else if (modesAdded && activeModeChanged) {
Slog.d(TAG, "New display modes are added and the active mode has changed, "
+ "use active mode as default mode.");
mDefaultModeId = activeRecord.mMode.getModeId();
- mDefaultConfigGroup = configs[activeConfigId].configGroup;
- } else if (findDisplayConfigIdLocked(mDefaultModeId, mDefaultConfigGroup) < 0) {
+ mDefaultModeGroup = displayModes[activeDisplayModeId].group;
+ } else if (findDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) {
Slog.w(TAG, "Default display mode no longer available, using currently"
+ " active mode as default.");
mDefaultModeId = activeRecord.mMode.getModeId();
- mDefaultConfigGroup = configs[activeConfigId].configGroup;
+ mDefaultModeGroup = displayModes[activeDisplayModeId].group;
}
// Determine whether the display mode specs' base mode is still there.
@@ -518,11 +520,11 @@ final class LocalDisplayAdapter extends DisplayAdapter {
return true;
}
- private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayConfig config,
+ private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayMode mode,
List<Float> alternativeRefreshRates) {
for (int i = 0; i < mSupportedModes.size(); i++) {
DisplayModeRecord record = mSupportedModes.valueAt(i);
- if (record.hasMatchingMode(config)
+ if (record.hasMatchingMode(mode)
&& refreshRatesEquals(alternativeRefreshRates,
record.mMode.getAlternativeRefreshRates())) {
return record;
@@ -554,10 +556,10 @@ final class LocalDisplayAdapter extends DisplayAdapter {
@Override
public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
if (mInfo == null) {
- SurfaceControl.DisplayConfig config = mDisplayConfigs[mActiveConfigId];
+ SurfaceControl.DisplayMode mode = mDisplayModes[mActiveDisplayModeId];
mInfo = new DisplayDeviceInfo();
- mInfo.width = config.width;
- mInfo.height = config.height;
+ mInfo.width = mode.width;
+ mInfo.height = mode.height;
mInfo.modeId = mActiveModeId;
mInfo.defaultModeId = mDefaultModeId;
mInfo.supportedModes = getDisplayModes(mSupportedModes);
@@ -570,16 +572,16 @@ final class LocalDisplayAdapter extends DisplayAdapter {
mInfo.supportedColorModes[i] = mSupportedColorModes.get(i);
}
mInfo.hdrCapabilities = mHdrCapabilities;
- mInfo.appVsyncOffsetNanos = config.appVsyncOffsetNanos;
- mInfo.presentationDeadlineNanos = config.presentationDeadlineNanos;
+ mInfo.appVsyncOffsetNanos = mode.appVsyncOffsetNanos;
+ mInfo.presentationDeadlineNanos = mode.presentationDeadlineNanos;
mInfo.state = mState;
mInfo.uniqueId = getUniqueId();
final DisplayAddress.Physical physicalAddress =
DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId);
mInfo.address = physicalAddress;
mInfo.densityDpi = (int) (mDisplayInfo.density * 160 + 0.5f);
- mInfo.xDpi = config.xDpi;
- mInfo.yDpi = config.yDpi;
+ mInfo.xDpi = mode.xDpi;
+ mInfo.yDpi = mode.yDpi;
mInfo.deviceProductInfo = mDisplayInfo.deviceProductInfo;
// Assume that all built-in displays that have secure output (eg. HDCP) also
@@ -857,16 +859,16 @@ final class LocalDisplayAdapter extends DisplayAdapter {
return;
}
- // Find the config Id based on the desired mode specs. In case there is more than one
- // config matching the mode spec, prefer the one that is in the default config group.
- // For now the default config group is taken from the active config when we got the
+ // Find the mode Id based on the desired mode specs. In case there is more than one
+ // mode matching the mode spec, prefer the one that is in the default mode group.
+ // For now the default config mode is taken from the active mode when we got the
// hotplug event for the display. In the future we might want to change the default
- // config based on vendor requirements.
- // Note: We prefer the default config group over the current one as this is the config
+ // mode based on vendor requirements.
+ // Note: We prefer the default mode group over the current one as this is the mode
// group the vendor prefers.
- int baseConfigId = findDisplayConfigIdLocked(displayModeSpecs.baseModeId,
- mDefaultConfigGroup);
- if (baseConfigId < 0) {
+ int baseModeId = findDisplayModeIdLocked(displayModeSpecs.baseModeId,
+ mDefaultModeGroup);
+ if (baseModeId < 0) {
// When a display is hotplugged, it's possible for a mode to be removed that was
// previously valid. Because of the way display changes are propagated through the
// framework, and the caching of the display mode specs in LogicalDisplay, it's
@@ -884,7 +886,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
getHandler().sendMessage(PooledLambda.obtainMessage(
LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this,
getDisplayTokenLocked(),
- new SurfaceControl.DesiredDisplayConfigSpecs(baseConfigId,
+ new SurfaceControl.DesiredDisplayModeSpecs(baseModeId,
mDisplayModeSpecs.allowGroupSwitching,
mDisplayModeSpecs.primaryRefreshRateRange.min,
mDisplayModeSpecs.primaryRefreshRateRange.max,
@@ -894,13 +896,13 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
private void setDesiredDisplayModeSpecsAsync(IBinder displayToken,
- SurfaceControl.DesiredDisplayConfigSpecs configSpecs) {
+ SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
// Do not lock when calling these SurfaceControl methods because they are sync
// operations that may block for a while when setting display power mode.
- SurfaceControl.setDesiredDisplayConfigSpecs(displayToken, configSpecs);
- final int activePhysIndex = SurfaceControl.getActiveConfig(displayToken);
+ SurfaceControl.setDesiredDisplayModeSpecs(displayToken, modeSpecs);
+ final int activeMode = SurfaceControl.getActiveDisplayMode(displayToken);
synchronized (getSyncRoot()) {
- if (updateActiveModeLocked(activePhysIndex)) {
+ if (updateActiveModeLocked(activeMode)) {
updateDeviceInfoLocked();
}
}
@@ -911,8 +913,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
updateDeviceInfoLocked();
}
- public void onActiveDisplayConfigChangedLocked(int configId) {
- if (updateActiveModeLocked(configId)) {
+ public void onActiveDisplayModeChangedLocked(int modeId) {
+ if (updateActiveModeLocked(modeId)) {
updateDeviceInfoLocked();
}
}
@@ -924,15 +926,15 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
}
- public boolean updateActiveModeLocked(int activeConfigId) {
- if (mActiveConfigId == activeConfigId) {
+ public boolean updateActiveModeLocked(int activeModeId) {
+ if (mActiveDisplayModeId == activeModeId) {
return false;
}
- mActiveConfigId = activeConfigId;
- mActiveModeId = findMatchingModeIdLocked(activeConfigId);
+ mActiveDisplayModeId = activeModeId;
+ mActiveModeId = findMatchingModeIdLocked(activeModeId);
if (mActiveModeId == NO_DISPLAY_MODE_ID) {
- Slog.w(TAG, "In unknown mode after setting allowed configs"
- + ", activeConfigId=" + mActiveConfigId);
+ Slog.w(TAG, "In unknown mode after setting allowed modes"
+ + ", activeModeId=" + mActiveDisplayModeId);
}
return true;
}
@@ -1012,7 +1014,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId);
pw.println("mDisplayModeSpecs={" + mDisplayModeSpecs + "}");
pw.println("mDisplayModeSpecsInvalid=" + mDisplayModeSpecsInvalid);
- pw.println("mActiveConfigId=" + mActiveConfigId);
+ pw.println("mActiveDisplayModeId=" + mActiveDisplayModeId);
pw.println("mActiveModeId=" + mActiveModeId);
pw.println("mActiveColorMode=" + mActiveColorMode);
pw.println("mDefaultModeId=" + mDefaultModeId);
@@ -1024,9 +1026,9 @@ final class LocalDisplayAdapter extends DisplayAdapter {
pw.println("mGameContentTypeSupported=" + mGameContentTypeSupported);
pw.println("mGameContentTypeRequested=" + mGameContentTypeRequested);
pw.println("mDisplayInfo=" + mDisplayInfo);
- pw.println("mDisplayConfigs=");
- for (int i = 0; i < mDisplayConfigs.length; i++) {
- pw.println(" " + mDisplayConfigs[i]);
+ pw.println("mDisplayModes=");
+ for (int i = 0; i < mDisplayModes.length; i++) {
+ pw.println(" " + mDisplayModes[i]);
}
pw.println("mSupportedModes=");
for (int i = 0; i < mSupportedModes.size(); i++) {
@@ -1036,37 +1038,37 @@ final class LocalDisplayAdapter extends DisplayAdapter {
pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
}
- private int findDisplayConfigIdLocked(int modeId, int configGroup) {
- int matchingConfigId = SurfaceControl.DisplayConfig.INVALID_DISPLAY_CONFIG_ID;
+ private int findDisplayModeIdLocked(int modeId, int modeGroup) {
+ int matchingModeId = SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID;
DisplayModeRecord record = mSupportedModes.get(modeId);
if (record != null) {
- for (int i = 0; i < mDisplayConfigs.length; i++) {
- SurfaceControl.DisplayConfig config = mDisplayConfigs[i];
- if (record.hasMatchingMode(config)) {
- if (matchingConfigId
- == SurfaceControl.DisplayConfig.INVALID_DISPLAY_CONFIG_ID) {
- matchingConfigId = i;
+ for (int i = 0; i < mDisplayModes.length; i++) {
+ SurfaceControl.DisplayMode mode = mDisplayModes[i];
+ if (record.hasMatchingMode(mode)) {
+ if (matchingModeId
+ == SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID) {
+ matchingModeId = i;
}
- // Prefer to return a config that matches the configGroup
- if (config.configGroup == configGroup) {
+ // Prefer to return a mode that matches the modeGroup
+ if (mode.group == modeGroup) {
return i;
}
}
}
}
- return matchingConfigId;
+ return matchingModeId;
}
- private int findMatchingModeIdLocked(int configId) {
- if (configId < 0 || configId >= mDisplayConfigs.length) {
- Slog.e(TAG, "Invalid display config index " + configId);
+ private int findMatchingModeIdLocked(int modeId) {
+ if (modeId < 0 || modeId >= mDisplayModes.length) {
+ Slog.e(TAG, "Invalid display config index " + modeId);
return NO_DISPLAY_MODE_ID;
}
- SurfaceControl.DisplayConfig config = mDisplayConfigs[configId];
+ SurfaceControl.DisplayMode mode = mDisplayModes[modeId];
for (int i = 0; i < mSupportedModes.size(); i++) {
DisplayModeRecord record = mSupportedModes.valueAt(i);
- if (record.hasMatchingMode(config)) {
+ if (record.hasMatchingMode(mode)) {
return record.mMode.getModeId();
}
}
@@ -1113,30 +1115,29 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
/**
- * Keeps track of a display configuration.
+ * Keeps track of a display mode.
*/
private static final class DisplayModeRecord {
public final Display.Mode mMode;
- DisplayModeRecord(SurfaceControl.DisplayConfig config,
+ DisplayModeRecord(SurfaceControl.DisplayMode mode,
float[] alternativeRefreshRates) {
- mMode = createMode(config.width, config.height, config.refreshRate,
+ mMode = createMode(mode.width, mode.height, mode.refreshRate,
alternativeRefreshRates);
}
/**
- * Returns whether the mode generated by the given DisplayConfig matches the mode
+ * Returns whether the mode generated by the given DisplayModes matches the mode
* contained by the record modulo mode ID.
*
- * Note that this doesn't necessarily mean that the DisplayConfigs are identical, just
+ * Note that this doesn't necessarily mean that the DisplayModes are identical, just
* that they generate identical modes.
*/
- public boolean hasMatchingMode(SurfaceControl.DisplayConfig config) {
- int modeRefreshRate = Float.floatToIntBits(mMode.getRefreshRate());
- int configRefreshRate = Float.floatToIntBits(config.refreshRate);
- return mMode.getPhysicalWidth() == config.width
- && mMode.getPhysicalHeight() == config.height
- && modeRefreshRate == configRefreshRate;
+ public boolean hasMatchingMode(SurfaceControl.DisplayMode mode) {
+ return mMode.getPhysicalWidth() == mode.width
+ && mMode.getPhysicalHeight() == mode.height
+ && Float.floatToIntBits(mMode.getRefreshRate())
+ == Float.floatToIntBits(mode.refreshRate);
}
public String toString() {
@@ -1153,7 +1154,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
public interface DisplayEventListener {
void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected);
- void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId);
+ void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId);
void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId,
DisplayEventReceiver.FrameRateOverride[] overrides);
@@ -1163,7 +1164,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private final DisplayEventListener mListener;
ProxyDisplayEventReceiver(Looper looper, DisplayEventListener listener) {
super(looper, VSYNC_SOURCE_APP,
- EVENT_REGISTRATION_CONFIG_CHANGED_FLAG
+ EVENT_REGISTRATION_MODE_CHANGED_FLAG
| EVENT_REGISTRATION_FRAME_RATE_OVERRIDE_FLAG);
mListener = listener;
}
@@ -1174,8 +1175,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
@Override
- public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
- mListener.onConfigChanged(timestampNanos, physicalDisplayId, configId);
+ public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
+ mListener.onModeChanged(timestampNanos, physicalDisplayId, modeId);
}
@Override
@@ -1198,23 +1199,23 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
@Override
- public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+ public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
if (DEBUG) {
- Slog.d(TAG, "onConfigChanged("
+ Slog.d(TAG, "onModeChanged("
+ "timestampNanos=" + timestampNanos
+ ", physicalDisplayId=" + physicalDisplayId
- + ", configId=" + configId + ")");
+ + ", modeId=" + modeId + ")");
}
synchronized (getSyncRoot()) {
LocalDisplayDevice device = mDevices.get(physicalDisplayId);
if (device == null) {
if (DEBUG) {
- Slog.d(TAG, "Received config change for unhandled physical display: "
+ Slog.d(TAG, "Received mode change for unhandled physical display: "
+ "physicalDisplayId=" + physicalDisplayId);
}
return;
}
- device.onActiveDisplayConfigChangedLocked(configId);
+ device.onActiveDisplayModeChangedLocked(modeId);
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 80781d280d5e..20b133ce4d0a 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -32,6 +32,7 @@ import android.view.SurfaceControl;
import com.android.server.wm.utils.InsetUtils;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.Arrays;
import java.util.Objects;
@@ -718,6 +719,7 @@ final class LogicalDisplay {
public void dumpLocked(PrintWriter pw) {
pw.println("mDisplayId=" + mDisplayId);
pw.println("mLayerStack=" + mLayerStack);
+ pw.println("mIsEnabled=" + mIsEnabled);
pw.println("mHasContent=" + mHasContent);
pw.println("mDesiredDisplayModeSpecs={" + mDesiredDisplayModeSpecs + "}");
pw.println("mRequestedColorMode=" + mRequestedColorMode);
@@ -731,4 +733,11 @@ final class LogicalDisplay {
pw.println("mFrameRateOverrides=" + Arrays.toString(mFrameRateOverrides));
pw.println("mPendingFrameRateOverrideUids=" + mPendingFrameRateOverrideUids);
}
+
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ dumpLocked(new PrintWriter(sw));
+ return sw.toString();
+ }
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 3ed6281b088b..b7c63cc1ac87 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -28,10 +28,10 @@ import android.view.DisplayEventReceiver;
import android.view.DisplayInfo;
import android.view.DisplayAddress;
+import com.android.server.display.layout.Layout;
import java.io.PrintWriter;
import java.util.Arrays;
-import java.util.Objects;
import java.util.function.Consumer;
/**
@@ -76,41 +76,26 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
private final boolean mSingleDisplayDemoMode;
/**
- * Physical Display ID of the DisplayDevice to associate with the default LogicalDisplay
- * when {@link mIsFolded} is set to {@code true}.
- */
- private String mDisplayIdToUseWhenFolded;
-
- /**
- * Physical Display ID of the DisplayDevice to associate with the default LogicalDisplay
- * when {@link mIsFolded} is set to {@code false}.
- */
- private String mDisplayIdToUseWhenUnfolded;
-
- /** Overrides the folded state of the device. For use with ADB commands. */
- private Boolean mIsFoldedOverride;
-
- /** Saves the last device fold state. */
- private boolean mIsFolded;
-
- /**
* List of all logical displays indexed by logical display id.
* Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache.
* TODO: multi-display - Move the aforementioned comment?
*/
private final SparseArray<LogicalDisplay> mLogicalDisplays =
new SparseArray<LogicalDisplay>();
- private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1;
- private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
private int mNextBuiltInDisplayId = 4096;
/** A mapping from logical display id to display group. */
private final SparseArray<DisplayGroup> mDisplayIdToGroupMap = new SparseArray<>();
private final DisplayDeviceRepository mDisplayDeviceRepo;
+ private final DeviceStateToLayoutMap mDeviceStateToLayoutMap;
private final Listener mListener;
private final int[] mFoldedDeviceStates;
+ private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
+ private Layout mCurrentLayout = null;
+ private boolean mIsFolded = false;
+
LogicalDisplayMapper(Context context, DisplayDeviceRepository repo, Listener listener) {
mDisplayDeviceRepo = repo;
mListener = listener;
@@ -120,7 +105,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
mFoldedDeviceStates = context.getResources().getIntArray(
com.android.internal.R.array.config_foldedDeviceStates);
- loadFoldedDisplayConfig(context);
+ mDeviceStateToLayoutMap = new DeviceStateToLayoutMap(context);
}
@Override
@@ -215,13 +200,12 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
ipw.increaseIndent();
ipw.println("mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
- ipw.println("mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId);
+
+ ipw.println("mCurrentLayout=" + mCurrentLayout);
final int logicalDisplayCount = mLogicalDisplays.size();
ipw.println();
ipw.println("Logical Displays: size=" + logicalDisplayCount);
-
-
for (int i = 0; i < logicalDisplayCount; i++) {
int displayId = mLogicalDisplays.keyAt(i);
LogicalDisplay display = mLogicalDisplays.valueAt(i);
@@ -231,6 +215,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
ipw.decreaseIndent();
ipw.println();
}
+ mDeviceStateToLayoutMap.dumpLocked(ipw);
}
void setDeviceStateLocked(int state) {
@@ -246,79 +231,55 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
void setDeviceFoldedLocked(boolean isFolded) {
mIsFolded = isFolded;
- if (mIsFoldedOverride != null) {
- isFolded = mIsFoldedOverride.booleanValue();
+
+ // Until we have fully functioning state mapping, use hardcoded states based on isFolded
+ final int state = mIsFolded ? DeviceStateToLayoutMap.STATE_FOLDED
+ : DeviceStateToLayoutMap.STATE_UNFOLDED;
+
+ if (DEBUG) {
+ Slog.d(TAG, "New device state: " + state);
}
- if (mDisplayIdToUseWhenFolded == null || mDisplayIdToUseWhenUnfolded == null
- || mLogicalDisplays.size() < 2) {
- // Do nothing if this behavior is disabled or there are less than two displays.
+ final Layout layout = mDeviceStateToLayoutMap.get(state);
+ if (layout == null) {
return;
}
-
- final DisplayDevice deviceFolded =
- mDisplayDeviceRepo.getByIdLocked(mDisplayIdToUseWhenFolded);
- final DisplayDevice deviceUnfolded =
- mDisplayDeviceRepo.getByIdLocked(mDisplayIdToUseWhenUnfolded);
- if (deviceFolded == null || deviceUnfolded == null) {
- // If the expected devices for folding functionality are not present, return early.
+ final Layout.Display displayLayout = layout.getById(Display.DEFAULT_DISPLAY);
+ if (displayLayout == null) {
return;
}
-
- // Find the associated LogicalDisplays for the configured "folding" DeviceDisplays.
- final LogicalDisplay displayFolded = getLocked(deviceFolded);
- final LogicalDisplay displayUnfolded = getLocked(deviceUnfolded);
- if (displayFolded == null || displayUnfolded == null) {
- // If the expected displays are not present, return early.
+ final DisplayDevice newDefaultDevice =
+ mDisplayDeviceRepo.getByAddressLocked(displayLayout.getAddress());
+ if (newDefaultDevice == null) {
return;
}
- // Find out which display is currently default and which is disabled.
final LogicalDisplay defaultDisplay = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
- final LogicalDisplay disabledDisplay;
- if (defaultDisplay == displayFolded) {
- disabledDisplay = displayUnfolded;
- } else if (defaultDisplay == displayUnfolded) {
- disabledDisplay = displayFolded;
- } else {
- // If neither folded or unfolded displays are currently set to the default display, we
- // are in an unknown state and it's best to log the error and bail.
- Slog.e(TAG, "Unexpected: when attempting to swap displays, neither of the two"
- + " configured displays were set up as the default display. Default: "
- + defaultDisplay.getDisplayInfoLocked() + ", ConfiguredDisplays: [ folded="
- + displayFolded.getDisplayInfoLocked() + ", unfolded="
- + displayUnfolded.getDisplayInfoLocked() + " ]");
+ mCurrentLayout = layout;
+
+ // If we're already set up accurately, return early
+ if (defaultDisplay.getPrimaryDisplayDeviceLocked() == newDefaultDevice) {
return;
}
- if (isFolded == (defaultDisplay == displayFolded)) {
- // Nothing to do, already in the right state.
+ // We need to swap the default display's display-device with the one that is supposed
+ // to be the default in the new layout.
+ final LogicalDisplay displayToSwap = getLocked(newDefaultDevice);
+ if (displayToSwap == null) {
+ Slog.w(TAG, "Canceling display swap - unexpected empty second display for: "
+ + newDefaultDevice);
return;
}
-
- // Everything was checked and we need to swap, lets swap.
- displayFolded.swapDisplaysLocked(displayUnfolded);
+ defaultDisplay.swapDisplaysLocked(displayToSwap);
// We ensure that the non-default Display is always forced to be off. This was likely
// already done in a previous iteration, but we do it with each swap in case something in
// the underlying LogicalDisplays changed: like LogicalDisplay recreation, for example.
defaultDisplay.setEnabled(true);
- disabledDisplay.setEnabled(false);
+ displayToSwap.setEnabled(false);
// Update the world
updateLogicalDisplaysLocked();
-
- if (DEBUG) {
- Slog.d(TAG, "Folded displays: isFolded: " + isFolded + ", defaultDisplay? "
- + defaultDisplay.getDisplayInfoLocked());
- }
- }
-
- void setFoldOverrideLocked(Boolean isFolded) {
- if (!Objects.equals(isFolded, mIsFoldedOverride)) {
- mIsFoldedOverride = isFolded;
- setDeviceFoldedLocked(mIsFolded);
- }
}
private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
@@ -335,7 +296,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
return;
}
- final int displayId = assignDisplayIdLocked(isDefault, deviceInfo.address);
+ final int displayId = Layout.assignDisplayIdLocked(isDefault, deviceInfo.address);
final int layerStack = assignLayerStackLocked(displayId);
final DisplayGroup displayGroup;
@@ -358,8 +319,15 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
return;
}
- mLogicalDisplays.put(displayId, display);
+ // For foldable devices, we start the internal non-default displays as disabled.
+ // TODO - b/168208162 - this will be removed when we recalculate the layout with each
+ // display-device addition.
+ if (mFoldedDeviceStates.length > 0 && deviceInfo.type == Display.TYPE_INTERNAL
+ && !isDefault) {
+ display.setEnabled(false);
+ }
+ mLogicalDisplays.put(displayId, display);
displayGroup.addDisplayLocked(display);
mDisplayIdToGroupMap.append(displayId, displayGroup);
@@ -377,6 +345,10 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED);
}
+
+ if (DEBUG) {
+ Slog.d(TAG, "New Display added: " + display);
+ }
}
/**
@@ -468,10 +440,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
}
- private int assignDisplayIdLocked(boolean isDefault) {
- return isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++;
- }
-
private int assignDisplayGroupIdLocked(boolean isDefault) {
return isDefault ? Display.DEFAULT_DISPLAY_GROUP : mNextNonDefaultGroupId++;
}
@@ -495,21 +463,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
return displayId;
}
- private void loadFoldedDisplayConfig(Context context) {
- final String[] displayIds = context.getResources().getStringArray(
- com.android.internal.R.array.config_internalFoldedPhysicalDisplayIds);
-
- if (displayIds.length != 2 || TextUtils.isEmpty(displayIds[0])
- || TextUtils.isEmpty(displayIds[1])) {
- Slog.w(TAG, "Folded display configuration invalid: [" + Arrays.toString(displayIds)
- + "]");
- return;
- }
-
- mDisplayIdToUseWhenFolded = displayIds[0];
- mDisplayIdToUseWhenUnfolded = displayIds[1];
- }
-
public interface Listener {
void onLogicalDisplayEventLocked(LogicalDisplay display, int event);
void onDisplayGroupEventLocked(int groupId, int event);
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index 7916d816dc9b..26004a8da1a1 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -20,7 +20,7 @@ import android.animation.ValueAnimator;
import android.util.FloatProperty;
import android.view.Choreographer;
-import com.android.internal.BrightnessSynchronizer;
+import com.android.internal.display.BrightnessSynchronizer;
/**
* A custom animator that progressively updates a property value at
diff --git a/services/core/java/com/android/server/display/layout/Layout.java b/services/core/java/com/android/server/display/layout/Layout.java
new file mode 100644
index 000000000000..18f39e6ade1d
--- /dev/null
+++ b/services/core/java/com/android/server/display/layout/Layout.java
@@ -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.display.layout;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.annotation.NonNull;
+import android.util.Slog;
+import android.view.DisplayAddress;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Holds a collection of {@link Display}s. A single instance of this class describes
+ * how to organize one or more DisplayDevices into LogicalDisplays for a particular device
+ * state. For example, there may be one instance of this class to describe display layout when
+ * a foldable device is folded, and a second instance for when the device is unfolded.
+ */
+public class Layout {
+ private static final String TAG = "Layout";
+ private static int sNextNonDefaultDisplayId = DEFAULT_DISPLAY + 1;
+
+ private final List<Display> mDisplays = new ArrayList<>(2);
+
+ /**
+ * @return The default display ID, or a new unique one to use.
+ */
+ public static int assignDisplayIdLocked(boolean isDefault) {
+ return isDefault ? DEFAULT_DISPLAY : sNextNonDefaultDisplayId++;
+ }
+
+ @Override
+ public String toString() {
+ return mDisplays.toString();
+ }
+
+ /**
+ * Creates a simple 1:1 LogicalDisplay mapping for the specified DisplayDevice.
+ *
+ * @param address Address of the device.
+ * @param isDefault Indicates if the device is meant to be the default display.
+ * @return The new layout.
+ */
+ public Display createDisplayLocked(
+ @NonNull DisplayAddress address, boolean isDefault) {
+ if (contains(address)) {
+ Slog.w(TAG, "Attempting to add second definition for display-device: " + address);
+ return null;
+ }
+
+ // See if we're dealing with the "default" display
+ if (isDefault && getById(DEFAULT_DISPLAY) != null) {
+ Slog.w(TAG, "Ignoring attempt to add a second default display: " + address);
+ isDefault = false;
+ }
+
+ // Assign a logical display ID and create the new display.
+ // Note that the logical display ID is saved into the layout, so when switching between
+ // different layouts, a logical display can be destroyed and later recreated with the
+ // same logical display ID.
+ final int logicalDisplayId = assignDisplayIdLocked(isDefault);
+ final Display layout = new Display(address, logicalDisplayId);
+
+ mDisplays.add(layout);
+ return layout;
+ }
+
+ /**
+ * @param address The address to check.
+ *
+ * @return True if the specified address is used in this layout.
+ */
+ public boolean contains(@NonNull DisplayAddress address) {
+ final int size = mDisplays.size();
+ for (int i = 0; i < size; i++) {
+ if (address.equals(mDisplays.get(i).getAddress())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param id The display ID to check.
+ *
+ * @return The display corresponding to the specified display ID.
+ */
+ public Display getById(int id) {
+ for (int i = 0; i < mDisplays.size(); i++) {
+ Display layout = mDisplays.get(i);
+ if (id == layout.getLogicalDisplayId()) {
+ return layout;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @param index The index of the display to return.
+ *
+ * @return the display at the specified index.
+ */
+ public Display getAt(int index) {
+ return mDisplays.get(index);
+ }
+
+ /**
+ * @return The number of displays defined for this layout.
+ */
+ public int size() {
+ return mDisplays.size();
+ }
+
+ /**
+ * Describes how a {@link LogicalDisplay} is built from {@link DisplayDevice}s.
+ */
+ public static class Display {
+ private final DisplayAddress mAddress;
+ private final int mLogicalDisplayId;
+
+ Display(@NonNull DisplayAddress address, int logicalDisplayId) {
+ mAddress = address;
+ mLogicalDisplayId = logicalDisplayId;
+ }
+
+ @Override
+ public String toString() {
+ return "{addr: " + mAddress + ", dispId: " + mLogicalDisplayId + "}";
+ }
+
+ public DisplayAddress getAddress() {
+ return mAddress;
+ }
+
+ public int getLogicalDisplayId() {
+ return mLogicalDisplayId;
+ }
+ }
+}
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 3e2b5ab795be..01e839dae07a 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -41,7 +41,6 @@ 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.FileIntegrityService;
import com.android.server.security.VerityUtils;
import java.io.File;
@@ -72,11 +71,11 @@ public final class FontManagerService extends IFontManager.Stub {
}
@Override
- public int updateFont(int baseVersion, @NonNull FontUpdateRequest request) {
+ public int updateFontFile(@NonNull FontUpdateRequest request, int baseVersion) {
+ Preconditions.checkArgumentNonnegative(baseVersion);
Objects.requireNonNull(request);
Objects.requireNonNull(request.getFd());
Objects.requireNonNull(request.getSignature());
- Preconditions.checkArgumentNonnegative(baseVersion);
getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS,
"UPDATE_FONTS permission required.");
try {
@@ -88,6 +87,21 @@ public final class FontManagerService extends IFontManager.Stub {
}
}
+ @Override
+ public int updateFontFamily(@NonNull List<FontUpdateRequest> requests, int baseVersion) {
+ Preconditions.checkArgumentNonnegative(baseVersion);
+ Objects.requireNonNull(requests);
+ getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS,
+ "UPDATE_FONTS permission required.");
+ try {
+ update(baseVersion, requests);
+ return FontManager.RESULT_SUCCESS;
+ } catch (SystemFontException e) {
+ Slog.e(TAG, "Failed to update font family", e);
+ return e.getErrorCode();
+ }
+ }
+
/* package */ static class SystemFontException extends AndroidException {
private final int mErrorCode;
@@ -211,7 +225,7 @@ public final class FontManagerService extends IFontManager.Stub {
@Nullable
private static UpdatableFontDir createUpdatableFontDir() {
// If apk verity is supported, fs-verity should be available.
- if (!FileIntegrityService.isApkVeritySupported()) return null;
+ if (!VerityUtils.isFsVeritySupported()) return null;
return new UpdatableFontDir(new File(FONT_FILES_DIR),
Arrays.asList(new File(SystemFonts.SYSTEM_FONT_DIR),
new File(SystemFonts.OEM_FONT_DIR)),
@@ -345,6 +359,7 @@ public final class FontManagerService extends IFontManager.Stub {
synchronized (mSerializedFontMapLock) {
mSerializedFontMap = serializeFontMap;
}
+ return;
} catch (IOException | ErrnoException e) {
Slog.w(TAG, "Failed to serialize updatable font map. "
+ "Retrying with system image fonts.", e);
diff --git a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
index 017f11ceaf06..d514aab31e8d 100644
--- a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
+++ b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
@@ -17,6 +17,8 @@
package com.android.server.graphics.fonts;
import android.annotation.NonNull;
+import android.graphics.FontListParser;
+import android.text.FontConfig;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
@@ -30,6 +32,8 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
/* package */ class PersistentSystemFontConfig {
@@ -38,11 +42,13 @@ import java.util.Set;
private static final String TAG_ROOT = "fontConfig";
private static final String TAG_LAST_MODIFIED_DATE = "lastModifiedDate";
private static final String TAG_UPDATED_FONT_DIR = "updatedFontDir";
+ private static final String TAG_FAMILY = "family";
private static final String ATTR_VALUE = "value";
/* package */ static class Config {
public long lastModifiedDate;
public final Set<String> updatedFontDirs = new ArraySet<>();
+ public final List<FontConfig.FontFamily> fontFamilies = new ArrayList<>();
}
/**
@@ -72,6 +78,11 @@ import java.util.Set;
case TAG_UPDATED_FONT_DIR:
out.updatedFontDirs.add(getAttribute(parser, ATTR_VALUE));
break;
+ case TAG_FAMILY:
+ // updatableFontMap is not ready here. We get the base file names by passing
+ // empty fontDir, and resolve font paths later.
+ out.fontFamilies.add(FontListParser.readFamily(
+ parser, "" /* fontDir */, null /* updatableFontMap */));
default:
Slog.w(TAG, "Skipping unknown tag: " + tag);
}
@@ -97,6 +108,13 @@ import java.util.Set;
out.attribute(null, ATTR_VALUE, dir);
out.endTag(null, TAG_UPDATED_FONT_DIR);
}
+ List<FontConfig.FontFamily> fontFamilies = config.fontFamilies;
+ for (int i = 0; i < fontFamilies.size(); i++) {
+ FontConfig.FontFamily fontFamily = fontFamilies.get(i);
+ out.startTag(null, TAG_FAMILY);
+ FontListParser.writeFamily(out, fontFamily);
+ out.endTag(null, TAG_FAMILY);
+ }
out.endTag(null, TAG_ROOT);
out.endDocument();
diff --git a/services/core/java/com/android/server/graphics/fonts/TEST_MAPPING b/services/core/java/com/android/server/graphics/fonts/TEST_MAPPING
new file mode 100644
index 000000000000..7fbf426c71c4
--- /dev/null
+++ b/services/core/java/com/android/server/graphics/fonts/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "UpdatableSystemFontTest"
+ }
+ ]
+}
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 dac94f6aa9d2..08ddc6ddf4ae 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -31,6 +31,8 @@ import android.util.ArrayMap;
import android.util.Base64;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
@@ -40,8 +42,10 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import java.time.Instant;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* Manages set of updatable font files.
@@ -118,6 +122,12 @@ final class UpdatableFontDir {
*/
private final ArrayMap<String, FontFileInfo> mFontFileInfoMap = new ArrayMap<>();
+ /**
+ * A mutable map containing mapping from font family name to {@link FontConfig.FontFamily}.
+ * The FontFamily entries only reference font files in {@link #mFontFileInfoMap}.
+ */
+ private final ArrayMap<String, FontConfig.FontFamily> mFontFamilyMap = new ArrayMap<>();
+
UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser,
FsverityUtil fsverityUtil) {
this(filesDir, preinstalledFontDirs, parser, fsverityUtil, new File(CONFIG_XML_FILE));
@@ -136,6 +146,7 @@ final class UpdatableFontDir {
/* package */ void loadFontFileMap() {
mFontFileInfoMap.clear();
+ mFontFamilyMap.clear();
mLastModifiedDate = 0;
boolean success = false;
try {
@@ -168,6 +179,17 @@ final class UpdatableFontDir {
FontFileInfo fontFileInfo = validateFontFile(files[0]);
addFileToMapIfNewer(fontFileInfo, true /* deleteOldFile */);
}
+ // Resolve font file paths.
+ List<FontConfig.FontFamily> fontFamilies = config.fontFamilies;
+ for (int i = 0; i < fontFamilies.size(); i++) {
+ FontConfig.FontFamily fontFamily = fontFamilies.get(i);
+ try {
+ addFontFamily(fontFamily);
+ } catch (SystemFontException e) {
+ // Ignore failures as updated fonts may be obsoleted by system OTA update.
+ Slog.i(TAG, "Obsolete font family: " + fontFamily.getName());
+ }
+ }
success = true;
} catch (Throwable t) {
// If something happened during loading system fonts, clear all contents in finally
@@ -177,6 +199,7 @@ final class UpdatableFontDir {
// Delete all files just in case if we find a problematic file.
if (!success) {
mFontFileInfoMap.clear();
+ mFontFamilyMap.clear();
mLastModifiedDate = 0;
FileUtils.deleteContents(mFilesDir);
}
@@ -186,10 +209,11 @@ final class UpdatableFontDir {
/* package */ void clearUpdates() throws SystemFontException {
mFontFileInfoMap.clear();
FileUtils.deleteContents(mFilesDir);
+ mFontFamilyMap.clear();
mLastModifiedDate = Instant.now().getEpochSecond();
try (FileOutputStream fos = new FileOutputStream(mConfigFile)) {
- PersistentSystemFontConfig.writeToXml(fos, getPersistentConfig());
+ PersistentSystemFontConfig.writeToXml(fos, createPersistentConfig());
} catch (Exception e) {
throw new SystemFontException(
FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
@@ -206,17 +230,26 @@ final class UpdatableFontDir {
public void update(List<FontUpdateRequest> requests) throws SystemFontException {
// Backup the mapping for rollback.
ArrayMap<String, FontFileInfo> backupMap = new ArrayMap<>(mFontFileInfoMap);
+ ArrayMap<String, FontConfig.FontFamily> backupFamilies = new ArrayMap<>(mFontFamilyMap);
long backupLastModifiedDate = mLastModifiedDate;
boolean success = false;
try {
for (FontUpdateRequest request : requests) {
- installFontFile(request.getFd().getFileDescriptor(), request.getSignature());
+ switch (request.getType()) {
+ case FontUpdateRequest.TYPE_UPDATE_FONT_FILE:
+ installFontFile(
+ request.getFd().getFileDescriptor(), request.getSignature());
+ break;
+ case FontUpdateRequest.TYPE_UPDATE_FONT_FAMILY:
+ addFontFamily(request.getFontFamily());
+ break;
+ }
}
// Write config file.
mLastModifiedDate = Instant.now().getEpochSecond();
try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) {
- PersistentSystemFontConfig.writeToXml(fos, getPersistentConfig());
+ PersistentSystemFontConfig.writeToXml(fos, createPersistentConfig());
} catch (Exception e) {
throw new SystemFontException(
FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
@@ -234,6 +267,8 @@ final class UpdatableFontDir {
if (!success) {
mFontFileInfoMap.clear();
mFontFileInfoMap.putAll(backupMap);
+ mFontFamilyMap.clear();
+ mFontFamilyMap.putAll(backupFamilies);
mLastModifiedDate = backupLastModifiedDate;
}
}
@@ -454,12 +489,49 @@ final class UpdatableFontDir {
}
}
- private PersistentSystemFontConfig.Config getPersistentConfig() {
+ /**
+ * Adds a font family to {@link #mFontFamilyMap} and returns true on success.
+ *
+ * <p>This method only accepts adding or updating a font family with a name.
+ * This is to prevent bad font family update from removing glyphs from font fallback chains.
+ * Unnamed font families are used as other named font family's fallback fonts to guarantee a
+ * complete glyph coverage.
+ */
+ private void addFontFamily(FontConfig.FontFamily fontFamily) throws SystemFontException {
+ Objects.requireNonNull(fontFamily.getName());
+ FontConfig.FontFamily resolvedFontFamily = resolveFontFiles(fontFamily);
+ if (resolvedFontFamily == null) {
+ throw new SystemFontException(
+ FontManager.RESULT_ERROR_FONT_NOT_FOUND,
+ "Required fonts are not available");
+ }
+ mFontFamilyMap.put(resolvedFontFamily.getName(), resolvedFontFamily);
+ }
+
+ @Nullable
+ private FontConfig.FontFamily resolveFontFiles(FontConfig.FontFamily fontFamily) {
+ List<FontConfig.Font> resolvedFonts = new ArrayList<>(fontFamily.getFontList().size());
+ 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());
+ if (info == null) {
+ return null;
+ }
+ resolvedFonts.add(new FontConfig.Font(info.mFile, null, font.getStyle(),
+ font.getTtcIndex(), font.getFontVariationSettings(), font.getFontFamilyName()));
+ }
+ return new FontConfig.FontFamily(resolvedFonts, fontFamily.getName(),
+ fontFamily.getLocaleList(), fontFamily.getVariant());
+ }
+
+ private PersistentSystemFontConfig.Config createPersistentConfig() {
PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
config.lastModifiedDate = mLastModifiedDate;
for (FontFileInfo info : mFontFileInfoMap.values()) {
config.updatedFontDirs.add(info.getRandomizedFontDir().getName());
}
+ config.fontFamilies.addAll(mFontFamilyMap.values());
return config;
}
@@ -471,8 +543,24 @@ final class UpdatableFontDir {
return map;
}
+ @VisibleForTesting
+ Map<String, FontConfig.FontFamily> getFontFamilyMap() {
+ return mFontFamilyMap;
+ }
+
/* package */ FontConfig getSystemFontConfig() {
- return SystemFonts.getSystemFontConfig(getFontFileMap(), mLastModifiedDate, mConfigVersion);
+ FontConfig config = SystemFonts.getSystemFontConfig(getFontFileMap(), 0, 0);
+ List<FontConfig.FontFamily> mergedFamilies =
+ new ArrayList<>(config.getFontFamilies().size() + mFontFamilyMap.size());
+ // We should keep the first font family (config.getFontFamilies().get(0)) because it's used
+ // as a fallback font. See SystemFonts.java.
+ mergedFamilies.addAll(config.getFontFamilies());
+ // When building Typeface, a latter font family definition will override the previous font
+ // family definition with the same name. An exception is config.getFontFamilies.get(0),
+ // which will be used as a fallback font without being overridden.
+ mergedFamilies.addAll(mFontFamilyMap.values());
+ return new FontConfig(
+ mergedFamilies, config.getAliases(), mLastModifiedDate, mConfigVersion);
}
/* package */ int getConfigVersion() {
diff --git a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
index 1a0a639c3c66..7b646b36124b 100644
--- a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
+++ b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java
@@ -17,6 +17,7 @@ package com.android.server.hdmi;
*/
import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.DisplayStatusCallback;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -65,6 +66,20 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction {
@Override
boolean start() {
+ HdmiControlService service = localDevice().mService;
+ if (service.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) {
+ HdmiDeviceInfo deviceInfo = service.getHdmiCecNetwork().getCecDeviceInfo(
+ mTargetAddress);
+ if (deviceInfo != null
+ && deviceInfo.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) {
+ int powerStatus = deviceInfo.getDevicePowerStatus();
+ if (powerStatus != HdmiControlManager.POWER_STATUS_UNKNOWN) {
+ invokeCallback(powerStatus);
+ finish();
+ return true;
+ }
+ }
+ }
queryDevicePowerStatus();
mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
index cb47bb25f152..86a8e36d748d 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
@@ -103,10 +103,11 @@ final class DeviceSelectAction extends HdmiCecFeatureAction {
if (mIsCec20) {
sendSetStreamPath();
}
- if (!mIsCec20 || mTarget.getDevicePowerStatus()
- == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+ int targetPowerStatus = localDevice().mService.getHdmiCecNetwork()
+ .getCecDeviceInfo(getTargetAddress()).getDevicePowerStatus();
+ if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
queryDevicePowerStatus();
- } else if (mTarget.getDevicePowerStatus() == HdmiControlManager.POWER_STATUS_ON) {
+ } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
invokeCallbackAndFinish(HdmiControlManager.RESULT_SUCCESS);
return true;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index 69180643661f..f64efe7c26cc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -411,6 +411,12 @@ public class HdmiCecConfig {
case Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED:
notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE);
break;
+ case Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED:
+ notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY);
+ break;
+ case Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED:
+ notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP);
+ break;
}
}
@@ -447,6 +453,8 @@ public class HdmiCecConfig {
Global.HDMI_CEC_VERSION,
Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED,
+ Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
+ Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
};
for (String setting: settings) {
resolver.registerContentObserver(Global.getUriFor(setting), false,
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 06bcada84ff9..1643ec162bc2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -50,6 +50,7 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
+import java.util.NoSuchElementException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.function.Predicate;
@@ -149,6 +150,12 @@ final class HdmiCecController {
* returns {@code null}.
*/
static HdmiCecController create(HdmiControlService service, HdmiCecAtomWriter atomWriter) {
+ HdmiCecController controller = createWithNativeWrapper(service, new NativeWrapperImpl11(),
+ atomWriter);
+ if (controller != null) {
+ return controller;
+ }
+ HdmiLogger.warning("Unable to use cec@1.1");
return createWithNativeWrapper(service, new NativeWrapperImpl(), atomWriter);
}
@@ -312,7 +319,7 @@ final class HdmiCecController {
}
/**
- * Return CEC version of the device.
+ * Return highest CEC version supported by this device.
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*/
@@ -745,13 +752,202 @@ final class HdmiCecController {
boolean nativeIsConnected(int port);
}
- private static final class NativeWrapperImpl implements NativeWrapper,
+ private static final class NativeWrapperImpl11 implements NativeWrapper,
IHwBinder.DeathRecipient, getPhysicalAddressCallback {
- private IHdmiCec mHdmiCec;
+ private android.hardware.tv.cec.V1_1.IHdmiCec mHdmiCec;
+ @Nullable private HdmiCecCallback mCallback;
+
private final Object mLock = new Object();
private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
+
+ @Override
+ public String nativeInit() {
+ return (connectToHal() ? mHdmiCec.toString() : null);
+ }
+
+ boolean connectToHal() {
+ try {
+ mHdmiCec = android.hardware.tv.cec.V1_1.IHdmiCec.getService(true);
+ try {
+ mHdmiCec.linkToDeath(this, HDMI_CEC_HAL_DEATH_COOKIE);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Couldn't link to death : ", e);
+ }
+ } catch (RemoteException | NoSuchElementException e) {
+ HdmiLogger.error("Couldn't connect to cec@1.1", e);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void onValues(int result, short addr) {
+ if (result == Result.SUCCESS) {
+ synchronized (mLock) {
+ mPhysicalAddress = new Short(addr).intValue();
+ }
+ }
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ if (cookie == HDMI_CEC_HAL_DEATH_COOKIE) {
+ HdmiLogger.error("Service died cookie : " + cookie + "; reconnecting");
+ connectToHal();
+ // Reconnect the callback
+ if (mCallback != null) {
+ setCallback(mCallback);
+ }
+ }
+ }
+
+ @Override
+ public void setCallback(HdmiCecCallback callback) {
+ mCallback = callback;
+ try {
+ mHdmiCec.setCallback_1_1(new HdmiCecCallback11(callback));
+ } catch (RemoteException e) {
+ HdmiLogger.error("Couldn't initialise tv.cec callback : ", e);
+ }
+ }
+
+ @Override
+ public int nativeSendCecCommand(int srcAddress, int dstAddress, byte[] body) {
+ android.hardware.tv.cec.V1_1.CecMessage message =
+ new android.hardware.tv.cec.V1_1.CecMessage();
+ message.initiator = srcAddress;
+ message.destination = dstAddress;
+ message.body = new ArrayList<>(body.length);
+ for (byte b : body) {
+ message.body.add(b);
+ }
+ try {
+ return mHdmiCec.sendMessage_1_1(message);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to send CEC message : ", e);
+ return SendMessageResult.FAIL;
+ }
+ }
+
+ @Override
+ public int nativeAddLogicalAddress(int logicalAddress) {
+ try {
+ return mHdmiCec.addLogicalAddress_1_1(logicalAddress);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to add a logical address : ", e);
+ return Result.FAILURE_INVALID_ARGS;
+ }
+ }
+
+ @Override
+ public void nativeClearLogicalAddress() {
+ try {
+ mHdmiCec.clearLogicalAddress();
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to clear logical address : ", e);
+ }
+ }
+
+ @Override
+ public int nativeGetPhysicalAddress() {
+ try {
+ mHdmiCec.getPhysicalAddress(this);
+ return mPhysicalAddress;
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get physical address : ", e);
+ return INVALID_PHYSICAL_ADDRESS;
+ }
+ }
+
+ @Override
+ public int nativeGetVersion() {
+ try {
+ return mHdmiCec.getCecVersion();
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get cec version : ", e);
+ return Result.FAILURE_UNKNOWN;
+ }
+ }
+
+ @Override
+ public int nativeGetVendorId() {
+ try {
+ return mHdmiCec.getVendorId();
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get vendor id : ", e);
+ return Result.FAILURE_UNKNOWN;
+ }
+ }
+
+ @Override
+ public HdmiPortInfo[] nativeGetPortInfos() {
+ try {
+ ArrayList<android.hardware.tv.cec.V1_0.HdmiPortInfo> hdmiPortInfos =
+ mHdmiCec.getPortInfo();
+ HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[hdmiPortInfos.size()];
+ int i = 0;
+ for (android.hardware.tv.cec.V1_0.HdmiPortInfo portInfo : hdmiPortInfos) {
+ hdmiPortInfo[i] = new HdmiPortInfo(portInfo.portId,
+ portInfo.type,
+ portInfo.physicalAddress,
+ portInfo.cecSupported,
+ false,
+ portInfo.arcSupported);
+ i++;
+ }
+ return hdmiPortInfo;
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get port information : ", e);
+ return null;
+ }
+ }
+
+ @Override
+ public void nativeSetOption(int flag, boolean enabled) {
+ try {
+ mHdmiCec.setOption(flag, enabled);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to set option : ", e);
+ }
+ }
+
+ @Override
+ public void nativeSetLanguage(String language) {
+ try {
+ mHdmiCec.setLanguage(language);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to set language : ", e);
+ }
+ }
+
+ @Override
+ public void nativeEnableAudioReturnChannel(int port, boolean flag) {
+ try {
+ mHdmiCec.enableAudioReturnChannel(port, flag);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to enable/disable ARC : ", e);
+ }
+ }
+
+ @Override
+ public boolean nativeIsConnected(int port) {
+ try {
+ return mHdmiCec.isConnected(port);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get connection info : ", e);
+ return false;
+ }
+ }
+ }
+
+ private static final class NativeWrapperImpl implements NativeWrapper,
+ IHwBinder.DeathRecipient, getPhysicalAddressCallback {
+ private android.hardware.tv.cec.V1_0.IHdmiCec mHdmiCec;
@Nullable private HdmiCecCallback mCallback;
+ private final Object mLock = new Object();
+ private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
+
@Override
public String nativeInit() {
return (connectToHal() ? mHdmiCec.toString() : null);
@@ -765,8 +961,8 @@ final class HdmiCecController {
} catch (RemoteException e) {
HdmiLogger.error("Couldn't link to death : ", e);
}
- } catch (RemoteException e) {
- HdmiLogger.error("Couldn't get tv.cec service : ", e);
+ } catch (RemoteException | NoSuchElementException e) {
+ HdmiLogger.error("Couldn't connect to cec@1.0", e);
return false;
}
return true;
@@ -776,7 +972,7 @@ final class HdmiCecController {
public void setCallback(@NonNull HdmiCecCallback callback) {
mCallback = callback;
try {
- mHdmiCec.setCallback(callback);
+ mHdmiCec.setCallback(new HdmiCecCallback10(callback));
} catch (RemoteException e) {
HdmiLogger.error("Couldn't initialise tv.cec callback : ", e);
}
@@ -931,20 +1127,69 @@ final class HdmiCecController {
}
}
- final class HdmiCecCallback extends IHdmiCecCallback.Stub {
+ final class HdmiCecCallback {
+ public void onCecMessage(int initiator, int destination, byte[] body) {
+ runOnServiceThread(
+ () -> handleIncomingCecCommand(initiator, destination, body));
+ }
+
+ public void onHotplugEvent(int portId, boolean connected) {
+ runOnServiceThread(() -> handleHotplug(portId, connected));
+ }
+ }
+
+ private static final class HdmiCecCallback10 extends IHdmiCecCallback.Stub {
+ private final HdmiCecCallback mHdmiCecCallback;
+
+ HdmiCecCallback10(HdmiCecCallback hdmiCecCallback) {
+ mHdmiCecCallback = hdmiCecCallback;
+ }
+
@Override
public void onCecMessage(CecMessage message) throws RemoteException {
byte[] body = new byte[message.body.size()];
for (int i = 0; i < message.body.size(); i++) {
body[i] = message.body.get(i);
}
- runOnServiceThread(
- () -> handleIncomingCecCommand(message.initiator, message.destination, body));
+ mHdmiCecCallback.onCecMessage(message.initiator, message.destination, body);
+ }
+
+ @Override
+ public void onHotplugEvent(HotplugEvent event) throws RemoteException {
+ mHdmiCecCallback.onHotplugEvent(event.portId, event.connected);
+ }
+ }
+
+ private static final class HdmiCecCallback11
+ extends android.hardware.tv.cec.V1_1.IHdmiCecCallback.Stub {
+ private final HdmiCecCallback mHdmiCecCallback;
+
+ HdmiCecCallback11(HdmiCecCallback hdmiCecCallback) {
+ mHdmiCecCallback = hdmiCecCallback;
+ }
+
+ @Override
+ public void onCecMessage_1_1(android.hardware.tv.cec.V1_1.CecMessage message)
+ throws RemoteException {
+ byte[] body = new byte[message.body.size()];
+ for (int i = 0; i < message.body.size(); i++) {
+ body[i] = message.body.get(i);
+ }
+ mHdmiCecCallback.onCecMessage(message.initiator, message.destination, body);
+ }
+
+ @Override
+ public void onCecMessage(CecMessage message) throws RemoteException {
+ byte[] body = new byte[message.body.size()];
+ for (int i = 0; i < message.body.size(); i++) {
+ body[i] = message.body.get(i);
+ }
+ mHdmiCecCallback.onCecMessage(message.initiator, message.destination, body);
}
@Override
public void onHotplugEvent(HotplugEvent event) throws RemoteException {
- runOnServiceThread(() -> handleHotplug(event.portId, event.connected));
+ mHdmiCecCallback.onHotplugEvent(event.portId, event.connected);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 382f0f9d6329..d8914b389191 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -654,7 +654,9 @@ abstract class HdmiCecLocalDevice {
FOLLOWER_SAFETY_TIMEOUT);
return true;
}
- return false;
+
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_INVALID_OPERAND);
+ return true;
}
@ServiceThreadOnly
@@ -666,9 +668,8 @@ abstract class HdmiCecLocalDevice {
final long upTime = SystemClock.uptimeMillis();
injectKeyEvent(upTime, KeyEvent.ACTION_UP, mLastKeycode, 0);
mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE;
- return true;
}
- return false;
+ return true;
}
static void injectKeyEvent(long time, int action, int keycode, int repeat) {
@@ -788,10 +789,7 @@ abstract class HdmiCecLocalDevice {
}
protected boolean handleRecordTvScreen(HdmiCecMessage message) {
- // The default behavior of <Record TV Screen> is replying <Feature Abort> with
- // "Cannot provide source".
- mService.maySendFeatureAbortCommand(message, Constants.ABORT_CANNOT_PROVIDE_SOURCE);
- return true;
+ return false;
}
protected boolean handleTimerClearedStatus(HdmiCecMessage message) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 75b52f95d5bb..299525207a60 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -239,6 +239,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
@ServiceThreadOnly
protected void onActiveSourceLost() {
assertRunOnServiceThread();
+ mService.pauseActiveMediaSessions();
switch (mService.getHdmiCecConfig().getStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST)) {
case HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 04acd5187f77..2ed84811250e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -139,8 +139,12 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
@ServiceThreadOnly
void toggleAndFollowTvPower() {
assertRunOnServiceThread();
- // Wake up Android framework to take over CEC control from the microprocessor.
- mService.wakeUp();
+ if (mService.getPowerManager().isInteractive()) {
+ mService.pauseActiveMediaSessions();
+ } else {
+ // Wake up Android framework to take over CEC control from the microprocessor.
+ mService.wakeUp();
+ }
mService.queryDisplayStatus(new IHdmiControlCallback.Stub() {
@Override
public void onComplete(int status) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index a3e18d161751..8d6bcadb3e2b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1079,7 +1079,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
message.getSource(),
HdmiControlManager.ONE_TOUCH_RECORD_PREVIOUS_RECORDING_IN_PROGRESS);
}
- return super.handleRecordTvScreen(message);
+ // The default behavior of <Record TV Screen> is replying <Feature Abort> with
+ // "Cannot provide source".
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_CANNOT_PROVIDE_SOURCE);
+ return true;
}
int recorderAddress = message.getSource();
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 0ae1994bd368..d014f149e831 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -56,6 +56,8 @@ import android.hardware.hdmi.IHdmiVendorCommandListener;
import android.hardware.tv.cec.V1_0.OptionKey;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.media.AudioManager;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionManager;
import android.media.tv.TvInputManager;
import android.media.tv.TvInputManager.TvInputCallback;
import android.net.Uri;
@@ -541,7 +543,7 @@ public class HdmiControlService extends SystemService {
private void bootCompleted() {
// on boot, if device is interactive, set HDMI CEC state as powered on as well
if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) {
- onWakeUp(WAKE_UP_BOOT_UP);
+ mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
}
}
@@ -772,8 +774,14 @@ public class HdmiControlService extends SystemService {
private void initializeCec(int initiatedBy) {
mAddressAllocated = false;
- mCecVersion = getHdmiCecConfig().getIntValue(
+ int settingsCecVersion = getHdmiCecConfig().getIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION);
+ int supportedCecVersion = mCecController.getVersion();
+
+ // Limit the used CEC version to the highest supported version by HAL and selected
+ // version in settings (but at least v1.4b).
+ mCecVersion = Math.max(HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
+ Math.min(settingsCecVersion, supportedCecVersion));
mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true);
mCecController.setLanguage(mMenuLanguage);
@@ -2182,6 +2190,7 @@ public class HdmiControlService extends SystemService {
pw.println("mProhibitMode: " + mProhibitMode);
pw.println("mPowerStatus: " + mPowerStatusController.getPowerStatus());
+ pw.println("mCecVersion: " + mCecVersion);
// System settings
pw.println("System_settings:");
@@ -3294,6 +3303,16 @@ public class HdmiControlService extends SystemService {
}
}
+ @VisibleForTesting
+ void pauseActiveMediaSessions() {
+ MediaSessionManager mediaSessionManager = getContext()
+ .getSystemService(MediaSessionManager.class);
+ List<MediaController> mediaControllers = mediaSessionManager.getActiveSessions(null);
+ for (MediaController mediaController : mediaControllers) {
+ mediaController.getTransportControls().pause();
+ }
+ }
+
void setActiveSource(int logicalAddress, int physicalAddress, String caller) {
synchronized (mLock) {
mActiveSource.logicalAddress = logicalAddress;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
index ee3427f0a383..a1e613635116 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
@@ -70,6 +70,10 @@ final class HdmiControlShellCommand extends ShellCommand {
pw.println(" --args <vendor specific arguments>");
pw.println(" [--id <true if vendor command should be sent with vendor id>]");
pw.println(" Send a Vendor Command to the given target device");
+ pw.println(" cec_setting get <setting name>");
+ pw.println(" Get the current value of a CEC setting");
+ pw.println(" cec_setting set <setting name> <value>");
+ pw.println(" Set the value of a CEC setting");
}
private int handleShellCommand(String cmd) throws RemoteException {
@@ -81,6 +85,8 @@ final class HdmiControlShellCommand extends ShellCommand {
return oneTouchPlay(pw);
case "vendorcommand":
return vendorCommand(pw);
+ case "cec_setting":
+ return cecSetting(pw);
}
getErrPrintWriter().println("Unhandled command: " + cmd);
@@ -157,4 +163,39 @@ final class HdmiControlShellCommand extends ShellCommand {
mBinderService.sendVendorCommand(deviceType, destination, params, hasVendorId);
return 0;
}
+
+ private int cecSetting(PrintWriter pw) throws RemoteException {
+ if (getRemainingArgsCount() < 1) {
+ throw new IllegalArgumentException("Expected at least 1 argument (operation).");
+ }
+ String operation = getNextArgRequired();
+ switch (operation) {
+ case "get": {
+ String setting = getNextArgRequired();
+ try {
+ String value = mBinderService.getCecSettingStringValue(setting);
+ pw.println(setting + " = " + value);
+ } catch (IllegalArgumentException e) {
+ int intValue = mBinderService.getCecSettingIntValue(setting);
+ pw.println(setting + " = " + intValue);
+ }
+ return 0;
+ }
+ case "set": {
+ String setting = getNextArgRequired();
+ String value = getNextArgRequired();
+ try {
+ mBinderService.setCecSettingStringValue(setting, value);
+ pw.println(setting + " = " + value);
+ } catch (IllegalArgumentException e) {
+ int intValue = Integer.parseInt(value);
+ mBinderService.setCecSettingIntValue(setting, intValue);
+ pw.println(setting + " = " + intValue);
+ }
+ return 0;
+ }
+ default:
+ throw new IllegalArgumentException("Unknown operation: " + operation);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
index 909fcda26c39..66fc0d9c1760 100644
--- a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
+++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
@@ -17,6 +17,7 @@ package com.android.server.hdmi;
import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_UNKNOWN;
+import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.util.SparseIntArray;
@@ -108,7 +109,10 @@ public class PowerStatusMonitorAction extends HdmiCecFeatureAction {
private void resetPowerStatus(List<HdmiDeviceInfo> deviceInfos) {
mPowerStatus.clear();
for (HdmiDeviceInfo info : deviceInfos) {
- mPowerStatus.append(info.getLogicalAddress(), info.getDevicePowerStatus());
+ if (localDevice().mService.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0
+ || info.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0) {
+ mPowerStatus.append(info.getLogicalAddress(), info.getDevicePowerStatus());
+ }
}
}
@@ -117,19 +121,22 @@ public class PowerStatusMonitorAction extends HdmiCecFeatureAction {
localDevice().mService.getHdmiCecNetwork().getDeviceInfoList(false);
resetPowerStatus(deviceInfos);
for (HdmiDeviceInfo info : deviceInfos) {
- final int logicalAddress = info.getLogicalAddress();
- sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
- logicalAddress),
- new SendMessageCallback() {
- @Override
- public void onSendCompleted(int error) {
- // If fails to send <Give Device Power Status>,
- // update power status into UNKNOWN.
- if (error != SendMessageResult.SUCCESS) {
- updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, true);
+ if (localDevice().mService.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0
+ || info.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0) {
+ final int logicalAddress = info.getLogicalAddress();
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
+ logicalAddress),
+ new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ // If fails to send <Give Device Power Status>,
+ // update power status into UNKNOWN.
+ if (error != SendMessageResult.SUCCESS) {
+ updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, true);
+ }
}
- }
- });
+ });
+ }
}
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index c4c0f688bd67..bd577f35f0a5 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -371,6 +371,9 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
int durationMs) {
Slog.i(mTag, "setTemporaryService(" + userId + ") to " + componentName + " for "
+ durationMs + "ms");
+ if (mServiceNameResolver == null) {
+ return;
+ }
enforceCallingPermissionForManagement();
Objects.requireNonNull(componentName);
@@ -404,6 +407,9 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
enforceCallingPermissionForManagement();
synchronized (mLock) {
+ if (mServiceNameResolver == null) {
+ return false;
+ }
final boolean changed = mServiceNameResolver.setDefaultServiceEnabled(userId, enabled);
if (!changed) {
if (verbose) {
@@ -434,6 +440,10 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
public final boolean isDefaultServiceEnabled(@UserIdInt int userId) {
enforceCallingPermissionForManagement();
+ if (mServiceNameResolver == null) {
+ return false;
+ }
+
synchronized (mLock) {
return mServiceNameResolver.isDefaultServiceEnabled(userId);
}
@@ -958,6 +968,10 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
public void onPackageModified(String packageName) {
if (verbose) Slog.v(mTag, "onPackageModified(): " + packageName);
+ if (mServiceNameResolver == null) {
+ return;
+ }
+
final int userId = getChangingUserId();
final String serviceName = mServiceNameResolver.getDefaultServiceName(userId);
if (serviceName == null) {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 2e4200c1f7d9..c5be20e39864 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -37,6 +37,7 @@ import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
+import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayViewport;
import android.hardware.input.IInputDevicesChangedListener;
@@ -57,6 +58,7 @@ import android.os.CombinedVibrationEffect;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IVibratorStateListener;
import android.os.InputEventInjectionResult;
import android.os.InputEventInjectionSync;
import android.os.LocaleList;
@@ -64,9 +66,11 @@ import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.os.Process;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.provider.DeviceConfig;
@@ -77,6 +81,7 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.view.Display;
import android.view.IInputFilter;
import android.view.IInputFilterHost;
@@ -100,6 +105,7 @@ import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
@@ -164,6 +170,9 @@ public class InputManagerService extends IInputManager.Stub
/** TODO(b/169067926): Remove this. */
private static final boolean UNTRUSTED_TOUCHES_TOAST = false;
+ public static final boolean ENABLE_PER_WINDOW_INPUT_ROTATION =
+ SystemProperties.getBoolean("persist.debug.per_window_input_rotation", false);
+
// Pointer to native input manager service object.
private final long mPtr;
@@ -221,13 +230,16 @@ public class InputManagerService extends IInputManager.Stub
private Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<IBinder, VibratorToken>();
private int mNextVibratorTokenValue;
+ // List of currently registered vibrator state changed listeners by device id.
+ @GuardedBy("mVibratorLock")
+ private final SparseArray<RemoteCallbackList<IVibratorStateListener>> mVibratorStateListeners =
+ new SparseArray<RemoteCallbackList<IVibratorStateListener>>();
+ // List of vibrator states by device id.
+ @GuardedBy("mVibratorLock")
+ private final SparseBooleanArray mIsVibrating = new SparseBooleanArray();
+
// State for lid switch
- // Lock for the lid switch state. Held when triggering callbacks to guarantee lid switch events
- // are delivered in order. For ex, when a new lid switch callback is registered the lock is held
- // while the callback is processing the initial lid switch event which guarantees that any
- // events that occur at the same time are delivered after the callback has returned.
private final Object mLidSwitchLock = new Object();
- @GuardedBy("mLidSwitchLock")
private List<LidSwitchCallback> mLidSwitchCallbacks = new ArrayList<>();
// State for the currently installed input filter.
@@ -375,6 +387,9 @@ public class InputManagerService extends IInputManager.Stub
public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE;
+ /** Indicates an open state for the lid switch. */
+ public static final int SW_STATE_LID_OPEN = 0;
+
/** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
final boolean mUseDevInputEventForAudioJack;
@@ -410,18 +425,13 @@ public class InputManagerService extends IInputManager.Stub
}
void registerLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
+ boolean lidOpen;
synchronized (mLidSwitchLock) {
mLidSwitchCallbacks.add(callback);
-
- // Skip triggering the initial callback if the system is not yet ready as the switch
- // state will be reported as KEY_STATE_UNKNOWN. The callback will be triggered in
- // systemRunning().
- if (mSystemReady) {
- boolean lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID)
- == KEY_STATE_UP;
- callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen);
- }
+ lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID)
+ == SW_STATE_LID_OPEN;
}
+ callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen);
}
void unregisterLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
@@ -469,18 +479,7 @@ public class InputManagerService extends IInputManager.Stub
}
mNotificationManager = (NotificationManager)mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
-
- synchronized (mLidSwitchLock) {
- mSystemReady = true;
-
- // Send the initial lid switch state to any callback registered before the system was
- // ready.
- int switchState = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID);
- for (int i = 0; i < mLidSwitchCallbacks.size(); i++) {
- LidSwitchCallback callback = mLidSwitchCallbacks.get(i);
- callback.notifyLidSwitchChanged(0 /* whenNanos */, switchState == KEY_STATE_UP);
- }
- }
+ mSystemReady = true;
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -524,8 +523,51 @@ public class InputManagerService extends IInputManager.Stub
nativeReloadDeviceAliases(mPtr);
}
+ /** Rotates CCW by `delta` 90-degree increments. */
+ private static void rotateBounds(Rect inOutBounds, int parentW, int parentH, int delta) {
+ int rdelta = ((delta % 4) + 4) % 4;
+ int origLeft = inOutBounds.left;
+ switch (rdelta) {
+ case 0:
+ return;
+ case 1:
+ inOutBounds.left = inOutBounds.top;
+ inOutBounds.top = parentW - inOutBounds.right;
+ inOutBounds.right = inOutBounds.bottom;
+ inOutBounds.bottom = parentW - origLeft;
+ return;
+ case 2:
+ inOutBounds.left = parentW - inOutBounds.right;
+ inOutBounds.right = parentW - origLeft;
+ return;
+ case 3:
+ inOutBounds.left = parentH - inOutBounds.bottom;
+ inOutBounds.bottom = inOutBounds.right;
+ inOutBounds.right = parentH - inOutBounds.top;
+ inOutBounds.top = origLeft;
+ return;
+ }
+ }
+
private void setDisplayViewportsInternal(List<DisplayViewport> viewports) {
- nativeSetDisplayViewports(mPtr, viewports.toArray(new DisplayViewport[0]));
+ 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.
+ 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.
+ int dw = (v.orientation % 2) == 0 ? v.deviceWidth : v.deviceHeight;
+ int dh = (v.orientation % 2) == 0 ? v.deviceHeight : v.deviceWidth;
+ v.logicalFrame.set(0, 0, dw, dh);
+ v.physicalFrame.set(0, 0, dw, dh);
+ v.orientation = 0;
+ }
+ } else {
+ for (int i = viewports.size() - 1; i >= 0; --i) {
+ vArray[i] = viewports.get(i);
+ }
+ }
+ nativeSetDisplayViewports(mPtr, vArray);
}
/**
@@ -2008,6 +2050,92 @@ public class InputManagerService extends IInputManager.Stub
}
}
+ // Native callback.
+ private void notifyVibratorState(int deviceId, boolean isOn) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyVibratorState: deviceId=" + deviceId + " isOn=" + isOn);
+ }
+ synchronized (mVibratorLock) {
+ mIsVibrating.put(deviceId, isOn);
+ notifyVibratorStateListenersLocked(deviceId);
+ }
+ }
+
+ @GuardedBy("mVibratorLock")
+ private void notifyVibratorStateListenersLocked(int deviceId) {
+ if (!mVibratorStateListeners.contains(deviceId)) {
+ if (DEBUG) {
+ Slog.v(TAG, "Device " + deviceId + " doesn't have vibrator state listener.");
+ }
+ return;
+ }
+ RemoteCallbackList<IVibratorStateListener> listeners =
+ mVibratorStateListeners.get(deviceId);
+ final int length = listeners.beginBroadcast();
+ try {
+ for (int i = 0; i < length; i++) {
+ notifyVibratorStateListenerLocked(deviceId, listeners.getBroadcastItem(i));
+ }
+ } finally {
+ listeners.finishBroadcast();
+ }
+ }
+
+ @GuardedBy("mVibratorLock")
+ private void notifyVibratorStateListenerLocked(int deviceId, IVibratorStateListener listener) {
+ try {
+ listener.onVibrating(mIsVibrating.get(deviceId));
+ } catch (RemoteException | RuntimeException e) {
+ Slog.e(TAG, "Vibrator state listener failed to call", e);
+ }
+ }
+
+ @Override // Binder call
+ public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+ Preconditions.checkNotNull(listener, "listener must not be null");
+
+ RemoteCallbackList<IVibratorStateListener> listeners;
+ synchronized (mVibratorLock) {
+ if (!mVibratorStateListeners.contains(deviceId)) {
+ listeners = new RemoteCallbackList<>();
+ mVibratorStateListeners.put(deviceId, listeners);
+ } else {
+ listeners = mVibratorStateListeners.get(deviceId);
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!listeners.register(listener)) {
+ Slog.e(TAG, "Could not register vibrator state listener " + listener);
+ return false;
+ }
+ // Notify its callback after new client registered.
+ notifyVibratorStateListenerLocked(deviceId, listener);
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override // Binder call
+ public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+ synchronized (mVibratorLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mVibratorStateListeners.contains(deviceId)) {
+ Slog.w(TAG, "Vibrator state listener " + deviceId + " doesn't exist");
+ return false;
+ }
+ RemoteCallbackList<IVibratorStateListener> listeners =
+ mVibratorStateListeners.get(deviceId);
+ return listeners.unregister(listener);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
// Binder call
@Override
public int getBatteryStatus(int deviceId) {
@@ -2251,13 +2379,14 @@ public class InputManagerService extends IInputManager.Stub
if ((switchMask & SW_LID_BIT) != 0) {
final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
+
+ ArrayList<LidSwitchCallback> callbacksCopy;
synchronized (mLidSwitchLock) {
- if (mSystemReady) {
- for (int i = 0; i < mLidSwitchCallbacks.size(); i++) {
- LidSwitchCallback callbacks = mLidSwitchCallbacks.get(i);
- callbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
- }
- }
+ callbacksCopy = new ArrayList<>(mLidSwitchCallbacks);
+ }
+ for (int i = 0; i < callbacksCopy.size(); i++) {
+ LidSwitchCallback callbacks = callbacksCopy.get(i);
+ callbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6f7f69e5b707..0754df0e6b9f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -178,6 +178,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
import com.android.internal.os.TransferPipe;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInlineSuggestionsResponseCallback;
@@ -5227,15 +5228,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- boolean asProto = false;
- for (int argIndex = 0; argIndex < args.length; argIndex++) {
- if (args[argIndex].equals(PROTO_ARG)) {
- asProto = true;
- break;
- }
- }
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- if (asProto) {
+ if (ArrayUtils.contains(args, PROTO_ARG)) {
final ImeTracing imeTracing = ImeTracing.getInstance();
if (imeTracing.isEnabled()) {
imeTracing.stopTrace(null, false /* writeToFile */);
@@ -5244,14 +5239,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
imeTracing.startTrace(null);
});
}
- }
- doDump(fd, pw, args, asProto);
- }
-
- private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- if (useProto) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
dumpDebug(proto, InputMethodManagerServiceTraceProto.INPUT_METHOD_MANAGER_SERVICE);
proto.flush();
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index 43c965dde27b..42b0add6136e 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -36,9 +36,9 @@ import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
-import com.android.internal.BrightnessSynchronizer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index d9cf86e2626b..c7af0ee109da 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -66,6 +66,7 @@ import android.location.LocationManagerInternal;
import android.location.LocationProvider;
import android.location.LocationRequest;
import android.location.LocationTime;
+import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.location.util.identity.CallerIdentity;
import android.os.Binder;
@@ -77,7 +78,9 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
+import android.provider.Settings;
import android.stats.location.LocationStatsEnums;
+import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.Log;
@@ -86,6 +89,7 @@ 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.location.eventlog.LocationEventLog;
import com.android.server.location.geofence.GeofenceManager;
import com.android.server.location.geofence.GeofenceProxy;
import com.android.server.location.gnss.GnssConfiguration;
@@ -94,10 +98,11 @@ import com.android.server.location.gnss.hal.GnssNative;
import com.android.server.location.injector.AlarmHelper;
import com.android.server.location.injector.AppForegroundHelper;
import com.android.server.location.injector.AppOpsHelper;
+import com.android.server.location.injector.DeviceIdleHelper;
+import com.android.server.location.injector.DeviceStationaryHelper;
import com.android.server.location.injector.EmergencyHelper;
import com.android.server.location.injector.Injector;
import com.android.server.location.injector.LocationAttributionHelper;
-import com.android.server.location.injector.LocationEventLog;
import com.android.server.location.injector.LocationPermissionsHelper;
import com.android.server.location.injector.LocationPowerSaveModeHelper;
import com.android.server.location.injector.LocationUsageLogger;
@@ -106,6 +111,8 @@ import com.android.server.location.injector.SettingsHelper;
import com.android.server.location.injector.SystemAlarmHelper;
import com.android.server.location.injector.SystemAppForegroundHelper;
import com.android.server.location.injector.SystemAppOpsHelper;
+import com.android.server.location.injector.SystemDeviceIdleHelper;
+import com.android.server.location.injector.SystemDeviceStationaryHelper;
import com.android.server.location.injector.SystemEmergencyHelper;
import com.android.server.location.injector.SystemLocationPermissionsHelper;
import com.android.server.location.injector.SystemLocationPowerSaveModeHelper;
@@ -118,6 +125,7 @@ import com.android.server.location.provider.LocationProviderManager;
import com.android.server.location.provider.MockLocationProvider;
import com.android.server.location.provider.PassiveLocationProvider;
import com.android.server.location.provider.PassiveLocationProviderManager;
+import com.android.server.location.provider.StationaryThrottlingLocationProvider;
import com.android.server.location.provider.proxy.ProxyLocationProvider;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
@@ -146,9 +154,10 @@ public class LocationManagerService extends ILocationManager.Stub {
public Lifecycle(Context context) {
super(context);
+ LocationEventLog eventLog = new LocationEventLog();
mUserInfoHelper = new LifecycleUserInfoHelper(context);
- mSystemInjector = new SystemInjector(context, mUserInfoHelper);
- mService = new LocationManagerService(context, mSystemInjector);
+ mSystemInjector = new SystemInjector(context, mUserInfoHelper, eventLog);
+ mService = new LocationManagerService(context, mSystemInjector, eventLog);
}
@Override
@@ -158,7 +167,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// client caching behavior is only enabled after seeing the first invalidate
LocationManager.invalidateLocalLocationEnabledCaches();
// disable caching for our own process
- Objects.requireNonNull(mService.mContext.getSystemService(LocationManager.class))
+ Objects.requireNonNull(getContext().getSystemService(LocationManager.class))
.disableLocalLocationEnabledCaches();
}
@@ -220,6 +229,7 @@ public class LocationManagerService extends ILocationManager.Stub {
private final Context mContext;
private final Injector mInjector;
+ private final LocationEventLog mEventLog;
private final LocalService mLocalService;
private final GeofenceManager mGeofenceManager;
@@ -249,10 +259,10 @@ public class LocationManagerService extends ILocationManager.Stub {
private final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers =
new CopyOnWriteArrayList<>();
- LocationManagerService(Context context, Injector injector) {
+ LocationManagerService(Context context, Injector injector, LocationEventLog eventLog) {
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mInjector = injector;
-
+ mEventLog = eventLog;
mLocalService = new LocalService();
LocalServices.addService(LocationManagerInternal.class, mLocalService);
@@ -260,7 +270,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// set up passive provider first since it will be required for all other location providers,
// which are loaded later once the system is ready.
- mPassiveManager = new PassiveLocationProviderManager(mContext, injector);
+ mPassiveManager = new PassiveLocationProviderManager(mContext, injector, mEventLog);
addLocationProviderManager(mPassiveManager, new PassiveLocationProvider(mContext));
// TODO: load the gps provider here as well, which will require refactoring
@@ -301,7 +311,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
LocationProviderManager manager = new LocationProviderManager(mContext, mInjector,
- providerName, mPassiveManager);
+ mEventLog, providerName, mPassiveManager);
addLocationProviderManager(manager, null);
return manager;
}
@@ -314,6 +324,18 @@ public class LocationManagerService extends ILocationManager.Stub {
manager.startManager();
if (realProvider != null) {
+
+ // custom logic wrapping all non-passive providers
+ if (manager != mPassiveManager) {
+ boolean enableStationaryThrottling = Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.LOCATION_ENABLE_STATIONARY_THROTTLE, 1) != 0;
+ if (enableStationaryThrottling) {
+ realProvider = new StationaryThrottlingLocationProvider(manager.getName(),
+ mInjector, realProvider, mEventLog);
+ }
+ }
+
manager.setRealProvider(realProvider);
}
mProviderManagers.add(manager);
@@ -345,7 +367,7 @@ public class LocationManagerService extends ILocationManager.Stub {
com.android.internal.R.string.config_networkLocationProviderPackageName);
if (networkProvider != null) {
LocationProviderManager networkManager = new LocationProviderManager(mContext,
- mInjector, NETWORK_PROVIDER, mPassiveManager);
+ mInjector, mEventLog, NETWORK_PROVIDER, mPassiveManager);
addLocationProviderManager(networkManager, networkProvider);
} else {
Log.w(TAG, "no network location provider found");
@@ -364,7 +386,7 @@ public class LocationManagerService extends ILocationManager.Stub {
com.android.internal.R.string.config_fusedLocationProviderPackageName);
if (fusedProvider != null) {
LocationProviderManager fusedManager = new LocationProviderManager(mContext, mInjector,
- FUSED_PROVIDER, mPassiveManager);
+ mEventLog, FUSED_PROVIDER, mPassiveManager);
addLocationProviderManager(fusedManager, fusedProvider);
} else {
Log.wtf(TAG, "no fused location provider found");
@@ -379,7 +401,7 @@ public class LocationManagerService extends ILocationManager.Stub {
mGnssManagerService.onSystemReady();
LocationProviderManager gnssManager = new LocationProviderManager(mContext, mInjector,
- GPS_PROVIDER, mPassiveManager);
+ mEventLog, GPS_PROVIDER, mPassiveManager);
addLocationProviderManager(gnssManager, mGnssManagerService.getGnssLocationProvider());
}
@@ -442,7 +464,7 @@ public class LocationManagerService extends ILocationManager.Stub {
Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
}
- mInjector.getLocationEventLog().logLocationEnabled(userId, enabled);
+ mEventLog.logLocationEnabled(userId, enabled);
Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled)
@@ -894,6 +916,20 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
+ public void addProviderRequestListener(IProviderRequestListener listener) {
+ for (LocationProviderManager manager : mProviderManagers) {
+ manager.addProviderRequestListener(listener);
+ }
+ }
+
+ @Override
+ public void removeProviderRequestListener(IProviderRequestListener listener) {
+ for (LocationProviderManager manager : mProviderManagers) {
+ manager.removeProviderRequestListener(listener);
+ }
+ }
+
+ @Override
public void injectGnssMeasurementCorrections(GnssMeasurementCorrections corrections) {
if (mGnssManagerService != null) {
mGnssManagerService.injectGnssMeasurementCorrections(corrections);
@@ -1190,9 +1226,27 @@ public class LocationManagerService extends ILocationManager.Stub {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- if (mGnssManagerService != null && args.length > 0 && args[0].equals("--gnssmetrics")) {
- mGnssManagerService.dump(fd, ipw, args);
- return;
+ if (args.length > 0) {
+ LocationProviderManager manager = getLocationProviderManager(args[0]);
+ if (manager != null) {
+ ipw.println("Provider:");
+ ipw.increaseIndent();
+ manager.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+
+ ipw.println("Event Log:");
+ ipw.increaseIndent();
+ mEventLog.iterate(manager.getName(), ipw::println);
+ ipw.decreaseIndent();
+ return;
+ }
+
+ if ("--gnssmetrics".equals(args[0])) {
+ if (mGnssManagerService != null) {
+ mGnssManagerService.dump(fd, ipw, args);
+ }
+ return;
+ }
}
ipw.println("Location Manager State:");
@@ -1224,6 +1278,26 @@ public class LocationManagerService extends ILocationManager.Stub {
}
ipw.decreaseIndent();
+ ipw.println("Historical Aggregate Location Provider Data:");
+ ipw.increaseIndent();
+ ArrayMap<String, ArrayMap<String, LocationEventLog.AggregateStats>> aggregateStats =
+ mEventLog.copyAggregateStats();
+ for (int i = 0; i < aggregateStats.size(); i++) {
+ ipw.print(aggregateStats.keyAt(i));
+ ipw.println(":");
+ ipw.increaseIndent();
+ ArrayMap<String, LocationEventLog.AggregateStats> providerStats =
+ aggregateStats.valueAt(i);
+ for (int j = 0; j < providerStats.size(); j++) {
+ ipw.print(providerStats.keyAt(j));
+ ipw.print(": ");
+ providerStats.valueAt(j).updateTotals();
+ ipw.println(providerStats.valueAt(j));
+ }
+ ipw.decreaseIndent();
+ }
+ ipw.decreaseIndent();
+
if (mGnssManagerService != null) {
ipw.println("GNSS Manager:");
ipw.increaseIndent();
@@ -1238,7 +1312,7 @@ public class LocationManagerService extends ILocationManager.Stub {
ipw.println("Event Log:");
ipw.increaseIndent();
- mInjector.getLocationEventLog().iterate(ipw::println);
+ mEventLog.iterate(ipw::println);
ipw.decreaseIndent();
}
@@ -1317,7 +1391,6 @@ public class LocationManagerService extends ILocationManager.Stub {
private final Context mContext;
private final UserInfoHelper mUserInfoHelper;
- private final LocationEventLog mLocationEventLog;
private final AlarmHelper mAlarmHelper;
private final SystemAppOpsHelper mAppOpsHelper;
private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
@@ -1325,6 +1398,8 @@ public class LocationManagerService extends ILocationManager.Stub {
private final SystemAppForegroundHelper mAppForegroundHelper;
private final SystemLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
private final SystemScreenInteractiveHelper mScreenInteractiveHelper;
+ private final SystemDeviceStationaryHelper mDeviceStationaryHelper;
+ private final SystemDeviceIdleHelper mDeviceIdleHelper;
private final LocationAttributionHelper mLocationAttributionHelper;
private final LocationUsageLogger mLocationUsageLogger;
@@ -1336,20 +1411,20 @@ public class LocationManagerService extends ILocationManager.Stub {
@GuardedBy("this")
private boolean mSystemReady;
- SystemInjector(Context context, UserInfoHelper userInfoHelper) {
+ SystemInjector(Context context, UserInfoHelper userInfoHelper, LocationEventLog eventLog) {
mContext = context;
mUserInfoHelper = userInfoHelper;
- mLocationEventLog = new LocationEventLog();
mAlarmHelper = new SystemAlarmHelper(context);
mAppOpsHelper = new SystemAppOpsHelper(context);
mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
mAppOpsHelper);
mSettingsHelper = new SystemSettingsHelper(context);
mAppForegroundHelper = new SystemAppForegroundHelper(context);
- mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context,
- mLocationEventLog);
+ mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context, eventLog);
mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context);
+ mDeviceStationaryHelper = new SystemDeviceStationaryHelper();
+ mDeviceIdleHelper = new SystemDeviceIdleHelper(context);
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
mLocationUsageLogger = new LocationUsageLogger();
}
@@ -1361,6 +1436,8 @@ public class LocationManagerService extends ILocationManager.Stub {
mAppForegroundHelper.onSystemReady();
mLocationPowerSaveModeHelper.onSystemReady();
mScreenInteractiveHelper.onSystemReady();
+ mDeviceStationaryHelper.onSystemReady();
+ mDeviceIdleHelper.onSystemReady();
if (mEmergencyCallHelper != null) {
mEmergencyCallHelper.onSystemReady();
@@ -1410,6 +1487,16 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
+ public DeviceStationaryHelper getDeviceStationaryHelper() {
+ return mDeviceStationaryHelper;
+ }
+
+ @Override
+ public DeviceIdleHelper getDeviceIdleHelper() {
+ return mDeviceIdleHelper;
+ }
+
+ @Override
public LocationAttributionHelper getLocationAttributionHelper() {
return mLocationAttributionHelper;
}
@@ -1427,11 +1514,6 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
- public LocationEventLog getLocationEventLog() {
- return mLocationEventLog;
- }
-
- @Override
public LocationUsageLogger getLocationUsageLogger() {
return mLocationUsageLogger;
}
diff --git a/services/core/java/com/android/server/location/contexthub/AuthStateDenialTimer.java b/services/core/java/com/android/server/location/contexthub/AuthStateDenialTimer.java
new file mode 100644
index 000000000000..85de4bb1eefc
--- /dev/null
+++ b/services/core/java/com/android/server/location/contexthub/AuthStateDenialTimer.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.server.location.contexthub;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+
+/**
+ * A class that manages a timer used to keep track of how much time is left before a
+ * {@link ContextHubClientBroker} has its authorization state changed with a nanoapp from
+ * DENIED_GRACE_PERIOD to DENIED. Much of this implementation is copied from
+ * {@link android.os.CountDownTimer} while adding the ability to specify the provided looper.
+ *
+ * @hide
+ */
+public class AuthStateDenialTimer {
+ private static final long TIMEOUT_MS = SECONDS.toMillis(60);
+
+ private final ContextHubClientBroker mClient;
+ private final long mNanoAppId;
+ private final Handler mHandler;
+
+ /**
+ * Indicates when the timer should stop in the future.
+ */
+ private long mStopTimeInFuture;
+
+ /**
+ * boolean representing if the timer was cancelled
+ */
+ private boolean mCancelled = false;
+
+ public AuthStateDenialTimer(ContextHubClientBroker client, long nanoAppId, Looper looper) {
+ mClient = client;
+ mNanoAppId = nanoAppId;
+ mHandler = new CountDownHandler(looper);
+ }
+
+ /**
+ * Cancel the countdown.
+ */
+ public synchronized void cancel() {
+ mCancelled = true;
+ mHandler.removeMessages(MSG);
+ }
+
+ /**
+ * Start the countdown.
+ */
+ public synchronized void start() {
+ mCancelled = false;
+ mStopTimeInFuture = SystemClock.elapsedRealtime() + TIMEOUT_MS;
+ mHandler.sendMessage(mHandler.obtainMessage(MSG));
+ }
+
+ /**
+ * Called when the timer has expired.
+ */
+ public void onFinish() {
+ mClient.handleAuthStateTimerExpiry(mNanoAppId);
+ }
+
+ // Message type used to trigger the timer.
+ private static final int MSG = 1;
+
+ private class CountDownHandler extends Handler {
+
+ CountDownHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ synchronized (AuthStateDenialTimer.this) {
+ if (mCancelled) {
+ return;
+ }
+ final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
+ if (millisLeft <= 0) {
+ onFinish();
+ } else {
+ sendMessageDelayed(obtainMessage(MSG), millisLeft);
+ }
+ }
+ }
+ };
+}
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 cc510fbc38bd..6249a068f591 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -17,31 +17,38 @@
package com.android.server.location.contexthub;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.hardware.location.ContextHubManager.AUTHORIZATION_DENIED;
+import static android.hardware.location.ContextHubManager.AUTHORIZATION_DENIED_GRACE_PERIOD;
+import static android.hardware.location.ContextHubManager.AUTHORIZATION_GRANTED;
import android.Manifest;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.hardware.contexthub.V1_0.ContextHubMsg;
-import android.hardware.contexthub.V1_0.IContexthub;
import android.hardware.contexthub.V1_0.Result;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubManager;
import android.hardware.location.ContextHubTransaction;
import android.hardware.location.IContextHubClient;
import android.hardware.location.IContextHubClientCallback;
+import android.hardware.location.IContextHubTransactionCallback;
import android.hardware.location.NanoAppMessage;
+import android.hardware.location.NanoAppState;
import android.os.Binder;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
import com.android.server.location.ClientBrokerProto;
-import java.util.Collections;
import java.util.Iterator;
-import java.util.Set;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
@@ -51,14 +58,53 @@ import java.util.function.Supplier;
* notification callbacks. This class implements the IContextHubClient object, and the implemented
* APIs must be thread-safe.
*
+ * Additionally, this class is responsible for enforcing permissions usage and attribution are
+ * handled appropriately for a given client. In general, this works as follows:
+ *
+ * Client sending a message to a nanoapp:
+ * 1) When initially sending a message to nanoapps, clients are by default in a grace period state
+ * which allows them to always send their first message to nanoapps. This is done to allow
+ * clients (especially callback clients) to reset their conection to the nanoapp if they are
+ * killed / restarted (e.g. following a permission revocation).
+ * 2) After the initial message is sent, a check of permissions state is performed. If the
+ * client doesn't have permissions to communicate, it is placed into the denied grace period
+ * state and notified so that it can clean up its communication before it is completely denied
+ * access.
+ * 3) For subsequent messages, the auth state is checked synchronously and messages are denied if
+ * the client is denied authorization
+ *
+ * Client receiving a message from a nanoapp:
+ * 1) If a nanoapp sends a message to the client, the authentication state is checked synchronously.
+ * If there has been no message between the two before, the auth state is assumed granted.
+ * 2) The broker then checks that the client has all permissions the nanoapp requires and attributes
+ * all permissions required to consume the message being sent. If both of those checks pass, then
+ * the message is delivered. Otherwise, it's dropped.
+ *
+ * Client losing or gaining permissions (callback client):
+ * 1) Clients are killed when they lose permissions. This will cause callback clients to completely
+ * disconnect from the service. When they are restarted, their initial message will still be
+ * be allowed through and their permissions will be rechecked at that time.
+ * 2) If they gain a permission, the broker will notify them if that permission allows them to
+ * communicate with a nanoapp again.
+ *
+ * Client losing or gaining permissions (PendingIntent client):
+ * 1) Unlike callback clients, PendingIntent clients are able to maintain their connection to the
+ * service when they are killed. In their case, they will receive notifications of the broker
+ * that they have been denied required permissions or gain required permissions.
+ *
* TODO: Consider refactoring this class via inheritance
*
* @hide
*/
public class ContextHubClientBroker extends IContextHubClient.Stub
- implements IBinder.DeathRecipient {
+ implements IBinder.DeathRecipient, AppOpsManager.OnOpChangedListener {
private static final String TAG = "ContextHubClientBroker";
+ /**
+ * Message used by noteOp when this client receives a message from a nanoapp.
+ */
+ private static final String RECEIVE_MSG_NOTE = "NanoappMessageDelivery ";
+
/*
* The context of the service.
*/
@@ -67,7 +113,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
/*
* The proxy to talk to the Context Hub HAL.
*/
- private final IContexthub mContextHubProxy;
+ private final IContextHubWrapper mContextHubProxy;
/*
* The manager that registered this client.
@@ -95,6 +141,12 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
*/
private boolean mRegistered = true;
+ /**
+ * String containing an attribution tag that was denoted in the {@link Context} of the
+ * creator of this broker. This is used when attributing the permissions usage of the broker.
+ */
+ private @Nullable String mAttributionTag;
+
/*
* Internal interface used to invoke client callbacks.
*/
@@ -112,6 +164,26 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
*/
private final String mPackage;
+ /**
+ * The PID associated with this client.
+ */
+ private final int mPid;
+
+ /**
+ * The UID associated with this client.
+ */
+ private final int mUid;
+
+ /**
+ * Manager used for noting permissions usage of this broker.
+ */
+ private final AppOpsManager mAppOpsManager;
+
+ /**
+ * Manager used to queue transactions to the context hub.
+ */
+ private final ContextHubTransactionManager mTransactionManager;
+
/*
* True if a PendingIntent has been cancelled.
*/
@@ -123,11 +195,44 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
private final boolean mHasAccessContextHubPermission;
/*
- * The set of nanoapp IDs that represent the group of nanoapps this client has a messaging
- * channel with, i.e. has sent or received messages from this particular nanoapp.
+ * Map containing all nanoapps this client has a messaging channel with and whether it is
+ * 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>();
+
+ /**
+ * Map containing all nanoapps that have active auth state denial timers.
+ */
+ private final Map<Long, AuthStateDenialTimer> mNappToAuthTimerMap =
+ new ConcurrentHashMap<Long, AuthStateDenialTimer>();
+
+ /**
+ * Callback used to obtain the latest set of nanoapp permissions and verify this client has
+ * each nanoapps permissions granted.
*/
- private final Set<Long> mMessageChannelNanoappIdSet =
- Collections.newSetFromMap(new ConcurrentHashMap<Long, Boolean>());
+ private final IContextHubTransactionCallback mQueryPermsCallback =
+ new IContextHubTransactionCallback.Stub() {
+ @Override
+ public void onTransactionComplete(int result) {
+ }
+
+ @Override
+ public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+ if (result != ContextHubTransaction.RESULT_SUCCESS && nanoAppStateList != null) {
+ Log.e(TAG, "Permissions query failed, but still received nanoapp state");
+ } else if (nanoAppStateList != null) {
+ for (NanoAppState state : nanoAppStateList) {
+ if (mMessageChannelNanoappIdMap.containsKey(state.getNanoAppId())) {
+ List<String> permissions = state.getNanoAppPermissions();
+ updateNanoAppAuthState(state.getNanoAppId(),
+ hasPermissions(permissions), false /* gracePeriodExpired */);
+ }
+ }
+ }
+ }
+ };
/*
* Helper class to manage registered PendingIntent requests from the client.
@@ -175,37 +280,57 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
}
}
- /* package */ ContextHubClientBroker(
- Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
- ContextHubInfo contextHubInfo, short hostEndPointId,
- IContextHubClientCallback callback) {
+ private ContextHubClientBroker(Context context, IContextHubWrapper contextHubProxy,
+ ContextHubClientManager clientManager, ContextHubInfo contextHubInfo,
+ short hostEndPointId, IContextHubClientCallback callback, String attributionTag,
+ ContextHubTransactionManager transactionManager, PendingIntent pendingIntent,
+ long nanoAppId, String packageName) {
mContext = context;
mContextHubProxy = contextHubProxy;
mClientManager = clientManager;
mAttachedContextHubInfo = contextHubInfo;
mHostEndPointId = hostEndPointId;
mCallbackInterface = callback;
- mPendingIntentRequest = new PendingIntentRequest();
- mPackage = mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
+ if (pendingIntent == null) {
+ mPendingIntentRequest = new PendingIntentRequest();
+ } else {
+ mPendingIntentRequest = new PendingIntentRequest(pendingIntent, nanoAppId);
+ }
+ mPackage = packageName;
+ mAttributionTag = attributionTag;
+ mTransactionManager = transactionManager;
+ mPid = Binder.getCallingPid();
+ mUid = Binder.getCallingUid();
mHasAccessContextHubPermission = context.checkCallingPermission(
Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED;
+ mAppOpsManager = context.getSystemService(AppOpsManager.class);
+
+ startMonitoringOpChanges();
}
/* package */ ContextHubClientBroker(
- Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
- ContextHubInfo contextHubInfo, short hostEndPointId, PendingIntent pendingIntent,
- long nanoAppId) {
- mContext = context;
- mContextHubProxy = contextHubProxy;
- mClientManager = clientManager;
- mAttachedContextHubInfo = contextHubInfo;
- mHostEndPointId = hostEndPointId;
- mPendingIntentRequest = new PendingIntentRequest(pendingIntent, nanoAppId);
- mPackage = pendingIntent.getCreatorPackage();
+ Context context, IContextHubWrapper contextHubProxy,
+ ContextHubClientManager clientManager, ContextHubInfo contextHubInfo,
+ short hostEndPointId, IContextHubClientCallback callback, String attributionTag,
+ ContextHubTransactionManager transactionManager, String packageName) {
+ this(context, contextHubProxy, clientManager, contextHubInfo, hostEndPointId, callback,
+ attributionTag, transactionManager, null /* pendingIntent */, 0 /* nanoAppId */,
+ packageName);
+ }
- mHasAccessContextHubPermission = context.checkCallingPermission(
- Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED;
+ /* package */ ContextHubClientBroker(
+ Context context, IContextHubWrapper contextHubProxy,
+ ContextHubClientManager clientManager, ContextHubInfo contextHubInfo,
+ short hostEndPointId, PendingIntent pendingIntent, long nanoAppId,
+ String attributionTag, ContextHubTransactionManager transactionManager) {
+ this(context, contextHubProxy, clientManager, contextHubInfo, hostEndPointId,
+ null /* callback */, attributionTag, transactionManager, pendingIntent, nanoAppId,
+ pendingIntent.getCreatorPackage());
+ }
+
+ private void startMonitoringOpChanges() {
+ mAppOpsManager.startWatchingMode(AppOpsManager.OP_NONE, mPackage, this);
}
/**
@@ -219,9 +344,38 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
public int sendMessageToNanoApp(NanoAppMessage message) {
ContextHubServiceUtil.checkPermissions(mContext);
+ int authState;
+ synchronized (mMessageChannelNanoappIdMap) {
+ // Default to the granted auth state. The true auth state will be checked async if it's
+ // not denied.
+ authState = mMessageChannelNanoappIdMap.getOrDefault(
+ message.getNanoAppId(), AUTHORIZATION_GRANTED);
+ if (authState == AUTHORIZATION_DENIED) {
+ return ContextHubTransaction.RESULT_FAILED_PERMISSION_DENIED;
+ }
+ }
+
int result;
if (isRegistered()) {
- mMessageChannelNanoappIdSet.add(message.getNanoAppId());
+ // Even though the auth state is currently not denied, query the nanoapp permissions
+ // async and verify that the host app currently holds all the requisite permissions.
+ // This can't be done synchronously due to the async query that needs to be performed to
+ // obtain the nanoapp permissions.
+ boolean initialNanoappMessage = false;
+ synchronized (mMessageChannelNanoappIdMap) {
+ if (mMessageChannelNanoappIdMap.get(message.getNanoAppId()) == null) {
+ mMessageChannelNanoappIdMap.put(message.getNanoAppId(), AUTHORIZATION_GRANTED);
+ initialNanoappMessage = true;
+ }
+ }
+
+ if (initialNanoappMessage) {
+ // Only check permissions the first time a nanoapp is queried since nanoapp
+ // permissions don't currently change at runtime. If the host permission changes
+ // later, that'll be checked by onOpChanged.
+ checkNanoappPermsAsync();
+ }
+
ContextHubMsg messageToNanoApp =
ContextHubServiceUtil.createHidlContextHubMessage(mHostEndPointId, message);
@@ -262,6 +416,34 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
onClientExit();
}
+ @Override
+ public void onOpChanged(String op, String packageName) {
+ if (packageName.equals(mPackage)) {
+ if (!mMessageChannelNanoappIdMap.isEmpty()) {
+ checkNanoappPermsAsync();
+ }
+ }
+ }
+
+ /* package */ String getPackageName() {
+ return mPackage;
+ }
+
+ /**
+ * Used to override the attribution tag with a newer value if a PendingIntent broker is
+ * retrieved.
+ */
+ /* package */ void setAttributionTag(String attributionTag) {
+ mAttributionTag = attributionTag;
+ }
+
+ /**
+ * @return the attribution tag associated with this broker.
+ */
+ /* package */ String getAttributionTag() {
+ return mAttributionTag;
+ }
+
/**
* @return the ID of the context hub this client is attached to
*/
@@ -280,15 +462,39 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* Sends a message to the client associated with this object.
*
* @param message the message that came from a nanoapp
- */
- /* package */ void sendMessageToClient(NanoAppMessage message) {
- mMessageChannelNanoappIdSet.add(message.getNanoAppId());
+ * @param nanoappPermissions permissions required to communicate with the nanoapp sending this
+ * message
+ * @param messagePermissions permissions required to consume the message being delivered. These
+ * permissions are what will be attributed to the client through noteOp.
+ */
+ /* package */ void sendMessageToClient(
+ NanoAppMessage message, List<String> nanoappPermissions,
+ List<String> messagePermissions) {
+ long nanoAppId = message.getNanoAppId();
+
+ int authState = mMessageChannelNanoappIdMap.getOrDefault(nanoAppId, AUTHORIZATION_GRANTED);
+
+ // If in the grace period, the host may not receive any messages containing permissions
+ // covered data.
+ if (authState == AUTHORIZATION_DENIED_GRACE_PERIOD && !messagePermissions.isEmpty()) {
+ Log.e(TAG, "Dropping message from " + Long.toHexString(nanoAppId) + ". " + mPackage
+ + " in grace period and napp msg has permissions");
+ return;
+ }
+
+ if (authState == AUTHORIZATION_DENIED || !hasPermissions(nanoappPermissions)
+ || !notePermissions(messagePermissions, RECEIVE_MSG_NOTE + nanoAppId)) {
+ Log.e(TAG, "Dropping message from " + Long.toHexString(nanoAppId) + ". " + mPackage
+ + " doesn't have permission");
+ return;
+ }
+
invokeCallback(callback -> callback.onMessageFromNanoApp(message));
Supplier<Intent> supplier =
- () -> createIntent(ContextHubManager.EVENT_NANOAPP_MESSAGE, message.getNanoAppId())
+ () -> createIntent(ContextHubManager.EVENT_NANOAPP_MESSAGE, nanoAppId)
.putExtra(ContextHubManager.EXTRA_MESSAGE, message);
- sendPendingIntent(supplier, message.getNanoAppId());
+ sendPendingIntent(supplier, nanoAppId);
}
/**
@@ -297,6 +503,10 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* @param nanoAppId the ID of the nanoapp that was loaded.
*/
/* package */ void onNanoAppLoaded(long nanoAppId) {
+ // Check the latest state to see if the loaded nanoapp's permissions changed such that the
+ // host app can communicate with it again.
+ checkNanoappPermsAsync();
+
invokeCallback(callback -> callback.onNanoAppLoaded(nanoAppId));
sendPendingIntent(
() -> createIntent(ContextHubManager.EVENT_NANOAPP_LOADED, nanoAppId), nanoAppId);
@@ -364,6 +574,43 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
}
/**
+ * Checks that this client has all of the provided permissions.
+ *
+ * @param permissions list of permissions to check
+ * @return true if the client has all of the permissions granted
+ */
+ /* package */ boolean hasPermissions(List<String> permissions) {
+ for (String permission : permissions) {
+ if (mContext.checkPermission(permission, mPid, mUid) != PERMISSION_GRANTED) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Attributes the provided permissions to the package of this client.
+ *
+ * @param permissions list of permissions covering data the client is about to receive
+ * @param noteMessage message that should be noted alongside permissions attribution to
+ * facilitate debugging
+ * @return true if client has ability to use all of the provided permissions
+ */
+ /* package */ boolean notePermissions(List<String> permissions, String noteMessage) {
+ for (String permission : permissions) {
+ int opCode = mAppOpsManager.permissionToOpCode(permission);
+ if (opCode != AppOpsManager.OP_NONE) {
+ if (mAppOpsManager.noteOp(opCode, mUid, mPackage, mAttributionTag, noteMessage)
+ != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
* @return true if the client is a PendingIntent client that has been cancelled.
*/
/* package */ boolean isPendingIntentCancelled() {
@@ -371,6 +618,101 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
}
/**
+ * Handles timer expiry for a client whose auth state with a nanoapp was previously in the grace
+ * period.
+ */
+ /* package */ void handleAuthStateTimerExpiry(long nanoAppId) {
+ AuthStateDenialTimer timer;
+ synchronized (mMessageChannelNanoappIdMap) {
+ timer = mNappToAuthTimerMap.remove(nanoAppId);
+ }
+
+ if (timer != null) {
+ updateNanoAppAuthState(
+ nanoAppId, false /* hasPermissions */, true /* gracePeriodExpired */);
+ }
+ }
+
+ /**
+ * Verifies this client has the permissions to communicate with all of the nanoapps it has
+ * communicated with in the past.
+ */
+ private void checkNanoappPermsAsync() {
+ ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
+ mAttachedContextHubInfo.getId(), mQueryPermsCallback, mPackage);
+ mTransactionManager.addTransaction(transaction);
+ }
+
+ /**
+ * Updates the latest authentication state for this client to be able to communicate with the
+ * given nanoapp.
+ */
+ private void updateNanoAppAuthState(
+ long nanoAppId, boolean hasPermissions, boolean gracePeriodExpired) {
+ updateNanoAppAuthState(
+ nanoAppId, hasPermissions, gracePeriodExpired, false /* forceDenied */);
+ }
+
+ /* package */ void updateNanoAppAuthState(
+ long nanoAppId, boolean hasPermissions, boolean gracePeriodExpired,
+ boolean forceDenied) {
+ int curAuthState;
+ int newAuthState;
+ synchronized (mMessageChannelNanoappIdMap) {
+ curAuthState = mMessageChannelNanoappIdMap.getOrDefault(
+ nanoAppId, AUTHORIZATION_GRANTED);
+ newAuthState = curAuthState;
+ // The below logic ensures that only the following transitions are possible:
+ // GRANTED -> DENIED_GRACE_PERIOD only if permissions have been lost
+ // DENIED_GRACE_PERIOD -> DENIED only if the grace period expires
+ // DENIED/DENIED_GRACE_PERIOD -> GRANTED only if permissions are granted again
+ // any state -> DENIED if "forceDenied" is true
+ if (forceDenied) {
+ newAuthState = AUTHORIZATION_DENIED;
+ } else if (gracePeriodExpired) {
+ if (curAuthState == AUTHORIZATION_DENIED_GRACE_PERIOD) {
+ newAuthState = AUTHORIZATION_DENIED;
+ }
+ } else {
+ if (curAuthState == AUTHORIZATION_GRANTED && !hasPermissions) {
+ newAuthState = AUTHORIZATION_DENIED_GRACE_PERIOD;
+ } else if (curAuthState != AUTHORIZATION_GRANTED && hasPermissions) {
+ newAuthState = AUTHORIZATION_GRANTED;
+ }
+ }
+
+ if (newAuthState != AUTHORIZATION_DENIED_GRACE_PERIOD) {
+ AuthStateDenialTimer timer = mNappToAuthTimerMap.remove(nanoAppId);
+ if (timer != null) {
+ timer.cancel();
+ }
+ } else if (curAuthState == AUTHORIZATION_GRANTED) {
+ AuthStateDenialTimer timer =
+ new AuthStateDenialTimer(this, nanoAppId, Looper.getMainLooper());
+ mNappToAuthTimerMap.put(nanoAppId, timer);
+ timer.start();
+ }
+
+ if (curAuthState != newAuthState) {
+ mMessageChannelNanoappIdMap.put(nanoAppId, newAuthState);
+ }
+ }
+ if (curAuthState != newAuthState) {
+ // Don't send the callback in the synchronized block or it could end up in a deadlock.
+ sendAuthStateCallback(nanoAppId, newAuthState);
+ }
+ }
+
+ private void sendAuthStateCallback(long nanoAppId, int authState) {
+ invokeCallback(callback -> callback.onClientAuthorizationChanged(nanoAppId, authState));
+
+ Supplier<Intent> supplier =
+ () -> createIntent(ContextHubManager.EVENT_CLIENT_AUTHORIZATION, nanoAppId)
+ .putExtra(ContextHubManager.EXTRA_CLIENT_AUTHORIZATION_STATE, authState);
+ sendPendingIntent(supplier, nanoAppId);
+ }
+
+ /**
* Helper function to invoke a specified client callback, if the connection is open.
*
* @param consumer the consumer specifying the callback to invoke
@@ -479,6 +821,20 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
mClientManager.unregisterClient(mHostEndPointId);
mRegistered = false;
}
+ mAppOpsManager.stopWatchingMode(this);
+ }
+
+ private String authStateToString(@ContextHubManager.AuthorizationState int state) {
+ switch (state) {
+ case AUTHORIZATION_DENIED:
+ return "DENIED";
+ case AUTHORIZATION_DENIED_GRACE_PERIOD:
+ return "DENIED_GRACE_PERIOD";
+ case AUTHORIZATION_GRANTED:
+ return "GRANTED";
+ default:
+ return "UNKNOWN";
+ }
}
/**
@@ -508,17 +864,23 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
String out = "[ContextHubClient ";
out += "endpointID: " + getHostEndPointId() + ", ";
out += "contextHub: " + getAttachedContextHubId() + ", ";
+ if (mAttributionTag != null) {
+ out += "attributionTag: " + getAttributionTag() + ", ";
+ }
if (mPendingIntentRequest.isValid()) {
out += "intentCreatorPackage: " + mPackage + ", ";
out += "nanoAppId: 0x" + Long.toHexString(mPendingIntentRequest.getNanoAppId());
} else {
out += "package: " + mPackage;
}
- if (mMessageChannelNanoappIdSet.size() > 0) {
+ if (mMessageChannelNanoappIdMap.size() > 0) {
out += " messageChannelNanoappSet: (";
- Iterator<Long> it = mMessageChannelNanoappIdSet.iterator();
+ Iterator<Map.Entry<Long, Integer>> it =
+ mMessageChannelNanoappIdMap.entrySet().iterator();
while (it.hasNext()) {
- out += "0x" + Long.toHexString(it.next());
+ Map.Entry<Long, Integer> entry = it.next();
+ out += "0x" + Long.toHexString(entry.getKey()) + " auth state: "
+ + authStateToString(entry.getValue());
if (it.hasNext()) {
out += ",";
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java
index eda89ab52c6f..e3522f6d487c 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java
@@ -20,7 +20,6 @@ import android.annotation.IntDef;
import android.app.PendingIntent;
import android.content.Context;
import android.hardware.contexthub.V1_0.ContextHubMsg;
-import android.hardware.contexthub.V1_0.IContexthub;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.IContextHubClient;
import android.hardware.location.IContextHubClientCallback;
@@ -37,6 +36,7 @@ import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
+import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -71,7 +71,7 @@ import java.util.function.Consumer;
/*
* The proxy to talk to the Context Hub.
*/
- private final IContexthub mContextHubProxy;
+ private final IContextHubWrapper mContextHubProxy;
/*
* A mapping of host endpoint IDs to the ContextHubClientBroker object of registered clients.
@@ -137,8 +137,7 @@ import java.util.function.Consumer;
}
}
- /* package */ ContextHubClientManager(
- Context context, IContexthub contextHubProxy) {
+ /* package */ ContextHubClientManager(Context context, IContextHubWrapper contextHubProxy) {
mContext = context;
mContextHubProxy = contextHubProxy;
}
@@ -148,19 +147,23 @@ import java.util.function.Consumer;
*
* @param contextHubInfo the object describing the hub this client is attached to
* @param clientCallback the callback interface of the client to register
+ * @param attributionTag an optional attribution tag within the given package
*
* @return the client interface
*
* @throws IllegalStateException if max number of clients have already registered
*/
/* package */ IContextHubClient registerClient(
- ContextHubInfo contextHubInfo, IContextHubClientCallback clientCallback) {
+ ContextHubInfo contextHubInfo, IContextHubClientCallback clientCallback,
+ String attributionTag, ContextHubTransactionManager transactionManager,
+ String packageName) {
ContextHubClientBroker broker;
synchronized (this) {
short hostEndPointId = getHostEndPointId();
broker = new ContextHubClientBroker(
mContext, mContextHubProxy, this /* clientManager */, contextHubInfo,
- hostEndPointId, clientCallback);
+ hostEndPointId, clientCallback, attributionTag, transactionManager,
+ packageName);
mHostEndPointIdToClientMap.put(hostEndPointId, broker);
mRegistrationRecordDeque.add(
new RegistrationRecord(broker.toString(), ACTION_REGISTERED));
@@ -185,13 +188,15 @@ import java.util.function.Consumer;
* @param pendingIntent the callback interface of the client to register
* @param contextHubInfo the object describing the hub this client is attached to
* @param nanoAppId the ID of the nanoapp to receive Intent events for
+ * @param attributionTag an optional attribution tag within the given package
*
* @return the client interface
*
* @throws IllegalStateException if there were too many registered clients at the service
*/
/* package */ IContextHubClient registerClient(
- ContextHubInfo contextHubInfo, PendingIntent pendingIntent, long nanoAppId) {
+ ContextHubInfo contextHubInfo, PendingIntent pendingIntent, long nanoAppId,
+ String attributionTag, ContextHubTransactionManager transactionManager) {
ContextHubClientBroker broker;
String registerString = "Regenerated";
synchronized (this) {
@@ -201,11 +206,16 @@ import java.util.function.Consumer;
short hostEndPointId = getHostEndPointId();
broker = new ContextHubClientBroker(
mContext, mContextHubProxy, this /* clientManager */, contextHubInfo,
- hostEndPointId, pendingIntent, nanoAppId);
+ hostEndPointId, pendingIntent, nanoAppId, attributionTag,
+ transactionManager);
mHostEndPointIdToClientMap.put(hostEndPointId, broker);
registerString = "Registered";
mRegistrationRecordDeque.add(
new RegistrationRecord(broker.toString(), ACTION_REGISTERED));
+ } else {
+ // Update the attribution tag to the latest value provided by the client app in
+ // case the app was updated and decided to change its tag.
+ broker.setAttributionTag(attributionTag);
}
}
@@ -217,9 +227,14 @@ import java.util.function.Consumer;
* Handles a message sent from a nanoapp.
*
* @param contextHubId the ID of the hub where the nanoapp sent the message from
- * @param message the message send by a nanoapp
+ * @param message the message send by a nanoapp
+ * @param nanoappPermissions the set of permissions the nanoapp holds
+ * @param messagePermissions the set of permissions that should be used for attributing
+ * permissions when this message is consumed by a client
*/
- /* package */ void onMessageFromNanoApp(int contextHubId, ContextHubMsg message) {
+ /* package */ void onMessageFromNanoApp(
+ int contextHubId, ContextHubMsg message, List<String> nanoappPermissions,
+ List<String> messagePermissions) {
NanoAppMessage clientMessage = ContextHubServiceUtil.createNanoAppMessage(message);
if (DEBUG_LOG_ENABLED) {
@@ -227,11 +242,19 @@ import java.util.function.Consumer;
}
if (clientMessage.isBroadcastMessage()) {
- broadcastMessage(contextHubId, clientMessage);
+ // Broadcast messages shouldn't be sent with any permissions tagged per CHRE API
+ // requirements.
+ if (!messagePermissions.isEmpty()) {
+ Log.wtf(TAG, "Received broadcast message with permissions from " + message.appName);
+ }
+
+ broadcastMessage(
+ contextHubId, clientMessage, nanoappPermissions, messagePermissions);
} else {
ContextHubClientBroker proxy = mHostEndPointIdToClientMap.get(message.hostEndPoint);
if (proxy != null) {
- proxy.sendMessageToClient(clientMessage);
+ proxy.sendMessageToClient(
+ clientMessage, nanoappPermissions, messagePermissions);
} else {
Log.e(TAG, "Cannot send message to unregistered client (host endpoint ID = "
+ message.hostEndPoint + ")");
@@ -296,6 +319,21 @@ import java.util.function.Consumer;
}
/**
+ * Runs a command for each client that is attached to a hub with the given ID.
+ *
+ * @param contextHubId the ID of the hub
+ * @param callback the command to invoke for the client
+ */
+ /* package */ void forEachClientOfHub(
+ int contextHubId, Consumer<ContextHubClientBroker> callback) {
+ for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
+ if (broker.getAttachedContextHubId() == contextHubId) {
+ callback.accept(broker);
+ }
+ }
+ }
+
+ /**
* Returns an available host endpoint ID.
*
* @returns an available host endpoint ID
@@ -326,22 +364,12 @@ import java.util.function.Consumer;
* @param contextHubId the ID of the hub where the nanoapp sent the message from
* @param message the message send by a nanoapp
*/
- private void broadcastMessage(int contextHubId, NanoAppMessage message) {
- forEachClientOfHub(contextHubId, client -> client.sendMessageToClient(message));
- }
-
- /**
- * Runs a command for each client that is attached to a hub with the given ID.
- *
- * @param contextHubId the ID of the hub
- * @param callback the command to invoke for the client
- */
- private void forEachClientOfHub(int contextHubId, Consumer<ContextHubClientBroker> callback) {
- for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
- if (broker.getAttachedContextHubId() == contextHubId) {
- callback.accept(broker);
- }
- }
+ private void broadcastMessage(
+ int contextHubId, NanoAppMessage message, List<String> nanoappPermissions,
+ List<String> messagePermissions) {
+ forEachClientOfHub(contextHubId,
+ client -> client.sendMessageToClient(
+ message, nanoappPermissions, messagePermissions));
}
/**
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 785e6745087f..1b8f0ded2e79 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -27,10 +27,10 @@ import android.hardware.SensorPrivacyManager;
import android.hardware.contexthub.V1_0.AsyncEventType;
import android.hardware.contexthub.V1_0.ContextHub;
import android.hardware.contexthub.V1_0.ContextHubMsg;
-import android.hardware.contexthub.V1_0.HubAppInfo;
-import android.hardware.contexthub.V1_0.IContexthubCallback;
import android.hardware.contexthub.V1_0.Result;
import android.hardware.contexthub.V1_0.TransactionResult;
+import android.hardware.contexthub.V1_2.HubAppInfo;
+import android.hardware.contexthub.V1_2.IContexthubCallback;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubMessage;
import android.hardware.location.ContextHubTransaction;
@@ -50,9 +50,12 @@ import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
+import android.util.Pair;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.DumpUtils;
@@ -63,6 +66,7 @@ import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -97,6 +101,7 @@ public class ContextHubService extends IContextHubService.Stub {
private final Context mContext;
private final Map<Integer, ContextHubInfo> mContextHubIdToInfoMap;
+ private final List<String> mSupportedContextHubPerms;
private final List<ContextHubInfo> mContextHubInfoList;
private final RemoteCallbackList<IContextHubCallback> mCallbacksList =
new RemoteCallbackList<>();
@@ -119,9 +124,6 @@ public class ContextHubService extends IContextHubService.Stub {
// True if WiFi is available for the Context Hub
private boolean mIsWifiAvailable = false;
- // True if audio is disabled for the ContextHub
- private boolean mIsAudioDisabled = false;
-
// Lock object for sendWifiSettingUpdate()
private final Object mSendWifiSettingUpdateLock = new Object();
@@ -137,7 +139,9 @@ public class ContextHubService extends IContextHubService.Stub {
@Override
public void handleClientMsg(ContextHubMsg message) {
- handleClientMessageCallback(mContextHubId, message);
+ handleClientMessageCallback(mContextHubId, message,
+ Collections.emptyList() /* nanoappPermissions */,
+ Collections.emptyList() /* messagePermissions */);
}
@Override
@@ -156,7 +160,21 @@ public class ContextHubService extends IContextHubService.Stub {
}
@Override
- public void handleAppsInfo(ArrayList<HubAppInfo> nanoAppInfoList) {
+ public void handleAppsInfo(
+ ArrayList<android.hardware.contexthub.V1_0.HubAppInfo> nanoAppInfoList) {
+ handleQueryAppsCallback(mContextHubId,
+ ContextHubServiceUtil.toHubAppInfo_1_2(nanoAppInfoList));
+ }
+
+ @Override
+ public void handleClientMsg_1_2(android.hardware.contexthub.V1_2.ContextHubMsg message,
+ ArrayList<String> messagePermissions) {
+ handleClientMessageCallback(mContextHubId, message.msg_1_0, message.permissions,
+ messagePermissions);
+ }
+
+ @Override
+ public void handleAppsInfo_1_2(ArrayList<HubAppInfo> nanoAppInfoList) {
handleQueryAppsCallback(mContextHubId, nanoAppInfoList);
}
}
@@ -170,34 +188,37 @@ public class ContextHubService extends IContextHubService.Stub {
mClientManager = null;
mDefaultClientMap = Collections.emptyMap();
mContextHubIdToInfoMap = Collections.emptyMap();
+ mSupportedContextHubPerms = Collections.emptyList();
mContextHubInfoList = Collections.emptyList();
return;
}
- mClientManager = new ContextHubClientManager(mContext, mContextHubWrapper.getHub());
- mTransactionManager = new ContextHubTransactionManager(
- mContextHubWrapper.getHub(), mClientManager, mNanoAppStateManager);
-
- List<ContextHub> hubList;
+ Pair<List<ContextHub>, List<String>> hubInfo;
try {
- hubList = mContextHubWrapper.getHub().getHubs();
+ hubInfo = mContextHubWrapper.getHubs();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while getting Context Hub info", e);
- hubList = Collections.emptyList();
+ hubInfo = new Pair(Collections.emptyList(), Collections.emptyList());
}
+
mContextHubIdToInfoMap = Collections.unmodifiableMap(
- ContextHubServiceUtil.createContextHubInfoMap(hubList));
+ ContextHubServiceUtil.createContextHubInfoMap(hubInfo.first));
+ mSupportedContextHubPerms = hubInfo.second;
mContextHubInfoList = new ArrayList<>(mContextHubIdToInfoMap.values());
+ mClientManager = new ContextHubClientManager(mContext, mContextHubWrapper);
+ mTransactionManager = new ContextHubTransactionManager(
+ mContextHubWrapper.getHub(), mClientManager, mNanoAppStateManager);
HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
IContextHubClient client = mClientManager.registerClient(
- contextHubInfo, createDefaultClientCallback(contextHubId));
+ contextHubInfo, createDefaultClientCallback(contextHubId),
+ null /* attributionTag */, mTransactionManager, mContext.getPackageName());
defaultClientMap.put(contextHubId, client);
try {
- mContextHubWrapper.getHub().registerCallback(
+ mContextHubWrapper.registerCallback(
contextHubId, new ContextHubServiceCallback(contextHubId));
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while registering service callback for hub (ID = "
@@ -344,6 +365,12 @@ public class ContextHubService extends IContextHubService.Stub {
}
@Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver result) {
+ new ContextHubShellCommand(mContext, this).exec(this, in, out, err, args, callback, result);
+ }
+
+ @Override
public int registerCallback(IContextHubCallback callback) throws RemoteException {
checkPermissions();
mCallbacksList.register(callback);
@@ -593,11 +620,15 @@ public class ContextHubService extends IContextHubService.Stub {
/**
* Handles a unicast or broadcast message from a nanoapp.
*
- * @param contextHubId the ID of the hub the message came from
- * @param message the message contents
+ * @param contextHubId the ID of the hub the message came from
+ * @param message the message contents
+ * @param reqPermissions the permissions required to consume this message
*/
- private void handleClientMessageCallback(int contextHubId, ContextHubMsg message) {
- mClientManager.onMessageFromNanoApp(contextHubId, message);
+ private void handleClientMessageCallback(
+ int contextHubId, ContextHubMsg message, List<String> nanoappPermissions,
+ List<String> messagePermissions) {
+ mClientManager.onMessageFromNanoApp(
+ contextHubId, message, nanoappPermissions, messagePermissions);
}
/**
@@ -703,6 +734,7 @@ public class ContextHubService extends IContextHubService.Stub {
* @param contextHubId the ID of the hub this client is attached to
* @param clientCallback the client interface to register with the service
* @param attributionTag an optional attribution tag within the given package
+ * @param packageName the name of the package creating this client
* @return the generated client interface, null if registration was unsuccessful
* @throws IllegalArgumentException if contextHubId is not a valid ID
* @throws IllegalStateException if max number of clients have already registered
@@ -711,7 +743,7 @@ public class ContextHubService extends IContextHubService.Stub {
@Override
public IContextHubClient createClient(
int contextHubId, IContextHubClientCallback clientCallback,
- @Nullable String attributionTag) throws RemoteException {
+ @Nullable String attributionTag, String packageName) throws RemoteException {
checkPermissions();
if (!isValidContextHubId(contextHubId)) {
throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);
@@ -721,7 +753,8 @@ public class ContextHubService extends IContextHubService.Stub {
}
ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
- return mClientManager.registerClient(contextHubInfo, clientCallback);
+ return mClientManager.registerClient(
+ contextHubInfo, clientCallback, attributionTag, mTransactionManager, packageName);
}
/**
@@ -745,7 +778,8 @@ public class ContextHubService extends IContextHubService.Stub {
}
ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
- return mClientManager.registerClient(contextHubInfo, pendingIntent, nanoAppId);
+ return mClientManager.registerClient(
+ contextHubInfo, pendingIntent, nanoAppId, attributionTag, mTransactionManager);
}
/**
@@ -886,6 +920,8 @@ public class ContextHubService extends IContextHubService.Stub {
for (ContextHubInfo hubInfo : mContextHubIdToInfoMap.values()) {
pw.println(hubInfo);
}
+ pw.println("Supported permissions: "
+ + Arrays.toString(mSupportedContextHubPerms.toArray()));
pw.println("");
pw.println("=================== NANOAPPS ====================");
// Dump nanoAppHash
@@ -902,6 +938,16 @@ public class ContextHubService extends IContextHubService.Stub {
// dump eventLog
}
+ /* package */ void denyClientAuthState(int contextHubId, String packageName, long nanoAppId) {
+ mClientManager.forEachClientOfHub(contextHubId, client -> {
+ if (client.getPackageName().equals(packageName)) {
+ client.updateNanoAppAuthState(
+ nanoAppId, false /* hasPermissions */, false /* gracePeriodExpired */,
+ true /* forceDenied */);
+ }
+ });
+ }
+
private void dump(ProtoOutputStream proto) {
mContextHubIdToInfoMap.values().forEach(hubInfo -> {
long token = proto.start(ContextHubServiceProto.CONTEXT_HUB_INFO);
@@ -1035,10 +1081,8 @@ public class ContextHubService extends IContextHubService.Stub {
SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext);
boolean disabled = manager.isIndividualSensorPrivacyEnabled(
SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE);
- if (mIsAudioDisabled != disabled) {
- mIsAudioDisabled = disabled;
- mContextHubWrapper.onMicrophoneDisableSettingChanged(disabled);
- }
+ Log.d(TAG, "Mic Disabled Setting: " + disabled);
+ mContextHubWrapper.onMicrophoneDisableSettingChanged(disabled);
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index 88ed1053616a..8361253dcd4a 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -23,8 +23,8 @@ import android.content.Context;
import android.hardware.contexthub.V1_0.ContextHub;
import android.hardware.contexthub.V1_0.ContextHubMsg;
import android.hardware.contexthub.V1_0.HostEndPoint;
-import android.hardware.contexthub.V1_0.HubAppInfo;
import android.hardware.contexthub.V1_0.Result;
+import android.hardware.contexthub.V1_2.HubAppInfo;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubTransaction;
import android.hardware.location.NanoAppBinary;
@@ -161,7 +161,8 @@ import java.util.Set;
ArrayList<NanoAppState> nanoAppStateList = new ArrayList<>();
for (HubAppInfo appInfo : nanoAppInfoList) {
nanoAppStateList.add(
- new NanoAppState(appInfo.appId, appInfo.version, appInfo.enabled));
+ new NanoAppState(appInfo.info_1_0.appId, appInfo.info_1_0.version,
+ appInfo.info_1_0.enabled, appInfo.permissions));
}
return nanoAppStateList;
@@ -255,4 +256,26 @@ import java.util.Set;
return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
}
}
+
+ /**
+ * Converts old list of HubAppInfo received from the HAL to V1.2 HubAppInfo objects.
+ *
+ * @param oldInfoList list of V1.0 HubAppInfo objects
+ * @return list of V1.2 HubAppInfo objects
+ */
+ /* package */
+ static ArrayList<HubAppInfo> toHubAppInfo_1_2(
+ ArrayList<android.hardware.contexthub.V1_0.HubAppInfo> oldInfoList) {
+ ArrayList newAppInfo = new ArrayList<HubAppInfo>();
+ for (android.hardware.contexthub.V1_0.HubAppInfo oldInfo : oldInfoList) {
+ HubAppInfo newInfo = new HubAppInfo();
+ newInfo.info_1_0.appId = oldInfo.appId;
+ newInfo.info_1_0.version = oldInfo.version;
+ newInfo.info_1_0.memUsage = oldInfo.memUsage;
+ newInfo.info_1_0.enabled = oldInfo.enabled;
+ newInfo.permissions = new ArrayList<String>();
+ newAppInfo.add(newInfo);
+ }
+ return newAppInfo;
+ }
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubShellCommand.java b/services/core/java/com/android/server/location/contexthub/ContextHubShellCommand.java
new file mode 100644
index 000000000000..5ec85e661b98
--- /dev/null
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubShellCommand.java
@@ -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.
+ */
+
+package com.android.server.location.contexthub;
+
+import android.content.Context;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/**
+ * ShellCommands for ContextHubService.
+ *
+ * Use with {@code adb shell cmd contexthub ...}.
+ *
+ * @hide
+ */
+public class ContextHubShellCommand extends ShellCommand {
+
+ // Internal service impl -- must perform security checks before touching.
+ private final ContextHubService mInternal;
+
+ public ContextHubShellCommand(Context context, ContextHubService service) {
+ mInternal = service;
+
+ context.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_CONTEXT_HUB, "ContextHubShellCommand");
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if ("deny".equals(cmd)) {
+ return runDisableAuth();
+ }
+
+ return handleDefaultCommands(cmd);
+ }
+
+ private int runDisableAuth() {
+ int contextHubId = Integer.decode(getNextArgRequired());
+ String packageName = getNextArgRequired();
+ long nanoAppId = Long.decode(getNextArgRequired());
+
+ mInternal.denyClientAuthState(contextHubId, packageName, nanoAppId);
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("ContextHub commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" deny [contextHubId] [packageName] [nanoAppId]");
+ pw.println(" Immediately transitions the package's authentication state to denied so");
+ pw.println(" can no longer communciate with the nanoapp.");
+ }
+}
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index c11e289c116b..3a5c220eeeae 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -16,11 +16,17 @@
package com.android.server.location.contexthub;
import android.annotation.Nullable;
+import android.hardware.contexthub.V1_0.ContextHub;
import android.hardware.contexthub.V1_1.Setting;
import android.hardware.contexthub.V1_1.SettingValue;
+import android.hardware.contexthub.V1_2.IContexthubCallback;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Pair;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.NoSuchElementException;
/**
@@ -87,6 +93,23 @@ public abstract class IContextHubWrapper {
}
/**
+ * Calls the appropriate getHubs function depending on the HAL version.
+ */
+ public abstract Pair<List<ContextHub>, List<String>> getHubs() throws RemoteException;
+
+ /**
+ * Calls the appropriate registerCallback function depending on the HAL version.
+ */
+ public abstract void registerCallback(
+ int hubId, IContexthubCallback callback) throws RemoteException;
+
+ /**
+ * Calls the appropriate sendMessageToHub function depending on the HAL version.
+ */
+ public abstract int sendMessageToHub(int hubId,
+ android.hardware.contexthub.V1_0.ContextHubMsg message) throws RemoteException;
+
+ /**
* @return A valid instance of Contexthub HAL 1.0.
*/
public abstract android.hardware.contexthub.V1_0.IContexthub getHub();
@@ -148,6 +171,20 @@ public abstract class IContextHubWrapper {
mHub = hub;
}
+ public Pair<List<ContextHub>, List<String>> getHubs() throws RemoteException {
+ return new Pair(mHub.getHubs(), new ArrayList<String>());
+ }
+
+ public void registerCallback(
+ int hubId, IContexthubCallback callback) throws RemoteException {
+ mHub.registerCallback(hubId, callback);
+ }
+
+ public int sendMessageToHub(int hubId,
+ android.hardware.contexthub.V1_0.ContextHubMsg message) throws RemoteException {
+ return mHub.sendMessageToHub(hubId, message);
+ }
+
public android.hardware.contexthub.V1_0.IContexthub getHub() {
return mHub;
}
@@ -188,6 +225,20 @@ public abstract class IContextHubWrapper {
mHub = hub;
}
+ public Pair<List<ContextHub>, List<String>> getHubs() throws RemoteException {
+ return new Pair(mHub.getHubs(), new ArrayList<String>());
+ }
+
+ public void registerCallback(
+ int hubId, IContexthubCallback callback) throws RemoteException {
+ mHub.registerCallback(hubId, callback);
+ }
+
+ public int sendMessageToHub(int hubId,
+ android.hardware.contexthub.V1_0.ContextHubMsg message) throws RemoteException {
+ return mHub.sendMessageToHub(hubId, message);
+ }
+
public android.hardware.contexthub.V1_0.IContexthub getHub() {
return mHub;
}
@@ -227,13 +278,40 @@ public abstract class IContextHubWrapper {
}
}
- private static class ContextHubWrapperV1_2 extends IContextHubWrapper {
- private android.hardware.contexthub.V1_2.IContexthub mHub;
+ private static class ContextHubWrapperV1_2 extends IContextHubWrapper
+ implements android.hardware.contexthub.V1_2.IContexthub.getHubs_1_2Callback {
+ private final android.hardware.contexthub.V1_2.IContexthub mHub;
+
+ private Pair<List<ContextHub>, List<String>> mHubInfo =
+ new Pair<>(Collections.emptyList(), Collections.emptyList());
ContextHubWrapperV1_2(android.hardware.contexthub.V1_2.IContexthub hub) {
mHub = hub;
}
+ @Override
+ public void onValues(ArrayList<ContextHub> hubs, ArrayList<String> supportedPermissions) {
+ mHubInfo = new Pair(hubs, supportedPermissions);
+ }
+
+ public Pair<List<ContextHub>, List<String>> getHubs() throws RemoteException {
+ mHub.getHubs_1_2(this);
+ return mHubInfo;
+ }
+
+ public void registerCallback(
+ int hubId, IContexthubCallback callback) throws RemoteException {
+ mHub.registerCallback_1_2(hubId, callback);
+ }
+
+ public int sendMessageToHub(int hubId,
+ android.hardware.contexthub.V1_0.ContextHubMsg message) throws RemoteException {
+ android.hardware.contexthub.V1_2.ContextHubMsg newMessage =
+ new android.hardware.contexthub.V1_2.ContextHubMsg();
+ newMessage.msg_1_0 = message;
+ return mHub.sendMessageToHub_1_2(hubId, newMessage);
+ }
+
public android.hardware.contexthub.V1_0.IContexthub getHub() {
return mHub;
}
diff --git a/services/core/java/com/android/server/location/contexthub/NanoAppStateManager.java b/services/core/java/com/android/server/location/contexthub/NanoAppStateManager.java
index 60109fe4b9f6..667fb98f8649 100644
--- a/services/core/java/com/android/server/location/contexthub/NanoAppStateManager.java
+++ b/services/core/java/com/android/server/location/contexthub/NanoAppStateManager.java
@@ -17,7 +17,7 @@
package com.android.server.location.contexthub;
import android.annotation.Nullable;
-import android.hardware.contexthub.V1_0.HubAppInfo;
+import android.hardware.contexthub.V1_2.HubAppInfo;
import android.hardware.location.NanoAppInstanceInfo;
import android.util.Log;
@@ -154,8 +154,8 @@ import java.util.function.Consumer;
synchronized void updateCache(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
HashSet<Long> nanoAppIdSet = new HashSet<>();
for (HubAppInfo appInfo : nanoAppInfoList) {
- handleQueryAppEntry(contextHubId, appInfo.appId, appInfo.version);
- nanoAppIdSet.add(appInfo.appId);
+ handleQueryAppEntry(contextHubId, appInfo.info_1_0.appId, appInfo.info_1_0.version);
+ nanoAppIdSet.add(appInfo.info_1_0.appId);
}
Iterator<NanoAppInstanceInfo> iterator = mNanoAppHash.values().iterator();
diff --git a/services/core/java/com/android/server/location/eventlog/LocalEventLog.java b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
index b5746bbf310a..12dd3e6a4a4c 100644
--- a/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
@@ -16,12 +16,13 @@
package com.android.server.location.eventlog;
+import android.annotation.Nullable;
import android.os.SystemClock;
import android.util.TimeUtils;
import com.android.internal.util.Preconditions;
-import java.util.ListIterator;
+import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
@@ -35,6 +36,7 @@ public abstract class LocalEventLog {
boolean isFiller();
long getTimeDeltaMs();
String getLogString();
+ boolean filter(@Nullable String filter);
}
private static final class FillerEvent implements Log {
@@ -62,6 +64,11 @@ public abstract class LocalEventLog {
public String getLogString() {
throw new AssertionError();
}
+
+ @Override
+ public boolean filter(String filter) {
+ return false;
+ }
}
/**
@@ -87,6 +94,11 @@ public abstract class LocalEventLog {
public final long getTimeDeltaMs() {
return Integer.toUnsignedLong(mTimeDelta);
}
+
+ @Override
+ public boolean filter(String filter) {
+ return false;
+ }
}
// circular buffer of log entries
@@ -198,6 +210,17 @@ public abstract class LocalEventLog {
}
}
+ /**
+ * Iterates over the event log, passing each filter-matching log string to the given
+ * consumer.
+ */
+ public synchronized void iterate(String filter, Consumer<String> consumer) {
+ LogIterator it = new LogIterator(filter);
+ while (it.hasNext()) {
+ consumer.accept(it.next());
+ }
+ }
+
// returns the index of the first element
private int startIndex() {
return wrapIndex(mLogEndIndex - mLogSize);
@@ -205,12 +228,13 @@ public abstract class LocalEventLog {
// returns the index after this one
private int incrementIndex(int index) {
- return wrapIndex(index + 1);
- }
-
- // returns the index before this one
- private int decrementIndex(int index) {
- return wrapIndex(index - 1);
+ if (index == -1) {
+ return startIndex();
+ } else if (index >= 0) {
+ return wrapIndex(index + 1);
+ } else {
+ throw new IllegalArgumentException();
+ }
}
// rolls over the given index if necessary
@@ -219,7 +243,9 @@ public abstract class LocalEventLog {
return (index % mLog.length + mLog.length) % mLog.length;
}
- private class LogIterator implements ListIterator<String> {
+ private class LogIterator implements Iterator<String> {
+
+ private final @Nullable String mFilter;
private final long mSystemTimeDeltaMs;
@@ -228,10 +254,17 @@ public abstract class LocalEventLog {
private int mCount;
LogIterator() {
+ this(null);
+ }
+
+ LogIterator(@Nullable String filter) {
+ mFilter = filter;
mSystemTimeDeltaMs = System.currentTimeMillis() - SystemClock.elapsedRealtime();
mCurrentRealtimeMs = mStartRealtimeMs;
- mIndex = startIndex();
- mCount = 0;
+ mIndex = -1;
+ mCount = -1;
+
+ increment();
}
@Override
@@ -239,75 +272,17 @@ public abstract class LocalEventLog {
return mCount < mLogSize;
}
- @Override
- public boolean hasPrevious() {
- return mCount > 0;
- }
-
- @Override
- // return then increment
public String next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Log log = mLog[mIndex];
- long nextDeltaMs = log.getTimeDeltaMs();
- long realtimeMs = mCurrentRealtimeMs + nextDeltaMs;
-
- // calculate next index, skipping filler events
- do {
- mCurrentRealtimeMs += nextDeltaMs;
- mIndex = incrementIndex(mIndex);
- if (++mCount < mLogSize) {
- nextDeltaMs = mLog[mIndex].getTimeDeltaMs();
- }
- } while (mCount < mLogSize && mLog[mIndex].isFiller());
-
- return getTimePrefix(realtimeMs + mSystemTimeDeltaMs) + log.getLogString();
- }
-
- @Override
- // decrement then return
- public String previous() {
- Log log;
- long currentDeltaMs;
- long realtimeMs;
-
- // calculate previous index, skipping filler events with MAX_TIME_DELTA
- do {
- if (!hasPrevious()) {
- throw new NoSuchElementException();
- }
-
- mIndex = decrementIndex(mIndex);
- mCount--;
-
- log = mLog[mIndex];
- realtimeMs = mCurrentRealtimeMs;
-
- if (mCount > 0) {
- currentDeltaMs = log.getTimeDeltaMs();
- mCurrentRealtimeMs -= currentDeltaMs;
- }
- } while (mCount >= 0 && log.isFiller());
-
- return getTimePrefix(realtimeMs + mSystemTimeDeltaMs) + log.getLogString();
- }
-
- @Override
- public int nextIndex() {
- throw new UnsupportedOperationException();
- }
+ long timeMs = mCurrentRealtimeMs + log.getTimeDeltaMs() + mSystemTimeDeltaMs;
- @Override
- public int previousIndex() {
- throw new UnsupportedOperationException();
- }
+ increment();
- @Override
- public void add(String s) {
- throw new UnsupportedOperationException();
+ return getTimePrefix(timeMs) + log.getLogString();
}
@Override
@@ -315,9 +290,16 @@ public abstract class LocalEventLog {
throw new UnsupportedOperationException();
}
- @Override
- public void set(String s) {
- throw new UnsupportedOperationException();
+ private void increment() {
+ long nextDeltaMs = mIndex == -1 ? 0 : mLog[mIndex].getTimeDeltaMs();
+ do {
+ mCurrentRealtimeMs += nextDeltaMs;
+ mIndex = incrementIndex(mIndex);
+ if (++mCount < mLogSize) {
+ nextDeltaMs = mLog[mIndex].getTimeDeltaMs();
+ }
+ } while (mCount < mLogSize && (mLog[mIndex].isFiller() || (mFilter != null
+ && !mLog[mIndex].filter(mFilter))));
}
}
}
diff --git a/services/core/java/com/android/server/location/injector/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index 8d73518bced1..dbfd0a52b013 100644
--- a/services/core/java/com/android/server/location/injector/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.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.
@@ -14,24 +14,32 @@
* limitations under the License.
*/
-package com.android.server.location.injector;
+package com.android.server.location.eventlog;
import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
import static android.os.PowerManager.LOCATION_MODE_NO_CHANGE;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+import static android.util.TimeUtils.formatDuration;
import static com.android.server.location.LocationManagerService.D;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
import android.annotation.Nullable;
import android.location.LocationRequest;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Build;
import android.os.PowerManager.LocationPowerSaveMode;
+import android.os.SystemClock;
+import android.util.ArrayMap;
-import com.android.server.location.eventlog.LocalEventLog;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
/** In memory event log for location events. */
public class LocationEventLog extends LocalEventLog {
@@ -52,10 +60,42 @@ public class LocationEventLog extends LocalEventLog {
private static final int EVENT_PROVIDER_UPDATE_REQUEST = 6;
private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 7;
private static final int EVENT_PROVIDER_DELIVER_LOCATION = 8;
- private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 9;
+ private static final int EVENT_PROVIDER_STATIONARY_THROTTLED = 9;
+ private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 10;
+
+ @GuardedBy("mAggregateStats")
+ private final ArrayMap<String, ArrayMap<String, AggregateStats>> mAggregateStats;
public LocationEventLog() {
super(getLogSize());
+ mAggregateStats = new ArrayMap<>(4);
+ }
+
+ public ArrayMap<String, ArrayMap<String, AggregateStats>> copyAggregateStats() {
+ synchronized (mAggregateStats) {
+ ArrayMap<String, ArrayMap<String, AggregateStats>> copy = new ArrayMap<>(
+ mAggregateStats);
+ for (int i = 0; i < copy.size(); i++) {
+ copy.setValueAt(i, new ArrayMap<>(copy.valueAt(i)));
+ }
+ return copy;
+ }
+ }
+
+ private AggregateStats getAggregateStats(String provider, String packageName) {
+ synchronized (mAggregateStats) {
+ ArrayMap<String, AggregateStats> packageMap = mAggregateStats.get(provider);
+ if (packageMap == null) {
+ packageMap = new ArrayMap<>(2);
+ mAggregateStats.put(provider, packageMap);
+ }
+ AggregateStats stats = packageMap.get(packageName);
+ if (stats == null) {
+ stats = new AggregateStats();
+ packageMap.put(packageName, stats);
+ }
+ return stats;
+ }
}
/** Logs a location enabled/disabled event. */
@@ -77,12 +117,34 @@ public class LocationEventLog extends LocalEventLog {
public void logProviderClientRegistered(String provider, CallerIdentity identity,
LocationRequest request) {
addLogEvent(EVENT_PROVIDER_REGISTER_CLIENT, provider, identity, request);
+ getAggregateStats(provider, identity.getPackageName())
+ .markRequestAdded(request.getIntervalMillis());
}
/** Logs a client unregistration for a location provider. */
- public void logProviderClientUnregistered(String provider,
- CallerIdentity identity) {
+ public void logProviderClientUnregistered(String provider, CallerIdentity identity) {
addLogEvent(EVENT_PROVIDER_UNREGISTER_CLIENT, provider, identity);
+ getAggregateStats(provider, identity.getPackageName()).markRequestRemoved();
+ }
+
+ /** Logs a client for a location provider entering the active state. */
+ public void logProviderClientActive(String provider, CallerIdentity identity) {
+ getAggregateStats(provider, identity.getPackageName()).markRequestActive();
+ }
+
+ /** Logs a client for a location provider leaving the active state. */
+ public void logProviderClientInactive(String provider, CallerIdentity identity) {
+ getAggregateStats(provider, identity.getPackageName()).markRequestInactive();
+ }
+
+ /** Logs a client for a location provider entering the foreground state. */
+ public void logProviderClientForeground(String provider, CallerIdentity identity) {
+ getAggregateStats(provider, identity.getPackageName()).markRequestForeground();
+ }
+
+ /** Logs a client for a location provider leaving the foreground state. */
+ public void logProviderClientBackground(String provider, CallerIdentity identity) {
+ getAggregateStats(provider, identity.getPackageName()).markRequestBackground();
}
/** Logs a change to the provider request for a location provider. */
@@ -103,6 +165,12 @@ public class LocationEventLog extends LocalEventLog {
if (Build.IS_DEBUGGABLE || D) {
addLogEvent(EVENT_PROVIDER_DELIVER_LOCATION, provider, numLocations, identity);
}
+ getAggregateStats(provider, identity.getPackageName()).markLocationDelivered();
+ }
+
+ /** Logs that a provider has entered or exited stationary throttling. */
+ public void logProviderStationaryThrottled(String provider, boolean throttled) {
+ addLogEvent(EVENT_PROVIDER_STATIONARY_THROTTLED, provider, throttled);
}
/** Logs that the location power save mode has changed. */
@@ -136,6 +204,9 @@ public class LocationEventLog extends LocalEventLog {
case EVENT_PROVIDER_DELIVER_LOCATION:
return new ProviderDeliverLocationEvent(timeDelta, (String) args[0],
(Integer) args[1], (CallerIdentity) args[2]);
+ case EVENT_PROVIDER_STATIONARY_THROTTLED:
+ return new ProviderStationaryThrottledEvent(timeDelta, (String) args[0],
+ (Boolean) args[1]);
case EVENT_LOCATION_POWER_SAVE_MODE_CHANGE:
return new LocationPowerSaveModeEvent(timeDelta, (Integer) args[0]);
default:
@@ -143,16 +214,29 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderEnabledEvent extends LogEvent {
+ private abstract static class ProviderEvent extends LogEvent {
+
+ protected final String mProvider;
+
+ protected ProviderEvent(long timeDelta, String provider) {
+ super(timeDelta);
+ mProvider = provider;
+ }
+
+ @Override
+ public boolean filter(String filter) {
+ return mProvider.equals(filter);
+ }
+ }
+
+ private static final class ProviderEnabledEvent extends ProviderEvent {
- private final String mProvider;
private final int mUserId;
private final boolean mEnabled;
protected ProviderEnabledEvent(long timeDelta, String provider, int userId,
boolean enabled) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mUserId = userId;
mEnabled = enabled;
}
@@ -164,14 +248,12 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderMockedEvent extends LogEvent {
+ private static final class ProviderMockedEvent extends ProviderEvent {
- private final String mProvider;
private final boolean mMocked;
protected ProviderMockedEvent(long timeDelta, String provider, boolean mocked) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mMocked = mocked;
}
@@ -185,17 +267,15 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderRegisterEvent extends LogEvent {
+ private static final class ProviderRegisterEvent extends ProviderEvent {
- private final String mProvider;
private final boolean mRegistered;
private final CallerIdentity mIdentity;
@Nullable private final LocationRequest mLocationRequest;
private ProviderRegisterEvent(long timeDelta, String provider, boolean registered,
CallerIdentity identity, @Nullable LocationRequest locationRequest) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mRegistered = registered;
mIdentity = identity;
mLocationRequest = locationRequest;
@@ -212,14 +292,12 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderUpdateEvent extends LogEvent {
+ private static final class ProviderUpdateEvent extends ProviderEvent {
- private final String mProvider;
private final ProviderRequest mRequest;
private ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mRequest = request;
}
@@ -229,14 +307,12 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderReceiveLocationEvent extends LogEvent {
+ private static final class ProviderReceiveLocationEvent extends ProviderEvent {
- private final String mProvider;
private final int mNumLocations;
private ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mNumLocations = numLocations;
}
@@ -246,16 +322,14 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class ProviderDeliverLocationEvent extends LogEvent {
+ private static final class ProviderDeliverLocationEvent extends ProviderEvent {
- private final String mProvider;
private final int mNumLocations;
@Nullable private final CallerIdentity mIdentity;
private ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations,
@Nullable CallerIdentity identity) {
- super(timeDelta);
- mProvider = provider;
+ super(timeDelta, provider);
mNumLocations = numLocations;
mIdentity = identity;
}
@@ -267,7 +341,24 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class LocationPowerSaveModeEvent extends LogEvent {
+ private static final class ProviderStationaryThrottledEvent extends ProviderEvent {
+
+ private final boolean mStationaryThrottled;
+
+ private ProviderStationaryThrottledEvent(long timeDelta, String provider,
+ boolean stationaryThrottled) {
+ super(timeDelta, provider);
+ mStationaryThrottled = stationaryThrottled;
+ }
+
+ @Override
+ public String getLogString() {
+ return mProvider + " provider stationary/idle " + (mStationaryThrottled ? "throttled"
+ : "unthrottled");
+ }
+ }
+
+ private static final class LocationPowerSaveModeEvent extends LogEvent {
@LocationPowerSaveMode
private final int mLocationPowerSaveMode;
@@ -305,7 +396,7 @@ public class LocationEventLog extends LocalEventLog {
}
}
- private static class LocationEnabledEvent extends LogEvent {
+ private static final class LocationEnabledEvent extends LogEvent {
private final int mUserId;
private final boolean mEnabled;
@@ -321,4 +412,125 @@ public class LocationEventLog extends LocalEventLog {
return "[u" + mUserId + "] location setting " + (mEnabled ? "enabled" : "disabled");
}
}
+
+ /**
+ * Aggregate statistics for a single package under a single provider.
+ */
+ public static final class AggregateStats {
+
+ @GuardedBy("this")
+ private int mAddedRequestCount;
+ @GuardedBy("this")
+ private int mActiveRequestCount;
+ @GuardedBy("this")
+ private int mForegroundRequestCount;
+ @GuardedBy("this")
+ private int mDeliveredLocationCount;
+
+ @GuardedBy("this")
+ private long mFastestIntervalMs = Long.MAX_VALUE;
+ @GuardedBy("this")
+ private long mSlowestIntervalMs = 0;
+
+ @GuardedBy("this")
+ private long mAddedTimeTotalMs;
+ @GuardedBy("this")
+ private long mAddedTimeLastUpdateRealtimeMs;
+
+ @GuardedBy("this")
+ private long mActiveTimeTotalMs;
+ @GuardedBy("this")
+ private long mActiveTimeLastUpdateRealtimeMs;
+
+ @GuardedBy("this")
+ private long mForegroundTimeTotalMs;
+ @GuardedBy("this")
+ private long mForegroundTimeLastUpdateRealtimeMs;
+
+ AggregateStats() {}
+
+ synchronized void markRequestAdded(long intervalMillis) {
+ if (mAddedRequestCount++ == 0) {
+ mAddedTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
+ }
+
+ mFastestIntervalMs = min(intervalMillis, mFastestIntervalMs);
+ mSlowestIntervalMs = max(intervalMillis, mSlowestIntervalMs);
+ }
+
+ synchronized void markRequestRemoved() {
+ updateTotals();
+ --mAddedRequestCount;
+ Preconditions.checkState(mAddedRequestCount >= 0);
+
+ mActiveRequestCount = min(mAddedRequestCount, mActiveRequestCount);
+ mForegroundRequestCount = min(mAddedRequestCount, mForegroundRequestCount);
+ }
+
+ synchronized void markRequestActive() {
+ Preconditions.checkState(mAddedRequestCount > 0);
+ if (mActiveRequestCount++ == 0) {
+ mActiveTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
+ }
+ }
+
+ synchronized void markRequestInactive() {
+ updateTotals();
+ --mActiveRequestCount;
+ Preconditions.checkState(mActiveRequestCount >= 0);
+ }
+
+ synchronized void markRequestForeground() {
+ Preconditions.checkState(mAddedRequestCount > 0);
+ if (mForegroundRequestCount++ == 0) {
+ mForegroundTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
+ }
+ }
+
+ synchronized void markRequestBackground() {
+ updateTotals();
+ --mForegroundRequestCount;
+ Preconditions.checkState(mForegroundRequestCount >= 0);
+ }
+
+ synchronized void markLocationDelivered() {
+ mDeliveredLocationCount++;
+ }
+
+ public synchronized void updateTotals() {
+ if (mAddedRequestCount > 0) {
+ long realtimeMs = SystemClock.elapsedRealtime();
+ mAddedTimeTotalMs += realtimeMs - mAddedTimeLastUpdateRealtimeMs;
+ mAddedTimeLastUpdateRealtimeMs = realtimeMs;
+ }
+ if (mActiveRequestCount > 0) {
+ long realtimeMs = SystemClock.elapsedRealtime();
+ mActiveTimeTotalMs += realtimeMs - mActiveTimeLastUpdateRealtimeMs;
+ mActiveTimeLastUpdateRealtimeMs = realtimeMs;
+ }
+ if (mForegroundRequestCount > 0) {
+ long realtimeMs = SystemClock.elapsedRealtime();
+ mForegroundTimeTotalMs += realtimeMs - mForegroundTimeLastUpdateRealtimeMs;
+ mForegroundTimeLastUpdateRealtimeMs = realtimeMs;
+ }
+ }
+
+ @Override
+ public synchronized String toString() {
+ return "min/max interval = " + intervalToString(mFastestIntervalMs) + "/"
+ + intervalToString(mSlowestIntervalMs)
+ + ", total/active/foreground duration = " + formatDuration(mAddedTimeTotalMs)
+ + "/" + formatDuration(mActiveTimeTotalMs) + "/"
+ + formatDuration(mForegroundTimeTotalMs) + ", locations = "
+ + mDeliveredLocationCount;
+ }
+
+ private static String intervalToString(long intervalMs) {
+ if (intervalMs == LocationRequest.PASSIVE_INTERVAL) {
+ return "passive";
+ } else {
+ return MILLISECONDS.toSeconds(intervalMs) + "s";
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/injector/DeviceIdleHelper.java b/services/core/java/com/android/server/location/injector/DeviceIdleHelper.java
new file mode 100644
index 000000000000..e820b00f3714
--- /dev/null
+++ b/services/core/java/com/android/server/location/injector/DeviceIdleHelper.java
@@ -0,0 +1,76 @@
+/*
+ * 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.location.injector;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for device idle status.
+ */
+public abstract class DeviceIdleHelper {
+
+ /**
+ * Listener for device stationary status.
+ */
+ public interface DeviceIdleListener {
+ /**
+ * Called when device idle state has changed.
+ */
+ void onDeviceIdleChanged(boolean deviceIdle);
+ }
+
+ private final CopyOnWriteArrayList<DeviceIdleListener> mListeners;
+
+ protected DeviceIdleHelper() {
+ mListeners = new CopyOnWriteArrayList<>();
+ }
+
+ /**
+ * Adds a listener for device idle status.
+ */
+ public final synchronized void addListener(DeviceIdleListener listener) {
+ if (mListeners.add(listener) && mListeners.size() == 1) {
+ registerInternal();
+ }
+ }
+
+ /**
+ * Removes a listener for device idle status.
+ */
+ public final synchronized void removeListener(DeviceIdleListener listener) {
+ if (mListeners.remove(listener) && mListeners.isEmpty()) {
+ unregisterInternal();
+ }
+ }
+
+ protected final void notifyDeviceIdleChanged() {
+ boolean deviceIdle = isDeviceIdle();
+
+ for (DeviceIdleListener listener : mListeners) {
+ listener.onDeviceIdleChanged(deviceIdle);
+ }
+ }
+
+ protected abstract void registerInternal();
+
+ protected abstract void unregisterInternal();
+
+ /**
+ * Returns true if the device is currently idle.
+ */
+ public abstract boolean isDeviceIdle();
+}
diff --git a/services/core/java/com/android/server/location/injector/DeviceStationaryHelper.java b/services/core/java/com/android/server/location/injector/DeviceStationaryHelper.java
new file mode 100644
index 000000000000..b77c0f7dfe7f
--- /dev/null
+++ b/services/core/java/com/android/server/location/injector/DeviceStationaryHelper.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.injector;
+
+import com.android.server.DeviceIdleInternal;
+
+/**
+ * Provides accessors and listeners for device stationary status.
+ */
+public abstract class DeviceStationaryHelper {
+
+ /**
+ * Adds a listener for stationary status. The listener will be immediately invoked with the
+ * current stationary status.
+ */
+ public abstract void addListener(DeviceIdleInternal.StationaryListener listener);
+
+ /**
+ * Removes a listener for stationary status.
+ */
+ public abstract void removeListener(DeviceIdleInternal.StationaryListener listener);
+}
diff --git a/services/core/java/com/android/server/location/injector/Injector.java b/services/core/java/com/android/server/location/injector/Injector.java
index 03938b2b8ba2..b0351184ffe5 100644
--- a/services/core/java/com/android/server/location/injector/Injector.java
+++ b/services/core/java/com/android/server/location/injector/Injector.java
@@ -48,6 +48,12 @@ public interface Injector {
/** Returns a ScreenInteractiveHelper. */
ScreenInteractiveHelper getScreenInteractiveHelper();
+ /** Returns a DeviceStationaryHelper. */
+ DeviceStationaryHelper getDeviceStationaryHelper();
+
+ /** Returns a DeviceIdleHelper. */
+ DeviceIdleHelper getDeviceIdleHelper();
+
/** Returns a LocationAttributionHelper. */
LocationAttributionHelper getLocationAttributionHelper();
@@ -56,7 +62,4 @@ public interface Injector {
/** Returns a LocationUsageLogger. */
LocationUsageLogger getLocationUsageLogger();
-
- /** Returns a LocationEventLog. */
- LocationEventLog getLocationEventLog();
}
diff --git a/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java
index 532826a02ab0..cc00d5684991 100644
--- a/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java
+++ b/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java
@@ -24,6 +24,8 @@ import static com.android.server.location.LocationManagerService.TAG;
import android.os.PowerManager.LocationPowerSaveMode;
import android.util.Log;
+import com.android.server.location.eventlog.LocationEventLog;
+
import java.util.concurrent.CopyOnWriteArrayList;
/**
diff --git a/services/core/java/com/android/server/location/injector/SystemDeviceIdleHelper.java b/services/core/java/com/android/server/location/injector/SystemDeviceIdleHelper.java
new file mode 100644
index 000000000000..736b65484de6
--- /dev/null
+++ b/services/core/java/com/android/server/location/injector/SystemDeviceIdleHelper.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.injector;
+
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Binder;
+import android.os.PowerManager;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+
+import java.util.Objects;
+
+/**
+ * Provides accessors and listeners for device stationary state.
+ */
+public class SystemDeviceIdleHelper extends DeviceIdleHelper {
+
+ private final Context mContext;
+
+ private PowerManager mPowerManager;
+
+ private boolean mSystemReady;
+ private boolean mRegistrationRequired;
+ private @Nullable BroadcastReceiver mReceiver;
+
+ public SystemDeviceIdleHelper(Context context) {
+ mContext = context;
+ }
+
+ public synchronized void onSystemReady() {
+ mSystemReady = true;
+ mPowerManager = Objects.requireNonNull(mContext.getSystemService(PowerManager.class));
+ onRegistrationStateChanged();
+ }
+
+ @Override
+ protected synchronized void registerInternal() {
+ mRegistrationRequired = true;
+ onRegistrationStateChanged();
+ }
+
+ @Override
+ protected synchronized void unregisterInternal() {
+ mRegistrationRequired = false;
+ onRegistrationStateChanged();
+ }
+
+ private void onRegistrationStateChanged() {
+ if (!mSystemReady) {
+ return;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (mRegistrationRequired && mReceiver == null) {
+ BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ notifyDeviceIdleChanged();
+ }
+ };
+ mContext.registerReceiver(receiver,
+ new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED), null,
+ FgThread.getHandler());
+ mReceiver = receiver;
+ } else if (!mRegistrationRequired && mReceiver != null) {
+ BroadcastReceiver receiver = mReceiver;
+ mReceiver = null;
+ mContext.unregisterReceiver(receiver);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public boolean isDeviceIdle() {
+ Preconditions.checkState(mPowerManager != null);
+ return mPowerManager.isDeviceIdleMode();
+ }
+}
diff --git a/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java b/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java
new file mode 100644
index 000000000000..9874ecfdefdf
--- /dev/null
+++ b/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.injector;
+
+import android.os.Binder;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.DeviceIdleInternal;
+import com.android.server.LocalServices;
+
+import java.util.Objects;
+
+/**
+ * Provides accessors and listeners for device stationary state.
+ */
+public class SystemDeviceStationaryHelper extends DeviceStationaryHelper {
+
+ private DeviceIdleInternal mDeviceIdle;
+
+ public SystemDeviceStationaryHelper() {}
+
+ public void onSystemReady() {
+ mDeviceIdle = Objects.requireNonNull(LocalServices.getService(DeviceIdleInternal.class));
+ }
+
+ @Override
+ public void addListener(DeviceIdleInternal.StationaryListener listener) {
+ Preconditions.checkState(mDeviceIdle != null);
+
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mDeviceIdle.registerStationaryListener(listener);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void removeListener(DeviceIdleInternal.StationaryListener listener) {
+ Preconditions.checkState(mDeviceIdle != null);
+
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mDeviceIdle.unregisterStationaryListener(listener);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java
index 1b74865b268e..c47a64d6d9a7 100644
--- a/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java
@@ -25,6 +25,7 @@ import android.os.PowerSaveState;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
+import com.android.server.location.eventlog.LocationEventLog;
import java.util.Objects;
import java.util.function.Consumer;
diff --git a/services/core/java/com/android/server/location/injector/SystemScreenInteractiveHelper.java b/services/core/java/com/android/server/location/injector/SystemScreenInteractiveHelper.java
index 0d7bcb049f15..03ade5f2abd8 100644
--- a/services/core/java/com/android/server/location/injector/SystemScreenInteractiveHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemScreenInteractiveHelper.java
@@ -68,7 +68,7 @@ public class SystemScreenInteractiveHelper extends ScreenInteractiveHelper {
mReady = true;
}
- private void onScreenInteractiveChanged(boolean interactive) {
+ void onScreenInteractiveChanged(boolean interactive) {
if (interactive == mIsInteractive) {
return;
}
diff --git a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
index e22a0145f2bf..4e0a0b89f238 100644
--- a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
@@ -337,8 +337,10 @@ public abstract class AbstractLocationProvider {
@Override
public State setListener(@Nullable Listener listener) {
- return mInternalState.updateAndGet(
- internalState -> internalState.withListener(listener)).state;
+ InternalState oldInternalState = mInternalState.getAndUpdate(
+ internalState -> internalState.withListener(listener));
+ Preconditions.checkState(listener == null || oldInternalState.listener == null);
+ return oldInternalState.state;
}
@Override
diff --git a/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java b/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java
new file mode 100644
index 000000000000..a3ec867220dd
--- /dev/null
+++ b/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.provider;
+
+import android.location.LocationResult;
+import android.location.provider.ProviderRequest;
+import android.os.Bundle;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.concurrent.Executor;
+
+/**
+ * Helper class for wrapping location providers. Subclasses MUST ensure that
+ * {@link #initializeDelegate()} is invoked before the delegate is used.
+ */
+class DelegateLocationProvider extends AbstractLocationProvider
+ implements AbstractLocationProvider.Listener {
+
+ private final Object mInitializationLock = new Object();
+
+ protected final AbstractLocationProvider mDelegate;
+
+ private boolean mInitialized = false;
+
+ DelegateLocationProvider(Executor executor, AbstractLocationProvider delegate) {
+ super(executor, null, null);
+
+ mDelegate = delegate;
+ }
+
+ /**
+ * This function initializes state from the delegate and allows all other callbacks to be
+ * immediately invoked. If this method is invoked from a subclass constructor, it should be the
+ * last statement in the constructor since it allows the subclass' reference to escape.
+ */
+ protected void initializeDelegate() {
+ synchronized (mInitializationLock) {
+ Preconditions.checkState(!mInitialized);
+ setState(previousState -> mDelegate.getController().setListener(this));
+ mInitialized = true;
+ }
+ }
+
+ // must be invoked in every listener callback to ensure they don't run until initialized
+ protected final void waitForInitialization() {
+ // callbacks can start coming as soon as the setListener() call in initializeDelegate
+ // completes - but we can't allow any to proceed until the setState call afterwards
+ // completes. acquiring the initialization lock here blocks until initialization is
+ // complete, and we verify this wasn't called before initializeDelegate for some reason.
+ synchronized (mInitializationLock) {
+ Preconditions.checkState(mInitialized);
+ }
+ }
+
+ @Override
+ public void onStateChanged(State oldState, State newState) {
+ waitForInitialization();
+ setState(previousState -> newState);
+ }
+
+ @Override
+ public void onReportLocation(LocationResult locationResult) {
+ waitForInitialization();
+ reportLocation(locationResult);
+ }
+
+ @Override
+ protected void onStart() {
+ Preconditions.checkState(mInitialized);
+ mDelegate.getController().start();
+ }
+
+ @Override
+ protected void onStop() {
+ Preconditions.checkState(mInitialized);
+ mDelegate.getController().stop();
+ }
+
+ @Override
+ protected void onSetRequest(ProviderRequest request) {
+ Preconditions.checkState(mInitialized);
+ mDelegate.getController().setRequest(request);
+ }
+
+ @Override
+ protected void onFlush(Runnable callback) {
+ Preconditions.checkState(mInitialized);
+ mDelegate.getController().flush(callback);
+ }
+
+ @Override
+ protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {
+ Preconditions.checkState(mInitialized);
+ mDelegate.getController().sendExtraCommand(uid, pid, command, extras);
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ Preconditions.checkState(mInitialized);
+ mDelegate.dump(fd, pw, args);
+ }
+}
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 221d4fb40ef9..388b5a4dd54e 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -55,6 +55,7 @@ import android.location.LocationManagerInternal;
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
+import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
@@ -88,6 +89,7 @@ import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.location.LocationPermissions;
import com.android.server.location.LocationPermissions.PermissionLevel;
+import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.fudger.LocationFudger;
import com.android.server.location.injector.AlarmHelper;
import com.android.server.location.injector.AppForegroundHelper;
@@ -95,7 +97,6 @@ import com.android.server.location.injector.AppForegroundHelper.AppForegroundLis
import com.android.server.location.injector.AppOpsHelper;
import com.android.server.location.injector.Injector;
import com.android.server.location.injector.LocationAttributionHelper;
-import com.android.server.location.injector.LocationEventLog;
import com.android.server.location.injector.LocationPermissionsHelper;
import com.android.server.location.injector.LocationPermissionsHelper.LocationPermissionsListener;
import com.android.server.location.injector.LocationPowerSaveModeHelper;
@@ -117,6 +118,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Predicate;
/**
@@ -321,7 +323,7 @@ public class LocationProviderManager extends
+ getRequest());
}
- mLocationEventLog.logProviderClientRegistered(mName, getIdentity(), super.getRequest());
+ mEventLog.logProviderClientRegistered(mName, getIdentity(), super.getRequest());
// initialization order is important as there are ordering dependencies
mPermitted = mLocationPermissionsHelper.hasLocationPermissions(mPermissionLevel,
@@ -331,6 +333,10 @@ public class LocationProviderManager extends
mIsUsingHighPower = isUsingHighPower();
onProviderListenerRegister();
+
+ if (mForeground) {
+ mEventLog.logProviderClientForeground(mName, getIdentity());
+ }
}
@GuardedBy("mLock")
@@ -342,7 +348,7 @@ public class LocationProviderManager extends
onProviderListenerUnregister();
- mLocationEventLog.logProviderClientUnregistered(mName, getIdentity());
+ mEventLog.logProviderClientUnregistered(mName, getIdentity());
if (D) {
Log.d(TAG, mName + " provider removed registration from " + getIdentity());
@@ -367,6 +373,8 @@ public class LocationProviderManager extends
Preconditions.checkState(Thread.holdsLock(mLock));
}
+ mEventLog.logProviderClientActive(mName, getIdentity());
+
if (!getRequest().isHiddenFromAppOps()) {
mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey());
}
@@ -387,6 +395,8 @@ public class LocationProviderManager extends
}
onProviderListenerInactive();
+
+ mEventLog.logProviderClientInactive(mName, getIdentity());
}
/**
@@ -522,6 +532,12 @@ public class LocationProviderManager extends
mForeground = foreground;
+ if (mForeground) {
+ mEventLog.logProviderClientForeground(mName, getIdentity());
+ } else {
+ mEventLog.logProviderClientBackground(mName, getIdentity());
+ }
+
// note that onProviderLocationRequestChanged() is always called
return onProviderLocationRequestChanged()
|| mLocationPowerSaveModeHelper.getLocationPowerSaveMode()
@@ -853,7 +869,7 @@ public class LocationProviderManager extends
listener.deliverOnLocationChanged(deliverLocationResult,
mUseWakeLock ? mWakeLock::release : null);
- mLocationEventLog.logProviderDeliveredLocations(mName, locationResult.size(),
+ mEventLog.logProviderDeliveredLocations(mName, locationResult.size(),
getIdentity());
}
@@ -1152,7 +1168,7 @@ public class LocationProviderManager extends
// we currently don't hold a wakelock for getCurrentLocation deliveries
listener.deliverOnLocationChanged(deliverLocationResult, null);
- mLocationEventLog.logProviderDeliveredLocations(mName,
+ mEventLog.logProviderDeliveredLocations(mName,
locationResult != null ? locationResult.size() : 0, getIdentity());
}
@@ -1219,6 +1235,9 @@ public class LocationProviderManager extends
@GuardedBy("mLock")
private final ArrayList<ProviderEnabledListener> mEnabledListeners;
+ private final CopyOnWriteArrayList<IProviderRequestListener> mProviderRequestListeners;
+
+ protected final LocationEventLog mEventLog;
protected final LocationManagerInternal mLocationManagerInternal;
protected final SettingsHelper mSettingsHelper;
protected final UserInfoHelper mUserHelper;
@@ -1231,7 +1250,6 @@ public class LocationProviderManager extends
protected final LocationAttributionHelper mLocationAttributionHelper;
protected final LocationUsageLogger mLocationUsageLogger;
protected final LocationFudger mLocationFudger;
- protected final LocationEventLog mLocationEventLog;
private final UserListener mUserChangedListener = this::onUserChanged;
private final UserSettingChangedListener mLocationEnabledChangedListener =
@@ -1269,8 +1287,8 @@ public class LocationProviderManager extends
@GuardedBy("mLock")
private @Nullable OnAlarmListener mDelayedRegister;
- public LocationProviderManager(Context context, Injector injector, String name,
- @Nullable PassiveLocationProviderManager passiveManager) {
+ public LocationProviderManager(Context context, Injector injector, LocationEventLog eventLog,
+ String name, @Nullable PassiveLocationProviderManager passiveManager) {
mContext = context;
mName = Objects.requireNonNull(name);
mPassiveManager = passiveManager;
@@ -1279,7 +1297,9 @@ public class LocationProviderManager extends
mLastLocations = new SparseArray<>(2);
mEnabledListeners = new ArrayList<>();
+ mProviderRequestListeners = new CopyOnWriteArrayList<>();
+ mEventLog = eventLog;
mLocationManagerInternal = Objects.requireNonNull(
LocalServices.getService(LocationManagerInternal.class));
mSettingsHelper = injector.getSettingsHelper();
@@ -1292,7 +1312,6 @@ public class LocationProviderManager extends
mScreenInteractiveHelper = injector.getScreenInteractiveHelper();
mLocationAttributionHelper = injector.getLocationAttributionHelper();
mLocationUsageLogger = injector.getLocationUsageLogger();
- mLocationEventLog = injector.getLocationEventLog();
mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
mProvider = new MockableLocationProvider(mLock);
@@ -1344,6 +1363,7 @@ public class LocationProviderManager extends
// if external entities are registering listeners it's their responsibility to
// unregister them before stopManager() is called
Preconditions.checkState(mEnabledListeners.isEmpty());
+ mProviderRequestListeners.clear();
mEnabled.clear();
mLastLocations.clear();
@@ -1404,6 +1424,16 @@ public class LocationProviderManager extends
}
}
+ /** Add a {@link IProviderRequestListener}. */
+ public void addProviderRequestListener(IProviderRequestListener listener) {
+ mProviderRequestListeners.add(listener);
+ }
+
+ /** Remove a {@link IProviderRequestListener}. */
+ public void removeProviderRequestListener(IProviderRequestListener listener) {
+ mProviderRequestListeners.remove(listener);
+ }
+
public void setRealProvider(@Nullable AbstractLocationProvider provider) {
synchronized (mLock) {
Preconditions.checkState(mState != STATE_STOPPED);
@@ -1421,7 +1451,7 @@ public class LocationProviderManager extends
synchronized (mLock) {
Preconditions.checkState(mState != STATE_STOPPED);
- mLocationEventLog.logProviderMocked(mName, provider != null);
+ mEventLog.logProviderMocked(mName, provider != null);
final long identity = Binder.clearCallingIdentity();
try {
@@ -1873,8 +1903,7 @@ public class LocationProviderManager extends
Preconditions.checkState(delayMs >= 0 && delayMs <= newRequest.getIntervalMillis());
if (delayMs < MIN_REQUEST_DELAY_MS) {
- mLocationEventLog.logProviderUpdateRequest(mName, newRequest);
- mProvider.getController().setRequest(newRequest);
+ setProviderRequest(newRequest);
} else {
if (D) {
Log.d(TAG, mName + " provider delaying request update " + newRequest + " by "
@@ -1886,8 +1915,7 @@ public class LocationProviderManager extends
public void onAlarm() {
synchronized (mLock) {
if (mDelayedRegister == this) {
- mLocationEventLog.logProviderUpdateRequest(mName, newRequest);
- mProvider.getController().setRequest(newRequest);
+ setProviderRequest(newRequest);
mDelayedRegister = null;
}
}
@@ -1906,8 +1934,23 @@ public class LocationProviderManager extends
Preconditions.checkState(Thread.holdsLock(mLock));
}
- mLocationEventLog.logProviderUpdateRequest(mName, ProviderRequest.EMPTY_REQUEST);
- mProvider.getController().setRequest(ProviderRequest.EMPTY_REQUEST);
+ setProviderRequest(ProviderRequest.EMPTY_REQUEST);
+ }
+
+ @GuardedBy("mLock")
+ private void setProviderRequest(ProviderRequest request) {
+ mEventLog.logProviderUpdateRequest(mName, request);
+ mProvider.getController().setRequest(request);
+
+ FgThread.getHandler().post(() -> {
+ for (IProviderRequestListener listener : mProviderRequestListeners) {
+ try {
+ listener.onProviderRequestChanged(mName, request);
+ } catch (RemoteException e) {
+ mProviderRequestListeners.remove(listener);
+ }
+ }
+ });
}
@GuardedBy("mLock")
@@ -2232,7 +2275,7 @@ public class LocationProviderManager extends
}
// don't log location received for passive provider because it's spammy
- mLocationEventLog.logProviderReceivedLocations(mName, filtered.size());
+ mEventLog.logProviderReceivedLocations(mName, filtered.size());
} else {
// passive provider should get already filtered results as input
filtered = locationResult;
@@ -2332,7 +2375,7 @@ public class LocationProviderManager extends
if (D) {
Log.d(TAG, "[u" + userId + "] " + mName + " provider enabled = " + enabled);
}
- mLocationEventLog.logProviderEnabled(mName, userId, enabled);
+ mEventLog.logProviderEnabled(mName, userId, enabled);
}
// clear last locations if we become disabled
diff --git a/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
index b35af4f6475c..027f4e94f55b 100644
--- a/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
@@ -24,6 +24,7 @@ import android.location.provider.ProviderRequest;
import android.os.Binder;
import com.android.internal.util.Preconditions;
+import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.injector.Injector;
import java.util.Collection;
@@ -33,8 +34,9 @@ import java.util.Collection;
*/
public class PassiveLocationProviderManager extends LocationProviderManager {
- public PassiveLocationProviderManager(Context context, Injector injector) {
- super(context, injector, LocationManager.PASSIVE_PROVIDER, null);
+ public PassiveLocationProviderManager(Context context, Injector injector,
+ LocationEventLog eventLog) {
+ super(context, injector, eventLog, LocationManager.PASSIVE_PROVIDER, null);
}
@Override
diff --git a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
new file mode 100644
index 000000000000..6f4aa642500f
--- /dev/null
+++ b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.provider;
+
+import static android.location.provider.ProviderRequest.INTERVAL_DISABLED;
+
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
+
+import android.annotation.Nullable;
+import android.location.Location;
+import android.location.LocationResult;
+import android.location.provider.ProviderRequest;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+import com.android.server.DeviceIdleInternal;
+import com.android.server.FgThread;
+import com.android.server.location.eventlog.LocationEventLog;
+import com.android.server.location.injector.DeviceIdleHelper;
+import com.android.server.location.injector.DeviceStationaryHelper;
+import com.android.server.location.injector.Injector;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Throttles location providers completely while the device is in doze and stationary, and returns
+ * the last known location as a new location at the appropriate interval instead. Hypothetically,
+ * this throttling could be applied only when the device is stationary - however we don't trust the
+ * accuracy of the on-device SMD (which could allow for 100s of feet of movement without triggering
+ * in some use cases) enough to rely just on this. Instead we require the device to be in doze mode
+ * and stationary to narrow down the effect of false positives/negatives.
+ */
+public final class StationaryThrottlingLocationProvider extends DelegateLocationProvider
+ implements DeviceIdleHelper.DeviceIdleListener, DeviceIdleInternal.StationaryListener {
+
+ private static final long MAX_STATIONARY_LOCATION_AGE_MS = 30000;
+
+ private final Object mLock = new Object();
+
+ private final String mName;
+ private final DeviceIdleHelper mDeviceIdleHelper;
+ private final DeviceStationaryHelper mDeviceStationaryHelper;
+ private final LocationEventLog mEventLog;
+
+ @GuardedBy("mLock")
+ private boolean mDeviceIdle = false;
+ @GuardedBy("mLock")
+ private boolean mDeviceStationary = false;
+ @GuardedBy("mLock")
+ private long mDeviceStationaryRealtimeMs = Long.MIN_VALUE;
+ @GuardedBy("mLock")
+ private ProviderRequest mIncomingRequest = ProviderRequest.EMPTY_REQUEST;
+ @GuardedBy("mLock")
+ private ProviderRequest mOutgoingRequest = ProviderRequest.EMPTY_REQUEST;
+ @GuardedBy("mLock")
+ private long mThrottlingIntervalMs = INTERVAL_DISABLED;
+ @GuardedBy("mLock")
+ private @Nullable DeliverLastLocationRunnable mDeliverLastLocationCallback = null;
+
+ @GuardedBy("mLock")
+ private @Nullable Location mLastLocation;
+
+ public StationaryThrottlingLocationProvider(String name, Injector injector,
+ AbstractLocationProvider delegate, LocationEventLog eventLog) {
+ super(DIRECT_EXECUTOR, delegate);
+
+ mName = name;
+ mDeviceIdleHelper = injector.getDeviceIdleHelper();
+ mDeviceStationaryHelper = injector.getDeviceStationaryHelper();
+ mEventLog = eventLog;
+
+ // must be last statement in the constructor because reference is escaping
+ initializeDelegate();
+ }
+
+ @Override
+ public void onReportLocation(LocationResult locationResult) {
+ super.onReportLocation(locationResult);
+
+ synchronized (mLock) {
+ mLastLocation = locationResult.getLastLocation();
+ onThrottlingChangedLocked(false);
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ mDelegate.getController().start();
+
+ synchronized (mLock) {
+ mDeviceIdleHelper.addListener(this);
+ mDeviceIdle = mDeviceIdleHelper.isDeviceIdle();
+ mDeviceStationaryHelper.addListener(this);
+ mDeviceStationary = false;
+ mDeviceStationaryRealtimeMs = Long.MIN_VALUE;
+
+ onThrottlingChangedLocked(false);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ synchronized (mLock) {
+ mDeviceStationaryHelper.removeListener(this);
+ mDeviceIdleHelper.removeListener(this);
+
+ mIncomingRequest = ProviderRequest.EMPTY_REQUEST;
+ mOutgoingRequest = ProviderRequest.EMPTY_REQUEST;
+ mThrottlingIntervalMs = INTERVAL_DISABLED;
+
+ if (mDeliverLastLocationCallback != null) {
+ FgThread.getHandler().removeCallbacks(mDeliverLastLocationCallback);
+ mDeliverLastLocationCallback = null;
+ }
+
+ mLastLocation = null;
+ }
+
+ mDelegate.getController().stop();
+ }
+
+ @Override
+ protected void onSetRequest(ProviderRequest request) {
+ synchronized (mLock) {
+ mIncomingRequest = request;
+ onThrottlingChangedLocked(true);
+ }
+ }
+
+ @Override
+ public void onDeviceIdleChanged(boolean deviceIdle) {
+ synchronized (mLock) {
+ if (deviceIdle == mDeviceIdle) {
+ return;
+ }
+
+ mDeviceIdle = deviceIdle;
+ onThrottlingChangedLocked(false);
+ }
+ }
+
+ @Override
+ public void onDeviceStationaryChanged(boolean deviceStationary) {
+ synchronized (mLock) {
+ if (mDeviceStationary == deviceStationary) {
+ return;
+ }
+
+ mDeviceStationary = deviceStationary;
+ if (mDeviceStationary) {
+ mDeviceStationaryRealtimeMs = SystemClock.elapsedRealtime();
+ } else {
+ mDeviceStationaryRealtimeMs = Long.MIN_VALUE;
+ }
+ onThrottlingChangedLocked(false);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void onThrottlingChangedLocked(boolean deliverImmediate) {
+ long throttlingIntervalMs = INTERVAL_DISABLED;
+ if (mDeviceStationary && mDeviceIdle && !mIncomingRequest.isLocationSettingsIgnored()
+ && mLastLocation != null
+ && mLastLocation.getElapsedRealtimeAgeMillis(mDeviceStationaryRealtimeMs)
+ <= MAX_STATIONARY_LOCATION_AGE_MS) {
+ throttlingIntervalMs = mIncomingRequest.getIntervalMillis();
+ }
+
+ ProviderRequest newRequest;
+ if (throttlingIntervalMs != INTERVAL_DISABLED) {
+ newRequest = ProviderRequest.EMPTY_REQUEST;
+ } else {
+ newRequest = mIncomingRequest;
+ }
+
+ if (!newRequest.equals(mOutgoingRequest)) {
+ mOutgoingRequest = newRequest;
+ mDelegate.getController().setRequest(mOutgoingRequest);
+ }
+
+ if (throttlingIntervalMs == mThrottlingIntervalMs) {
+ return;
+ }
+
+ long oldThrottlingIntervalMs = mThrottlingIntervalMs;
+ mThrottlingIntervalMs = throttlingIntervalMs;
+
+ if (mThrottlingIntervalMs != INTERVAL_DISABLED) {
+ if (oldThrottlingIntervalMs == INTERVAL_DISABLED) {
+ if (D) {
+ Log.d(TAG, mName + " provider stationary throttled");
+ }
+ mEventLog.logProviderStationaryThrottled(mName, true);
+ }
+
+ if (mDeliverLastLocationCallback != null) {
+ FgThread.getHandler().removeCallbacks(mDeliverLastLocationCallback);
+ }
+ mDeliverLastLocationCallback = new DeliverLastLocationRunnable();
+
+ Preconditions.checkState(mLastLocation != null);
+
+ if (deliverImmediate) {
+ FgThread.getHandler().post(mDeliverLastLocationCallback);
+ } else {
+ long delayMs = mThrottlingIntervalMs - mLastLocation.getElapsedRealtimeAgeMillis();
+ FgThread.getHandler().postDelayed(mDeliverLastLocationCallback, delayMs);
+ }
+ } else {
+ if (oldThrottlingIntervalMs != INTERVAL_DISABLED) {
+ mEventLog.logProviderStationaryThrottled(mName, false);
+ if (D) {
+ Log.d(TAG, mName + " provider stationary unthrottled");
+ }
+ }
+
+ FgThread.getHandler().removeCallbacks(mDeliverLastLocationCallback);
+ mDeliverLastLocationCallback = null;
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mThrottlingIntervalMs != INTERVAL_DISABLED) {
+ pw.println("stationary throttled=" + mLastLocation);
+ } else {
+ pw.print("stationary throttled=false");
+ if (!mDeviceIdle) {
+ pw.print(" (not idle)");
+ }
+ if (!mDeviceStationary) {
+ pw.print(" (not stationary)");
+ }
+ pw.println();
+ }
+
+ mDelegate.dump(fd, pw, args);
+ }
+
+ private class DeliverLastLocationRunnable implements Runnable {
+ @Override
+ public void run() {
+ Location location;
+ synchronized (mLock) {
+ if (mDeliverLastLocationCallback != this) {
+ return;
+ }
+ if (mLastLocation == null) {
+ return;
+ }
+
+ location = new Location(mLastLocation);
+ location.setTime(System.currentTimeMillis());
+ location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+ if (location.hasSpeed()) {
+ location.removeSpeed();
+ if (location.hasSpeedAccuracy()) {
+ location.removeSpeedAccuracy();
+ }
+ }
+ if (location.hasBearing()) {
+ location.removeBearing();
+ if (location.hasBearingAccuracy()) {
+ location.removeBearingAccuracy();
+ }
+ }
+
+ mLastLocation = location;
+ FgThread.getHandler().postDelayed(this, mThrottlingIntervalMs);
+ }
+
+ reportLocation(LocationResult.wrap(location));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
index 32d637ffa730..4c97f645aac9 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
@@ -140,7 +140,7 @@ public class ProxyLocationProvider extends AbstractLocationProvider {
}
@Override
- public void onSetRequest(ProviderRequest request) {
+ protected void onSetRequest(ProviderRequest request) {
mRequest = request;
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider provider = ILocationProvider.Stub.asInterface(binder);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 5a4cc13babcf..de87c541f12b 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -88,6 +88,7 @@ import android.os.storage.StorageManager;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.provider.Settings.SettingNotFoundException;
+import android.security.AndroidKeyStoreMaintenance;
import android.security.Authorization;
import android.security.KeyStore;
import android.security.keystore.AndroidKeyStoreProvider;
@@ -281,6 +282,7 @@ public class LockSettingsService extends ILockSettings.Stub {
super.onBootPhase(phase);
if (phase == PHASE_ACTIVITY_MANAGER_READY) {
mLockSettingsService.migrateOldDataAfterSystemReady();
+ mLockSettingsService.loadEscrowData();
}
}
@@ -796,6 +798,7 @@ public class LockSettingsService extends ILockSettings.Stub {
if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
// Notify keystore that a new user was added.
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ AndroidKeyStoreMaintenance.onUserAdded(userHandle);
final KeyStore ks = KeyStore.getInstance();
final UserInfo parentInfo = mUserManager.getProfileParent(userHandle);
final int parentHandle = parentInfo != null ? parentInfo.id : -1;
@@ -826,13 +829,17 @@ public class LockSettingsService extends ILockSettings.Stub {
mSpManager.initWeaverService();
getAuthSecretHal();
mDeviceProvisionedObserver.onSystemReady();
- mRebootEscrowManager.loadRebootEscrowDataIfAvailable();
+
// TODO: maybe skip this for split system user mode.
mStorage.prefetchUser(UserHandle.USER_SYSTEM);
mBiometricDeferredQueue.systemReady(mInjector.getFingerprintManager(),
mInjector.getFaceManager());
}
+ private void loadEscrowData() {
+ mRebootEscrowManager.loadRebootEscrowDataIfAvailable(mHandler);
+ }
+
private void getAuthSecretHal() {
try {
mAuthSecretService = IAuthSecret.getService(/* retry */ true);
@@ -1302,6 +1309,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
private void setKeystorePassword(byte[] password, int userHandle) {
+ AndroidKeyStoreMaintenance.onUserPasswordChanged(userHandle, password);
final KeyStore ks = KeyStore.getInstance();
// TODO(b/120484642): Update keystore to accept byte[] passwords
String passwordString = password == null ? null : new String(password);
@@ -1310,7 +1318,7 @@ public class LockSettingsService extends ILockSettings.Stub {
private void unlockKeystore(byte[] password, int userHandle) {
if (DEBUG) Slog.v(TAG, "Unlock keystore for user: " + userHandle);
- new Authorization().onLockScreenEvent(false, userHandle, password);
+ Authorization.onLockScreenEvent(false, userHandle, password);
// TODO(b/120484642): Update keystore to accept byte[] passwords
String passwordString = password == null ? null : new String(password);
final KeyStore ks = KeyStore.getInstance();
@@ -2336,6 +2344,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mSpManager.removeUser(userId);
mStrongAuth.removeUser(userId);
+ AndroidKeyStoreMaintenance.onUserRemoved(userId);
final KeyStore ks = KeyStore.getInstance();
ks.onUserRemoved(userId);
mManagedProfilePasswordCache.removePassword(userId);
@@ -2421,10 +2430,17 @@ public class LockSettingsService extends ILockSettings.Stub {
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
enforceShell();
+ final int origPid = Binder.getCallingPid();
+ final int origUid = Binder.getCallingUid();
+
+ // The original identity is an opaque integer.
final long origId = Binder.clearCallingIdentity();
+ Slog.e(TAG, "Caller pid " + origPid + " Caller uid " + origUid);
try {
- (new LockSettingsShellCommand(new LockPatternUtils(mContext))).exec(
- this, in, out, err, args, callback, resultReceiver);
+ final LockSettingsShellCommand command =
+ new LockSettingsShellCommand(new LockPatternUtils(mContext), mContext, origPid,
+ origUid);
+ command.exec(this, in, out, err, args, callback, resultReceiver);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -2939,11 +2955,8 @@ public class LockSettingsService extends ILockSettings.Stub {
FingerprintManager mFingerprintManager = mInjector.getFingerprintManager();
if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
if (mFingerprintManager.hasEnrolledFingerprints(userId)) {
- CountDownLatch latch = new CountDownLatch(1);
- // For the purposes of M and N, groupId is the same as userId.
- Fingerprint finger = new Fingerprint(null, userId, 0, 0);
- mFingerprintManager.remove(finger, userId,
- fingerprintManagerRemovalCallback(latch));
+ final CountDownLatch latch = new CountDownLatch(1);
+ mFingerprintManager.removeAll(userId, fingerprintManagerRemovalCallback(latch));
try {
latch.await(10000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
@@ -2957,9 +2970,8 @@ public class LockSettingsService extends ILockSettings.Stub {
FaceManager mFaceManager = mInjector.getFaceManager();
if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
if (mFaceManager.hasEnrolledTemplates(userId)) {
- CountDownLatch latch = new CountDownLatch(1);
- Face face = new Face(null, 0, 0);
- mFaceManager.remove(face, userId, faceManagerRemovalCallback(latch));
+ final CountDownLatch latch = new CountDownLatch(1);
+ mFaceManager.removeAll(userId, faceManagerRemovalCallback(latch));
try {
latch.await(10000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
@@ -2973,10 +2985,8 @@ public class LockSettingsService extends ILockSettings.Stub {
CountDownLatch latch) {
return new FingerprintManager.RemovalCallback() {
@Override
- public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence err) {
- Slog.e(TAG, String.format(
- "Can't remove fingerprint %d in group %d. Reason: %s",
- fp.getBiometricId(), fp.getGroupId(), err));
+ public void onRemovalError(@Nullable Fingerprint fp, int errMsgId, CharSequence err) {
+ Slog.e(TAG, "Unable to remove fingerprint, error: " + err);
latch.countDown();
}
@@ -2992,9 +3002,8 @@ public class LockSettingsService extends ILockSettings.Stub {
private FaceManager.RemovalCallback faceManagerRemovalCallback(CountDownLatch latch) {
return new FaceManager.RemovalCallback() {
@Override
- public void onRemovalError(Face face, int errMsgId, CharSequence err) {
- Slog.e(TAG, String.format("Can't remove face %d. Reason: %s",
- face.getBiometricId(), err));
+ public void onRemovalError(@Nullable Face face, int errMsgId, CharSequence err) {
+ Slog.e(TAG, "Unable to remove face, error: " + err);
latch.countDown();
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 834cf0536865..6a5c2d89a4e2 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -21,8 +21,11 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTE
import android.app.ActivityManager;
import android.app.admin.PasswordMetrics;
+import android.content.Context;
import android.os.ShellCommand;
+import android.os.SystemProperties;
import android.text.TextUtils;
+import android.util.Slog;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
@@ -43,15 +46,25 @@ class LockSettingsShellCommand extends ShellCommand {
private static final String COMMAND_VERIFY = "verify";
private static final String COMMAND_GET_DISABLED = "get-disabled";
private static final String COMMAND_REMOVE_CACHE = "remove-cache";
+ private static final String COMMAND_SET_ROR_PROVIDER_PACKAGE =
+ "set-resume-on-reboot-provider-package";
private static final String COMMAND_HELP = "help";
private int mCurrentUserId;
private final LockPatternUtils mLockPatternUtils;
+ private final Context mContext;
+ private final int mCallingPid;
+ private final int mCallingUid;
+
private String mOld = "";
private String mNew = "";
- LockSettingsShellCommand(LockPatternUtils lockPatternUtils) {
+ LockSettingsShellCommand(LockPatternUtils lockPatternUtils, Context context, int callingPid,
+ int callingUid) {
mLockPatternUtils = lockPatternUtils;
+ mCallingPid = callingPid;
+ mCallingUid = callingUid;
+ mContext = context;
}
@Override
@@ -68,6 +81,7 @@ class LockSettingsShellCommand extends ShellCommand {
case COMMAND_HELP:
case COMMAND_GET_DISABLED:
case COMMAND_SET_DISABLED:
+ case COMMAND_SET_ROR_PROVIDER_PACKAGE:
break;
default:
getErrPrintWriter().println(
@@ -80,6 +94,9 @@ class LockSettingsShellCommand extends ShellCommand {
case COMMAND_REMOVE_CACHE:
runRemoveCache();
return 0;
+ case COMMAND_SET_ROR_PROVIDER_PACKAGE:
+ runSetResumeOnRebootProviderPackage();
+ return 0;
case COMMAND_HELP:
onHelp();
return 0;
@@ -171,6 +188,10 @@ class LockSettingsShellCommand extends ShellCommand {
pw.println(" remove-cache [--user USER_ID]");
pw.println(" Removes cached unified challenge for the managed profile.");
pw.println("");
+ pw.println(" set-resume-on-reboot-provider-package <package_name>");
+ pw.println(" Sets the package name for server based resume on reboot service "
+ + "provider.");
+ pw.println("");
}
}
@@ -256,6 +277,17 @@ class LockSettingsShellCommand extends ShellCommand {
return true;
}
+ private boolean runSetResumeOnRebootProviderPackage() {
+ final String packageName = mNew;
+ String name = ResumeOnRebootServiceProvider.PROP_ROR_PROVIDER_PACKAGE;
+ Slog.i(TAG, "Setting " + name + " to " + packageName);
+
+ mContext.enforcePermission(android.Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE,
+ mCallingPid, mCallingUid, TAG);
+ SystemProperties.set(name, packageName);
+ return true;
+ }
+
private boolean runClear() {
LockscreenCredential none = LockscreenCredential.createNone();
if (!isNewCredentialSufficient(none)) {
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowData.java b/services/core/java/com/android/server/locksettings/RebootEscrowData.java
index 38eeb88e63b0..af0774c6c3fa 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowData.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowData.java
@@ -35,6 +35,12 @@ class RebootEscrowData {
*/
private static final int CURRENT_VERSION = 2;
+ /**
+ * This is the legacy version of the escrow data format for R builds. The escrow data is only
+ * encrypted by the escrow key, without additional wrap of another key from keystore.
+ */
+ private static final int LEGACY_SINGLE_ENCRYPTED_VERSION = 1;
+
private RebootEscrowData(byte spVersion, byte[] syntheticPassword, byte[] blob,
RebootEscrowKey key) {
mSpVersion = spVersion;
@@ -64,6 +70,19 @@ class RebootEscrowData {
return mKey;
}
+ private static byte[] decryptBlobCurrentVersion(SecretKey kk, RebootEscrowKey ks,
+ DataInputStream dis) throws IOException {
+ if (kk == null) {
+ throw new IOException("Failed to find wrapper key in keystore, cannot decrypt the"
+ + " escrow data");
+ }
+
+ // Decrypt the blob with the key from keystore first, then decrypt again with the reboot
+ // escrow key.
+ byte[] ksEncryptedBlob = AesEncryptionUtil.decrypt(kk, dis);
+ return AesEncryptionUtil.decrypt(ks.getKey(), ksEncryptedBlob);
+ }
+
static RebootEscrowData fromEncryptedData(RebootEscrowKey ks, byte[] blob, SecretKey kk)
throws IOException {
Objects.requireNonNull(ks);
@@ -71,17 +90,20 @@ class RebootEscrowData {
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(blob));
int version = dis.readInt();
- if (version != CURRENT_VERSION) {
- throw new IOException("Unsupported version " + version);
- }
byte spVersion = dis.readByte();
-
- // Decrypt the blob with the key from keystore first, then decrypt again with the reboot
- // escrow key.
- byte[] ksEncryptedBlob = AesEncryptionUtil.decrypt(kk, dis);
- final byte[] syntheticPassword = AesEncryptionUtil.decrypt(ks.getKey(), ksEncryptedBlob);
-
- return new RebootEscrowData(spVersion, syntheticPassword, blob, ks);
+ switch (version) {
+ case CURRENT_VERSION: {
+ byte[] syntheticPassword = decryptBlobCurrentVersion(kk, ks, dis);
+ return new RebootEscrowData(spVersion, syntheticPassword, blob, ks);
+ }
+ case LEGACY_SINGLE_ENCRYPTED_VERSION: {
+ // Decrypt the blob with the escrow key directly.
+ byte[] syntheticPassword = AesEncryptionUtil.decrypt(ks.getKey(), dis);
+ return new RebootEscrowData(spVersion, syntheticPassword, blob, ks);
+ }
+ default:
+ throw new IOException("Unsupported version " + version);
+ }
}
static RebootEscrowData fromSyntheticPassword(RebootEscrowKey ks, byte spVersion,
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 06962d414009..30ea5556b41c 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.os.Handler;
import android.os.SystemClock;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -39,6 +40,7 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import javax.crypto.SecretKey;
@@ -76,6 +78,13 @@ class RebootEscrowManager {
private static final int BOOT_COUNT_TOLERANCE = 5;
/**
+ * The default retry specs for loading reboot escrow data. We will attempt to retry loading
+ * escrow data on temporarily errors, e.g. unavailable network.
+ */
+ private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT = 3;
+ private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS = 30;
+
+ /**
* Logs events for later debugging in bugreports.
*/
private final RebootEscrowEventLog mEventLog;
@@ -137,6 +146,7 @@ class RebootEscrowManager {
RebootEscrowProviderInterface rebootEscrowProvider;
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
"server_based_ror_enabled", false)) {
+ Slog.i(TAG, "Using server based resume on reboot");
rebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mContext, mStorage);
} else {
rebootEscrowProvider = new RebootEscrowProviderHalImpl();
@@ -148,6 +158,14 @@ class RebootEscrowManager {
return null;
}
+ void post(Handler handler, Runnable runnable) {
+ handler.post(runnable);
+ }
+
+ void postDelayed(Handler handler, Runnable runnable, long delayMillis) {
+ handler.postDelayed(runnable, delayMillis);
+ }
+
public Context getContext() {
return mContext;
}
@@ -199,7 +217,18 @@ class RebootEscrowManager {
mKeyStoreManager = injector.getKeyStoreManager();
}
- void loadRebootEscrowDataIfAvailable() {
+ private void onGetRebootEscrowKeyFailed(List<UserInfo> users) {
+ Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage.");
+ for (UserInfo user : users) {
+ mStorage.removeRebootEscrow(user.id);
+ }
+
+ // Clear the old key in keystore.
+ mKeyStoreManager.clearKeyStoreEncryptionKey();
+ onEscrowRestoreComplete(false);
+ }
+
+ void loadRebootEscrowDataIfAvailable(Handler retryHandler) {
List<UserInfo> users = mUserManager.getUsers();
List<UserInfo> rebootEscrowUsers = new ArrayList<>();
for (UserInfo user : users) {
@@ -212,17 +241,53 @@ class RebootEscrowManager {
return;
}
+ mInjector.post(retryHandler, () -> loadRebootEscrowDataWithRetry(
+ retryHandler, 0, users, rebootEscrowUsers));
+ }
+
+ void scheduleLoadRebootEscrowDataOrFail(Handler retryHandler, int attemptNumber,
+ List<UserInfo> users, List<UserInfo> rebootEscrowUsers) {
+ Objects.requireNonNull(retryHandler);
+
+ final int retryLimit = DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA,
+ "load_escrow_data_retry_count", DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT);
+ final int retryIntervalInSeconds = DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA,
+ "load_escrow_data_retry_interval_seconds",
+ DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS);
+
+ if (attemptNumber < retryLimit) {
+ Slog.i(TAG, "Scheduling loadRebootEscrowData retry number: " + attemptNumber);
+ mInjector.postDelayed(retryHandler, () -> loadRebootEscrowDataWithRetry(
+ retryHandler, attemptNumber, users, rebootEscrowUsers),
+ retryIntervalInSeconds * 1000);
+ return;
+ }
+
+ Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts");
+ onGetRebootEscrowKeyFailed(users);
+ }
+
+ void loadRebootEscrowDataWithRetry(Handler retryHandler, int attemptNumber,
+ List<UserInfo> users, List<UserInfo> rebootEscrowUsers) {
// Fetch the key from keystore to decrypt the escrow data & escrow key; this key is
// generated before reboot. Note that we will clear the escrow key even if the keystore key
// is null.
SecretKey kk = mKeyStoreManager.getKeyStoreEncryptionKey();
- RebootEscrowKey escrowKey = getAndClearRebootEscrowKey(kk);
- if (kk == null || escrowKey == null) {
- Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage.");
- for (UserInfo user : users) {
- mStorage.removeRebootEscrow(user.id);
- }
- onEscrowRestoreComplete(false);
+ if (kk == null) {
+ Slog.i(TAG, "Failed to load the key for resume on reboot from key store.");
+ }
+
+ RebootEscrowKey escrowKey;
+ try {
+ escrowKey = getAndClearRebootEscrowKey(kk);
+ } catch (IOException e) {
+ scheduleLoadRebootEscrowDataOrFail(retryHandler, attemptNumber + 1, users,
+ rebootEscrowUsers);
+ return;
+ }
+
+ if (escrowKey == null) {
+ onGetRebootEscrowKeyFailed(users);
return;
}
@@ -249,7 +314,7 @@ class RebootEscrowManager {
}
}
- private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) {
+ private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) throws IOException {
RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
if (rebootEscrowProvider == null) {
Slog.w(TAG,
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java
index 6c1040b596c8..4b00772088f2 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java
@@ -33,7 +33,7 @@ import javax.crypto.SecretKey;
* An implementation of the {@link RebootEscrowProviderInterface} by calling the RebootEscrow HAL.
*/
class RebootEscrowProviderHalImpl implements RebootEscrowProviderInterface {
- private static final String TAG = "RebootEscrowProvider";
+ private static final String TAG = "RebootEscrowProviderHal";
private final Injector mInjector;
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java
index 857ad5fc312a..af6faad3c76e 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java
@@ -16,6 +16,8 @@
package com.android.server.locksettings;
+import java.io.IOException;
+
import javax.crypto.SecretKey;
/**
@@ -33,9 +35,10 @@ public interface RebootEscrowProviderInterface {
/**
* Returns the stored RebootEscrowKey, and clears the storage. If the stored key is encrypted,
- * use the input key to decrypt the RebootEscrowKey. Returns null on failure.
+ * use the input key to decrypt the RebootEscrowKey. Returns null on failure. Throws an
+ * IOException if the failure is non-fatal, and a retry may succeed.
*/
- RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey);
+ RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) throws IOException;
/**
* Clears the stored RebootEscrowKey.
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
index ba1a680ba7fb..b3b45460899d 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
@@ -35,7 +35,7 @@ import javax.crypto.SecretKey;
* encrypt & decrypt the blob.
*/
class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterface {
- private static final String TAG = "RebootEscrowProvider";
+ private static final String TAG = "RebootEscrowProviderServerBased";
// Timeout for service binding
private static final long DEFAULT_SERVICE_TIMEOUT_IN_SECONDS = 10;
@@ -50,6 +50,8 @@ class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterfa
private final Injector mInjector;
+ private byte[] mServerBlob;
+
static class Injector {
private ResumeOnRebootServiceConnection mServiceConnection = null;
@@ -124,17 +126,25 @@ class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterfa
}
@Override
- public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) {
- byte[] serverBlob = mStorage.readRebootEscrowServerBlob();
+ public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) throws IOException {
+ if (mServerBlob == null) {
+ mServerBlob = mStorage.readRebootEscrowServerBlob();
+ }
// Delete the server blob in storage.
mStorage.removeRebootEscrowServerBlob();
- if (serverBlob == null) {
+ if (mServerBlob == null) {
Slog.w(TAG, "Failed to read reboot escrow server blob from storage");
return null;
}
+ if (decryptionKey == null) {
+ Slog.w(TAG, "Failed to decrypt the escrow key; decryption key from keystore is"
+ + " null.");
+ return null;
+ }
+ Slog.i(TAG, "Loaded reboot escrow server blob from storage");
try {
- byte[] escrowKeyBytes = unwrapServerBlob(serverBlob, decryptionKey);
+ byte[] escrowKeyBytes = unwrapServerBlob(mServerBlob, decryptionKey);
if (escrowKeyBytes == null) {
Slog.w(TAG, "Decrypted reboot escrow key bytes should not be null");
return null;
@@ -145,7 +155,7 @@ class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterfa
}
return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
- } catch (TimeoutException | RemoteException | IOException e) {
+ } catch (TimeoutException | RemoteException e) {
Slog.w(TAG, "Failed to decrypt the server blob ", e);
return null;
}
diff --git a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
index a1e18bd5a6bd..9c471b85eb76 100644
--- a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
+++ b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
@@ -31,6 +31,7 @@ import android.os.IBinder;
import android.os.ParcelableException;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.service.resumeonreboot.IResumeOnRebootService;
@@ -55,6 +56,10 @@ public class ResumeOnRebootServiceProvider {
Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE;
private static final String TAG = "ResumeOnRebootServiceProvider";
+ // The system property name that overrides the default service provider package name.
+ static final String PROP_ROR_PROVIDER_PACKAGE =
+ "persist.sys.resume_on_reboot_provider_package";
+
private final Context mContext;
private final PackageManager mPackageManager;
@@ -72,12 +77,19 @@ public class ResumeOnRebootServiceProvider {
private ServiceInfo resolveService() {
Intent intent = new Intent();
intent.setAction(ResumeOnRebootService.SERVICE_INTERFACE);
- if (PROVIDER_PACKAGE != null && !PROVIDER_PACKAGE.equals("")) {
- intent.setPackage(PROVIDER_PACKAGE);
+ int queryFlag = PackageManager.GET_SERVICES;
+ String testAppName = SystemProperties.get(PROP_ROR_PROVIDER_PACKAGE, "");
+ if (!testAppName.isEmpty()) {
+ Slog.i(TAG, "Using test app: " + testAppName);
+ intent.setPackage(testAppName);
+ } else {
+ queryFlag |= PackageManager.MATCH_SYSTEM_ONLY;
+ if (PROVIDER_PACKAGE != null && !PROVIDER_PACKAGE.equals("")) {
+ intent.setPackage(PROVIDER_PACKAGE);
+ }
}
- List<ResolveInfo> resolvedIntents =
- mPackageManager.queryIntentServices(intent, PackageManager.MATCH_SYSTEM_ONLY);
+ List<ResolveInfo> resolvedIntents = mPackageManager.queryIntentServices(intent, queryFlag);
for (ResolveInfo resolvedInfo : resolvedIntents) {
if (resolvedInfo.serviceInfo != null
&& PROVIDER_REQUIRED_PERMISSION.equals(resolvedInfo.serviceInfo.permission)) {
@@ -120,6 +132,7 @@ public class ResumeOnRebootServiceProvider {
if (mServiceConnection != null) {
mContext.unbindService(mServiceConnection);
}
+ mBinder = null;
}
/** Bind to the service */
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
index 6d420a95e26c..35e6489debcf 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
@@ -18,7 +18,6 @@ package com.android.server.locksettings;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
-import android.security.keystore2.AndroidKeyStoreProvider;
import android.util.Slog;
import java.io.ByteArrayOutputStream;
@@ -141,19 +140,8 @@ public class SyntheticPasswordCrypto {
}
}
- /**
- * TODO This function redirects keystore access to the legacy keystore during a transitional
- * phase during which not all calling code has been adjusted to use Keystore 2.0.
- * This can be reverted to a constant of "AndroidKeyStore" when b/171305684 is complete.
- * The specific bug for this component is b/171305115.
- */
static String androidKeystoreProviderName() {
- if (AndroidKeyStoreProvider.isInstalled()) {
- return "AndroidKeyStoreLegacy";
- } else {
- return "AndroidKeystore";
- }
-
+ return "AndroidKeyStore";
}
public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] applicationId) {
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index e2e5046d98bf..87e170ad12df 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -18,6 +18,7 @@ package com.android.server.media;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.app.BroadcastOptions;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -58,6 +59,16 @@ final class MediaButtonReceiverHolder {
private static final String TAG = "PendingIntentHolder";
private static final boolean DEBUG_KEY_EVENT = MediaSessionService.DEBUG_KEY_EVENT;
private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
+ // Filter apps regardless of the phone's locked/unlocked state.
+ private static final int PACKAGE_MANAGER_COMMON_FLAGS =
+ PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
+ /**
+ * Denotes the duration during which a media button receiver 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 key event.
+ */
+ private static final long FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS = 10_000;
private final int mUserId;
private final PendingIntent mPendingIntent;
@@ -105,40 +116,22 @@ final class MediaButtonReceiverHolder {
* @return Can be {@code null} if pending intent was null.
*/
public static MediaButtonReceiverHolder create(Context context, int userId,
- PendingIntent pendingIntent) {
+ PendingIntent pendingIntent, String sessionPackageName) {
if (pendingIntent == null) {
return null;
}
- ComponentName componentName = (pendingIntent != null && pendingIntent.getIntent() != null)
- ? pendingIntent.getIntent().getComponent() : null;
+ int componentType = getComponentType(pendingIntent);
+ ComponentName componentName = getComponentName(pendingIntent, componentType);
if (componentName != null) {
- // Explicit intent, where component name is in the PendingIntent.
return new MediaButtonReceiverHolder(userId, pendingIntent, componentName,
- getComponentType(context, componentName));
- }
-
- // Implicit intent, where component name isn't in the PendingIntent. Try resolve.
- PackageManager pm = context.getPackageManager();
- Intent intent = pendingIntent.getIntent();
- if ((componentName = resolveImplicitServiceIntent(pm, intent)) != null) {
- return new MediaButtonReceiverHolder(
- userId, pendingIntent, componentName, COMPONENT_TYPE_SERVICE);
- } else if ((componentName = resolveManifestDeclaredBroadcastReceiverIntent(pm, intent))
- != null) {
- return new MediaButtonReceiverHolder(
- userId, pendingIntent, componentName, COMPONENT_TYPE_BROADCAST);
- } else if ((componentName = resolveImplicitActivityIntent(pm, intent)) != null) {
- return new MediaButtonReceiverHolder(
- userId, pendingIntent, componentName, COMPONENT_TYPE_ACTIVITY);
+ componentType);
}
// Failed to resolve target component for the pending intent. It's unlikely to be usable.
- // However, the pending intent would be still used, just to follow the legacy behavior.
+ // However, the pending intent would be still used, so setting the package name to the
+ // package name of the session that set this pending intent.
Log.w(TAG, "Unresolvable implicit intent is set, pi=" + pendingIntent);
- String packageName = (pendingIntent != null && pendingIntent.getIntent() != null)
- ? pendingIntent.getIntent().getPackage() : null;
- return new MediaButtonReceiverHolder(userId, pendingIntent,
- packageName != null ? packageName : "");
+ return new MediaButtonReceiverHolder(userId, pendingIntent, sessionPackageName);
}
public static MediaButtonReceiverHolder create(int userId, ComponentName broadcastReceiver) {
@@ -201,6 +194,9 @@ final class MediaButtonReceiverHolder {
// TODO: Find a way to also send PID/UID in secure way.
mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callingPackageName);
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setTemporaryAppWhitelistDuration(
+ FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS);
if (mPendingIntent != null) {
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Sending " + keyEvent + " to the last known PendingIntent "
@@ -208,7 +204,8 @@ final class MediaButtonReceiverHolder {
}
try {
mPendingIntent.send(
- context, resultCode, mediaButtonIntent, onFinishedListener, handler);
+ context, resultCode, mediaButtonIntent, onFinishedListener, handler,
+ /* requiredPermission= */ null, options.toBundle());
} catch (PendingIntent.CanceledException e) {
Log.w(TAG, "Error sending key event to media button receiver " + mPendingIntent, e);
return false;
@@ -231,7 +228,8 @@ final class MediaButtonReceiverHolder {
break;
default:
// Legacy behavior for other cases.
- context.sendBroadcastAsUser(mediaButtonIntent, userHandle);
+ context.sendBroadcastAsUser(mediaButtonIntent, userHandle,
+ /* receiverPermission= */ null, options.toBundle());
}
} catch (Exception e) {
Log.w(TAG, "Error sending media button to the restored intent "
@@ -269,6 +267,18 @@ final class MediaButtonReceiverHolder {
String.valueOf(mComponentType));
}
+ @ComponentType
+ private static int getComponentType(PendingIntent pendingIntent) {
+ if (pendingIntent.isBroadcast()) {
+ return COMPONENT_TYPE_BROADCAST;
+ } else if (pendingIntent.isActivity()) {
+ return COMPONENT_TYPE_ACTIVITY;
+ } else if (pendingIntent.isForegroundService() || pendingIntent.isService()) {
+ return COMPONENT_TYPE_SERVICE;
+ }
+ return COMPONENT_TYPE_INVALID;
+ }
+
/**
* Gets the type of the component
*
@@ -284,9 +294,7 @@ final class MediaButtonReceiverHolder {
PackageManager pm = context.getPackageManager();
try {
ActivityInfo activityInfo = pm.getActivityInfo(componentName,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.GET_ACTIVITIES);
+ PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_ACTIVITIES);
if (activityInfo != null) {
return COMPONENT_TYPE_ACTIVITY;
}
@@ -294,9 +302,7 @@ final class MediaButtonReceiverHolder {
}
try {
ServiceInfo serviceInfo = pm.getServiceInfo(componentName,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.GET_SERVICES);
+ PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_SERVICES);
if (serviceInfo != null) {
return COMPONENT_TYPE_SERVICE;
}
@@ -306,40 +312,29 @@ final class MediaButtonReceiverHolder {
return COMPONENT_TYPE_BROADCAST;
}
- private static ComponentName resolveImplicitServiceIntent(PackageManager pm, Intent intent) {
- // Flag explanations.
- // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
- // filter apps regardless of the phone's locked/unlocked state.
- // - GET_SERVICES: Return service
- return createComponentName(pm.resolveService(intent,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.GET_SERVICES));
- }
-
- private static ComponentName resolveManifestDeclaredBroadcastReceiverIntent(
- PackageManager pm, Intent intent) {
- // Flag explanations.
- // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
- // filter apps regardless of the phone's locked/unlocked state.
- List<ResolveInfo> resolveInfos = pm.queryBroadcastReceivers(intent,
- PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
- return (resolveInfos != null && !resolveInfos.isEmpty())
- ? createComponentName(resolveInfos.get(0)) : null;
- }
-
- private static ComponentName resolveImplicitActivityIntent(PackageManager pm, Intent intent) {
- // Flag explanations.
- // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
- // Filter apps regardless of the phone's locked/unlocked state.
- // - MATCH_DEFAULT_ONLY:
- // Implicit intent receiver should be set as default. Only needed for activity.
- // - GET_ACTIVITIES: Return activity
- return createComponentName(pm.resolveActivity(intent,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DEFAULT_ONLY
- | PackageManager.GET_ACTIVITIES));
+ private static ComponentName getComponentName(PendingIntent pendingIntent, int componentType) {
+ List<ResolveInfo> resolveInfos = null;
+ switch (componentType) {
+ case COMPONENT_TYPE_ACTIVITY:
+ resolveInfos = pendingIntent.queryIntentComponents(
+ PACKAGE_MANAGER_COMMON_FLAGS
+ | PackageManager.MATCH_DEFAULT_ONLY /* Implicit intent receiver
+ should be set as default. Only needed for activity. */
+ | PackageManager.GET_ACTIVITIES);
+ break;
+ case COMPONENT_TYPE_SERVICE:
+ resolveInfos = pendingIntent.queryIntentComponents(
+ PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_SERVICES);
+ break;
+ case COMPONENT_TYPE_BROADCAST:
+ resolveInfos = pendingIntent.queryIntentComponents(
+ PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_RECEIVERS);
+ break;
+ }
+ if (resolveInfos != null && !resolveInfos.isEmpty()) {
+ return createComponentName(resolveInfos.get(0));
+ }
+ return null;
}
private static ComponentName createComponentName(ResolveInfo resolveInfo) {
diff --git a/services/core/java/com/android/server/media/MediaKeyDispatcher.java b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
index 63618eef250a..55511ad637ac 100644
--- a/services/core/java/com/android/server/media/MediaKeyDispatcher.java
+++ b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
@@ -77,7 +77,7 @@ public abstract class MediaKeyDispatcher {
mOverriddenKeyEvents.put(KeyEvent.KEYCODE_VOLUME_MUTE, 0);
}
- // TODO: Move this method into SessionPolicyProvider.java for better readability.
+ // TODO: Move this method into MediaSessionPolicyProvider.java for better readability.
/**
* Implement this to customize the logic for which MediaSession should consume which key event.
*
@@ -100,9 +100,12 @@ public abstract class MediaKeyDispatcher {
/**
* Implement this to customize the logic for which MediaButtonReceiver should consume a
* dispatched key event.
- *
- * Note: This pending intent will have lower priority over the {@link MediaSession.Token}
+ * <p>
+ * This pending intent will have lower priority over the {@link MediaSession.Token}
* returned from {@link #getMediaSession(KeyEvent, int, boolean)}.
+ * <p>
+ * Use a pending intent with an explicit intent; setting a pending intent with an implicit
+ * intent that cannot be resolved to a certain component name will fail.
*
* @return a {@link PendingIntent} instance that should receive the dispatched key event.
*/
diff --git a/services/core/java/com/android/server/media/SessionPolicyProvider.java b/services/core/java/com/android/server/media/MediaSessionPolicyProvider.java
index 332c85adec01..ceadb596dd42 100644
--- a/services/core/java/com/android/server/media/SessionPolicyProvider.java
+++ b/services/core/java/com/android/server/media/MediaSessionPolicyProvider.java
@@ -31,7 +31,7 @@ import java.lang.annotation.RetentionPolicy;
* without any parameters.
*/
// TODO: Move this class to apex/media/
-public abstract class SessionPolicyProvider {
+public abstract class MediaSessionPolicyProvider {
@IntDef(value = {
SESSION_POLICY_IGNORE_BUTTON_RECEIVER,
SESSION_POLICY_IGNORE_BUTTON_SESSION
@@ -55,7 +55,7 @@ public abstract class SessionPolicyProvider {
*/
static final int SESSION_POLICY_IGNORE_BUTTON_SESSION = 1 << 1;
- public SessionPolicyProvider(Context context) {
+ public MediaSessionPolicyProvider(Context context) {
// Constructor used for reflection
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index ae58d4c40622..0a074e1e7c50 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -55,7 +55,9 @@ import android.view.KeyEvent;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* This is the system implementation of a Session. Apps will interact with the
@@ -120,8 +122,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
private final Context mContext;
private final Object mLock = new Object();
- private final ArrayList<ISessionControllerCallbackHolder> mControllerCallbackHolders =
- new ArrayList<>();
+ private final CopyOnWriteArrayList<ISessionControllerCallbackHolder>
+ mControllerCallbackHolders = new CopyOnWriteArrayList<>();
private long mFlags;
private MediaButtonReceiverHolder mMediaButtonReceiverHolder;
@@ -545,51 +547,66 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
private void pushPlaybackStateUpdate() {
+ PlaybackState playbackState;
synchronized (mLock) {
if (mDestroyed) {
return;
}
- for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
- try {
- holder.mCallback.onPlaybackStateChanged(mPlaybackState);
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushPlaybackStateUpdate",
- holder, e);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushPlaybackStateUpdate",
- holder, e);
+ playbackState = mPlaybackState;
+ }
+ Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
+ for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+ try {
+ holder.mCallback.onPlaybackStateChanged(playbackState);
+ } catch (DeadObjectException e) {
+ if (deadCallbackHolders == null) {
+ deadCallbackHolders = new ArrayList<>();
}
+ deadCallbackHolders.add(holder);
+ logCallbackException("Removing dead callback in pushPlaybackStateUpdate", holder,
+ e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushPlaybackStateUpdate", holder, e);
}
}
+ if (deadCallbackHolders != null) {
+ mControllerCallbackHolders.removeAll(deadCallbackHolders);
+ }
}
private void pushMetadataUpdate() {
+ MediaMetadata metadata;
synchronized (mLock) {
if (mDestroyed) {
return;
}
- for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
- try {
- holder.mCallback.onMetadataChanged(mMetadata);
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushMetadataUpdate", holder, e);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushMetadataUpdate", holder, e);
+ metadata = mMetadata;
+ }
+ Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
+ for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+ try {
+ holder.mCallback.onMetadataChanged(metadata);
+ } catch (DeadObjectException e) {
+ if (deadCallbackHolders == null) {
+ deadCallbackHolders = new ArrayList<>();
}
+ deadCallbackHolders.add(holder);
+ logCallbackException("Removing dead callback in pushMetadataUpdate", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushMetadataUpdate", holder, e);
}
}
+ if (deadCallbackHolders != null) {
+ mControllerCallbackHolders.removeAll(deadCallbackHolders);
+ }
}
private void pushQueueUpdate() {
+ ParceledListSlice<QueueItem> parcelableQueue;
synchronized (mLock) {
if (mDestroyed) {
return;
}
- ParceledListSlice<QueueItem> parcelableQueue;
if (mQueue == null) {
parcelableQueue = null;
} else {
@@ -598,77 +615,105 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
// as onQueueChanged is an async binder call.
parcelableQueue.setInlineCountLimit(1);
}
- for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
- try {
- holder.mCallback.onQueueChanged(parcelableQueue);
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushQueueUpdate", holder, e);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushQueueUpdate", holder, e);
+ }
+ Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
+ for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+ try {
+ holder.mCallback.onQueueChanged(parcelableQueue);
+ } catch (DeadObjectException e) {
+ if (deadCallbackHolders == null) {
+ deadCallbackHolders = new ArrayList<>();
}
+ deadCallbackHolders.add(holder);
+ logCallbackException("Removing dead callback in pushQueueUpdate", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushQueueUpdate", holder, e);
}
}
+ if (deadCallbackHolders != null) {
+ mControllerCallbackHolders.removeAll(deadCallbackHolders);
+ }
}
private void pushQueueTitleUpdate() {
+ CharSequence queueTitle;
synchronized (mLock) {
if (mDestroyed) {
return;
}
- for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
- try {
- holder.mCallback.onQueueTitleChanged(mQueueTitle);
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushQueueTitleUpdate",
- holder, e);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushQueueTitleUpdate", holder, e);
+ queueTitle = mQueueTitle;
+ }
+ Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
+ for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+ try {
+ holder.mCallback.onQueueTitleChanged(queueTitle);
+ } catch (DeadObjectException e) {
+ if (deadCallbackHolders == null) {
+ deadCallbackHolders = new ArrayList<>();
}
+ deadCallbackHolders.add(holder);
+ logCallbackException("Removing dead callback in pushQueueTitleUpdate", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushQueueTitleUpdate", holder, e);
}
}
+ if (deadCallbackHolders != null) {
+ mControllerCallbackHolders.removeAll(deadCallbackHolders);
+ }
}
private void pushExtrasUpdate() {
+ Bundle extras;
synchronized (mLock) {
if (mDestroyed) {
return;
}
- for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
- try {
- holder.mCallback.onExtrasChanged(mExtras);
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushExtrasUpdate", holder, e);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushExtrasUpdate", holder, e);
+ extras = mExtras;
+ }
+ Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
+ for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+ try {
+ holder.mCallback.onExtrasChanged(extras);
+ } catch (DeadObjectException e) {
+ if (deadCallbackHolders == null) {
+ deadCallbackHolders = new ArrayList<>();
}
+ deadCallbackHolders.add(holder);
+ logCallbackException("Removing dead callback in pushExtrasUpdate", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushExtrasUpdate", holder, e);
}
}
+ if (deadCallbackHolders != null) {
+ mControllerCallbackHolders.removeAll(deadCallbackHolders);
+ }
}
private void pushVolumeUpdate() {
+ PlaybackInfo info;
synchronized (mLock) {
if (mDestroyed) {
return;
}
- PlaybackInfo info = getVolumeAttributes();
- for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
- try {
- holder.mCallback.onVolumeInfoChanged(info);
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushVolumeUpdate", holder, e);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushVolumeUpdate", holder, e);
+ info = getVolumeAttributes();
+ }
+ Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
+ for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+ try {
+ holder.mCallback.onVolumeInfoChanged(info);
+ } catch (DeadObjectException e) {
+ if (deadCallbackHolders == null) {
+ deadCallbackHolders = new ArrayList<>();
}
+ deadCallbackHolders.add(holder);
+ logCallbackException("Removing dead callback in pushVolumeUpdate", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushVolumeUpdate", holder, e);
}
}
+ if (deadCallbackHolders != null) {
+ mControllerCallbackHolders.removeAll(deadCallbackHolders);
+ }
}
private void pushEvent(String event, Bundle data) {
@@ -676,18 +721,24 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
if (mDestroyed) {
return;
}
- for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
- try {
- holder.mCallback.onEvent(event, data);
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushEvent", holder, e);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushEvent", holder, e);
+ }
+ Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
+ for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+ try {
+ holder.mCallback.onEvent(event, data);
+ } catch (DeadObjectException e) {
+ if (deadCallbackHolders == null) {
+ deadCallbackHolders = new ArrayList<>();
}
+ deadCallbackHolders.add(holder);
+ logCallbackException("Removing dead callback in pushEvent", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushEvent", holder, e);
}
}
+ if (deadCallbackHolders != null) {
+ mControllerCallbackHolders.removeAll(deadCallbackHolders);
+ }
}
private void pushSessionDestroyed() {
@@ -697,21 +748,18 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
if (!mDestroyed) {
return;
}
- for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
- try {
- holder.mCallback.onSessionDestroyed();
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removing dead callback in pushSessionDestroyed",
- holder, e);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushSessionDestroyed", holder, e);
- }
+ }
+ for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+ try {
+ holder.mCallback.onSessionDestroyed();
+ } catch (DeadObjectException e) {
+ logCallbackException("Removing dead callback in pushSessionDestroyed", holder, e);
+ } catch (RemoteException e) {
+ logCallbackException("unexpected exception in pushSessionDestroyed", holder, e);
}
- // After notifying clear all listeners
- mControllerCallbackHolders.clear();
}
+ // After notifying clear all listeners
+ mControllerCallbackHolders.clear();
}
private PlaybackState getStateWithUpdatedPosition() {
@@ -843,15 +891,16 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
@Override
- public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException {
+ public void setMediaButtonReceiver(PendingIntent pi, String sessionPackageName)
+ throws RemoteException {
final long token = Binder.clearCallingIdentity();
try {
- if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
+ if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
!= 0) {
return;
}
mMediaButtonReceiverHolder =
- MediaButtonReceiverHolder.create(mContext, mUserId, pi);
+ MediaButtonReceiverHolder.create(mContext, mUserId, pi, sessionPackageName);
mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
} finally {
Binder.restoreCallingIdentity(token);
@@ -862,7 +911,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
public void setMediaButtonBroadcastReceiver(ComponentName receiver) throws RemoteException {
final long token = Binder.clearCallingIdentity();
try {
- if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
+ if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
!= 0) {
return;
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 032523ef9694..3c50597b8cfc 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -20,7 +20,7 @@ import android.media.AudioManager;
import android.os.ResultReceiver;
import android.view.KeyEvent;
-import com.android.server.media.SessionPolicyProvider.SessionPolicy;
+import com.android.server.media.MediaSessionPolicyProvider.SessionPolicy;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index c0381e4b1715..18f2d8450246 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -148,7 +148,7 @@ public class MediaSessionService extends SystemService implements Monitor {
final RemoteCallbackList<IRemoteSessionCallback> mRemoteVolumeControllers =
new RemoteCallbackList<>();
- private SessionPolicyProvider mCustomSessionPolicyProvider;
+ private MediaSessionPolicyProvider mCustomMediaSessionPolicyProvider;
private MediaKeyDispatcher mCustomMediaKeyDispatcher;
public MediaSessionService(Context context) {
@@ -191,8 +191,10 @@ public class MediaSessionService extends SystemService implements Monitor {
updateUser();
- instantiateCustomProvider(null);
- instantiateCustomDispatcher(null);
+ instantiateCustomProvider(mContext.getResources().getString(
+ R.string.config_customMediaSessionPolicyProvider));
+ instantiateCustomDispatcher(mContext.getResources().getString(
+ R.string.config_customMediaKeyDispatcher));
mRecordThread.start();
final IntentFilter filter = new IntentFilter(
@@ -589,8 +591,8 @@ public class MediaSessionService extends SystemService implements Monitor {
String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) {
synchronized (mLock) {
int policies = 0;
- if (mCustomSessionPolicyProvider != null) {
- policies = mCustomSessionPolicyProvider.getSessionPoliciesForApplication(
+ if (mCustomMediaSessionPolicyProvider != null) {
+ policies = mCustomMediaSessionPolicyProvider.getSessionPoliciesForApplication(
callerUid, callerPackageName);
}
@@ -778,16 +780,13 @@ public class MediaSessionService extends SystemService implements Monitor {
return null;
}
- private void instantiateCustomDispatcher(String nameFromTesting) {
+ private void instantiateCustomDispatcher(String componentName) {
synchronized (mLock) {
mCustomMediaKeyDispatcher = null;
- String customDispatcherClassName = (nameFromTesting == null)
- ? mContext.getResources().getString(R.string.config_customMediaKeyDispatcher)
- : nameFromTesting;
try {
- if (!TextUtils.isEmpty(customDispatcherClassName)) {
- Class customDispatcherClass = Class.forName(customDispatcherClassName);
+ if (componentName != null && !TextUtils.isEmpty(componentName)) {
+ Class customDispatcherClass = Class.forName(componentName);
Constructor constructor =
customDispatcherClass.getDeclaredConstructor(Context.class);
mCustomMediaKeyDispatcher =
@@ -801,20 +800,17 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
- private void instantiateCustomProvider(String nameFromTesting) {
+ private void instantiateCustomProvider(String componentName) {
synchronized (mLock) {
- mCustomSessionPolicyProvider = null;
+ mCustomMediaSessionPolicyProvider = null;
- String customProviderClassName = (nameFromTesting == null)
- ? mContext.getResources().getString(R.string.config_customSessionPolicyProvider)
- : nameFromTesting;
try {
- if (!TextUtils.isEmpty(customProviderClassName)) {
- Class customProviderClass = Class.forName(customProviderClassName);
+ if (componentName != null && !TextUtils.isEmpty(componentName)) {
+ Class customProviderClass = Class.forName(componentName);
Constructor constructor =
customProviderClass.getDeclaredConstructor(Context.class);
- mCustomSessionPolicyProvider =
- (SessionPolicyProvider) constructor.newInstance(mContext);
+ mCustomMediaSessionPolicyProvider =
+ (MediaSessionPolicyProvider) constructor.newInstance(mContext);
}
} catch (ClassNotFoundException | InstantiationException | InvocationTargetException
| IllegalAccessException | NoSuchMethodException e) {
@@ -1933,16 +1929,30 @@ public class MediaSessionService extends SystemService implements Monitor {
}
@Override
- public void setCustomMediaKeyDispatcherForTesting(String name) {
+ public void setCustomMediaKeyDispatcher(String name) {
instantiateCustomDispatcher(name);
}
@Override
- public void setCustomSessionPolicyProviderForTesting(String name) {
+ public void setCustomMediaSessionPolicyProvider(String name) {
instantiateCustomProvider(name);
}
@Override
+ public boolean hasCustomMediaKeyDispatcher(String componentName) {
+ return mCustomMediaKeyDispatcher == null ? false
+ : TextUtils.equals(componentName,
+ mCustomMediaKeyDispatcher.getClass().getName());
+ }
+
+ @Override
+ public boolean hasCustomMediaSessionPolicyProvider(String componentName) {
+ return mCustomMediaSessionPolicyProvider == null ? false
+ : TextUtils.equals(componentName,
+ mCustomMediaSessionPolicyProvider.getClass().getName());
+ }
+
+ @Override
public int getSessionPolicies(MediaSession.Token token) {
synchronized (mLock) {
MediaSessionRecord record = getMediaSessionRecordLocked(token);
@@ -2124,7 +2134,8 @@ public class MediaSessionService extends SystemService implements Monitor {
uid, asSystemService);
if (pi != null) {
mediaButtonReceiverHolder = MediaButtonReceiverHolder.create(mContext,
- mCurrentFullUserRecord.mFullUserId, pi);
+ mCurrentFullUserRecord.mFullUserId, pi,
+ /* sessionPackageName= */ "");
}
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index f8ff5b5f8e66..50eed19389dc 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -16,7 +16,7 @@
package com.android.server.media;
-import static com.android.server.media.SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_SESSION;
+import static com.android.server.media.MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_SESSION;
import android.media.Session2Token;
import android.media.session.MediaSession;
diff --git a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
index d48b9a4e7879..639dda6f8981 100644
--- a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
@@ -83,7 +83,14 @@ public final class MediaMetricsManagerService extends SystemService {
@Override
public void reportPlaybackStateEvent(
String sessionId, PlaybackStateEvent event, int userId) {
- // TODO: log it to statsd
+ StatsEvent statsEvent = StatsEvent.newBuilder()
+ .setAtomId(322)
+ .writeString(sessionId)
+ .writeInt(event.getState())
+ .writeLong(event.getTimeSinceCreatedMillis())
+ .usePooledBuffer()
+ .build();
+ StatsLog.write(statsEvent);
}
@Override
@@ -103,7 +110,7 @@ public final class MediaMetricsManagerService extends SystemService {
.writeString(event.getExceptionStack())
.writeInt(event.getErrorCode())
.writeInt(event.getSubErrorCode())
- .writeLong(event.getTimeSincePlaybackCreatedMillis())
+ .writeLong(event.getTimeSinceCreatedMillis())
.usePooledBuffer()
.build();
StatsLog.write(statsEvent);
@@ -114,8 +121,8 @@ public final class MediaMetricsManagerService extends SystemService {
StatsEvent statsEvent = StatsEvent.newBuilder()
.setAtomId(321)
.writeString(sessionId)
- .writeInt(event.getType())
- .writeLong(event.getTimeSincePlaybackCreatedMillis())
+ .writeInt(event.getNetworkType())
+ .writeLong(event.getTimeSinceCreatedMillis())
.usePooledBuffer()
.build();
StatsLog.write(statsEvent);
@@ -125,7 +132,7 @@ public final class MediaMetricsManagerService extends SystemService {
public void reportTrackChangeEvent(
String sessionId, TrackChangeEvent event, int userId) {
StatsEvent statsEvent = StatsEvent.newBuilder()
- .setAtomId(321)
+ .setAtomId(324)
.writeString(sessionId)
.writeInt(event.getTrackState())
.writeInt(event.getTrackChangeReason())
@@ -133,7 +140,7 @@ public final class MediaMetricsManagerService extends SystemService {
.writeString(event.getSampleMimeType())
.writeString(event.getCodecName())
.writeInt(event.getBitrate())
- .writeLong(event.getTimeSincePlaybackCreatedMillis())
+ .writeLong(event.getTimeSinceCreatedMillis())
.writeInt(event.getTrackType())
.writeString(event.getLanguage())
.writeString(event.getLanguageRegion())
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 6d1c68039ee5..3cc32bef0e67 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -16,10 +16,10 @@
package com.android.server.net;
-import static android.net.ConnectivityManager.TYPE_NONE;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
import static android.provider.Settings.ACTION_VPN_SETTINGS;
-import static com.android.server.connectivity.NetworkNotificationManager.NOTIFICATION_CHANNEL_VPN;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,43 +28,37 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.Network;
import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkInfo.State;
+import android.net.NetworkRequest;
import android.os.Handler;
import android.security.KeyStore;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
-import com.android.server.ConnectivityService;
-import com.android.server.EventLogTags;
import com.android.server.connectivity.Vpn;
import java.util.List;
import java.util.Objects;
/**
- * State tracker for lockdown mode. Watches for normal {@link NetworkInfo} to be
- * connected and kicks off VPN connection, managing any required {@code netd}
- * firewall rules.
+ * State tracker for legacy lockdown VPN. Watches for physical networks to be
+ * connected and kicks off VPN connection.
*/
public class LockdownVpnTracker {
private static final String TAG = "LockdownVpnTracker";
- /** Number of VPN attempts before waiting for user intervention. */
- private static final int MAX_ERROR_COUNT = 4;
-
public static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET";
@NonNull private final Context mContext;
- @NonNull private final ConnectivityService mConnService;
+ @NonNull private final ConnectivityManager mCm;
@NonNull private final NotificationManager mNotificationManager;
@NonNull private final Handler mHandler;
@NonNull private final Vpn mVpn;
@@ -76,19 +70,73 @@ public class LockdownVpnTracker {
@NonNull private final PendingIntent mConfigIntent;
@NonNull private final PendingIntent mResetIntent;
+ @NonNull private final NetworkCallback mDefaultNetworkCallback = new NetworkCallback();
+ @NonNull private final VpnNetworkCallback mVpnNetworkCallback = new VpnNetworkCallback();
+
+ private class NetworkCallback extends ConnectivityManager.NetworkCallback {
+ private Network mNetwork = null;
+ private LinkProperties mLinkProperties = null;
+
+ @Override
+ public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
+ boolean networkChanged = false;
+ if (!network.equals(mNetwork)) {
+ // The default network just changed.
+ mNetwork = network;
+ networkChanged = true;
+ }
+ mLinkProperties = lp;
+ // Backwards compatibility: previously, LockdownVpnTracker only responded to connects
+ // and disconnects, not LinkProperties changes on existing networks.
+ if (networkChanged) {
+ synchronized (mStateLock) {
+ handleStateChangedLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onLost(Network network) {
+ // The default network has gone down.
+ mNetwork = null;
+ mLinkProperties = null;
+ synchronized (mStateLock) {
+ handleStateChangedLocked();
+ }
+ }
+
+ public Network getNetwork() {
+ return mNetwork;
+ }
+
+ public LinkProperties getLinkProperties() {
+ return mLinkProperties;
+ }
+ }
+
+ private class VpnNetworkCallback extends NetworkCallback {
+ @Override
+ public void onAvailable(Network network) {
+ synchronized (mStateLock) {
+ handleStateChangedLocked();
+ }
+ }
+ @Override
+ public void onLost(Network network) {
+ onAvailable(network);
+ }
+ }
+
@Nullable
private String mAcceptedEgressIface;
- private int mErrorCount;
-
public LockdownVpnTracker(@NonNull Context context,
- @NonNull ConnectivityService connService,
@NonNull Handler handler,
@NonNull KeyStore keyStore,
@NonNull Vpn vpn,
@NonNull VpnProfile profile) {
mContext = Objects.requireNonNull(context);
- mConnService = Objects.requireNonNull(connService);
+ mCm = mContext.getSystemService(ConnectivityManager.class);
mHandler = Objects.requireNonNull(handler);
mVpn = Objects.requireNonNull(vpn);
mProfile = Objects.requireNonNull(profile);
@@ -110,24 +158,20 @@ public class LockdownVpnTracker {
* connection when ready, or setting firewall rules once VPN is connected.
*/
private void handleStateChangedLocked() {
-
- final NetworkInfo egressInfo = mConnService.getActiveNetworkInfoUnfiltered();
- final LinkProperties egressProp = mConnService.getActiveLinkProperties();
+ final Network network = mDefaultNetworkCallback.getNetwork();
+ final LinkProperties egressProp = mDefaultNetworkCallback.getLinkProperties();
final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
final VpnConfig vpnConfig = mVpn.getLegacyVpnConfig();
// Restart VPN when egress network disconnected or changed
- final boolean egressDisconnected = egressInfo == null
- || State.DISCONNECTED.equals(egressInfo.getState());
+ final boolean egressDisconnected = (network == null);
final boolean egressChanged = egressProp == null
|| !TextUtils.equals(mAcceptedEgressIface, egressProp.getInterfaceName());
- final int egressType = (egressInfo == null) ? TYPE_NONE : egressInfo.getType();
final String egressIface = (egressProp == null) ?
null : egressProp.getInterfaceName();
- Log.d(TAG, "handleStateChanged: egress=" + egressType
- + " " + mAcceptedEgressIface + "->" + egressIface);
+ Log.d(TAG, "handleStateChanged: egress=" + mAcceptedEgressIface + "->" + egressIface);
if (egressDisconnected || egressChanged) {
mAcceptedEgressIface = null;
@@ -138,46 +182,49 @@ public class LockdownVpnTracker {
return;
}
- if (vpnInfo.getDetailedState() == DetailedState.FAILED) {
- EventLogTags.writeLockdownVpnError(egressType);
- }
-
- if (mErrorCount > MAX_ERROR_COUNT) {
- showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
-
- } else if (egressInfo.isConnected() && !vpnInfo.isConnectedOrConnecting()) {
- if (mProfile.isValidLockdownProfile()) {
- Log.d(TAG, "Active network connected; starting VPN");
- EventLogTags.writeLockdownVpnConnecting(egressType);
- showNotification(R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected);
-
- mAcceptedEgressIface = egressProp.getInterfaceName();
- try {
- // Use the privileged method because Lockdown VPN is initiated by the system, so
- // no additional permission checks are necessary.
- mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, null, egressProp);
- } catch (IllegalStateException e) {
- mAcceptedEgressIface = null;
- Log.e(TAG, "Failed to start VPN", e);
- showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
- }
- } else {
+ // At this point, |network| is known to be non-null.
+ if (!vpnInfo.isConnectedOrConnecting()) {
+ if (!mProfile.isValidLockdownProfile()) {
Log.e(TAG, "Invalid VPN profile; requires IP-based server and DNS");
showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
+ return;
}
+ Log.d(TAG, "Active network connected; starting VPN");
+ showNotification(R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected);
+
+ mAcceptedEgressIface = egressIface;
+ try {
+ // Use the privileged method because Lockdown VPN is initiated by the system, so
+ // no additional permission checks are necessary.
+ //
+ // Pass in the underlying network here because the legacy VPN is, in fact, tightly
+ // coupled to a given underlying network and cannot provide mobility. This makes
+ // things marginally more correct in two ways:
+ //
+ // 1. When the legacy lockdown VPN connects, LegacyTypeTracker broadcasts an extra
+ // CONNECTED broadcast for the underlying network type. The underlying type comes
+ // from here. LTT *could* assume that the underlying network is the default
+ // network, but that might introduce a race condition if, say, the VPN starts
+ // connecting on cell, but when the connection succeeds and the agent is
+ // registered, the default network is now wifi.
+ // 2. If no underlying network is passed in, then CS will assume the underlying
+ // network is the system default. So, if the VPN is up and underlying network
+ // (e.g., wifi) disconnects, CS will inform apps that the VPN's capabilities have
+ // changed to match the new default network (e.g., cell).
+ mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, network, egressProp);
+ } catch (IllegalStateException e) {
+ mAcceptedEgressIface = null;
+ Log.e(TAG, "Failed to start VPN", e);
+ showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
+ }
} else if (vpnInfo.isConnected() && vpnConfig != null) {
final String iface = vpnConfig.interfaze;
final List<LinkAddress> sourceAddrs = vpnConfig.addresses;
Log.d(TAG, "VPN connected using iface=" + iface
+ ", sourceAddr=" + sourceAddrs.toString());
- EventLogTags.writeLockdownVpnConnected(egressType);
showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected);
-
- final NetworkInfo clone = new NetworkInfo(egressInfo);
- augmentNetworkInfo(clone);
- mConnService.sendConnectedBroadcast(clone);
}
}
@@ -192,7 +239,15 @@ public class LockdownVpnTracker {
mVpn.setEnableTeardown(false);
mVpn.setLockdown(true);
+ mCm.setLegacyLockdownVpnEnabled(true);
handleStateChangedLocked();
+
+ mCm.registerSystemDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
+ final NetworkRequest vpnRequest = new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(TRANSPORT_VPN)
+ .build();
+ mCm.registerNetworkCallback(vpnRequest, mVpnNetworkCallback, mHandler);
}
public void shutdown() {
@@ -205,20 +260,21 @@ public class LockdownVpnTracker {
Log.d(TAG, "shutdownLocked()");
mAcceptedEgressIface = null;
- mErrorCount = 0;
mVpn.stopVpnRunnerPrivileged();
mVpn.setLockdown(false);
+ mCm.setLegacyLockdownVpnEnabled(false);
hideNotification();
mVpn.setEnableTeardown(true);
+ mCm.unregisterNetworkCallback(mDefaultNetworkCallback);
+ mCm.unregisterNetworkCallback(mVpnNetworkCallback);
}
/**
* Reset VPN lockdown tracker. Called by ConnectivityService when receiving
* {@link #ACTION_LOCKDOWN_RESET} pending intent.
*/
- @GuardedBy("mConnService.mVpns")
public void reset() {
Log.d(TAG, "reset()");
synchronized (mStateLock) {
@@ -229,28 +285,6 @@ public class LockdownVpnTracker {
}
}
- public void onNetworkInfoChanged() {
- synchronized (mStateLock) {
- handleStateChangedLocked();
- }
- }
-
- public void onVpnStateChanged(NetworkInfo info) {
- if (info.getDetailedState() == DetailedState.FAILED) {
- mErrorCount++;
- }
- synchronized (mStateLock) {
- handleStateChangedLocked();
- }
- }
-
- public void augmentNetworkInfo(NetworkInfo info) {
- if (info.isConnected()) {
- final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
- info.setDetailedState(vpnInfo.getDetailedState(), vpnInfo.getReason(), null);
- }
- }
-
private void showNotification(int titleRes, int iconRes) {
final Notification.Builder builder =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_VPN)
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index b99a55271943..aa7da54b2e1d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1961,14 +1961,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (state.network != null) {
mNetIdToSubId.put(state.network.netId, parseSubId(state));
}
- if (state.networkInfo != null && state.networkInfo.isConnected()) {
- // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
- // in the object created here is never used and its value doesn't matter, so use
- // NETWORK_TYPE_UNKNOWN.
- final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
- true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
- identified.put(state, ident);
- }
+
+ // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
+ // in the object created here is never used and its value doesn't matter, so use
+ // NETWORK_TYPE_UNKNOWN.
+ final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+ true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
+ identified.put(state, ident);
}
final ArraySet<String> newMeteredIfaces = new ArraySet<>();
@@ -2043,8 +2042,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// One final pass to catch any metered ifaces that don't have explicitly
// defined policies; typically Wi-Fi networks.
for (NetworkState state : states) {
- if (state.networkInfo != null && state.networkInfo.isConnected()
- && !state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+ if (!state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
matchingIfaces.clear();
collectIfaces(matchingIfaces, state);
for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index ec53f4f16d01..16fa2c8094ce 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -96,7 +96,6 @@ import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
-import android.net.NetworkInfo;
import android.net.NetworkStack;
import android.net.NetworkState;
import android.net.NetworkStats;
@@ -1268,7 +1267,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
/**
* Inspect all current {@link NetworkState} to derive mapping from {@code iface} to {@link
- * NetworkStatsHistory}. When multiple {@link NetworkInfo} are active on a single {@code iface},
+ * NetworkStatsHistory}. When multiple networks are active on a single {@code iface},
* they are combined under a single {@link NetworkIdentitySet}.
*/
@GuardedBy("mStatsLock")
@@ -1298,84 +1297,82 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final boolean combineSubtypeEnabled = mSettings.getCombineSubtypeEnabled();
final ArraySet<String> mobileIfaces = new ArraySet<>();
for (NetworkState state : states) {
- if (state.networkInfo.isConnected()) {
- final boolean isMobile = isNetworkTypeMobile(state.networkInfo.getType());
- final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network);
- final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
- : getSubTypeForState(state);
- final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
- isDefault, subType);
-
- // Traffic occurring on the base interface is always counted for
- // both total usage and UID details.
- final String baseIface = state.linkProperties.getInterfaceName();
- if (baseIface != null) {
- findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
- findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
-
- // Build a separate virtual interface for VT (Video Telephony) data usage.
- // Only do this when IMS is not metered, but VT is metered.
- // If IMS is metered, then the IMS network usage has already included VT usage.
- // VT is considered always metered in framework's layer. If VT is not metered
- // per carrier's policy, modem will report 0 usage for VT calls.
- if (state.networkCapabilities.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) {
-
- // Copy the identify from IMS one but mark it as metered.
- NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
- ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
- ident.getRoaming(), true /* metered */,
- true /* onDefaultNetwork */);
- findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent);
- findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent);
- }
+ final boolean isMobile = isNetworkTypeMobile(state.legacyNetworkType);
+ final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network);
+ final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
+ : getSubTypeForState(state);
+ final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+ isDefault, subType);
+
+ // Traffic occurring on the base interface is always counted for
+ // both total usage and UID details.
+ final String baseIface = state.linkProperties.getInterfaceName();
+ if (baseIface != null) {
+ findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
+ findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
+
+ // Build a separate virtual interface for VT (Video Telephony) data usage.
+ // Only do this when IMS is not metered, but VT is metered.
+ // If IMS is metered, then the IMS network usage has already included VT usage.
+ // VT is considered always metered in framework's layer. If VT is not metered
+ // per carrier's policy, modem will report 0 usage for VT calls.
+ if (state.networkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) {
+
+ // Copy the identify from IMS one but mark it as metered.
+ NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
+ ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
+ ident.getRoaming(), true /* metered */,
+ true /* onDefaultNetwork */);
+ findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent);
+ findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent);
+ }
- if (isMobile) {
- mobileIfaces.add(baseIface);
- }
+ if (isMobile) {
+ mobileIfaces.add(baseIface);
}
+ }
- // Traffic occurring on stacked interfaces is usually clatd.
- //
- // UID stats are always counted on the stacked interface and never on the base
- // interface, because the packets on the base interface do not actually match
- // application sockets (they're not IPv4) and thus the app uid is not known.
- // For receive this is obvious: packets must be translated from IPv6 to IPv4
- // before the application socket can be found.
- // For transmit: either they go through the clat daemon which by virtue of going
- // through userspace strips the original socket association during the IPv4 to
- // IPv6 translation process, or they are offloaded by eBPF, which doesn't:
- // However, on an ebpf device the accounting is done in cgroup ebpf hooks,
- // which don't trigger again post ebpf translation.
- // (as such stats accounted to the clat uid are ignored)
- //
- // Interface stats are more complicated.
- //
- // eBPF offloaded 464xlat'ed packets never hit base interface ip6tables, and thus
- // *all* statistics are collected by iptables on the stacked v4-* interface.
- //
- // Additionally for ingress all packets bound for the clat IPv6 address are dropped
- // in ip6tables raw prerouting and thus even non-offloaded packets are only
- // accounted for on the stacked interface.
- //
- // For egress, packets subject to eBPF offload never appear on the base interface
- // and only appear on the stacked interface. Thus to ensure packets increment
- // interface stats, we must collate data from stacked interfaces. For xt_qtaguid
- // (or non eBPF offloaded) TX they would appear on both, however egress interface
- // accounting is explicitly bypassed for traffic from the clat uid.
- //
- final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
- for (LinkProperties stackedLink : stackedLinks) {
- final String stackedIface = stackedLink.getInterfaceName();
- if (stackedIface != null) {
- findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
- findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
- if (isMobile) {
- mobileIfaces.add(stackedIface);
- }
-
- mStatsFactory.noteStackedIface(stackedIface, baseIface);
+ // Traffic occurring on stacked interfaces is usually clatd.
+ //
+ // UID stats are always counted on the stacked interface and never on the base
+ // interface, because the packets on the base interface do not actually match
+ // application sockets (they're not IPv4) and thus the app uid is not known.
+ // For receive this is obvious: packets must be translated from IPv6 to IPv4
+ // before the application socket can be found.
+ // For transmit: either they go through the clat daemon which by virtue of going
+ // through userspace strips the original socket association during the IPv4 to
+ // IPv6 translation process, or they are offloaded by eBPF, which doesn't:
+ // However, on an ebpf device the accounting is done in cgroup ebpf hooks,
+ // which don't trigger again post ebpf translation.
+ // (as such stats accounted to the clat uid are ignored)
+ //
+ // Interface stats are more complicated.
+ //
+ // eBPF offloaded 464xlat'ed packets never hit base interface ip6tables, and thus
+ // *all* statistics are collected by iptables on the stacked v4-* interface.
+ //
+ // Additionally for ingress all packets bound for the clat IPv6 address are dropped
+ // in ip6tables raw prerouting and thus even non-offloaded packets are only
+ // accounted for on the stacked interface.
+ //
+ // For egress, packets subject to eBPF offload never appear on the base interface
+ // and only appear on the stacked interface. Thus to ensure packets increment
+ // interface stats, we must collate data from stacked interfaces. For xt_qtaguid
+ // (or non eBPF offloaded) TX they would appear on both, however egress interface
+ // accounting is explicitly bypassed for traffic from the clat uid.
+ //
+ final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
+ for (LinkProperties stackedLink : stackedLinks) {
+ final String stackedIface = stackedLink.getInterfaceName();
+ if (stackedIface != null) {
+ findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
+ findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
+ if (isMobile) {
+ mobileIfaces.add(stackedIface);
}
+
+ mStatsFactory.noteStackedIface(stackedIface, baseIface);
}
}
}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 769b781b1ae9..78c1a95a891d 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -21,6 +21,7 @@ import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.IPackageManager;
+import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.IBinder;
import android.os.IInterface;
@@ -197,6 +198,11 @@ public class ConditionProviders extends ManagedServices {
}
@Override
+ protected void ensureFilters(ServiceInfo si, int userId) {
+ // nothing to filter
+ }
+
+ @Override
protected void loadDefaultsFromConfig() {
String defaultDndAccess = mContext.getResources().getString(
R.string.config_defaultDndAccessPackages);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 21537e6aaa22..bbdcac28fb02 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -189,6 +189,8 @@ abstract public class ManagedServices {
abstract protected void onServiceAdded(ManagedServiceInfo info);
+ abstract protected void ensureFilters(ServiceInfo si, int userId);
+
protected List<ManagedServiceInfo> getServices() {
synchronized (mMutex) {
List<ManagedServiceInfo> services = new ArrayList<>(mServices);
@@ -1342,8 +1344,10 @@ abstract public class ManagedServices {
for (ComponentName component : add) {
try {
ServiceInfo info = mPm.getServiceInfo(component,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ PackageManager.GET_META_DATA
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ userId);
if (info == null) {
Slog.w(TAG, "Not binding " + getCaption() + " service " + component
+ ": service not found");
@@ -1356,7 +1360,7 @@ abstract public class ManagedServices {
}
Slog.v(TAG,
"enabling " + getCaption() + " for " + userId + ": " + component);
- registerService(component, userId);
+ registerService(info, userId);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -1368,9 +1372,15 @@ abstract public class ManagedServices {
* Version of registerService that takes the name of a service component to bind to.
*/
@VisibleForTesting
- void registerService(final ComponentName name, final int userid) {
+ void registerService(final ServiceInfo si, final int userId) {
+ ensureFilters(si, userId);
+ registerService(si.getComponentName(), userId);
+ }
+
+ @VisibleForTesting
+ void registerService(final ComponentName cn, final int userId) {
synchronized (mMutex) {
- registerServiceLocked(name, userid);
+ registerServiceLocked(cn, userId);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 1051423ea17f..160d2daab6a2 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -18,6 +18,7 @@ package com.android.server.notification;
import android.app.Notification;
import android.net.Uri;
+import android.os.Bundle;
import android.os.UserHandle;
import android.service.notification.NotificationStats;
@@ -88,5 +89,13 @@ public interface NotificationDelegate {
void onNotificationSmartReplySent(String key, int clickedIndex, CharSequence reply,
int notificationLocation, boolean modifiedBeforeSending);
+ /**
+ * Notifies a user feedback is provided.
+ *
+ * @param key the notification key
+ * @param feedback the feedback detail
+ */
+ void onNotificationFeedbackReceived(String key, Bundle feedback);
+
void prepareForPossibleShutdown();
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 571b6934e425..8b4c6392fec0 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -110,6 +110,8 @@ import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
+import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_ANIM_BUFFER;
+import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
import static com.android.server.utils.PriorityDump.PRIORITY_ARG;
import static com.android.server.utils.PriorityDump.PRIORITY_ARG_CRITICAL;
import static com.android.server.utils.PriorityDump.PRIORITY_ARG_NORMAL;
@@ -170,11 +172,11 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
-import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
+import android.content.pm.VersionedPackage;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.media.AudioAttributes;
@@ -284,7 +286,6 @@ import com.android.server.notification.toast.CustomToastRecord;
import com.android.server.notification.toast.TextToastRecord;
import com.android.server.notification.toast.ToastRecord;
import com.android.server.pm.PackageManagerService;
-import com.android.server.policy.PhoneWindowManager;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.utils.quota.MultiRateLimiter;
@@ -358,7 +359,7 @@ public class NotificationManagerService extends SystemService {
private static final int MESSAGE_RECONSIDER_RANKING = 1000;
private static final int MESSAGE_RANKING_SORT = 1001;
- static final int LONG_DELAY = PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
+ static final int LONG_DELAY = TOAST_WINDOW_TIMEOUT - TOAST_WINDOW_ANIM_BUFFER; // 3.5 seconds
static final int SHORT_DELAY = 2000; // 2 seconds
// 1 second past the ANR timeout.
@@ -378,7 +379,10 @@ public class NotificationManagerService extends SystemService {
static final String[] DEFAULT_ALLOWED_ADJUSTMENTS = new String[] {
Adjustment.KEY_CONTEXTUAL_ACTIONS,
Adjustment.KEY_TEXT_REPLIES,
- Adjustment.KEY_NOT_CONVERSATION};
+ Adjustment.KEY_NOT_CONVERSATION,
+ Adjustment.KEY_IMPORTANCE,
+ Adjustment.KEY_RANKING_SCORE
+ };
static final String[] NON_BLOCKABLE_DEFAULT_ROLES = new String[] {
RoleManager.ROLE_DIALER,
@@ -427,8 +431,6 @@ public class NotificationManagerService extends SystemService {
private static final String SCHEME_TIMEOUT = "timeout";
private static final String EXTRA_KEY = "key";
- private static final String FEEDBACK_KEY = "feedback_key";
-
private static final int NOTIFICATION_INSTANCE_ID_MAX = (1 << 13);
/**
@@ -1015,30 +1017,26 @@ public class NotificationManagerService extends SystemService {
return;
}
final long now = System.currentTimeMillis();
- //TODO(b/154257994): remove this when feedback apis are in place
- boolean isFeedback = action.getExtras().containsKey(FEEDBACK_KEY);
- if (!isFeedback) {
- MetricsLogger.action(r.getLogMaker(now)
- .setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION)
- .setType(MetricsEvent.TYPE_ACTION)
- .setSubtype(actionIndex)
- .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
- .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count)
- .addTaggedData(MetricsEvent.NOTIFICATION_ACTION_IS_SMART,
- action.isContextual() ? 1 : 0)
- .addTaggedData(
- MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
- generatedByAssistant ? 1 : 0)
- .addTaggedData(MetricsEvent.NOTIFICATION_LOCATION,
- nv.location.toMetricsEventEnum()));
- mNotificationRecordLogger.log(
- NotificationRecordLogger.NotificationEvent.fromAction(actionIndex,
- generatedByAssistant, action.isContextual()), r);
- EventLogTags.writeNotificationActionClicked(key, actionIndex,
- r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
- nv.rank, nv.count);
- nv.recycle();
- }
+ MetricsLogger.action(r.getLogMaker(now)
+ .setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .setSubtype(actionIndex)
+ .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
+ .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count)
+ .addTaggedData(MetricsEvent.NOTIFICATION_ACTION_IS_SMART,
+ action.isContextual() ? 1 : 0)
+ .addTaggedData(
+ MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
+ generatedByAssistant ? 1 : 0)
+ .addTaggedData(MetricsEvent.NOTIFICATION_LOCATION,
+ nv.location.toMetricsEventEnum()));
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.fromAction(actionIndex,
+ generatedByAssistant, action.isContextual()), r);
+ EventLogTags.writeNotificationActionClicked(key, actionIndex,
+ r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
+ nv.rank, nv.count);
+ nv.recycle();
reportUserInteraction(r);
mAssistants.notifyAssistantActionClicked(r, action, generatedByAssistant);
}
@@ -1381,6 +1379,20 @@ public class NotificationManagerService extends SystemService {
}
}
}
+
+ @Override
+ public void onNotificationFeedbackReceived(String key, Bundle feedback) {
+ exitIdle();
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r == null) {
+ if (DBG) Slog.w(TAG, "No notification with key: " + key);
+ return;
+ }
+ mAssistants.notifyAssistantFeedbackReceived(r, feedback);
+ }
+ }
+
};
@VisibleForTesting
@@ -3054,7 +3066,7 @@ public class NotificationManagerService extends SystemService {
// If the callback fails, this will remove it from the list, so don't
// assume that it's valid after this.
if (index == 0) {
- showNextToastLocked();
+ showNextToastLocked(false);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -7390,7 +7402,7 @@ public class NotificationManagerService extends SystemService {
}
@GuardedBy("mToastQueue")
- void showNextToastLocked() {
+ void showNextToastLocked(boolean lastToastWasTextRecord) {
if (mIsCurrentToastShown) {
return; // Don't show the same toast twice.
}
@@ -7404,7 +7416,7 @@ public class NotificationManagerService extends SystemService {
mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG);
if (tryShowToast(record, rateLimitingEnabled, isWithinQuota)) {
- scheduleDurationReachedLocked(record);
+ scheduleDurationReachedLocked(record, lastToastWasTextRecord);
mIsCurrentToastShown = true;
if (rateLimitingEnabled) {
mToastRateLimiter.noteEvent(userId, record.pkg, TOAST_QUOTA_TAG);
@@ -7475,7 +7487,7 @@ public class NotificationManagerService extends SystemService {
// Show the next one. If the callback fails, this will remove
// it from the list, so don't assume that the list hasn't changed
// after this point.
- showNextToastLocked();
+ showNextToastLocked(lastToast instanceof TextToastRecord);
}
}
@@ -7489,7 +7501,7 @@ public class NotificationManagerService extends SystemService {
}
@GuardedBy("mToastQueue")
- private void scheduleDurationReachedLocked(ToastRecord r)
+ private void scheduleDurationReachedLocked(ToastRecord r, boolean lastToastWasTextRecord)
{
mHandler.removeCallbacksAndMessages(r);
Message m = Message.obtain(mHandler, MESSAGE_DURATION_REACHED, r);
@@ -7499,6 +7511,14 @@ public class NotificationManagerService extends SystemService {
// preference.
delay = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
AccessibilityManager.FLAG_CONTENT_TEXT);
+
+ if (lastToastWasTextRecord) {
+ delay += 250; // delay to account for previous toast's "out" animation
+ }
+ if (r instanceof TextToastRecord) {
+ delay += 333; // delay to account for this toast's "in" animation
+ }
+
mHandler.sendMessageDelayed(m, delay);
}
@@ -8958,7 +8978,8 @@ public class NotificationManagerService extends SystemService {
NotificationListenerFilter nls = mListeners.getNotificationListenerFilter(listener.mKey);
if (nls != null
&& (!nls.isTypeAllowed(notificationType)
- || !nls.isPackageAllowed(sbn.getPackageName()))) {
+ || !nls.isPackageAllowed(
+ new VersionedPackage(sbn.getPackageName(), sbn.getUid())))) {
return false;
}
return true;
@@ -9048,7 +9069,8 @@ public class NotificationManagerService extends SystemService {
public class NotificationAssistants extends ManagedServices {
static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
- private static final String TAG_ALLOWED_ADJUSTMENT_TYPES = "q_allowed_adjustments";
+ private static final String TAG_ALLOWED_ADJUSTMENT_TYPES_OLD = "q_allowed_adjustments";
+ private static final String TAG_ALLOWED_ADJUSTMENT_TYPES = "s_allowed_adjustments";
private static final String ATT_TYPES = "types";
private final Object mLock = new Object();
@@ -9120,6 +9142,12 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ protected void ensureFilters(ServiceInfo si, int userId) {
+ // nothing to filter; no user visible settings for types/packages like other
+ // listeners
+ }
+
+ @Override
@GuardedBy("mNotificationLock")
protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
mListeners.unregisterService(removed.service, removed.userid);
@@ -9150,13 +9178,19 @@ public class NotificationManagerService extends SystemService {
@Override
protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {
- if (TAG_ALLOWED_ADJUSTMENT_TYPES.equals(tag)) {
+ if (TAG_ALLOWED_ADJUSTMENT_TYPES_OLD.equals(tag)
+ || TAG_ALLOWED_ADJUSTMENT_TYPES.equals(tag)) {
final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES);
synchronized (mLock) {
mAllowedAdjustments.clear();
if (!TextUtils.isEmpty(types)) {
mAllowedAdjustments.addAll(Arrays.asList(types.split(",")));
}
+ if (TAG_ALLOWED_ADJUSTMENT_TYPES_OLD.equals(tag)) {
+ if (DEBUG) Slog.d(TAG, "Migrate allowed adjustments.");
+ mAllowedAdjustments.addAll(
+ Arrays.asList(DEFAULT_ALLOWED_ADJUSTMENTS));
+ }
}
}
}
@@ -9478,6 +9512,26 @@ public class NotificationManagerService extends SystemService {
});
}
+ @GuardedBy("mNotificationLock")
+ void notifyAssistantFeedbackReceived(final NotificationRecord r, Bundle feedback) {
+ final StatusBarNotification sbn = r.getSbn();
+
+ for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
+ boolean sbnVisible = isVisibleToListener(
+ sbn, r.getNotificationType(), info)
+ && info.isSameUser(r.getUserId());
+ if (sbnVisible) {
+ final INotificationListener assistant = (INotificationListener) info.service;
+ try {
+ final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
+ assistant.onNotificationFeedbackReceived(sbn.getKey(), update, feedback);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "unable to notify assistant (feedback): " + assistant, ex);
+ }
+ }
+ }
+ }
+
/**
* Notifies the assistant something about the specified notification, only assistant
* that is visible to the notification will be notified.
@@ -9558,11 +9612,12 @@ public class NotificationManagerService extends SystemService {
public class NotificationListeners extends ManagedServices {
static final String TAG_ENABLED_NOTIFICATION_LISTENERS = "enabled_listeners";
- static final String TAG_REQUESTED_LISTENERS = "req_listeners";
+ static final String TAG_REQUESTED_LISTENERS = "request_listeners";
static final String TAG_REQUESTED_LISTENER = "listener";
static final String ATT_COMPONENT = "component";
static final String ATT_TYPES = "types";
- static final String ATT_PKGS = "pkgs";
+ static final String ATT_PKG = "pkg";
+ static final String ATT_UID = "uid";
static final String TAG_APPROVED = "allowed";
static final String TAG_DISALLOWED= "disallowed";
static final String XML_SEPARATOR = ",";
@@ -9680,28 +9735,6 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public void onUserUnlocked(int user) {
- int flags = PackageManager.GET_SERVICES | PackageManager.GET_META_DATA;
-
- final PackageManager pmWrapper = mContext.getPackageManager();
- List<ResolveInfo> installedServices = pmWrapper.queryIntentServicesAsUser(
- new Intent(getConfig().serviceInterface), flags, user);
-
- for (ResolveInfo resolveInfo : installedServices) {
- ServiceInfo info = resolveInfo.serviceInfo;
-
- if (!getConfig().bindPermission.equals(info.permission)) {
- continue;
- }
- Pair key = Pair.create(info.getComponentName(), user);
- if (!mRequestedNotificationListeners.containsKey(key)) {
- mRequestedNotificationListeners.put(key, new NotificationListenerFilter());
- }
- }
- super.onUserUnlocked(user);
- }
-
- @Override
public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uidList) {
super.onPackagesChanged(removingPackage, pkgList, uidList);
@@ -9720,6 +9753,18 @@ public class NotificationManagerService extends SystemService {
}
}
}
+
+ // clean up anything in the disallowed pkgs list
+ for (int i = 0; i < pkgList.length; i++) {
+ String pkg = pkgList[i];
+ int userId = UserHandle.getUserId(uidList[i]);
+ for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
+ NotificationListenerFilter nlf = mRequestedNotificationListeners.valueAt(j);
+
+ VersionedPackage ai = new VersionedPackage(pkg, uidList[i]);
+ nlf.removePackage(ai);
+ }
+ }
}
@Override
@@ -9749,15 +9794,17 @@ public class NotificationManagerService extends SystemService {
int approved = FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ALERTING
| FLAG_FILTER_TYPE_SILENT | FLAG_FILTER_TYPE_ONGOING;
- ArraySet<String> disallowedPkgs = new ArraySet<>();
+ ArraySet<VersionedPackage> disallowedPkgs = new ArraySet<>();
final int listenerOuterDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, listenerOuterDepth)) {
if (TAG_APPROVED.equals(parser.getName())) {
approved = XmlUtils.readIntAttribute(parser, ATT_TYPES);
} else if (TAG_DISALLOWED.equals(parser.getName())) {
- String pkgs = XmlUtils.readStringAttribute(parser, ATT_PKGS);
- if (!TextUtils.isEmpty(pkgs)) {
- disallowedPkgs = new ArraySet<>(pkgs.split(XML_SEPARATOR));
+ String pkg = XmlUtils.readStringAttribute(parser, ATT_PKG);
+ int uid = XmlUtils.readIntAttribute(parser, ATT_UID);
+ if (!TextUtils.isEmpty(pkg)) {
+ VersionedPackage ai = new VersionedPackage(pkg, uid);
+ disallowedPkgs.add(ai);
}
}
}
@@ -9782,10 +9829,14 @@ public class NotificationManagerService extends SystemService {
XmlUtils.writeIntAttribute(out, ATT_TYPES, nlf.getTypes());
out.endTag(null, TAG_APPROVED);
- out.startTag(null, TAG_DISALLOWED);
- XmlUtils.writeStringAttribute(
- out, ATT_PKGS, String.join(XML_SEPARATOR, nlf.getDisallowedPackages()));
- out.endTag(null, TAG_DISALLOWED);
+ for (VersionedPackage ai : nlf.getDisallowedPackages()) {
+ if (!TextUtils.isEmpty(ai.getPackageName())) {
+ out.startTag(null, TAG_DISALLOWED);
+ XmlUtils.writeStringAttribute(out, ATT_PKG, ai.getPackageName());
+ XmlUtils.writeIntAttribute(out, ATT_UID, ai.getVersionCode());
+ out.endTag(null, TAG_DISALLOWED);
+ }
+ }
out.endTag(null, TAG_REQUESTED_LISTENER);
}
@@ -9803,6 +9854,38 @@ public class NotificationManagerService extends SystemService {
mRequestedNotificationListeners.put(pair, nlf);
}
+ @Override
+ protected void ensureFilters(ServiceInfo si, int userId) {
+ Pair listener = Pair.create(si.getComponentName(), userId);
+ NotificationListenerFilter existingNlf =
+ mRequestedNotificationListeners.get(listener);
+ if (existingNlf == null) {
+ // no stored filters for this listener; see if they provided a default
+ if (si.metaData != null) {
+ String typeList = si.metaData.getString(
+ NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES);
+ if (typeList != null) {
+ int types = 0;
+ String[] typeStrings = typeList.split(XML_SEPARATOR);
+ for (int i = 0; i < typeStrings.length; i++) {
+ if (TextUtils.isEmpty(typeStrings[i])) {
+ continue;
+ }
+ try {
+ types |= Integer.parseInt(typeStrings[i]);
+ } catch (NumberFormatException e) {
+ // skip
+ }
+ }
+
+ NotificationListenerFilter nlf =
+ new NotificationListenerFilter(types, new ArraySet<>());
+ mRequestedNotificationListeners.put(listener, nlf);
+ }
+ }
+ }
+ }
+
@GuardedBy("mNotificationLock")
public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
if (trim == TRIM_LIGHT) {
@@ -10104,7 +10187,9 @@ public class NotificationManagerService extends SystemService {
}
BackgroundThread.getHandler().post(() -> {
- if (info.isSystem || hasCompanionDevice(info)) {
+ if (info.isSystem
+ || hasCompanionDevice(info)
+ || mAssistants.isServiceTokenValidLocked(info.service)) {
notifyNotificationChannelChanged(
info, pkg, user, channel, modificationType);
}
diff --git a/services/core/java/com/android/server/om/DumpState.java b/services/core/java/com/android/server/om/DumpState.java
index 1e2e054bbc6c..88afcb2f5041 100644
--- a/services/core/java/com/android/server/om/DumpState.java
+++ b/services/core/java/com/android/server/om/DumpState.java
@@ -18,6 +18,7 @@ package com.android.server.om;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.content.om.OverlayIdentifier;
import android.os.UserHandle;
/**
@@ -26,6 +27,7 @@ import android.os.UserHandle;
public final class DumpState {
@UserIdInt private int mUserId = UserHandle.USER_ALL;
@Nullable private String mPackageName;
+ @Nullable private String mOverlayName;
@Nullable private String mField;
private boolean mVerbose;
@@ -38,14 +40,19 @@ public final class DumpState {
}
/** Sets the name of the package to dump the state for */
- public void setPackageName(String packageName) {
- mPackageName = packageName;
+ public void setOverlyIdentifier(String overlayIdentifier) {
+ final OverlayIdentifier overlay = OverlayIdentifier.fromString(overlayIdentifier);
+ mPackageName = overlay.getPackageName();
+ mOverlayName = overlay.getOverlayName();
}
@Nullable public String getPackageName() {
return mPackageName;
}
+ @Nullable public String getOverlayName() {
+ return mOverlayName;
+ }
- /** Sets the name of the field to dump the state of */
+ /** Sets the name of the field to dump the state for */
public void setField(String field) {
mField = field;
}
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index 19fa9204935d..2ebc8edb6740 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -20,17 +20,23 @@ import static android.content.Context.IDMAP_SERVICE;
import static com.android.server.om.OverlayManagerService.TAG;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.FabricatedOverlayInfo;
+import android.os.FabricatedOverlayInternal;
import android.os.IBinder;
import android.os.IIdmap2;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemService;
+import android.text.TextUtils;
import android.util.Slog;
import com.android.server.FgThread;
import java.io.File;
+import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
@@ -104,10 +110,12 @@ class IdmapDaemon {
return sInstance;
}
- String createIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
- int userId) throws TimeoutException, RemoteException {
+ String createIdmap(@NonNull String targetPath, @NonNull String overlayPath,
+ @Nullable String overlayName, int policies, boolean enforce, int userId)
+ throws TimeoutException, RemoteException {
try (Connection c = connect()) {
- return mService.createIdmap(targetPath, overlayPath, policies, enforce, userId);
+ return mService.createIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName),
+ policies, enforce, userId);
}
}
@@ -117,11 +125,12 @@ class IdmapDaemon {
}
}
- boolean verifyIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
- int userId)
+ boolean verifyIdmap(@NonNull String targetPath, @NonNull String overlayPath,
+ @Nullable String overlayName, int policies, boolean enforce, int userId)
throws Exception {
try (Connection c = connect()) {
- return mService.verifyIdmap(targetPath, overlayPath, policies, enforce, userId);
+ return mService.verifyIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName),
+ policies, enforce, userId);
}
}
@@ -129,12 +138,38 @@ class IdmapDaemon {
try (Connection c = connect()) {
return new File(mService.getIdmapPath(overlayPath, userId)).isFile();
} catch (Exception e) {
- Slog.wtf(TAG, "failed to check if idmap exists for " + overlayPath + ": "
- + e.getMessage());
+ Slog.wtf(TAG, "failed to check if idmap exists for " + overlayPath, e);
return false;
}
}
+ FabricatedOverlayInfo createFabricatedOverlay(@NonNull FabricatedOverlayInternal overlay) {
+ try (Connection c = connect()) {
+ return mService.createFabricatedOverlay(overlay);
+ } catch (Exception e) {
+ Slog.wtf(TAG, "failed to fabricate overlay " + overlay, e);
+ return null;
+ }
+ }
+
+ boolean deleteFabricatedOverlay(@NonNull String path) {
+ try (Connection c = connect()) {
+ return mService.deleteFabricatedOverlay(path);
+ } catch (Exception e) {
+ Slog.wtf(TAG, "failed to delete fabricated overlay '" + path + "'", e);
+ return false;
+ }
+ }
+
+ List<FabricatedOverlayInfo> getFabricatedOverlayInfos() {
+ try (Connection c = connect()) {
+ return mService.getFabricatedOverlayInfos();
+ } catch (Exception e) {
+ Slog.wtf(TAG, "failed to get fabricated overlays", e);
+ return null;
+ }
+ }
+
private IBinder getIdmapService() throws TimeoutException, RemoteException {
SystemService.start(IDMAP_DAEMON);
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index eeb26552e63d..64362c9b1d69 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -22,15 +22,19 @@ import static com.android.server.om.OverlayManagerService.TAG;
import android.annotation.NonNull;
import android.content.om.OverlayInfo;
import android.content.om.OverlayableInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
+import android.content.pm.PackageParser;
import android.os.Build.VERSION_CODES;
+import android.os.FabricatedOverlayInfo;
+import android.os.FabricatedOverlayInternal;
import android.os.OverlayablePolicy;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Slog;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
import java.io.IOException;
+import java.util.List;
/**
* Handle the creation and deletion of idmap files.
@@ -74,25 +78,26 @@ final class IdmapManager {
* Creates the idmap for the target/overlay combination and returns whether the idmap file was
* modified.
*/
- boolean createIdmap(@NonNull final PackageInfo targetPackage,
- @NonNull final PackageInfo overlayPackage, int userId) {
+ boolean createIdmap(@NonNull final AndroidPackage targetPackage,
+ @NonNull final AndroidPackage overlayPackage, String overlayBasePath,
+ String overlayName, int userId) {
if (DEBUG) {
- Slog.d(TAG, "create idmap for " + targetPackage.packageName + " and "
- + overlayPackage.packageName);
+ Slog.d(TAG, "create idmap for " + targetPackage.getPackageName() + " and "
+ + overlayPackage.getPackageName());
}
- final String targetPath = targetPackage.applicationInfo.getBaseCodePath();
- final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
+ final String targetPath = targetPackage.getBaseApkPath();
try {
int policies = calculateFulfilledPolicies(targetPackage, overlayPackage, userId);
boolean enforce = enforceOverlayable(overlayPackage);
- if (mIdmapDaemon.verifyIdmap(targetPath, overlayPath, policies, enforce, userId)) {
+ if (mIdmapDaemon.verifyIdmap(targetPath, overlayBasePath, overlayName, policies,
+ enforce, userId)) {
return false;
}
- return mIdmapDaemon.createIdmap(targetPath, overlayPath, policies,
+ return mIdmapDaemon.createIdmap(targetPath, overlayBasePath, overlayName, policies,
enforce, userId) != null;
} catch (Exception e) {
Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
- + overlayPath + ": " + e.getMessage());
+ + overlayBasePath, e);
return false;
}
}
@@ -104,7 +109,7 @@ final class IdmapManager {
try {
return mIdmapDaemon.removeIdmap(oi.baseCodePath, userId);
} catch (Exception e) {
- Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath + ": " + e.getMessage());
+ Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath, e);
return false;
}
}
@@ -113,22 +118,40 @@ final class IdmapManager {
return mIdmapDaemon.idmapExists(oi.baseCodePath, oi.userId);
}
- boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) {
- return mIdmapDaemon.idmapExists(overlayPackage.applicationInfo.getBaseCodePath(), userId);
+ /**
+ * @return the list of all fabricated overlays
+ */
+ List<FabricatedOverlayInfo> getFabricatedOverlayInfos() {
+ return mIdmapDaemon.getFabricatedOverlayInfos();
+ }
+
+ /**
+ * Creates a fabricated overlay and persists it to disk.
+ * @return the path to the fabricated overlay
+ */
+ FabricatedOverlayInfo createFabricatedOverlay(@NonNull FabricatedOverlayInternal overlay) {
+ return mIdmapDaemon.createFabricatedOverlay(overlay);
+ }
+
+ /**
+ * Deletes the fabricated overlay file on disk.
+ * @return whether the path was deleted
+ */
+ boolean deleteFabricatedOverlay(@NonNull String path) {
+ return mIdmapDaemon.deleteFabricatedOverlay(path);
}
/**
* Checks if overlayable and policies should be enforced on the specified overlay for backwards
* compatibility with pre-Q overlays.
*/
- private boolean enforceOverlayable(@NonNull final PackageInfo overlayPackage) {
- final ApplicationInfo ai = overlayPackage.applicationInfo;
- if (ai.targetSdkVersion >= VERSION_CODES.Q) {
+ private boolean enforceOverlayable(@NonNull final AndroidPackage overlayPackage) {
+ if (overlayPackage.getTargetSdkVersion() >= VERSION_CODES.Q) {
// Always enforce policies for overlays targeting Q+.
return true;
}
- if (ai.isVendor()) {
+ if (overlayPackage.isVendor()) {
// If the overlay is on a pre-Q vendor partition, do not enforce overlayable
// restrictions on this overlay because the pre-Q platform has no understanding of
// overlayable.
@@ -137,20 +160,19 @@ final class IdmapManager {
// Do not enforce overlayable restrictions on pre-Q overlays that are signed with the
// platform signature or that are preinstalled.
- return !(ai.isSystemApp() || ai.isSignedWithPlatformKey());
+ return !(overlayPackage.isSystem() || overlayPackage.isSignedWithPlatformKey());
}
/**
* Retrieves a bitmask for idmap2 that represents the policies the overlay fulfills.
*/
- private int calculateFulfilledPolicies(@NonNull final PackageInfo targetPackage,
- @NonNull final PackageInfo overlayPackage, int userId) {
- final ApplicationInfo ai = overlayPackage.applicationInfo;
+ private int calculateFulfilledPolicies(@NonNull final AndroidPackage targetPackage,
+ @NonNull final AndroidPackage overlayPackage, int userId) {
int fulfilledPolicies = OverlayablePolicy.PUBLIC;
// Overlay matches target signature
- if (mPackageManager.signaturesMatching(targetPackage.packageName,
- overlayPackage.packageName, userId)) {
+ if (mPackageManager.signaturesMatching(targetPackage.getPackageName(),
+ overlayPackage.getPackageName(), userId)) {
fulfilledPolicies |= OverlayablePolicy.SIGNATURE;
}
@@ -164,52 +186,52 @@ final class IdmapManager {
// preinstalled package, check if overlay matches its signature.
if (!TextUtils.isEmpty(mConfigSignaturePackage)
&& mPackageManager.signaturesMatching(mConfigSignaturePackage,
- overlayPackage.packageName,
+ overlayPackage.getPackageName(),
userId)) {
fulfilledPolicies |= OverlayablePolicy.CONFIG_SIGNATURE;
}
// Vendor partition (/vendor)
- if (ai.isVendor()) {
+ if (overlayPackage.isVendor()) {
return fulfilledPolicies | OverlayablePolicy.VENDOR_PARTITION;
}
// Product partition (/product)
- if (ai.isProduct()) {
+ if (overlayPackage.isProduct()) {
return fulfilledPolicies | OverlayablePolicy.PRODUCT_PARTITION;
}
// Odm partition (/odm)
- if (ai.isOdm()) {
+ if (overlayPackage.isOdm()) {
return fulfilledPolicies | OverlayablePolicy.ODM_PARTITION;
}
// Oem partition (/oem)
- if (ai.isOem()) {
+ if (overlayPackage.isOem()) {
return fulfilledPolicies | OverlayablePolicy.OEM_PARTITION;
}
// System_ext partition (/system_ext) is considered as system
// Check this last since every partition except for data is scanned as system in the PMS.
- if (ai.isSystemApp() || ai.isSystemExt()) {
+ if (overlayPackage.isSystem() || overlayPackage.isSystemExt()) {
return fulfilledPolicies | OverlayablePolicy.SYSTEM_PARTITION;
}
return fulfilledPolicies;
}
- private boolean matchesActorSignature(@NonNull PackageInfo targetPackage,
- @NonNull PackageInfo overlayPackage, int userId) {
- String targetOverlayableName = overlayPackage.targetOverlayableName;
+ private boolean matchesActorSignature(@NonNull AndroidPackage targetPackage,
+ @NonNull AndroidPackage overlayPackage, int userId) {
+ String targetOverlayableName = overlayPackage.getOverlayTargetName();
if (targetOverlayableName != null) {
try {
OverlayableInfo overlayableInfo = mPackageManager.getOverlayableForTarget(
- targetPackage.packageName, targetOverlayableName, userId);
+ targetPackage.getPackageName(), targetOverlayableName, userId);
if (overlayableInfo != null && overlayableInfo.actor != null) {
String actorPackageName = OverlayActorEnforcer.getPackageNameForActor(
overlayableInfo.actor, mPackageManager.getNamedActors()).first;
if (mPackageManager.signaturesMatching(actorPackageName,
- overlayPackage.packageName, userId)) {
+ overlayPackage.getPackageName(), userId)) {
return true;
}
}
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index 8121a43e9060..c4b6485d2a2f 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -19,16 +19,16 @@ package com.android.server.om;
import android.annotation.NonNull;
import android.content.om.OverlayInfo;
import android.content.om.OverlayableInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.net.Uri;
import android.os.Process;
import android.text.TextUtils;
import android.util.Pair;
+import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.IOException;
import java.util.List;
@@ -43,9 +43,6 @@ import java.util.Map;
*/
public class OverlayActorEnforcer {
- // By default, the reason is not logged to prevent leaks of why it failed
- private static final boolean DEBUG_REASON = false;
-
private final PackageManagerHelper mPackageManager;
/**
@@ -86,17 +83,18 @@ public class OverlayActorEnforcer {
void enforceActor(@NonNull OverlayInfo overlayInfo, @NonNull String methodName,
int callingUid, int userId) throws SecurityException {
- ActorState actorState = isAllowedActor(methodName, overlayInfo, callingUid, userId);
+ final ActorState actorState = isAllowedActor(methodName, overlayInfo, callingUid, userId);
if (actorState == ActorState.ALLOWED) {
return;
}
- String targetOverlayableName = overlayInfo.targetOverlayableName;
- throw new SecurityException("UID" + callingUid + " is not allowed to call "
- + methodName + " for "
+ final String targetOverlayableName = overlayInfo.targetOverlayableName;
+ final String errorMessage = "UID" + callingUid + " is not allowed to call " + methodName
+ + " for "
+ (TextUtils.isEmpty(targetOverlayableName) ? "" : (targetOverlayableName + " in "))
- + overlayInfo.targetPackageName + (DEBUG_REASON ? (" because " + actorState) : "")
- );
+ + overlayInfo.targetPackageName + " for user " + userId;
+ Slog.w(OverlayManagerService.TAG, errorMessage + " because " + actorState);
+ throw new SecurityException(errorMessage);
}
/**
@@ -114,12 +112,13 @@ public class OverlayActorEnforcer {
}
final String targetPackageName = overlayInfo.targetPackageName;
- final PackageInfo targetPkgInfo = mPackageManager.getPackageInfo(targetPackageName, userId);
+ final AndroidPackage targetPkgInfo = mPackageManager.getPackageForUser(targetPackageName,
+ userId);
if (targetPkgInfo == null) {
return ActorState.TARGET_NOT_FOUND;
}
- if ((targetPkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+ if (targetPkgInfo.isDebuggable()) {
return ActorState.ALLOWED;
}
@@ -189,23 +188,18 @@ public class OverlayActorEnforcer {
return actorUriState;
}
- String packageName = actorUriPair.first;
- PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, userId);
- if (packageInfo == null) {
- return ActorState.MISSING_APP_INFO;
- }
-
- ApplicationInfo appInfo = packageInfo.applicationInfo;
- if (appInfo == null) {
- return ActorState.MISSING_APP_INFO;
+ String actorPackageName = actorUriPair.first;
+ AndroidPackage actorPackage = mPackageManager.getPackageForUser(actorPackageName, userId);
+ if (actorPackage == null) {
+ return ActorState.ACTOR_NOT_FOUND;
}
// Currently only pre-installed apps can be actors
- if (!appInfo.isSystemApp()) {
+ if (!actorPackage.isSystem()) {
return ActorState.ACTOR_NOT_PREINSTALLED;
}
- if (ArrayUtils.contains(callingPackageNames, packageName)) {
+ if (ArrayUtils.contains(callingPackageNames, actorPackageName)) {
return ActorState.ALLOWED;
}
@@ -231,7 +225,7 @@ public class OverlayActorEnforcer {
NO_NAMED_ACTORS,
MISSING_NAMESPACE,
MISSING_ACTOR_NAME,
- MISSING_APP_INFO,
+ ACTOR_NOT_FOUND,
ACTOR_NOT_PREINSTALLED,
INVALID_ACTOR,
ALLOWED
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 50fb176e3a5a..dbd121137b57 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -24,8 +24,10 @@ import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_REASON;
+import static android.content.om.OverlayManagerTransaction.Request.TYPE_REGISTER_FABRICATED;
import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_DISABLED;
import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED;
+import static android.content.om.OverlayManagerTransaction.Request.TYPE_UNREGISTER_FABRICATED;
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
import static android.os.Trace.TRACE_TAG_RRO;
import static android.os.Trace.traceBegin;
@@ -43,19 +45,22 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
+import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.content.om.OverlayManagerTransaction;
import android.content.om.OverlayableInfo;
import android.content.pm.IPackageManager;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
+import android.content.pm.overlay.OverlayPaths;
import android.content.res.ApkAssets;
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
+import android.os.FabricatedOverlayInternal;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
@@ -70,12 +75,15 @@ import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.content.om.OverlayConfig;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
+
import com.android.server.pm.UserManagerService;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import libcore.util.EmptyArray;
@@ -91,13 +99,12 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
-import java.util.function.Consumer;
/**
* Service to manage asset overlays.
@@ -246,15 +253,6 @@ public final class OverlayManagerService extends SystemService {
private final OverlayActorEnforcer mActorEnforcer;
- private final Consumer<PackageAndUser> mPropagateOverlayChange = (pair) -> {
- persistSettings();
- FgThread.getHandler().post(() -> {
- List<String> affectedTargets = updatePackageManager(pair.packageName, pair.userId);
- updateActivityManager(affectedTargets, pair.userId);
- broadcastActionOverlayChanged(pair.packageName, pair.userId);
- });
- };
-
public OverlayManagerService(@NonNull final Context context) {
super(context);
try {
@@ -314,8 +312,7 @@ public final class OverlayManagerService extends SystemService {
// Initialize any users that can't be switched to, as their state would
// never be setup in onSwitchUser(). We will switch to the system user right
// after this, and its state will be setup there.
- final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id);
- updatePackageManager(targets, users.get(i).id);
+ updatePackageManager(mImpl.updateOverlaysForUser(users.get(i).id));
}
}
}
@@ -332,9 +329,7 @@ public final class OverlayManagerService extends SystemService {
// ensure overlays in the settings are up-to-date, and propagate
// any asset changes to the rest of the system
synchronized (mLock) {
- final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
- final List<String> affectedTargets = updatePackageManager(targets, newUserId);
- updateActivityManager(affectedTargets, newUserId);
+ updateActivityManager(updatePackageManager(mImpl.updateOverlaysForUser(newUserId)));
}
persistSettings();
} finally {
@@ -416,19 +411,11 @@ public final class OverlayManagerService extends SystemService {
traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName);
for (final int userId : userIds) {
synchronized (mLock) {
- final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
- false);
- if (pi != null && !pi.applicationInfo.isInstantApp()) {
- mPackageManager.cachePackageInfo(packageName, userId, pi);
-
+ final AndroidPackage pkg = mPackageManager.onPackageAdded(
+ packageName, userId);
+ if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) {
try {
- if (pi.isOverlayPackage()) {
- mImpl.onOverlayPackageAdded(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
- } else {
- mImpl.onTargetPackageAdded(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
- }
+ updateTargetPackages(mImpl.onPackageAdded(packageName, userId));
} catch (OperationFailedException e) {
Slog.e(TAG, "onPackageAdded internal error", e);
}
@@ -446,19 +433,11 @@ public final class OverlayManagerService extends SystemService {
traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName);
for (int userId : userIds) {
synchronized (mLock) {
- final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
- false);
- if (pi != null && pi.applicationInfo.isInstantApp()) {
- mPackageManager.cachePackageInfo(packageName, userId, pi);
-
+ final AndroidPackage pkg = mPackageManager.onPackageUpdated(
+ packageName, userId);
+ if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) {
try {
- if (pi.isOverlayPackage()) {
- mImpl.onOverlayPackageChanged(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
- } else {
- mImpl.onTargetPackageChanged(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
- }
+ updateTargetPackages(mImpl.onPackageChanged(packageName, userId));
} catch (OperationFailedException e) {
Slog.e(TAG, "onPackageChanged internal error", e);
}
@@ -476,12 +455,11 @@ public final class OverlayManagerService extends SystemService {
traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName);
for (int userId : userIds) {
synchronized (mLock) {
- mPackageManager.forgetPackageInfo(packageName, userId);
- final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
- if (oi != null) {
+ final AndroidPackage pkg = mPackageManager.onPackageUpdated(
+ packageName, userId);
+ if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) {
try {
- mImpl.onOverlayPackageReplacing(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
+ updateTargetPackages(mImpl.onPackageReplacing(packageName, userId));
} catch (OperationFailedException e) {
Slog.e(TAG, "onPackageReplacing internal error", e);
}
@@ -499,18 +477,11 @@ public final class OverlayManagerService extends SystemService {
traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName);
for (int userId : userIds) {
synchronized (mLock) {
- final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
- false);
- if (pi != null && !pi.applicationInfo.isInstantApp()) {
- mPackageManager.cachePackageInfo(packageName, userId, pi);
+ final AndroidPackage pkg = mPackageManager.onPackageUpdated(
+ packageName, userId);
+ if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) {
try {
- if (pi.isOverlayPackage()) {
- mImpl.onOverlayPackageReplaced(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
- } else {
- mImpl.onTargetPackageReplaced(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
- }
+ updateTargetPackages(mImpl.onPackageReplaced(packageName, userId));
} catch (OperationFailedException e) {
Slog.e(TAG, "onPackageReplaced internal error", e);
}
@@ -528,20 +499,8 @@ public final class OverlayManagerService extends SystemService {
traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName);
for (int userId : userIds) {
synchronized (mLock) {
- mPackageManager.forgetPackageInfo(packageName, userId);
- final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
-
- try {
- if (oi != null) {
- mImpl.onOverlayPackageRemoved(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
- } else {
- mImpl.onTargetPackageRemoved(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
- }
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageRemoved internal error", e);
- }
+ mPackageManager.onPackageRemoved(packageName, userId);
+ updateTargetPackages(mImpl.onPackageRemoved(packageName, userId));
}
}
} finally {
@@ -559,11 +518,9 @@ public final class OverlayManagerService extends SystemService {
if (userId != UserHandle.USER_NULL) {
try {
traceBegin(TRACE_TAG_RRO, "OMS ACTION_USER_ADDED");
- final ArrayList<String> targets;
synchronized (mLock) {
- targets = mImpl.updateOverlaysForUser(userId);
+ updatePackageManager(mImpl.updateOverlaysForUser(userId));
}
- updatePackageManager(targets, userId);
} finally {
traceEnd(TRACE_TAG_RRO);
}
@@ -627,16 +584,22 @@ public final class OverlayManagerService extends SystemService {
@Override
public OverlayInfo getOverlayInfo(@Nullable final String packageName,
final int userIdArg) {
- if (packageName == null) {
+ return getOverlayInfoByIdentifier(new OverlayIdentifier(packageName), userIdArg);
+ }
+
+ @Override
+ public OverlayInfo getOverlayInfoByIdentifier(@Nullable final OverlayIdentifier overlay,
+ final int userIdArg) {
+ if (overlay == null || overlay.getPackageName() == null) {
return null;
}
try {
- traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfo " + packageName);
+ traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfo " + overlay);
final int realUserId = handleIncomingUser(userIdArg, "getOverlayInfo");
synchronized (mLock) {
- return mImpl.getOverlayInfo(packageName, realUserId);
+ return mImpl.getOverlayInfo(overlay, realUserId);
}
} finally {
traceEnd(TRACE_TAG_RRO);
@@ -652,15 +615,16 @@ public final class OverlayManagerService extends SystemService {
try {
traceBegin(TRACE_TAG_RRO, "OMS#setEnabled " + packageName + " " + enable);
+
+ final OverlayIdentifier overlay = new OverlayIdentifier(packageName);
final int realUserId = handleIncomingUser(userIdArg, "setEnabled");
- enforceActor(packageName, "setEnabled", realUserId);
+ enforceActor(overlay, "setEnabled", realUserId);
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
try {
- mImpl.setEnabled(packageName, enable, realUserId)
- .ifPresent(mPropagateOverlayChange);
+ updateTargetPackages(mImpl.setEnabled(overlay, enable, realUserId));
return true;
} catch (OperationFailedException e) {
return false;
@@ -683,16 +647,18 @@ public final class OverlayManagerService extends SystemService {
try {
traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusive " + packageName + " " + enable);
+
+ final OverlayIdentifier overlay = new OverlayIdentifier(packageName);
final int realUserId = handleIncomingUser(userIdArg, "setEnabledExclusive");
- enforceActor(packageName, "setEnabledExclusive", realUserId);
+ enforceActor(overlay, "setEnabledExclusive", realUserId);
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
try {
- mImpl.setEnabledExclusive(packageName,
+ mImpl.setEnabledExclusive(overlay,
false /* withinCategory */, realUserId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(OverlayManagerService.this::updateTargetPackages);
return true;
} catch (OperationFailedException e) {
return false;
@@ -715,17 +681,19 @@ public final class OverlayManagerService extends SystemService {
try {
traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusiveInCategory " + packageName);
+
+ final OverlayIdentifier overlay = new OverlayIdentifier(packageName);
final int realUserId = handleIncomingUser(userIdArg,
"setEnabledExclusiveInCategory");
- enforceActor(packageName, "setEnabledExclusiveInCategory", realUserId);
+ enforceActor(overlay, "setEnabledExclusiveInCategory", realUserId);
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
try {
- mImpl.setEnabledExclusive(packageName,
+ mImpl.setEnabledExclusive(overlay,
true /* withinCategory */, realUserId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(OverlayManagerService.this::updateTargetPackages);
return true;
} catch (OperationFailedException e) {
return false;
@@ -749,15 +717,18 @@ public final class OverlayManagerService extends SystemService {
try {
traceBegin(TRACE_TAG_RRO, "OMS#setPriority " + packageName + " "
+ parentPackageName);
+
+ final OverlayIdentifier overlay = new OverlayIdentifier(packageName);
+ final OverlayIdentifier parentOverlay = new OverlayIdentifier(parentPackageName);
final int realUserId = handleIncomingUser(userIdArg, "setPriority");
- enforceActor(packageName, "setPriority", realUserId);
+ enforceActor(overlay, "setPriority", realUserId);
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
try {
- mImpl.setPriority(packageName, parentPackageName, realUserId)
- .ifPresent(mPropagateOverlayChange);
+ mImpl.setPriority(overlay, parentOverlay, realUserId)
+ .ifPresent(OverlayManagerService.this::updateTargetPackages);
return true;
} catch (OperationFailedException e) {
return false;
@@ -779,15 +750,16 @@ public final class OverlayManagerService extends SystemService {
try {
traceBegin(TRACE_TAG_RRO, "OMS#setHighestPriority " + packageName);
+
+ final OverlayIdentifier overlay = new OverlayIdentifier(packageName);
final int realUserId = handleIncomingUser(userIdArg, "setHighestPriority");
- enforceActor(packageName, "setHighestPriority", realUserId);
+ enforceActor(overlay, "setHighestPriority", realUserId);
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
try {
- mImpl.setHighestPriority(packageName, realUserId)
- .ifPresent(mPropagateOverlayChange);
+ updateTargetPackages(mImpl.setHighestPriority(overlay, realUserId));
return true;
} catch (OperationFailedException e) {
return false;
@@ -809,15 +781,17 @@ public final class OverlayManagerService extends SystemService {
try {
traceBegin(TRACE_TAG_RRO, "OMS#setLowestPriority " + packageName);
+
+ final OverlayIdentifier overlay = new OverlayIdentifier(packageName);
final int realUserId = handleIncomingUser(userIdArg, "setLowestPriority");
- enforceActor(packageName, "setLowestPriority", realUserId);
+ enforceActor(overlay, "setLowestPriority", realUserId);
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
try {
- mImpl.setLowestPriority(packageName, realUserId)
- .ifPresent(mPropagateOverlayChange);
+ mImpl.setLowestPriority(overlay, realUserId)
+ .ifPresent(OverlayManagerService.this::updateTargetPackages);
return true;
} catch (OperationFailedException e) {
return false;
@@ -857,12 +831,17 @@ public final class OverlayManagerService extends SystemService {
return;
}
+ final OverlayIdentifier overlay = new OverlayIdentifier(packageName);
final int realUserId = handleIncomingUser(userIdArg, "invalidateCachesForOverlay");
- enforceActor(packageName, "invalidateCachesForOverlay", realUserId);
+ enforceActor(overlay, "invalidateCachesForOverlay", realUserId);
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- mImpl.removeIdmapForOverlay(packageName, realUserId);
+ try {
+ mImpl.removeIdmapForOverlay(overlay, realUserId);
+ } catch (OperationFailedException e) {
+ Slog.w(TAG, "invalidate caches for overlay '" + overlay + "' failed", e);
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -892,25 +871,63 @@ public final class OverlayManagerService extends SystemService {
}
}
- private Optional<PackageAndUser> executeRequest(
- @NonNull final OverlayManagerTransaction.Request request) throws Exception {
- final int realUserId = handleIncomingUser(request.userId, request.typeToString());
- enforceActor(request.packageName, request.typeToString(), realUserId);
+ private Set<PackageAndUser> executeRequest(
+ @NonNull final OverlayManagerTransaction.Request request)
+ throws OperationFailedException {
+ Objects.requireNonNull(request, "Transaction contains a null request");
+ Objects.requireNonNull(request.overlay,
+ "Transaction overlay identifier must be non-null");
+
+ final int callingUid = Binder.getCallingUid();
+ final int realUserId;
+ if (request.type == TYPE_REGISTER_FABRICATED
+ || request.type == TYPE_UNREGISTER_FABRICATED) {
+ if (request.userId != UserHandle.USER_ALL) {
+ throw new IllegalArgumentException(request.typeToString()
+ + " unsupported for user " + request.userId);
+ }
+ realUserId = UserHandle.USER_ALL;
+
+ // Enforce that the calling process can only register and unregister fabricated
+ // overlays using its package name.
+ final String pkgName = request.overlay.getPackageName();
+ if (callingUid != Process.ROOT_UID && !ArrayUtils.contains(
+ mPackageManager.getPackagesForUid(callingUid), pkgName)) {
+ throw new IllegalArgumentException("UID " + callingUid + " does own package"
+ + "name " + pkgName);
+ }
+ } else {
+ // Enforce actor requirements for enabling, disabling, and reordering overlays.
+ realUserId = handleIncomingUser(request.userId, request.typeToString());
+ enforceActor(request.overlay, request.typeToString(), realUserId);
+ }
final long ident = Binder.clearCallingIdentity();
try {
switch (request.type) {
case TYPE_SET_ENABLED:
- Optional<PackageAndUser> opt1 =
- mImpl.setEnabled(request.packageName, true, request.userId);
- Optional<PackageAndUser> opt2 =
- mImpl.setHighestPriority(request.packageName, request.userId);
- // Both setEnabled and setHighestPriority affected the same
- // target package and user: if both return non-empty
- // Optionals, they are identical
- return opt1.isPresent() ? opt1 : opt2;
+ Set<PackageAndUser> result = null;
+ result = CollectionUtils.addAll(result,
+ mImpl.setEnabled(request.overlay, true, realUserId));
+ result = CollectionUtils.addAll(result,
+ mImpl.setHighestPriority(request.overlay, realUserId));
+ return CollectionUtils.emptyIfNull(result);
+
case TYPE_SET_DISABLED:
- return mImpl.setEnabled(request.packageName, false, request.userId);
+ return mImpl.setEnabled(request.overlay, false, realUserId);
+
+ case TYPE_REGISTER_FABRICATED:
+ final FabricatedOverlayInternal fabricated =
+ request.extras.getParcelable(
+ OverlayManagerTransaction.Request.BUNDLE_FABRICATED_OVERLAY
+ );
+ Objects.requireNonNull(fabricated,
+ "no fabricated overlay attached to request");
+ return mImpl.registerFabricatedOverlay(fabricated);
+
+ case TYPE_UNREGISTER_FABRICATED:
+ return mImpl.unregisterFabricatedOverlay(request.overlay);
+
default:
throw new IllegalArgumentException("unsupported request: " + request);
}
@@ -920,7 +937,7 @@ public final class OverlayManagerService extends SystemService {
}
private void executeAllRequests(@NonNull final OverlayManagerTransaction transaction)
- throws Exception {
+ throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "commit " + transaction);
}
@@ -930,25 +947,25 @@ public final class OverlayManagerService extends SystemService {
// map: userId -> set<package-name>: target packages of overlays in
// this transaction
- SparseArray<Set<String>> transactionTargets = new SparseArray<>();
+ final SparseArray<Set<String>> transactionTargets = new SparseArray<>();
// map: userId -> set<package-name>: packages that need to reload
// their resources due to changes to the overlays in this
// transaction
- SparseArray<List<String>> affectedPackagesToUpdate = new SparseArray<>();
+ final SparseArray<List<String>> affectedPackagesToUpdate = new SparseArray<>();
synchronized (mLock) {
-
// execute the requests (as calling user)
for (final OverlayManagerTransaction.Request request : transaction) {
- executeRequest(request).ifPresent(target -> {
- Set<String> userTargets = transactionTargets.get(target.userId);
- if (userTargets == null) {
- userTargets = new ArraySet<String>();
- transactionTargets.put(target.userId, userTargets);
- }
- userTargets.add(target.packageName);
- });
+ executeRequest(request).forEach(
+ target -> {
+ Set<String> userTargets = transactionTargets.get(target.userId);
+ if (userTargets == null) {
+ userTargets = new ArraySet<>();
+ transactionTargets.put(target.userId, userTargets);
+ }
+ userTargets.add(target.packageName);
+ });
}
// past the point of no return: the entire transaction has been
@@ -974,10 +991,7 @@ public final class OverlayManagerService extends SystemService {
final long ident = Binder.clearCallingIdentity();
try {
// schedule apps to refresh
- for (int index = 0; index < affectedPackagesToUpdate.size(); index++) {
- final int userId = affectedPackagesToUpdate.keyAt(index);
- updateActivityManager(affectedPackagesToUpdate.valueAt(index), userId);
- }
+ updateActivityManager(affectedPackagesToUpdate);
// broadcast the ACTION_OVERLAY_CHANGED intents
for (int index = 0; index < transactionTargets.size(); index++) {
@@ -1058,12 +1072,12 @@ public final class OverlayManagerService extends SystemService {
dumpState.setField(arg);
break;
default:
- dumpState.setPackageName(arg);
+ dumpState.setOverlyIdentifier(arg);
break;
}
}
if (dumpState.getPackageName() == null && opti < args.length) {
- dumpState.setPackageName(args[opti]);
+ dumpState.setOverlyIdentifier(args[opti]);
opti++;
}
@@ -1100,12 +1114,12 @@ public final class OverlayManagerService extends SystemService {
getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, message);
}
- private void enforceActor(String packageName, String methodName, int realUserId)
- throws SecurityException {
- OverlayInfo overlayInfo = mImpl.getOverlayInfo(packageName, realUserId);
+ private void enforceActor(@NonNull OverlayIdentifier overlay, @NonNull String methodName,
+ int realUserId) throws SecurityException {
+ OverlayInfo overlayInfo = mImpl.getOverlayInfo(overlay, realUserId);
if (overlayInfo == null) {
throw new IllegalArgumentException("Unable to retrieve overlay information for "
- + packageName);
+ + overlay);
}
int callingUid = Binder.getCallingUid();
@@ -1114,7 +1128,13 @@ public final class OverlayManagerService extends SystemService {
};
private static final class PackageManagerHelperImpl implements PackageManagerHelper {
-
+ private static class AndroidPackageUsers {
+ private AndroidPackage mPackage;
+ private final Set<Integer> mInstalledUsers = new ArraySet<>();
+ private AndroidPackageUsers(@NonNull AndroidPackage pkg) {
+ this.mPackage = pkg;
+ }
+ }
private final Context mContext;
private final IPackageManager mPackageManager;
private final PackageManagerInternal mPackageManagerInternal;
@@ -1124,7 +1144,8 @@ public final class OverlayManagerService extends SystemService {
// intent, querying the PackageManagerService for the actual current
// state may lead to contradictions within OMS. Better then to lag
// behind until all pending intents have been processed.
- private final SparseArray<HashMap<String, PackageInfo>> mCache = new SparseArray<>();
+ private final ArrayMap<String, AndroidPackageUsers> mCache = new ArrayMap<>();
+ private final Set<Integer> mInitializedUsers = new ArraySet<>();
PackageManagerHelperImpl(Context context) {
mContext = context;
@@ -1132,29 +1153,112 @@ public final class OverlayManagerService extends SystemService {
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
}
- public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId,
- final boolean useCache) {
- if (useCache) {
- final PackageInfo cachedPi = getCachedPackageInfo(packageName, userId);
- if (cachedPi != null) {
- return cachedPi;
+ /**
+ * Initializes the helper for the user. This only needs to be invoked one time before
+ * packages of this user are queried.
+ * @param userId the user id to initialize
+ * @return a map of package name to all packages installed in the user
+ */
+ @NonNull
+ public ArrayMap<String, AndroidPackage> initializeForUser(final int userId) {
+ if (!mInitializedUsers.contains(userId)) {
+ mInitializedUsers.add(userId);
+ mPackageManagerInternal.forEachInstalledPackage(
+ (pkg) -> addPackageUser(pkg, userId), userId);
+ }
+
+ final ArrayMap<String, AndroidPackage> userPackages = new ArrayMap<>();
+ for (int i = 0, n = mCache.size(); i < n; i++) {
+ final AndroidPackageUsers pkg = mCache.valueAt(i);
+ if (pkg.mInstalledUsers.contains(userId)) {
+ userPackages.put(mCache.keyAt(i), pkg.mPackage);
}
}
+ return userPackages;
+ }
+
+ @Override
+ @Nullable
+ public AndroidPackage getPackageForUser(@NonNull final String packageName,
+ final int userId) {
+ final AndroidPackageUsers pkg = mCache.get(packageName);
+ if (pkg != null && pkg.mInstalledUsers.contains(userId)) {
+ return pkg.mPackage;
+ }
try {
- final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0, userId);
- if (useCache && pi != null) {
- cachePackageInfo(packageName, userId, pi);
+ if (!mPackageManager.isPackageAvailable(packageName, userId)) {
+ return null;
}
- return pi;
} catch (RemoteException e) {
- // Intentionally left empty.
+ Slog.w(TAG, "Failed to check availability of package '" + packageName
+ + "' for user " + userId, e);
+ return null;
+ }
+ return addPackageUser(packageName, userId);
+ }
+
+ @NonNull
+ private AndroidPackage addPackageUser(@NonNull final String packageName,
+ final int user) {
+ final AndroidPackage pkg = mPackageManagerInternal.getPackage(packageName);
+ if (pkg == null) {
+ Slog.w(TAG, "Android package for '" + packageName + "' could not be found;"
+ + " continuing as if package was never added", new Throwable());
+ return null;
+ }
+ return addPackageUser(pkg, user);
+ }
+
+ @NonNull
+ private AndroidPackage addPackageUser(@NonNull final AndroidPackage pkg,
+ final int user) {
+ AndroidPackageUsers pkgUsers = mCache.get(pkg.getPackageName());
+ if (pkgUsers == null) {
+ pkgUsers = new AndroidPackageUsers(pkg);
+ mCache.put(pkg.getPackageName(), pkgUsers);
+ } else {
+ pkgUsers.mPackage = pkg;
+ }
+ pkgUsers.mInstalledUsers.add(user);
+ return pkgUsers.mPackage;
+ }
+
+
+ @NonNull
+ private void removePackageUser(@NonNull final String packageName, final int user) {
+ final AndroidPackageUsers pkgUsers = mCache.get(packageName);
+ if (pkgUsers == null) {
+ return;
}
- return null;
+ removePackageUser(pkgUsers, user);
+ }
+
+ @NonNull
+ private void removePackageUser(@NonNull final AndroidPackageUsers pkg, final int user) {
+ pkg.mInstalledUsers.remove(user);
+ if (pkg.mInstalledUsers.isEmpty()) {
+ mCache.remove(pkg.mPackage.getPackageName());
+ }
+ }
+
+ @Nullable
+ public AndroidPackage onPackageAdded(@NonNull final String packageName, final int userId) {
+ return addPackageUser(packageName, userId);
+ }
+
+ @Nullable
+ public AndroidPackage onPackageUpdated(@NonNull final String packageName,
+ final int userId) {
+ return addPackageUser(packageName, userId);
+ }
+
+ public void onPackageRemoved(@NonNull final String packageName, final int userId) {
+ removePackageUser(packageName, userId);
}
@Override
- public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId) {
- return getPackageInfo(packageName, userId, true);
+ public boolean isInstantApp(@NonNull final String packageName, final int userId) {
+ return mPackageManagerInternal.isInstantApp(packageName, userId);
}
@NonNull
@@ -1178,15 +1282,6 @@ public final class OverlayManagerService extends SystemService {
}
@Override
- public List<PackageInfo> getOverlayPackages(final int userId) {
- final List<PackageInfo> overlays = mPackageManagerInternal.getOverlayPackages(userId);
- for (final PackageInfo info : overlays) {
- cachePackageInfo(info.packageName, userId, info);
- }
- return overlays;
- }
-
- @Override
public String getConfigSignaturePackage() {
final String[] pkgs = mPackageManagerInternal.getKnownPackageNames(
PackageManagerInternal.PACKAGE_OVERLAY_CONFIG_SIGNATURE,
@@ -1199,16 +1294,14 @@ public final class OverlayManagerService extends SystemService {
public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
@NonNull String targetOverlayableName, int userId)
throws IOException {
- PackageInfo packageInfo = getPackageInfo(packageName, userId);
+ final AndroidPackage packageInfo = getPackageForUser(packageName, userId);
if (packageInfo == null) {
throw new IOException("Unable to get target package");
}
- String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
-
ApkAssets apkAssets = null;
try {
- apkAssets = ApkAssets.loadFromPath(baseCodePath);
+ apkAssets = ApkAssets.loadFromPath(packageInfo.getBaseApkPath());
return apkAssets.getOverlayableInfo(targetOverlayableName);
} finally {
if (apkAssets != null) {
@@ -1223,16 +1316,14 @@ public final class OverlayManagerService extends SystemService {
@Override
public boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
throws IOException {
- PackageInfo packageInfo = getPackageInfo(targetPackageName, userId);
+ AndroidPackage packageInfo = getPackageForUser(targetPackageName, userId);
if (packageInfo == null) {
throw new IOException("Unable to get target package");
}
- String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
-
ApkAssets apkAssets = null;
try {
- apkAssets = ApkAssets.loadFromPath(baseCodePath);
+ apkAssets = ApkAssets.loadFromPath(packageInfo.getBaseApkPath());
return apkAssets.definesOverlayable();
} finally {
if (apkAssets != null) {
@@ -1249,35 +1340,12 @@ public final class OverlayManagerService extends SystemService {
mContext.enforceCallingOrSelfPermission(permission, message);
}
- public PackageInfo getCachedPackageInfo(@NonNull final String packageName,
- final int userId) {
- final HashMap<String, PackageInfo> map = mCache.get(userId);
- return map == null ? null : map.get(packageName);
- }
-
- public void cachePackageInfo(@NonNull final String packageName, final int userId,
- @NonNull final PackageInfo pi) {
- HashMap<String, PackageInfo> map = mCache.get(userId);
- if (map == null) {
- map = new HashMap<>();
- mCache.put(userId, map);
- }
- map.put(packageName, pi);
- }
-
- public void forgetPackageInfo(@NonNull final String packageName, final int userId) {
- final HashMap<String, PackageInfo> map = mCache.get(userId);
- if (map == null) {
- return;
- }
- map.remove(packageName);
- if (map.isEmpty()) {
- mCache.delete(userId);
- }
- }
-
public void forgetAllPackageInfos(final int userId) {
- mCache.delete(userId);
+ // Iterate in reverse order since removing the package in all users will remove the
+ // package from the cache.
+ for (int i = mCache.size() - 1; i >= 0; i--) {
+ removePackageUser(mCache.valueAt(i), userId);
+ }
}
@Nullable
@@ -1291,19 +1359,12 @@ public final class OverlayManagerService extends SystemService {
}
private static final String TAB1 = " ";
- private static final String TAB2 = TAB1 + TAB1;
public void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
- pw.println("PackageInfo cache");
+ pw.println("AndroidPackage cache");
if (!dumpState.isVerbose()) {
- int count = 0;
- final int n = mCache.size();
- for (int i = 0; i < n; i++) {
- final int userId = mCache.keyAt(i);
- count += mCache.get(userId).size();
- }
- pw.println(TAB1 + count + " package(s)");
+ pw.println(TAB1 + mCache.size() + " package(s)");
return;
}
@@ -1312,25 +1373,70 @@ public final class OverlayManagerService extends SystemService {
return;
}
- final int n = mCache.size();
- for (int i = 0; i < n; i++) {
- final int userId = mCache.keyAt(i);
- pw.println(TAB1 + "User " + userId);
- final HashMap<String, PackageInfo> map = mCache.get(userId);
- for (Map.Entry<String, PackageInfo> entry : map.entrySet()) {
- pw.println(TAB2 + entry.getKey() + ": " + entry.getValue());
- }
+ for (int i = 0, n = mCache.size(); i < n; i++) {
+ final String packageName = mCache.keyAt(i);
+ final AndroidPackageUsers pkg = mCache.valueAt(i);
+ pw.print(TAB1 + packageName + ": " + pkg.mPackage + " users=");
+ pw.println(TextUtils.join(", ", pkg.mInstalledUsers));
}
}
}
+ private void updateTargetPackages(@Nullable PackageAndUser updatedTarget) {
+ if (updatedTarget != null) {
+ updateTargetPackages(Set.of(updatedTarget));
+ }
+ }
+
+ private void updateTargetPackages(@Nullable Set<PackageAndUser> updatedTargets) {
+ if (CollectionUtils.isEmpty(updatedTargets)) {
+ return;
+ }
+ persistSettings();
+ final SparseArray<ArraySet<String>> userTargets = groupTargetsByUserId(updatedTargets);
+ FgThread.getHandler().post(() -> {
+ for (int i = 0, n = userTargets.size(); i < n; i++) {
+ final ArraySet<String> targets = userTargets.valueAt(i);
+ final int userId = userTargets.keyAt(i);
+
+ // Update the overlay paths in package manager.
+ final List<String> affectedPackages = updatePackageManager(targets, userId);
+ updateActivityManager(affectedPackages, userId);
+
+ // Overlays targeting shared libraries may cause more packages to need to be
+ // refreshed.
+ broadcastActionOverlayChanged(targets, userId);
+ }
+ });
+ }
+
+ @Nullable
+ private static SparseArray<ArraySet<String>> groupTargetsByUserId(
+ @Nullable final Set<PackageAndUser> targetsAndUsers) {
+ final SparseArray<ArraySet<String>> userTargets = new SparseArray<>();
+ CollectionUtils.forEach(targetsAndUsers, target -> {
+ ArraySet<String> targets = userTargets.get(target.userId);
+ if (targets == null) {
+ targets = new ArraySet<>();
+ userTargets.put(target.userId, targets);
+ }
+ targets.add(target.packageName);
+ });
+ return userTargets;
+ }
+
// Helper methods to update other parts of the system or read/write
// settings: these methods should never call into each other!
- private void broadcastActionOverlayChanged(@NonNull final String targetPackageName,
+ private static void broadcastActionOverlayChanged(@NonNull final Set<String> targetPackages,
final int userId) {
+ CollectionUtils.forEach(targetPackages,
+ target -> broadcastActionOverlayChanged(target, userId));
+ }
+
+ private static void broadcastActionOverlayChanged(String targetPackage, final int userId) {
final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
- Uri.fromParts("package", targetPackageName, null));
+ Uri.fromParts("package", targetPackage, null));
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
try {
ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null, null,
@@ -1344,7 +1450,7 @@ public final class OverlayManagerService extends SystemService {
* Tell the activity manager to tell a set of packages to reload their
* resources.
*/
- private void updateActivityManager(List<String> targetPackageNames, final int userId) {
+ private void updateActivityManager(@NonNull List<String> targetPackageNames, final int userId) {
final IActivityManager am = ActivityManager.getService();
try {
am.scheduleApplicationInfoChanged(targetPackageNames, userId);
@@ -1353,16 +1459,33 @@ public final class OverlayManagerService extends SystemService {
}
}
- private ArrayList<String> updatePackageManager(String targetPackageNames, final int userId) {
- return updatePackageManager(Collections.singletonList(targetPackageNames), userId);
+ private void updateActivityManager(@NonNull SparseArray<List<String>> targetPackageNames) {
+ for (int i = 0, n = targetPackageNames.size(); i < n; i++) {
+ updateActivityManager(targetPackageNames.valueAt(i), targetPackageNames.keyAt(i));
+ }
+ }
+
+ @NonNull
+ private SparseArray<List<String>> updatePackageManager(@Nullable Set<PackageAndUser> targets) {
+ if (CollectionUtils.isEmpty(targets)) {
+ return new SparseArray<>();
+ }
+ final SparseArray<List<String>> affectedTargets = new SparseArray<>();
+ final SparseArray<ArraySet<String>> userTargets = groupTargetsByUserId(targets);
+ for (int i = 0, n = userTargets.size(); i < n; i++) {
+ final int userId = userTargets.keyAt(i);
+ affectedTargets.put(userId, updatePackageManager(userTargets.valueAt(i), userId));
+ }
+ return affectedTargets;
}
/**
* Updates the target packages' set of enabled overlays in PackageManager.
* @return the package names of affected targets (a superset of
- * targetPackageNames: the target themserlves and shared libraries)
+ * targetPackageNames: the target themselves and shared libraries)
*/
- private ArrayList<String> updatePackageManager(@NonNull Collection<String> targetPackageNames,
+ @NonNull
+ private List<String> updatePackageManager(@NonNull Collection<String> targetPackageNames,
final int userId) {
try {
traceBegin(TRACE_TAG_RRO, "OMS#updatePackageManager " + targetPackageNames);
@@ -1376,18 +1499,18 @@ public final class OverlayManagerService extends SystemService {
targetPackageNames = pm.getTargetPackageNames(userId);
}
- final Map<String, List<String>> pendingChanges =
+ final Map<String, OverlayPaths> pendingChanges =
new ArrayMap<>(targetPackageNames.size());
synchronized (mLock) {
- final List<String> frameworkOverlays =
- mImpl.getEnabledOverlayPackageNames("android", userId);
+ final OverlayPaths frameworkOverlays =
+ mImpl.getEnabledOverlayPaths("android", userId);
for (final String targetPackageName : targetPackageNames) {
- List<String> list = new ArrayList<>();
+ final OverlayPaths.Builder list = new OverlayPaths.Builder();
if (!"android".equals(targetPackageName)) {
list.addAll(frameworkOverlays);
}
- list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
- pendingChanges.put(targetPackageName, list);
+ list.addAll(mImpl.getEnabledOverlayPaths(targetPackageName, userId));
+ pendingChanges.put(targetPackageName, list.build());
}
}
@@ -1395,7 +1518,7 @@ public final class OverlayManagerService extends SystemService {
for (final String targetPackageName : targetPackageNames) {
if (DEBUG) {
Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
- + TextUtils.join(",", pendingChanges.get(targetPackageName))
+ + pendingChanges.get(targetPackageName)
+ "] userId=" + userId);
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index e60411bb78c5..799ab4633b2a 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -28,25 +28,31 @@ import static com.android.server.om.OverlayManagerService.TAG;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.om.CriticalOverlayInfo;
+import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
+import android.content.pm.overlay.OverlayPaths;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.os.FabricatedOverlayInfo;
+import android.os.FabricatedOverlayInternal;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.content.om.OverlayConfig;
-import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Iterator;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.function.Predicate;
/**
* Internal implementation of OverlayManagerService.
@@ -84,29 +90,42 @@ final class OverlayManagerServiceImpl {
* should either scrap the overlay manager's previous settings or merge the old
* settings with the new.
*/
- private boolean mustReinitializeOverlay(@NonNull final PackageInfo theTruth,
+ private boolean mustReinitializeOverlay(@NonNull final AndroidPackage theTruth,
@Nullable final OverlayInfo oldSettings) {
if (oldSettings == null) {
return true;
}
- if (!Objects.equals(theTruth.overlayTarget, oldSettings.targetPackageName)) {
+ if (!Objects.equals(theTruth.getOverlayTarget(), oldSettings.targetPackageName)) {
return true;
}
- if (!Objects.equals(theTruth.targetOverlayableName, oldSettings.targetOverlayableName)) {
+ if (!Objects.equals(theTruth.getOverlayTargetName(), oldSettings.targetOverlayableName)) {
return true;
}
-
- boolean isMutable = isPackageConfiguredMutable(theTruth.packageName);
+ if (oldSettings.isFabricated) {
+ return true;
+ }
+ boolean isMutable = isPackageConfiguredMutable(theTruth);
if (isMutable != oldSettings.isMutable) {
return true;
}
-
// If an immutable overlay changes its configured enabled state, reinitialize the overlay.
- if (!isMutable && isPackageConfiguredEnabled(theTruth.packageName)
- != oldSettings.isEnabled()) {
+ if (!isMutable && isPackageConfiguredEnabled(theTruth) != oldSettings.isEnabled()) {
return true;
}
+ return false;
+ }
+ private boolean mustReinitializeOverlay(@NonNull final FabricatedOverlayInfo theTruth,
+ @Nullable final OverlayInfo oldSettings) {
+ if (oldSettings == null) {
+ return true;
+ }
+ if (!Objects.equals(theTruth.targetPackageName, oldSettings.targetPackageName)) {
+ return true;
+ }
+ if (!Objects.equals(theTruth.targetOverlayable, oldSettings.targetOverlayableName)) {
+ return true;
+ }
return false;
}
@@ -128,87 +147,45 @@ final class OverlayManagerServiceImpl {
* of two sets: the set of targets with currently active overlays, and the
* set of targets that had, but no longer have, active overlays.
*/
- ArrayList<String> updateOverlaysForUser(final int newUserId) {
+ @NonNull
+ ArraySet<PackageAndUser> updateOverlaysForUser(final int newUserId) {
if (DEBUG) {
Slog.d(TAG, "updateOverlaysForUser newUserId=" + newUserId);
}
- final Set<String> packagesToUpdateAssets = new ArraySet<>();
- final ArrayMap<String, List<OverlayInfo>> tmp = mSettings.getOverlaysForUser(newUserId);
- final int tmpSize = tmp.size();
- final ArrayMap<String, OverlayInfo> storedOverlayInfos = new ArrayMap<>(tmpSize);
- for (int i = 0; i < tmpSize; i++) {
- final List<OverlayInfo> chunk = tmp.valueAt(i);
- final int chunkSize = chunk.size();
- for (int j = 0; j < chunkSize; j++) {
- final OverlayInfo oi = chunk.get(j);
- storedOverlayInfos.put(oi.packageName, oi);
- }
- }
+ // Remove the settings of all overlays that are no longer installed for this user.
+ final ArraySet<PackageAndUser> updatedTargets = new ArraySet<>();
+ final ArrayMap<String, AndroidPackage> userPackages = mPackageManager.initializeForUser(
+ newUserId);
+ CollectionUtils.addAll(updatedTargets, removeOverlaysForUser(
+ (info) -> !userPackages.containsKey(info.packageName), newUserId));
- // Reset overlays if something critical like the target package name
- // has changed
- List<PackageInfo> overlayPackages = mPackageManager.getOverlayPackages(newUserId);
- final int overlayPackagesSize = overlayPackages.size();
- for (int i = 0; i < overlayPackagesSize; i++) {
- final PackageInfo overlayPackage = overlayPackages.get(i);
- final OverlayInfo oi = storedOverlayInfos.get(overlayPackage.packageName);
-
- int priority = getPackageConfiguredPriority(overlayPackage.packageName);
- if (mustReinitializeOverlay(overlayPackage, oi)) {
- // if targetPackageName has changed the package that *used* to
- // be the target must also update its assets
- if (oi != null) {
- packagesToUpdateAssets.add(oi.targetPackageName);
- }
-
- mSettings.init(overlayPackage.packageName, newUserId,
- overlayPackage.overlayTarget,
- overlayPackage.targetOverlayableName,
- overlayPackage.applicationInfo.getBaseCodePath(),
- isPackageConfiguredMutable(overlayPackage.packageName),
- isPackageConfiguredEnabled(overlayPackage.packageName),
- priority, overlayPackage.overlayCategory);
- } else if (priority != oi.priority) {
- mSettings.setPriority(overlayPackage.packageName, newUserId, priority);
- packagesToUpdateAssets.add(oi.targetPackageName);
- }
-
- storedOverlayInfos.remove(overlayPackage.packageName);
- }
-
- // any OverlayInfo left in storedOverlayInfos is no longer
- // installed and should be removed
- final int storedOverlayInfosSize = storedOverlayInfos.size();
- for (int i = 0; i < storedOverlayInfosSize; i++) {
- final OverlayInfo oi = storedOverlayInfos.valueAt(i);
- mSettings.remove(oi.packageName, oi.userId);
- removeIdmapIfPossible(oi);
- packagesToUpdateAssets.add(oi.targetPackageName);
- }
-
- // make sure every overlay's state is up-to-date; this needs to happen
- // after old overlays have been removed, or we risk removing a
- // legitimate idmap file if a new overlay package has the same apk path
- // as the removed overlay package used to have
- for (int i = 0; i < overlayPackagesSize; i++) {
- final PackageInfo overlayPackage = overlayPackages.get(i);
+ // Update the state of all installed packages containing overlays, and initialize new
+ // overlays that are not currently in the settings.
+ for (int i = 0, n = userPackages.size(); i < n; i++) {
+ final AndroidPackage pkg = userPackages.valueAt(i);
try {
- updateState(overlayPackage.overlayTarget, overlayPackage.packageName,
- newUserId, 0);
- } catch (OverlayManagerSettings.BadKeyException e) {
- Slog.e(TAG, "failed to update settings", e);
- mSettings.remove(overlayPackage.packageName, newUserId);
+ CollectionUtils.addAll(updatedTargets,
+ updatePackageOverlays(pkg, newUserId, 0 /* flags */));
+
+ // When a new user is switched to for the first time, package manager must be
+ // informed of the overlay paths for all packages installed in the user.
+ updatedTargets.add(new PackageAndUser(pkg.getPackageName(), newUserId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "failed to initialize overlays of '" + pkg.getPackageName()
+ + "' for user " + newUserId + "", e);
}
- packagesToUpdateAssets.add(overlayPackage.overlayTarget);
}
- // remove target packages that are not installed
- final Iterator<String> iter = packagesToUpdateAssets.iterator();
- while (iter.hasNext()) {
- String targetPackageName = iter.next();
- if (mPackageManager.getPackageInfo(targetPackageName, newUserId) == null) {
- iter.remove();
+ // Update the state of all fabricated overlays, and initialize fabricated overlays in the
+ // new user.
+ for (final FabricatedOverlayInfo info : getFabricatedOverlayInfos()) {
+ try {
+ CollectionUtils.addAll(updatedTargets, registerFabricatedOverlay(
+ info, newUserId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "failed to initialize fabricated overlay of '" + info.path
+ + "' for user " + newUserId + "", e);
}
}
@@ -231,14 +208,20 @@ final class OverlayManagerServiceImpl {
// Enable the default overlay if its category does not have a single overlay enabled.
for (final String defaultOverlay : mDefaultOverlays) {
try {
- final OverlayInfo oi = mSettings.getOverlayInfo(defaultOverlay, newUserId);
+ // OverlayConfig is the new preferred way to enable overlays by default. This legacy
+ // default enabled method was created before overlays could have a name specified.
+ // Only allow enabling overlays without a name using this mechanism.
+ final OverlayIdentifier overlay = new OverlayIdentifier(defaultOverlay);
+
+ final OverlayInfo oi = mSettings.getOverlayInfo(overlay, newUserId);
if (!enabledCategories.contains(oi.category)) {
Slog.w(TAG, "Enabling default overlay '" + defaultOverlay + "' for target '"
+ oi.targetPackageName + "' in category '" + oi.category + "' for user "
+ newUserId);
- mSettings.setEnabled(oi.packageName, newUserId, true);
- if (updateState(oi.targetPackageName, oi.packageName, newUserId, 0)) {
- packagesToUpdateAssets.add(oi.targetPackageName);
+ mSettings.setEnabled(overlay, newUserId, true);
+ if (updateState(oi, newUserId, 0)) {
+ CollectionUtils.add(updatedTargets,
+ new PackageAndUser(oi.targetPackageName, oi.userId));
}
}
} catch (OverlayManagerSettings.BadKeyException e) {
@@ -247,7 +230,8 @@ final class OverlayManagerServiceImpl {
}
}
- return new ArrayList<>(packagesToUpdateAssets);
+ cleanStaleResourceCache();
+ return updatedTargets;
}
void onUserRemoved(final int userId) {
@@ -257,236 +241,155 @@ final class OverlayManagerServiceImpl {
mSettings.removeUser(userId);
}
- Optional<PackageAndUser> onTargetPackageAdded(@NonNull final String packageName,
+ @NonNull
+ Set<PackageAndUser> onPackageAdded(@NonNull final String pkgName,
final int userId) throws OperationFailedException {
- if (DEBUG) {
- Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
- }
-
- return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+ final Set<PackageAndUser> updatedTargets = new ArraySet<>();
+ // Always update the overlays of newly added packages.
+ updatedTargets.add(new PackageAndUser(pkgName, userId));
+ updatedTargets.addAll(reconcileSettingsForPackage(pkgName, userId, 0 /* flags */));
+ return updatedTargets;
}
- Optional<PackageAndUser> onTargetPackageChanged(@NonNull final String packageName,
+ @NonNull
+ Set<PackageAndUser> onPackageChanged(@NonNull final String pkgName,
final int userId) throws OperationFailedException {
- if (DEBUG) {
- Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
- }
-
- return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+ return reconcileSettingsForPackage(pkgName, userId, 0 /* flags */);
}
- Optional<PackageAndUser> onTargetPackageReplacing(@NonNull final String packageName,
- final int userId) throws OperationFailedException {
- if (DEBUG) {
- Slog.d(TAG, "onTargetPackageReplacing packageName=" + packageName + " userId="
- + userId);
- }
+ @NonNull
+ Set<PackageAndUser> onPackageReplacing(@NonNull final String pkgName, final int userId)
+ throws OperationFailedException {
+ return reconcileSettingsForPackage(pkgName, userId, FLAG_OVERLAY_IS_BEING_REPLACED);
+ }
- return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+ @NonNull
+ Set<PackageAndUser> onPackageReplaced(@NonNull final String pkgName, final int userId)
+ throws OperationFailedException {
+ return reconcileSettingsForPackage(pkgName, userId, 0 /* flags */);
}
- Optional<PackageAndUser> onTargetPackageReplaced(@NonNull final String packageName,
- final int userId) throws OperationFailedException {
+ @NonNull
+ Set<PackageAndUser> onPackageRemoved(@NonNull final String pkgName, final int userId) {
if (DEBUG) {
- Slog.d(TAG, "onTargetPackageReplaced packageName=" + packageName + " userId=" + userId);
+ Slog.d(TAG, "onPackageRemoved pkgName=" + pkgName + " userId=" + userId);
}
+ // Update the state of all overlays that target this package.
+ final Set<PackageAndUser> targets = updateOverlaysForTarget(pkgName, userId, 0 /* flags */);
- return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+ // Remove all the overlays this package declares.
+ return CollectionUtils.addAll(targets,
+ removeOverlaysForUser(oi -> pkgName.equals(oi.packageName), userId));
}
- Optional<PackageAndUser> onTargetPackageRemoved(@NonNull final String packageName,
- final int userId) throws OperationFailedException {
- if (DEBUG) {
- Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
- }
+ @NonNull
+ private Set<PackageAndUser> removeOverlaysForUser(
+ @NonNull final Predicate<OverlayInfo> condition, final int userId) {
+ final List<OverlayInfo> overlays = mSettings.removeIf(
+ io -> userId == io.userId && condition.test(io));
+ Set<PackageAndUser> targets = Collections.emptySet();
+ for (int i = 0, n = overlays.size(); i < n; i++) {
+ final OverlayInfo info = overlays.get(i);
+ targets = CollectionUtils.add(targets,
+ new PackageAndUser(info.targetPackageName, userId));
- return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+ // Remove the idmap if the overlay is no longer installed for any user.
+ removeIdmapIfPossible(info);
+ }
+ return targets;
}
- /**
- * Update the state of any overlays for this target.
- */
- private Optional<PackageAndUser> updateAndRefreshOverlaysForTarget(
- @NonNull final String targetPackageName, final int userId, final int flags)
- throws OperationFailedException {
- final List<OverlayInfo> targetOverlays = mSettings.getOverlaysForTarget(targetPackageName,
- userId);
-
- // Update the state for any overlay that targets this package.
+ @NonNull
+ private Set<PackageAndUser> updateOverlaysForTarget(@NonNull final String targetPackage,
+ final int userId, final int flags) {
boolean modified = false;
- for (final OverlayInfo oi : targetOverlays) {
- final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName,
- userId);
- if (overlayPackage == null) {
- modified |= mSettings.remove(oi.packageName, oi.userId);
- removeIdmapIfPossible(oi);
- } else {
- try {
- modified |= updateState(targetPackageName, oi.packageName, userId, flags);
- } catch (OverlayManagerSettings.BadKeyException e) {
- Slog.e(TAG, "failed to update settings", e);
- modified |= mSettings.remove(oi.packageName, userId);
- }
+ final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackage, userId);
+ for (int i = 0, n = overlays.size(); i < n; i++) {
+ final OverlayInfo oi = overlays.get(i);
+ try {
+ modified |= updateState(oi, userId, flags);
+ } catch (OverlayManagerSettings.BadKeyException e) {
+ Slog.e(TAG, "failed to update settings", e);
+ modified |= mSettings.remove(oi.getOverlayIdentifier(), userId);
}
}
-
if (!modified) {
- // Update the overlay paths of the target within package manager if necessary.
- final List<String> enabledOverlayPaths = new ArrayList<>(targetOverlays.size());
-
- // Framework overlays are first in the overlay paths of a package within PackageManager.
- for (final OverlayInfo oi : mSettings.getOverlaysForTarget("android", userId)) {
- if (oi.isEnabled()) {
- enabledOverlayPaths.add(oi.baseCodePath);
- }
- }
-
- for (final OverlayInfo oi : targetOverlays) {
- if (oi.isEnabled()) {
- enabledOverlayPaths.add(oi.baseCodePath);
- }
- }
-
- // TODO(): Use getEnabledOverlayPaths(userId, targetPackageName) instead of
- // resourceDirs if in the future resourceDirs contains APKs other than overlays
- PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, userId);
- ApplicationInfo appInfo = packageInfo == null ? null : packageInfo.applicationInfo;
- String[] resourceDirs = appInfo == null ? null : appInfo.resourceDirs;
-
- // If the lists aren't the same length, the enabled overlays have changed
- if (ArrayUtils.size(resourceDirs) != enabledOverlayPaths.size()) {
- modified = true;
- } else if (resourceDirs != null) {
- // If any element isn't equal, an overlay or the order of overlays has changed
- for (int index = 0; index < resourceDirs.length; index++) {
- if (!resourceDirs[index].equals(enabledOverlayPaths.get(index))) {
- modified = true;
- break;
- }
- }
- }
+ return Collections.emptySet();
}
-
- if (modified) {
- return Optional.of(new PackageAndUser(targetPackageName, userId));
- }
- return Optional.empty();
+ return Set.of(new PackageAndUser(targetPackage, userId));
}
- Optional<PackageAndUser> onOverlayPackageAdded(@NonNull final String packageName,
- final int userId) throws OperationFailedException {
- if (DEBUG) {
- Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId);
- }
-
- final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
- if (overlayPackage == null) {
- Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found");
- return onOverlayPackageRemoved(packageName, userId);
+ @NonNull
+ private Set<PackageAndUser> updatePackageOverlays(@NonNull AndroidPackage pkg,
+ final int userId, final int flags) throws OperationFailedException {
+ if (pkg.getOverlayTarget() == null) {
+ // This package does not have overlays declared in its manifest.
+ return Collections.emptySet();
}
- mSettings.init(packageName, userId, overlayPackage.overlayTarget,
- overlayPackage.targetOverlayableName,
- overlayPackage.applicationInfo.getBaseCodePath(),
- isPackageConfiguredMutable(overlayPackage.packageName),
- isPackageConfiguredEnabled(overlayPackage.packageName),
- getPackageConfiguredPriority(overlayPackage.packageName),
- overlayPackage.overlayCategory);
+ Set<PackageAndUser> updatedTargets = Collections.emptySet();
+ final OverlayIdentifier overlay = new OverlayIdentifier(pkg.getPackageName());
+ final int priority = getPackageConfiguredPriority(pkg);
try {
- if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) {
- return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
- }
- return Optional.empty();
- } catch (OverlayManagerSettings.BadKeyException e) {
- mSettings.remove(packageName, userId);
- throw new OperationFailedException("failed to update settings", e);
- }
- }
-
- Optional<PackageAndUser> onOverlayPackageChanged(@NonNull final String packageName,
- final int userId) throws OperationFailedException {
- if (DEBUG) {
- Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId);
- }
+ OverlayInfo currentInfo = mSettings.getNullableOverlayInfo(overlay, userId);
+ if (mustReinitializeOverlay(pkg, currentInfo)) {
+ if (currentInfo != null) {
+ // If the targetPackageName has changed, the package that *used* to
+ // be the target must also update its assets.
+ updatedTargets = CollectionUtils.add(updatedTargets,
+ new PackageAndUser(currentInfo.targetPackageName, userId));
+ }
- try {
- final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
- if (updateState(oi.targetPackageName, packageName, userId, 0)) {
- return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
+ currentInfo = mSettings.init(overlay, userId, pkg.getOverlayTarget(),
+ pkg.getOverlayTargetName(), pkg.getBaseApkPath(),
+ isPackageConfiguredMutable(pkg),
+ isPackageConfiguredEnabled(pkg),
+ getPackageConfiguredPriority(pkg), pkg.getOverlayCategory(),
+ false);
+ } else if (priority != currentInfo.priority) {
+ // Changing the priority of an overlay does not cause its settings to be
+ // reinitialized. Reorder the overlay and update its target package.
+ mSettings.setPriority(overlay, userId, priority);
+ updatedTargets = CollectionUtils.add(updatedTargets,
+ new PackageAndUser(currentInfo.targetPackageName, userId));
}
- return Optional.empty();
- } catch (OverlayManagerSettings.BadKeyException e) {
- throw new OperationFailedException("failed to update settings", e);
- }
- }
- Optional<PackageAndUser> onOverlayPackageReplacing(@NonNull final String packageName,
- final int userId) throws OperationFailedException {
- if (DEBUG) {
- Slog.d(TAG, "onOverlayPackageReplacing packageName=" + packageName + " userId="
- + userId);
- }
-
- try {
- final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
- if (updateState(oi.targetPackageName, packageName, userId,
- FLAG_OVERLAY_IS_BEING_REPLACED)) {
- removeIdmapIfPossible(oi);
- return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
+ // Update the enabled state of the overlay.
+ if (updateState(currentInfo, userId, flags)) {
+ updatedTargets = CollectionUtils.add(updatedTargets,
+ new PackageAndUser(currentInfo.targetPackageName, userId));
}
- return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
throw new OperationFailedException("failed to update settings", e);
}
+ return updatedTargets;
}
- Optional<PackageAndUser> onOverlayPackageReplaced(@NonNull final String packageName,
- final int userId) throws OperationFailedException {
+ @NonNull
+ private Set<PackageAndUser> reconcileSettingsForPackage(@NonNull final String pkgName,
+ final int userId, final int flags) throws OperationFailedException {
if (DEBUG) {
- Slog.d(TAG, "onOverlayPackageReplaced packageName=" + packageName + " userId="
- + userId);
- }
-
- final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId);
- if (pkg == null) {
- Slog.w(TAG, "overlay package " + packageName + " was replaced, but couldn't be found");
- return onOverlayPackageRemoved(packageName, userId);
+ Slog.d(TAG, "reconcileSettingsForPackage pkgName=" + pkgName + " userId=" + userId);
}
- try {
- final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId);
- if (mustReinitializeOverlay(pkg, oldOi)) {
- mSettings.init(packageName, userId, pkg.overlayTarget, pkg.targetOverlayableName,
- pkg.applicationInfo.getBaseCodePath(),
- isPackageConfiguredMutable(pkg.packageName),
- isPackageConfiguredEnabled(pkg.packageName),
- getPackageConfiguredPriority(pkg.packageName), pkg.overlayCategory);
- }
+ // Update the state of overlays that target this package.
+ Set<PackageAndUser> updatedTargets = Collections.emptySet();
+ updatedTargets = CollectionUtils.addAll(updatedTargets,
+ updateOverlaysForTarget(pkgName, userId, flags));
- if (updateState(pkg.overlayTarget, packageName, userId, 0)) {
- return Optional.of(new PackageAndUser(pkg.overlayTarget, userId));
- }
- return Optional.empty();
- } catch (OverlayManagerSettings.BadKeyException e) {
- throw new OperationFailedException("failed to update settings", e);
+ // Realign the overlay settings with PackageManager's view of the package.
+ final AndroidPackage pkg = mPackageManager.getPackageForUser(pkgName, userId);
+ if (pkg == null) {
+ return onPackageRemoved(pkgName, userId);
}
- }
- Optional<PackageAndUser> onOverlayPackageRemoved(@NonNull final String packageName,
- final int userId) throws OperationFailedException {
- try {
- final OverlayInfo overlayInfo = mSettings.getOverlayInfo(packageName, userId);
- if (mSettings.remove(packageName, userId)) {
- removeIdmapIfPossible(overlayInfo);
- return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId));
- }
- return Optional.empty();
- } catch (OverlayManagerSettings.BadKeyException e) {
- throw new OperationFailedException("failed to remove overlay", e);
- }
+ // Update the state of the overlays this package declares in its manifest.
+ updatedTargets = CollectionUtils.addAll(updatedTargets,
+ updatePackageOverlays(pkg, userId, flags));
+ return updatedTargets;
}
- OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId) {
+ OverlayInfo getOverlayInfo(@NonNull final OverlayIdentifier packageName, final int userId) {
try {
return mSettings.getOverlayInfo(packageName, userId);
} catch (OverlayManagerSettings.BadKeyException e) {
@@ -503,94 +406,78 @@ final class OverlayManagerServiceImpl {
return mSettings.getOverlaysForUser(userId);
}
- Optional<PackageAndUser> setEnabled(@NonNull final String packageName, final boolean enable,
- final int userId) throws OperationFailedException {
+ @NonNull
+ Set<PackageAndUser> setEnabled(@NonNull final OverlayIdentifier overlay,
+ final boolean enable, final int userId) throws OperationFailedException {
if (DEBUG) {
- Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
- packageName, enable, userId));
- }
-
- final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
- if (overlayPackage == null) {
- throw new OperationFailedException(
- String.format("failed to find overlay package %s for user %d",
- packageName, userId));
+ Slog.d(TAG, String.format("setEnabled overlay=%s enable=%s userId=%d",
+ overlay, enable, userId));
}
try {
- final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
+ final OverlayInfo oi = mSettings.getOverlayInfo(overlay, userId);
if (!oi.isMutable) {
// Ignore immutable overlays.
throw new OperationFailedException(
"cannot enable immutable overlay packages in runtime");
}
- boolean modified = mSettings.setEnabled(packageName, userId, enable);
- modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
+ boolean modified = mSettings.setEnabled(overlay, userId, enable);
+ modified |= updateState(oi, userId, 0);
if (modified) {
- return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
+ return Set.of(new PackageAndUser(oi.targetPackageName, userId));
}
- return Optional.empty();
+ return Set.of();
} catch (OverlayManagerSettings.BadKeyException e) {
throw new OperationFailedException("failed to update settings", e);
}
}
- Optional<PackageAndUser> setEnabledExclusive(@NonNull final String packageName,
+ Optional<PackageAndUser> setEnabledExclusive(@NonNull final OverlayIdentifier overlay,
boolean withinCategory, final int userId) throws OperationFailedException {
if (DEBUG) {
- Slog.d(TAG, String.format("setEnabledExclusive packageName=%s"
- + " withinCategory=%s userId=%d", packageName, withinCategory, userId));
- }
-
- final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
- if (overlayPackage == null) {
- throw new OperationFailedException(String.format(
- "failed to find overlay package %s for user %d", packageName, userId));
+ Slog.d(TAG, String.format("setEnabledExclusive overlay=%s"
+ + " withinCategory=%s userId=%d", overlay, withinCategory, userId));
}
try {
- final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
- final String targetPackageName = oi.targetPackageName;
+ final OverlayInfo enabledInfo = mSettings.getOverlayInfo(overlay, userId);
+ if (!enabledInfo.isMutable) {
+ throw new OperationFailedException(
+ "cannot enable immutable overlay packages in runtime");
+ }
- List<OverlayInfo> allOverlays = getOverlayInfosForTarget(targetPackageName, userId);
+ // Remove the overlay to have enabled from the list of overlays to disable.
+ List<OverlayInfo> allOverlays = getOverlayInfosForTarget(enabledInfo.targetPackageName,
+ userId);
+ allOverlays.remove(enabledInfo);
boolean modified = false;
-
- // Disable all other overlays.
- allOverlays.remove(oi);
for (int i = 0; i < allOverlays.size(); i++) {
final OverlayInfo disabledInfo = allOverlays.get(i);
- final String disabledOverlayPackageName = disabledInfo.packageName;
- final PackageInfo disabledOverlayPackageInfo = mPackageManager.getPackageInfo(
- disabledOverlayPackageName, userId);
- if (disabledOverlayPackageInfo == null) {
- modified |= mSettings.remove(disabledOverlayPackageName, userId);
- continue;
- }
-
+ final OverlayIdentifier disabledOverlay = disabledInfo.getOverlayIdentifier();
if (!disabledInfo.isMutable) {
// Don't touch immutable overlays.
continue;
}
- if (withinCategory && !Objects.equals(disabledOverlayPackageInfo.overlayCategory,
- oi.category)) {
+ if (withinCategory && !Objects.equals(disabledInfo.category,
+ enabledInfo.category)) {
// Don't touch overlays from other categories.
continue;
}
// Disable the overlay.
- modified |= mSettings.setEnabled(disabledOverlayPackageName, userId, false);
- modified |= updateState(targetPackageName, disabledOverlayPackageName, userId, 0);
+ modified |= mSettings.setEnabled(disabledOverlay, userId, false);
+ modified |= updateState(disabledInfo, userId, 0);
}
// Enable the selected overlay.
- modified |= mSettings.setEnabled(packageName, userId, true);
- modified |= updateState(targetPackageName, packageName, userId, 0);
+ modified |= mSettings.setEnabled(overlay, userId, true);
+ modified |= updateState(enabledInfo, userId, 0);
if (modified) {
- return Optional.of(new PackageAndUser(targetPackageName, userId));
+ return Optional.of(new PackageAndUser(enabledInfo.targetPackageName, userId));
}
return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
@@ -598,87 +485,200 @@ final class OverlayManagerServiceImpl {
}
}
- private boolean isPackageConfiguredMutable(@NonNull final String packageName) {
- return mOverlayConfig.isMutable(packageName);
- }
+ @NonNull
+ Set<PackageAndUser> registerFabricatedOverlay(
+ @NonNull final FabricatedOverlayInternal overlay)
+ throws OperationFailedException {
+ if (ParsingPackageUtils.validateName(overlay.overlayName,
+ false /* requireSeparator */, true /* requireFilename */) != null) {
+ throw new OperationFailedException(
+ "overlay name can only consist of alphanumeric characters, '_', and '.'");
+ }
- private int getPackageConfiguredPriority(@NonNull final String packageName) {
- return mOverlayConfig.getPriority(packageName);
- }
+ final FabricatedOverlayInfo info = mIdmapManager.createFabricatedOverlay(overlay);
+ if (info == null) {
+ throw new OperationFailedException("failed to create fabricated overlay");
+ }
- private boolean isPackageConfiguredEnabled(@NonNull final String packageName) {
- return mOverlayConfig.isEnabled(packageName);
+ final Set<PackageAndUser> updatedTargets = new ArraySet<>();
+ for (int userId : mSettings.getUsers()) {
+ updatedTargets.addAll(registerFabricatedOverlay(info, userId));
+ }
+ return updatedTargets;
}
- Optional<PackageAndUser> setPriority(@NonNull final String packageName,
- @NonNull final String newParentPackageName, final int userId)
+ @NonNull
+ private Set<PackageAndUser> registerFabricatedOverlay(
+ @NonNull final FabricatedOverlayInfo info, int userId)
throws OperationFailedException {
- if (DEBUG) {
- Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName="
- + newParentPackageName + " userId=" + userId);
+ final OverlayIdentifier overlayIdentifier = new OverlayIdentifier(
+ info.packageName, info.overlayName);
+ final Set<PackageAndUser> updatedTargets = new ArraySet<>();
+ OverlayInfo oi = mSettings.getNullableOverlayInfo(overlayIdentifier, userId);
+ if (oi != null) {
+ if (!oi.isFabricated) {
+ throw new OperationFailedException("non-fabricated overlay with name '" +
+ oi.overlayName + "' already present in '" + oi.packageName + "'");
+ }
}
-
- if (!isPackageConfiguredMutable(packageName)) {
- throw new OperationFailedException(String.format(
- "overlay package %s user %d is not updatable", packageName, userId));
+ try {
+ if (mustReinitializeOverlay(info, oi)) {
+ if (oi != null) {
+ // If the fabricated overlay changes its target package, update the previous
+ // target package so it no longer is overlaid.
+ updatedTargets.add(new PackageAndUser(oi.targetPackageName, userId));
+ }
+ oi = mSettings.init(overlayIdentifier, userId, info.targetPackageName,
+ info.targetOverlayable, info.path, true, false,
+ OverlayConfig.DEFAULT_PRIORITY, null, true);
+ } else {
+ // The only non-critical part of the info that will change is path to the fabricated
+ // overlay.
+ mSettings.setBaseCodePath(overlayIdentifier, userId, info.path);
+ }
+ if (updateState(oi, userId, 0)) {
+ updatedTargets.add(new PackageAndUser(oi.targetPackageName, userId));
+ }
+ } catch (OverlayManagerSettings.BadKeyException e) {
+ throw new OperationFailedException("failed to update settings", e);
}
- final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
- if (overlayPackage == null) {
- throw new OperationFailedException(String.format(
- "failed to find overlay package %s for user %d", packageName, userId));
- }
+ return updatedTargets;
+ }
- if (mSettings.setPriority(packageName, newParentPackageName, userId)) {
- return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
+ @NonNull
+ Set<PackageAndUser> unregisterFabricatedOverlay(@NonNull final OverlayIdentifier overlay) {
+ final Set<PackageAndUser> updatedTargets = new ArraySet<>();
+ for (int userId : mSettings.getUsers()) {
+ updatedTargets.addAll(unregisterFabricatedOverlay(overlay, userId));
}
- return Optional.empty();
+ return updatedTargets;
}
- Optional<PackageAndUser> setHighestPriority(@NonNull final String packageName,
- final int userId) throws OperationFailedException {
- if (DEBUG) {
- Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId);
+ @NonNull
+ private Set<PackageAndUser> unregisterFabricatedOverlay(
+ @NonNull final OverlayIdentifier overlay, int userId) {
+ final OverlayInfo oi = mSettings.getNullableOverlayInfo(overlay, userId);
+ if (oi != null) {
+ mSettings.remove(overlay, userId);
+ if (oi.isEnabled()) {
+ // Removing a fabricated overlay only changes the overlay path of a package if it is
+ // currently enabled.
+ return Set.of(new PackageAndUser(oi.targetPackageName, userId));
+ }
}
+ return Set.of();
+ }
- if (!isPackageConfiguredMutable(packageName)) {
- throw new OperationFailedException(String.format(
- "overlay package %s user %d is not updatable", packageName, userId));
- }
- final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
- if (overlayPackage == null) {
- throw new OperationFailedException(String.format(
- "failed to find overlay package %s for user %d", packageName, userId));
+ private void cleanStaleResourceCache() {
+ // Clean up fabricated overlays that are no longer registered in any user.
+ final Set<String> fabricatedPaths = mSettings.getAllBaseCodePaths();
+ for (final FabricatedOverlayInfo info : mIdmapManager.getFabricatedOverlayInfos()) {
+ if (!fabricatedPaths.contains(info.path)) {
+ mIdmapManager.deleteFabricatedOverlay(info.path);
+ }
}
+ }
- if (mSettings.setHighestPriority(packageName, userId)) {
- return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
- }
- return Optional.empty();
+ /**
+ * Retrieves information about the fabricated overlays still in use.
+ * @return
+ */
+ @NonNull
+ private List<FabricatedOverlayInfo> getFabricatedOverlayInfos() {
+ final Set<String> fabricatedPaths = mSettings.getAllBaseCodePaths();
+ // Filter out stale fabricated overlays.
+ final ArrayList<FabricatedOverlayInfo> infos = new ArrayList<>(
+ mIdmapManager.getFabricatedOverlayInfos());
+ infos.removeIf(info -> !fabricatedPaths.contains(info.path));
+ return infos;
+ }
+
+ private boolean isPackageConfiguredMutable(@NonNull final AndroidPackage overlay) {
+ // TODO(162841629): Support overlay name in OverlayConfig
+ return mOverlayConfig.isMutable(overlay.getPackageName());
}
- Optional<PackageAndUser> setLowestPriority(@NonNull final String packageName, final int userId)
+ private int getPackageConfiguredPriority(@NonNull final AndroidPackage overlay) {
+ // TODO(162841629): Support overlay name in OverlayConfig
+ return mOverlayConfig.getPriority(overlay.getPackageName());
+ }
+
+ private boolean isPackageConfiguredEnabled(@NonNull final AndroidPackage overlay) {
+ // TODO(162841629): Support overlay name in OverlayConfig
+ return mOverlayConfig.isEnabled(overlay.getPackageName());
+ }
+
+ Optional<PackageAndUser> setPriority(@NonNull final OverlayIdentifier overlay,
+ @NonNull final OverlayIdentifier newParentOverlay, final int userId)
throws OperationFailedException {
- if (DEBUG) {
- Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId);
- }
+ try {
+ if (DEBUG) {
+ Slog.d(TAG, "setPriority overlay=" + overlay + " newParentOverlay="
+ + newParentOverlay + " userId=" + userId);
+ }
+
+ final OverlayInfo overlayInfo = mSettings.getOverlayInfo(overlay, userId);
+ if (!overlayInfo.isMutable) {
+ // Ignore immutable overlays.
+ throw new OperationFailedException(
+ "cannot change priority of an immutable overlay package at runtime");
+ }
- if (!isPackageConfiguredMutable(packageName)) {
- throw new OperationFailedException(String.format(
- "overlay package %s user %d is not updatable", packageName, userId));
+ if (mSettings.setPriority(overlay, newParentOverlay, userId)) {
+ return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId));
+ }
+ return Optional.empty();
+ } catch (OverlayManagerSettings.BadKeyException e) {
+ throw new OperationFailedException("failed to update settings", e);
}
+ }
- final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
- if (overlayPackage == null) {
- throw new OperationFailedException(String.format(
- "failed to find overlay package %s for user %d", packageName, userId));
+ Set<PackageAndUser> setHighestPriority(@NonNull final OverlayIdentifier overlay,
+ final int userId) throws OperationFailedException {
+ try{
+ if (DEBUG) {
+ Slog.d(TAG, "setHighestPriority overlay=" + overlay + " userId=" + userId);
+ }
+
+ final OverlayInfo overlayInfo = mSettings.getOverlayInfo(overlay, userId);
+ if (!overlayInfo.isMutable) {
+ // Ignore immutable overlays.
+ throw new OperationFailedException(
+ "cannot change priority of an immutable overlay package at runtime");
+ }
+
+ if (mSettings.setHighestPriority(overlay, userId)) {
+ return Set.of(new PackageAndUser(overlayInfo.targetPackageName, userId));
+ }
+ return Set.of();
+ } catch (OverlayManagerSettings.BadKeyException e) {
+ throw new OperationFailedException("failed to update settings", e);
}
+ }
+
+ Optional<PackageAndUser> setLowestPriority(@NonNull final OverlayIdentifier overlay,
+ final int userId) throws OperationFailedException {
+ try{
+ if (DEBUG) {
+ Slog.d(TAG, "setLowestPriority packageName=" + overlay + " userId=" + userId);
+ }
+
+ final OverlayInfo overlayInfo = mSettings.getOverlayInfo(overlay, userId);
+ if (!overlayInfo.isMutable) {
+ // Ignore immutable overlays.
+ throw new OperationFailedException(
+ "cannot change priority of an immutable overlay package at runtime");
+ }
- if (mSettings.setLowestPriority(packageName, userId)) {
- return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
+ if (mSettings.setLowestPriority(overlay, userId)) {
+ return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId));
+ }
+ return Optional.empty();
+ } catch (OverlayManagerSettings.BadKeyException e) {
+ throw new OperationFailedException("failed to update settings", e);
}
- return Optional.empty();
}
void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
@@ -692,72 +692,86 @@ final class OverlayManagerServiceImpl {
return mDefaultOverlays;
}
- void removeIdmapForOverlay(String packageName, int userId) {
- final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
- removeIdmapIfPossible(oi);
+ void removeIdmapForOverlay(OverlayIdentifier overlay, int userId)
+ throws OperationFailedException {
+ try {
+ final OverlayInfo oi = mSettings.getOverlayInfo(overlay, userId);
+ removeIdmapIfPossible(oi);
+ } catch (OverlayManagerSettings.BadKeyException e) {
+ throw new OperationFailedException("failed to update settings", e);
+ }
}
- List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName,
+ OverlayPaths getEnabledOverlayPaths(@NonNull final String targetPackageName,
final int userId) {
final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
userId);
- final List<String> paths = new ArrayList<>(overlays.size());
+ final OverlayPaths.Builder paths = new OverlayPaths.Builder();
final int n = overlays.size();
for (int i = 0; i < n; i++) {
final OverlayInfo oi = overlays.get(i);
- if (oi.isEnabled()) {
- paths.add(oi.packageName);
+ if (!oi.isEnabled()) {
+ continue;
+ }
+ if (oi.isFabricated()) {
+ paths.addNonApkPath(oi.baseCodePath);
+ } else {
+ paths.addApkPath(oi.baseCodePath);
}
}
- return paths;
+ return paths.build();
}
/**
* Returns true if the settings/state was modified, false otherwise.
*/
- private boolean updateState(@NonNull final String targetPackageName,
- @NonNull final String overlayPackageName, final int userId, final int flags)
- throws OverlayManagerSettings.BadKeyException {
-
- final PackageInfo targetPackage = mPackageManager.getPackageInfo(targetPackageName, userId);
- final PackageInfo overlayPackage = mPackageManager.getPackageInfo(overlayPackageName,
- userId);
+ private boolean updateState(@NonNull final CriticalOverlayInfo info,
+ final int userId, final int flags) throws OverlayManagerSettings.BadKeyException {
+ final OverlayIdentifier overlay = info.getOverlayIdentifier();
+ final AndroidPackage targetPackage = mPackageManager.getPackageForUser(
+ info.getTargetPackageName(), userId);
+ final AndroidPackage overlayPackage = mPackageManager.getPackageForUser(
+ info.getPackageName(), userId);
- // Immutable RROs targeting to "android", ie framework-res.apk, are handled by native
- // layers.
boolean modified = false;
- if (targetPackage != null && overlayPackage != null
- && !("android".equals(targetPackageName)
- && !isPackageConfiguredMutable(overlayPackageName))) {
- modified |= mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
+ if (overlayPackage == null) {
+ removeIdmapIfPossible(mSettings.getOverlayInfo(overlay, userId));
+ return mSettings.remove(overlay, userId);
}
- if (overlayPackage != null) {
- modified |= mSettings.setBaseCodePath(overlayPackageName, userId,
- overlayPackage.applicationInfo.getBaseCodePath());
- modified |= mSettings.setCategory(overlayPackageName, userId,
- overlayPackage.overlayCategory);
+ modified |= mSettings.setCategory(overlay, userId, overlayPackage.getOverlayCategory());
+ if (!info.isFabricated()) {
+ modified |= mSettings.setBaseCodePath(overlay, userId, overlayPackage.getBaseApkPath());
}
- final @OverlayInfo.State int currentState = mSettings.getState(overlayPackageName, userId);
- final @OverlayInfo.State int newState = calculateNewState(targetPackage, overlayPackage,
+ // Immutable RROs targeting to "android", ie framework-res.apk, are handled by native
+ // layers.
+ final OverlayInfo updatedOverlayInfo = mSettings.getOverlayInfo(overlay, userId);
+ if (targetPackage != null && !("android".equals(info.getTargetPackageName())
+ && !isPackageConfiguredMutable(overlayPackage))) {
+ modified |= mIdmapManager.createIdmap(targetPackage, overlayPackage,
+ updatedOverlayInfo.baseCodePath, overlay.getOverlayName(), userId);
+ }
+
+ final @OverlayInfo.State int currentState = mSettings.getState(overlay, userId);
+ final @OverlayInfo.State int newState = calculateNewState(updatedOverlayInfo, targetPackage,
userId, flags);
if (currentState != newState) {
if (DEBUG) {
Slog.d(TAG, String.format("%s:%d: %s -> %s",
- overlayPackageName, userId,
+ overlay, userId,
OverlayInfo.stateToString(currentState),
OverlayInfo.stateToString(newState)));
}
- modified |= mSettings.setState(overlayPackageName, userId, newState);
+ modified |= mSettings.setState(overlay, userId, newState);
}
+
return modified;
}
- private @OverlayInfo.State int calculateNewState(@Nullable final PackageInfo targetPackage,
- @Nullable final PackageInfo overlayPackage, final int userId, final int flags)
+ private @OverlayInfo.State int calculateNewState(@NonNull final OverlayInfo info,
+ @Nullable final AndroidPackage targetPackage, final int userId, final int flags)
throws OverlayManagerSettings.BadKeyException {
-
if ((flags & FLAG_TARGET_IS_BEING_REPLACED) != 0) {
return STATE_TARGET_IS_BEING_REPLACED;
}
@@ -766,20 +780,15 @@ final class OverlayManagerServiceImpl {
return STATE_OVERLAY_IS_BEING_REPLACED;
}
- // assert expectation on overlay package: can only be null if the flags are used
- if (DEBUG && overlayPackage == null) {
- throw new IllegalArgumentException("null overlay package not compatible with no flags");
- }
-
if (targetPackage == null) {
return STATE_MISSING_TARGET;
}
- if (!mIdmapManager.idmapExists(overlayPackage, userId)) {
+ if (!mIdmapManager.idmapExists(info)) {
return STATE_NO_IDMAP;
}
- final boolean enabled = mSettings.getEnabled(overlayPackage.packageName, userId);
+ final boolean enabled = mSettings.getEnabled(info.getOverlayIdentifier(), userId);
return enabled ? STATE_ENABLED : STATE_DISABLED;
}
@@ -808,7 +817,7 @@ final class OverlayManagerServiceImpl {
final int[] userIds = mSettings.getUsers();
for (int userId : userIds) {
try {
- final OverlayInfo tmp = mSettings.getOverlayInfo(oi.packageName, userId);
+ final OverlayInfo tmp = mSettings.getOverlayInfo(oi.getOverlayIdentifier(), userId);
if (tmp != null && tmp.isEnabled()) {
// someone is still using the idmap file -> we cannot remove it
return;
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 0613dff31da5..e3e090663a89 100755
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -21,31 +21,32 @@ import static com.android.server.om.OverlayManagerService.TAG;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import java.util.stream.Collectors;
+import java.util.Set;
+import java.util.function.Predicate;
import java.util.stream.Stream;
/**
@@ -68,34 +69,45 @@ final class OverlayManagerSettings {
*/
private final ArrayList<SettingsItem> mItems = new ArrayList<>();
- void init(@NonNull final String packageName, final int userId,
+ @NonNull
+ OverlayInfo init(@NonNull final OverlayIdentifier overlay, final int userId,
@NonNull final String targetPackageName, @Nullable final String targetOverlayableName,
@NonNull final String baseCodePath, boolean isMutable, boolean isEnabled, int priority,
- @Nullable String overlayCategory) {
- remove(packageName, userId);
- insert(new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName,
- baseCodePath, OverlayInfo.STATE_UNKNOWN, isEnabled, isMutable, priority,
- overlayCategory));
+ @Nullable String overlayCategory, boolean isFabricated) {
+ remove(overlay, userId);
+ final SettingsItem item = new SettingsItem(overlay, userId, targetPackageName,
+ targetOverlayableName, baseCodePath, OverlayInfo.STATE_UNKNOWN, isEnabled,
+ isMutable, priority, overlayCategory, isFabricated);
+ insert(item);
+ return item.getOverlayInfo();
}
/**
* Returns true if the settings were modified, false if they remain the same.
*/
- boolean remove(@NonNull final String packageName, final int userId) {
- final int idx = select(packageName, userId);
+ boolean remove(@NonNull final OverlayIdentifier overlay, final int userId) {
+ final int idx = select(overlay, userId);
if (idx < 0) {
return false;
}
-
mItems.remove(idx);
return true;
}
- @NonNull OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId)
+ @NonNull OverlayInfo getOverlayInfo(@NonNull final OverlayIdentifier overlay, final int userId)
throws BadKeyException {
- final int idx = select(packageName, userId);
+ final int idx = select(overlay, userId);
if (idx < 0) {
- throw new BadKeyException(packageName, userId);
+ throw new BadKeyException(overlay, userId);
+ }
+ return mItems.get(idx).getOverlayInfo();
+ }
+
+ @Nullable
+ OverlayInfo getNullableOverlayInfo(@NonNull final OverlayIdentifier overlay, final int userId) {
+ final int idx = select(overlay, userId);
+ if (idx < 0) {
+ return null;
}
return mItems.get(idx).getOverlayInfo();
}
@@ -103,28 +115,29 @@ final class OverlayManagerSettings {
/**
* Returns true if the settings were modified, false if they remain the same.
*/
- boolean setBaseCodePath(@NonNull final String packageName, final int userId,
+ boolean setBaseCodePath(@NonNull final OverlayIdentifier overlay, final int userId,
@NonNull final String path) throws BadKeyException {
- final int idx = select(packageName, userId);
+ final int idx = select(overlay, userId);
if (idx < 0) {
- throw new BadKeyException(packageName, userId);
+ throw new BadKeyException(overlay, userId);
}
return mItems.get(idx).setBaseCodePath(path);
}
- boolean setCategory(@NonNull final String packageName, final int userId,
+ boolean setCategory(@NonNull final OverlayIdentifier overlay, final int userId,
@Nullable String category) throws BadKeyException {
- final int idx = select(packageName, userId);
+ final int idx = select(overlay, userId);
if (idx < 0) {
- throw new BadKeyException(packageName, userId);
+ throw new BadKeyException(overlay, userId);
}
return mItems.get(idx).setCategory(category);
}
- boolean getEnabled(@NonNull final String packageName, final int userId) throws BadKeyException {
- final int idx = select(packageName, userId);
+ boolean getEnabled(@NonNull final OverlayIdentifier overlay, final int userId)
+ throws BadKeyException {
+ final int idx = select(overlay, userId);
if (idx < 0) {
- throw new BadKeyException(packageName, userId);
+ throw new BadKeyException(overlay, userId);
}
return mItems.get(idx).isEnabled();
}
@@ -132,20 +145,20 @@ final class OverlayManagerSettings {
/**
* Returns true if the settings were modified, false if they remain the same.
*/
- boolean setEnabled(@NonNull final String packageName, final int userId, final boolean enable)
- throws BadKeyException {
- final int idx = select(packageName, userId);
+ boolean setEnabled(@NonNull final OverlayIdentifier overlay, final int userId,
+ final boolean enable) throws BadKeyException {
+ final int idx = select(overlay, userId);
if (idx < 0) {
- throw new BadKeyException(packageName, userId);
+ throw new BadKeyException(overlay, userId);
}
return mItems.get(idx).setEnabled(enable);
}
- @OverlayInfo.State int getState(@NonNull final String packageName, final int userId)
+ @OverlayInfo.State int getState(@NonNull final OverlayIdentifier overlay, final int userId)
throws BadKeyException {
- final int idx = select(packageName, userId);
+ final int idx = select(overlay, userId);
if (idx < 0) {
- throw new BadKeyException(packageName, userId);
+ throw new BadKeyException(overlay, userId);
}
return mItems.get(idx).getState();
}
@@ -153,11 +166,11 @@ final class OverlayManagerSettings {
/**
* Returns true if the settings were modified, false if they remain the same.
*/
- boolean setState(@NonNull final String packageName, final int userId,
+ boolean setState(@NonNull final OverlayIdentifier overlay, final int userId,
final @OverlayInfo.State int state) throws BadKeyException {
- final int idx = select(packageName, userId);
+ final int idx = select(overlay, userId);
if (idx < 0) {
- throw new BadKeyException(packageName, userId);
+ throw new BadKeyException(overlay, userId);
}
return mItems.get(idx).setState(state);
}
@@ -166,53 +179,82 @@ final class OverlayManagerSettings {
final int userId) {
// Immutable RROs targeting "android" are loaded from AssetManager, and so they should be
// ignored in OverlayManagerService.
- return selectWhereTarget(targetPackageName, userId)
- .filter((i) -> i.isMutable() || !"android".equals(i.getTargetPackageName()))
- .map(SettingsItem::getOverlayInfo)
- .collect(Collectors.toList());
+ final List<SettingsItem> items = selectWhereTarget(targetPackageName, userId);
+ items.removeIf(OverlayManagerSettings::isImmutableFrameworkOverlay);
+ return CollectionUtils.map(items, SettingsItem::getOverlayInfo);
}
ArrayMap<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
// Immutable RROs targeting "android" are loaded from AssetManager, and so they should be
// ignored in OverlayManagerService.
- return selectWhereUser(userId)
- .filter((i) -> i.isMutable() || !"android".equals(i.getTargetPackageName()))
- .map(SettingsItem::getOverlayInfo)
- .collect(Collectors.groupingBy(info -> info.targetPackageName, ArrayMap::new,
- Collectors.toList()));
+ final List<SettingsItem> items = selectWhereUser(userId);
+ items.removeIf(OverlayManagerSettings::isImmutableFrameworkOverlay);
+
+ final ArrayMap<String, List<OverlayInfo>> targetInfos = new ArrayMap<>();
+ for (int i = 0, n = items.size(); i < n; i++) {
+ final SettingsItem item = items.get(i);
+ targetInfos.computeIfAbsent(item.mTargetPackageName, (String) -> new ArrayList<>())
+ .add(item.getOverlayInfo());
+ }
+ return targetInfos;
+ }
+
+ Set<String> getAllBaseCodePaths() {
+ final Set<String> paths = new ArraySet<>();
+ mItems.forEach(item -> paths.add(item.mBaseCodePath));
+ return paths;
+ }
+
+ @NonNull
+ List<OverlayInfo> removeIf(@NonNull final Predicate<OverlayInfo> predicate, final int userId) {
+ return removeIf(info -> (predicate.test(info) && info.userId == userId));
+ }
+
+ @NonNull
+ List<OverlayInfo> removeIf(final @NonNull Predicate<OverlayInfo> predicate) {
+ List<OverlayInfo> removed = null;
+ for (int i = mItems.size() - 1; i >= 0; i--) {
+ final OverlayInfo info = mItems.get(i).getOverlayInfo();
+ if (predicate.test(info)) {
+ mItems.remove(i);
+ removed = CollectionUtils.add(removed, info);
+ }
+ }
+ return CollectionUtils.emptyIfNull(removed);
}
int[] getUsers() {
return mItems.stream().mapToInt(SettingsItem::getUserId).distinct().toArray();
}
+ private static boolean isImmutableFrameworkOverlay(@NonNull SettingsItem item) {
+ return !item.isMutable() && "android".equals(item.getTargetPackageName());
+ }
+
/**
* Returns true if the settings were modified, false if they remain the same.
*/
boolean removeUser(final int userId) {
- boolean removed = false;
- for (int i = 0; i < mItems.size(); i++) {
- final SettingsItem item = mItems.get(i);
+ return mItems.removeIf(item -> {
if (item.getUserId() == userId) {
if (DEBUG) {
- Slog.d(TAG, "Removing overlay " + item.mPackageName + " for user " + userId
+ Slog.d(TAG, "Removing overlay " + item.mOverlay + " for user " + userId
+ " from settings because user was removed");
}
- mItems.remove(i);
- removed = true;
- i--;
+ return true;
}
- }
- return removed;
+ return false;
+ });
}
/**
* Reassigns the priority of an overlay maintaining the values of the overlays other settings.
*/
- void setPriority(@NonNull final String packageName, final int userId, final int priority) {
- final int moveIdx = select(packageName, userId);
+ void setPriority(@NonNull final OverlayIdentifier overlay, final int userId,
+ final int priority) throws BadKeyException {
+ final int moveIdx = select(overlay, userId);
if (moveIdx < 0) {
- throw new BadKeyException(packageName, userId);
+ throw new BadKeyException(overlay, userId);
}
final SettingsItem itemToMove = mItems.get(moveIdx);
@@ -224,17 +266,17 @@ final class OverlayManagerSettings {
/**
* Returns true if the settings were modified, false if they remain the same.
*/
- boolean setPriority(@NonNull final String packageName,
- @NonNull final String newParentPackageName, final int userId) {
- if (packageName.equals(newParentPackageName)) {
+ boolean setPriority(@NonNull final OverlayIdentifier overlay,
+ @NonNull final OverlayIdentifier newOverlay, final int userId) {
+ if (overlay.equals(newOverlay)) {
return false;
}
- final int moveIdx = select(packageName, userId);
+ final int moveIdx = select(overlay, userId);
if (moveIdx < 0) {
return false;
}
- final int parentIdx = select(newParentPackageName, userId);
+ final int parentIdx = select(newOverlay, userId);
if (parentIdx < 0) {
return false;
}
@@ -248,7 +290,7 @@ final class OverlayManagerSettings {
}
mItems.remove(moveIdx);
- final int newParentIdx = select(newParentPackageName, userId) + 1;
+ final int newParentIdx = select(newOverlay, userId) + 1;
mItems.add(newParentIdx, itemToMove);
return moveIdx != newParentIdx;
}
@@ -256,8 +298,8 @@ final class OverlayManagerSettings {
/**
* Returns true if the settings were modified, false if they remain the same.
*/
- boolean setLowestPriority(@NonNull final String packageName, final int userId) {
- final int idx = select(packageName, userId);
+ boolean setLowestPriority(@NonNull final OverlayIdentifier overlay, final int userId) {
+ final int idx = select(overlay, userId);
if (idx <= 0) {
// If the item doesn't exist or is already the lowest, don't change anything.
return false;
@@ -272,8 +314,8 @@ final class OverlayManagerSettings {
/**
* Returns true if the settings were modified, false if they remain the same.
*/
- boolean setHighestPriority(@NonNull final String packageName, final int userId) {
- final int idx = select(packageName, userId);
+ boolean setHighestPriority(@NonNull final OverlayIdentifier overlay, final int userId) {
+ final int idx = select(overlay, userId);
// If the item doesn't exist or is already the highest, don't change anything.
if (idx < 0 || idx == mItems.size() - 1) {
@@ -297,7 +339,6 @@ final class OverlayManagerSettings {
break;
}
}
-
mItems.add(i + 1, item);
}
@@ -308,7 +349,12 @@ final class OverlayManagerSettings {
items = items.filter(item -> item.mUserId == dumpState.getUserId());
}
if (dumpState.getPackageName() != null) {
- items = items.filter(item -> item.mPackageName.equals(dumpState.getPackageName()));
+ items = items.filter(item -> item.mOverlay.getPackageName()
+ .equals(dumpState.getPackageName()));
+ }
+ if (dumpState.getOverlayName() != null) {
+ items = items.filter(item -> item.mOverlay.getOverlayName()
+ .equals(dumpState.getOverlayName()));
}
// display items
@@ -322,10 +368,11 @@ final class OverlayManagerSettings {
private void dumpSettingsItem(@NonNull final IndentingPrintWriter pw,
@NonNull final SettingsItem item) {
- pw.println(item.mPackageName + ":" + item.getUserId() + " {");
+ pw.println(item.mOverlay + ":" + item.getUserId() + " {");
pw.increaseIndent();
- pw.println("mPackageName...........: " + item.mPackageName);
+ pw.println("mPackageName...........: " + item.mOverlay.getPackageName());
+ pw.println("mOverlayName...........: " + item.mOverlay.getOverlayName());
pw.println("mUserId................: " + item.getUserId());
pw.println("mTargetPackageName.....: " + item.getTargetPackageName());
pw.println("mTargetOverlayableName.: " + item.getTargetOverlayableName());
@@ -335,6 +382,7 @@ final class OverlayManagerSettings {
pw.println("mIsMutable.............: " + item.isMutable());
pw.println("mPriority..............: " + item.mPriority);
pw.println("mCategory..............: " + item.mCategory);
+ pw.println("mIsFabricated..........: " + item.mIsFabricated);
pw.decreaseIndent();
pw.println("}");
@@ -344,7 +392,10 @@ final class OverlayManagerSettings {
@NonNull final SettingsItem item, @NonNull final String field) {
switch (field) {
case "packagename":
- pw.println(item.mPackageName);
+ pw.println(item.mOverlay.getPackageName());
+ break;
+ case "overlayname":
+ pw.println(item.mOverlay.getOverlayName());
break;
case "userid":
pw.println(item.mUserId);
@@ -392,6 +443,7 @@ final class OverlayManagerSettings {
private static final String ATTR_BASE_CODE_PATH = "baseCodePath";
private static final String ATTR_IS_ENABLED = "isEnabled";
private static final String ATTR_PACKAGE_NAME = "packageName";
+ private static final String ATTR_OVERLAY_NAME = "overlayName";
private static final String ATTR_STATE = "state";
private static final String ATTR_TARGET_PACKAGE_NAME = "targetPackageName";
private static final String ATTR_TARGET_OVERLAYABLE_NAME = "targetOverlayableName";
@@ -400,30 +452,26 @@ final class OverlayManagerSettings {
private static final String ATTR_CATEGORY = "category";
private static final String ATTR_USER_ID = "userId";
private static final String ATTR_VERSION = "version";
+ private static final String ATTR_IS_FABRICATED = "fabricated";
@VisibleForTesting
static final int CURRENT_VERSION = 4;
public static void restore(@NonNull final ArrayList<SettingsItem> table,
@NonNull final InputStream is) throws IOException, XmlPullParserException {
+ table.clear();
+ final TypedXmlPullParser parser = Xml.resolvePullParser(is);
+ XmlUtils.beginDocument(parser, TAG_OVERLAYS);
+ final int version = parser.getAttributeInt(null, ATTR_VERSION);
+ if (version != CURRENT_VERSION) {
+ upgrade(version);
+ }
- {
- table.clear();
- final TypedXmlPullParser parser = Xml.resolvePullParser(is);
- XmlUtils.beginDocument(parser, TAG_OVERLAYS);
- int version = parser.getAttributeInt(null, ATTR_VERSION);
- if (version != CURRENT_VERSION) {
- upgrade(version);
- }
- int depth = parser.getDepth();
-
- while (XmlUtils.nextElementWithin(parser, depth)) {
- switch (parser.getName()) {
- case TAG_ITEM:
- final SettingsItem item = restoreRow(parser, depth + 1);
- table.add(item);
- break;
- }
+ final int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (TAG_ITEM.equals(parser.getName())) {
+ final SettingsItem item = restoreRow(parser, depth + 1);
+ table.add(item);
}
}
}
@@ -447,7 +495,9 @@ final class OverlayManagerSettings {
private static SettingsItem restoreRow(@NonNull final TypedXmlPullParser parser,
final int depth) throws IOException, XmlPullParserException {
- final String packageName = XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME);
+ final OverlayIdentifier overlay = new OverlayIdentifier(
+ XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME),
+ XmlUtils.readStringAttribute(parser, ATTR_OVERLAY_NAME));
final int userId = parser.getAttributeInt(null, ATTR_USER_ID);
final String targetPackageName = XmlUtils.readStringAttribute(parser,
ATTR_TARGET_PACKAGE_NAME);
@@ -459,9 +509,11 @@ final class OverlayManagerSettings {
final boolean isStatic = parser.getAttributeBoolean(null, ATTR_IS_STATIC, false);
final int priority = parser.getAttributeInt(null, ATTR_PRIORITY);
final String category = XmlUtils.readStringAttribute(parser, ATTR_CATEGORY);
+ final boolean isFabricated = parser.getAttributeBoolean(null, ATTR_IS_FABRICATED,
+ false);
- return new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName,
- baseCodePath, state, isEnabled, !isStatic, priority, category);
+ return new SettingsItem(overlay, userId, targetPackageName, targetOverlayableName,
+ baseCodePath, state, isEnabled, !isStatic, priority, category, isFabricated);
}
public static void persist(@NonNull final ArrayList<SettingsItem> table,
@@ -484,7 +536,8 @@ final class OverlayManagerSettings {
private static void persistRow(@NonNull final TypedXmlSerializer xml,
@NonNull final SettingsItem item) throws IOException {
xml.startTag(null, TAG_ITEM);
- XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mPackageName);
+ XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mOverlay.getPackageName());
+ XmlUtils.writeStringAttribute(xml, ATTR_OVERLAY_NAME, item.mOverlay.getOverlayName());
xml.attributeInt(null, ATTR_USER_ID, item.mUserId);
XmlUtils.writeStringAttribute(xml, ATTR_TARGET_PACKAGE_NAME, item.mTargetPackageName);
XmlUtils.writeStringAttribute(xml, ATTR_TARGET_OVERLAYABLE_NAME,
@@ -495,13 +548,14 @@ final class OverlayManagerSettings {
XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, !item.mIsMutable);
xml.attributeInt(null, ATTR_PRIORITY, item.mPriority);
XmlUtils.writeStringAttribute(xml, ATTR_CATEGORY, item.mCategory);
+ XmlUtils.writeBooleanAttribute(xml, ATTR_IS_FABRICATED, item.mIsFabricated);
xml.endTag(null, TAG_ITEM);
}
}
private static final class SettingsItem {
private final int mUserId;
- private final String mPackageName;
+ private final OverlayIdentifier mOverlay;
private final String mTargetPackageName;
private final String mTargetOverlayableName;
private String mBaseCodePath;
@@ -511,13 +565,15 @@ final class OverlayManagerSettings {
private boolean mIsMutable;
private int mPriority;
private String mCategory;
+ private boolean mIsFabricated;
- SettingsItem(@NonNull final String packageName, final int userId,
+ SettingsItem(@NonNull final OverlayIdentifier overlay, final int userId,
@NonNull final String targetPackageName,
@Nullable final String targetOverlayableName, @NonNull final String baseCodePath,
final @OverlayInfo.State int state, final boolean isEnabled,
- final boolean isMutable, final int priority, @Nullable String category) {
- mPackageName = packageName;
+ final boolean isMutable, final int priority, @Nullable String category,
+ final boolean isFabricated) {
+ mOverlay = overlay;
mUserId = userId;
mTargetPackageName = targetPackageName;
mTargetOverlayableName = targetOverlayableName;
@@ -528,6 +584,7 @@ final class OverlayManagerSettings {
mCache = null;
mIsMutable = isMutable;
mPriority = priority;
+ mIsFabricated = isFabricated;
}
private String getTargetPackageName() {
@@ -596,8 +653,9 @@ final class OverlayManagerSettings {
private OverlayInfo getOverlayInfo() {
if (mCache == null) {
- mCache = new OverlayInfo(mPackageName, mTargetPackageName, mTargetOverlayableName,
- mCategory, mBaseCodePath, mState, mUserId, mPriority, mIsMutable);
+ mCache = new OverlayInfo(mOverlay.getPackageName(), mOverlay.getOverlayName(),
+ mTargetPackageName, mTargetOverlayableName, mCategory, mBaseCodePath,
+ mState, mUserId, mPriority, mIsMutable, mIsFabricated);
}
return mCache;
}
@@ -620,30 +678,40 @@ final class OverlayManagerSettings {
}
}
- private int select(@NonNull final String packageName, final int userId) {
+ private int select(@NonNull final OverlayIdentifier overlay, final int userId) {
final int n = mItems.size();
for (int i = 0; i < n; i++) {
final SettingsItem item = mItems.get(i);
- if (item.mUserId == userId && item.mPackageName.equals(packageName)) {
+ if (item.mUserId == userId && item.mOverlay.equals(overlay)) {
return i;
}
}
return -1;
}
- private Stream<SettingsItem> selectWhereUser(final int userId) {
- return mItems.stream().filter(item -> item.mUserId == userId);
+ private List<SettingsItem> selectWhereUser(final int userId) {
+ final List<SettingsItem> selectedItems = new ArrayList<>();
+ CollectionUtils.addIf(mItems, selectedItems, i -> i.mUserId == userId);
+ return selectedItems;
+ }
+
+ private List<SettingsItem> selectWhereOverlay(@NonNull final String packageName,
+ final int userId) {
+ final List<SettingsItem> items = selectWhereUser(userId);
+ items.removeIf(i -> !i.mOverlay.getPackageName().equals(packageName));
+ return items;
}
- private Stream<SettingsItem> selectWhereTarget(@NonNull final String targetPackageName,
+ private List<SettingsItem> selectWhereTarget(@NonNull final String targetPackageName,
final int userId) {
- return selectWhereUser(userId)
- .filter(item -> item.getTargetPackageName().equals(targetPackageName));
+ final List<SettingsItem> items = selectWhereUser(userId);
+ items.removeIf(i -> !i.getTargetPackageName().equals(targetPackageName));
+ return items;
}
- static final class BadKeyException extends RuntimeException {
- BadKeyException(@NonNull final String packageName, final int userId) {
- super("Bad key mPackageName=" + packageName + " mUserId=" + userId);
+ static final class BadKeyException extends Exception {
+ BadKeyException(@NonNull final OverlayIdentifier overlay, final int userId) {
+ super("Bad key '" + overlay + "' for user " + userId );
}
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index bf99bd6248cd..0b52c2e90ab9 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -19,20 +19,28 @@ package com.android.server.om;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.om.FabricatedOverlay;
import android.content.om.IOverlayManager;
+import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
+import android.content.om.OverlayManagerTransaction;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.os.Binder;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
import android.util.TypedValue;
+import com.android.internal.util.ArrayUtils;
+
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -72,6 +80,8 @@ final class OverlayManagerShellCommand extends ShellCommand {
return runSetPriority();
case "lookup":
return runLookup();
+ case "fabricate":
+ return runFabricate();
default:
return handleDefaultCommands(cmd);
}
@@ -89,35 +99,36 @@ final class OverlayManagerShellCommand extends ShellCommand {
out.println("Overlay manager (overlay) commands:");
out.println(" help");
out.println(" Print this help text.");
- out.println(" dump [--verbose] [--user USER_ID] [[FIELD] PACKAGE]");
+ out.println(" dump [--verbose] [--user USER_ID] [[FIELD] PACKAGE[:NAME]]");
out.println(" Print debugging information about the overlay manager.");
- out.println(" With optional parameter PACKAGE, limit output to the specified");
- out.println(" package. With optional parameter FIELD, limit output to");
+ out.println(" With optional parameters PACKAGE and NAME, limit output to the specified");
+ out.println(" overlay or target. With optional parameter FIELD, limit output to");
out.println(" the value of that SettingsItem field. Field names are");
out.println(" case insensitive and out.println the m prefix can be omitted,");
out.println(" so the following are equivalent: mState, mstate, State, state.");
- out.println(" list [--user USER_ID] [PACKAGE]");
+ out.println(" list [--user USER_ID] [PACKAGE[:NAME]]");
out.println(" Print information about target and overlay packages.");
out.println(" Overlay packages are printed in priority order. With optional");
- out.println(" parameter PACKAGE, limit output to the specified package.");
- out.println(" enable [--user USER_ID] PACKAGE");
- out.println(" Enable overlay package PACKAGE.");
- out.println(" disable [--user USER_ID] PACKAGE");
- out.println(" Disable overlay package PACKAGE.");
+ out.println(" parameters PACKAGE and NAME, limit output to the specified overlay or");
+ out.println(" target.");
+ out.println(" enable [--user USER_ID] PACKAGE[:NAME]");
+ out.println(" Enable overlay within or owned by PACKAGE with optional unique NAME.");
+ out.println(" disable [--user USER_ID] PACKAGE[:NAME]");
+ out.println(" Disable overlay within or owned by PACKAGE with optional unique NAME.");
out.println(" enable-exclusive [--user USER_ID] [--category] PACKAGE");
- out.println(" Enable overlay package PACKAGE and disable all other overlays for");
- out.println(" its target package. If the --category option is given, only disables");
+ out.println(" Enable overlay within or owned by PACKAGE and disable all other overlays");
+ out.println(" for its target package. If the --category option is given, only disables");
out.println(" other overlays in the same category.");
out.println(" set-priority [--user USER_ID] PACKAGE PARENT|lowest|highest");
- out.println(" Change the priority of the overlay PACKAGE to be just higher than");
- out.println(" the priority of PACKAGE_PARENT If PARENT is the special keyword");
+ out.println(" Change the priority of the overlay to be just higher than");
+ out.println(" the priority of PARENT If PARENT is the special keyword");
out.println(" 'lowest', change priority of PACKAGE to the lowest priority.");
out.println(" If PARENT is the special keyword 'highest', change priority of");
out.println(" PACKAGE to the highest priority.");
out.println(" lookup [--verbose] PACKAGE-TO-LOAD PACKAGE:TYPE/NAME");
out.println(" Load a package and print the value of a given resource");
out.println(" applying the current configuration and enabled overlays.");
- out.println(" For a more fine-grained alernative, use 'idmap2 lookup'.");
+ out.println(" For a more fine-grained alternative, use 'idmap2 lookup'.");
}
private int runList() throws RemoteException {
@@ -192,7 +203,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
status = "---";
break;
}
- out.println(String.format("%s %s", status, oi.packageName));
+ out.println(String.format("%s %s", status, oi.getOverlayIdentifier()));
}
private int runEnableDisable(final boolean enable) throws RemoteException {
@@ -211,8 +222,88 @@ final class OverlayManagerShellCommand extends ShellCommand {
}
}
- final String packageName = getNextArgRequired();
- return mInterface.setEnabled(packageName, enable, userId) ? 0 : 1;
+ final OverlayIdentifier overlay = OverlayIdentifier.fromString(getNextArgRequired());
+ mInterface.commit(new OverlayManagerTransaction.Builder()
+ .setEnabled(overlay, enable, userId)
+ .build());
+ return 0;
+ }
+
+ private int runFabricate() throws RemoteException {
+ final PrintWriter err = getErrPrintWriter();
+ if (Binder.getCallingUid() != Process.ROOT_UID) {
+ err.println("Error: must be root to fabricate overlays through the shell");
+ return 1;
+ }
+
+ int userId = UserHandle.USER_SYSTEM;
+ String targetPackage = "";
+ String targetOverlayable = "";
+ String name = "";
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "--user":
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ case "--target":
+ targetPackage = getNextArgRequired();
+ break;
+ case "--target-name":
+ targetOverlayable = getNextArgRequired();
+ break;
+ case "--name":
+ name = getNextArgRequired();
+ break;
+ default:
+ err.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ if (name.isEmpty()) {
+ err.println("Error: Missing required arg '--name'");
+ return 1;
+ }
+
+ if (targetPackage.isEmpty()) {
+ err.println("Error: Missing required arg '--target'");
+ return 1;
+ }
+
+ final String resourceName = getNextArgRequired();
+ final String typeStr = getNextArgRequired();
+ final int type;
+ if (typeStr.startsWith("0x")) {
+ type = Integer.parseUnsignedInt(typeStr.substring(2), 16);
+ } else {
+ type = Integer.parseUnsignedInt(typeStr);
+ }
+ final String dataStr = getNextArgRequired();
+ final int data;
+ if (dataStr.startsWith("0x")) {
+ data = Integer.parseUnsignedInt(dataStr.substring(2), 16);
+ } else {
+ data = Integer.parseUnsignedInt(dataStr);
+ }
+
+ final PackageManager pm = mContext.getPackageManager();
+ if (pm == null) {
+ err.println("Error: failed to get package manager");
+ return 1;
+ }
+
+ final String overlayPackageName = "com.android.shell";
+ final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
+ overlayPackageName, name, targetPackage)
+ .setTargetOverlayable(targetOverlayable)
+ .setResourceValue(resourceName, type, data)
+ .build();
+
+ mInterface.commit(new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(overlay)
+ .build());
+ return 0;
}
private int runEnableExclusive() throws RemoteException {
diff --git a/services/core/java/com/android/server/om/PackageManagerHelper.java b/services/core/java/com/android/server/om/PackageManagerHelper.java
index b1a8b4ee4d9f..750f5c3ec06b 100644
--- a/services/core/java/com/android/server/om/PackageManagerHelper.java
+++ b/services/core/java/com/android/server/om/PackageManagerHelper.java
@@ -22,10 +22,15 @@ import android.content.om.OverlayableInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Slog;
import com.android.server.pm.PackageManagerServiceUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.IOException;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -36,6 +41,33 @@ import java.util.Map;
* @hide
*/
interface PackageManagerHelper {
+
+ /**
+ * Initializes the helper for the user. This only needs to be invoked one time before
+ * packages of this user are queried.
+ * @param userId the user id to initialize
+ * @return a map of package name to all packages installed in the user
+ */
+ @NonNull
+ ArrayMap<String, AndroidPackage> initializeForUser(final int userId);
+
+ /**
+ * Retrieves the package information if it is installed for the user.
+ */
+ @Nullable
+ AndroidPackage getPackageForUser(@NonNull final String packageName, final int userId);
+
+ /**
+ * Returns whether the package is an instant app.
+ */
+ boolean isInstantApp(@NonNull final String packageName, final int userId);
+
+ /**
+ * @see PackageManager#getPackagesForUid(int)
+ */
+ @Nullable
+ String[] getPackagesForUid(int uid);
+
/**
* @return true if the target package has declared an overlayable
*/
@@ -64,11 +96,6 @@ interface PackageManagerHelper {
Map<String, Map<String, String>> getNamedActors();
/**
- * @see PackageManagerInternal#getOverlayPackages(int)
- */
- List<PackageInfo> getOverlayPackages(int userId);
-
- /**
* Read from the APK and AndroidManifest of a package to return the overlayable defined for
* a given name.
*
@@ -80,19 +107,6 @@ interface PackageManagerHelper {
throws IOException;
/**
- * @see PackageManager#getPackagesForUid(int)
- */
- @Nullable
- String[] getPackagesForUid(int uid);
-
- /**
- * @param userId user to filter package visibility by
- * @see PackageManager#getPackageInfo(String, int)
- */
- @Nullable
- PackageInfo getPackageInfo(@NonNull String packageName, int userId);
-
- /**
* @return true if {@link PackageManagerServiceUtils#compareSignatures} run on both packages
* in the system returns {@link PackageManager#SIGNATURE_MATCH}
*/
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index a83edb75badb..d95a7254efe1 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -16,24 +16,38 @@
package com.android.server.os;
+import static android.app.ApplicationExitInfo.REASON_CRASH_NATIVE;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import android.annotation.AppIdInt;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ApplicationExitInfo;
+import android.app.IParcelFileDescriptorRetriever;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.os.FileObserver;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStat;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoParseException;
import com.android.internal.annotations.GuardedBy;
import com.android.server.BootReceiver;
import com.android.server.ServiceThread;
+import com.android.server.os.TombstoneProtos.Cause;
import com.android.server.os.TombstoneProtos.Tombstone;
import libcore.io.IoUtils;
@@ -42,7 +56,11 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
/**
* A class to manage native tombstones.
@@ -75,6 +93,9 @@ public final class NativeTombstoneManager {
}
void onSystemReady() {
+ registerForUserRemoval();
+ registerForPackageRemoval();
+
// Scan existing tombstones.
mHandler.post(() -> {
final File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
@@ -94,8 +115,9 @@ public final class NativeTombstoneManager {
if (filename.endsWith(".pb")) {
handleProtoTombstone(path);
+ BootReceiver.addTombstoneToDropBox(mContext, path, true);
} else {
- BootReceiver.addTombstoneToDropBox(mContext, path);
+ BootReceiver.addTombstoneToDropBox(mContext, path, false);
}
}
@@ -145,18 +167,164 @@ public final class NativeTombstoneManager {
}
}
+ /**
+ * Remove native tombstones matching a user and/or app.
+ *
+ * @param userId user id to filter by, selects all users if empty
+ * @param appId app id to filter by, selects all users if empty
+ */
+ public void purge(Optional<Integer> userId, Optional<Integer> appId) {
+ mHandler.post(() -> {
+ synchronized (mLock) {
+ for (int i = mTombstones.size() - 1; i >= 0; --i) {
+ TombstoneFile tombstone = mTombstones.valueAt(i);
+ if (tombstone.matches(userId, appId)) {
+ tombstone.purge();
+ mTombstones.removeAt(i);
+ }
+ }
+ }
+ });
+ }
+
+ private void purgePackage(int uid, boolean allUsers) {
+ final int appId = UserHandle.getAppId(uid);
+ Optional<Integer> userId;
+ if (allUsers) {
+ userId = Optional.empty();
+ } else {
+ userId = Optional.of(UserHandle.getUserId(uid));
+ }
+ purge(userId, Optional.of(appId));
+ }
+
+ private void purgeUser(int uid) {
+ purge(Optional.of(uid), Optional.empty());
+ }
+
+ private void registerForPackageRemoval() {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+ filter.addDataScheme("package");
+ mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL);
+ if (uid == UserHandle.USER_NULL) return;
+
+ final boolean allUsers = intent.getBooleanExtra(
+ Intent.EXTRA_REMOVED_FOR_ALL_USERS, false);
+
+ purgePackage(uid, allUsers);
+ }
+ }, filter, null, mHandler);
+ }
+
+ private void registerForUserRemoval() {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userId < 1) return;
+
+ purgeUser(userId);
+ }
+ }, filter, null, mHandler);
+ }
+
+ /**
+ * Collect native tombstones.
+ *
+ * @param output list to append to
+ * @param callingUid POSIX uid to filter by
+ * @param pid pid to filter by, ignored if zero
+ * @param maxNum maximum number of elements in output
+ */
+ public void collectTombstones(ArrayList<ApplicationExitInfo> output, int callingUid, int pid,
+ int maxNum) {
+ CompletableFuture<Object> future = new CompletableFuture<>();
+
+ if (!UserHandle.isApp(callingUid)) {
+ return;
+ }
+
+ final int userId = UserHandle.getUserId(callingUid);
+ final int appId = UserHandle.getAppId(callingUid);
+
+ mHandler.post(() -> {
+ boolean appendedTombstones = false;
+
+ synchronized (mLock) {
+ final int tombstonesSize = mTombstones.size();
+
+ tombstoneIter:
+ for (int i = 0; i < tombstonesSize; ++i) {
+ TombstoneFile tombstone = mTombstones.valueAt(i);
+ if (tombstone.matches(Optional.of(userId), Optional.of(appId))) {
+ if (pid != 0 && tombstone.mPid != pid) {
+ continue;
+ }
+
+ // Try to attach to an existing REASON_CRASH_NATIVE.
+ final int outputSize = output.size();
+ for (int j = 0; j < outputSize; ++j) {
+ ApplicationExitInfo exitInfo = output.get(j);
+ if (tombstone.matches(exitInfo)) {
+ exitInfo.setNativeTombstoneRetriever(tombstone.getPfdRetriever());
+ continue tombstoneIter;
+ }
+ }
+
+ if (output.size() < maxNum) {
+ appendedTombstones = true;
+ output.add(tombstone.toAppExitInfo());
+ }
+ }
+ }
+ }
+
+ if (appendedTombstones) {
+ Collections.sort(output, (lhs, rhs) -> {
+ // Reports should be ordered with newest reports first.
+ long diff = rhs.getTimestamp() - lhs.getTimestamp();
+ if (diff < 0) {
+ return -1;
+ } else if (diff == 0) {
+ return 0;
+ } else {
+ return 1;
+ }
+ });
+ }
+ future.complete(null);
+ });
+
+ try {
+ future.get();
+ } catch (ExecutionException | InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
static class TombstoneFile {
final ParcelFileDescriptor mPfd;
- final @UserIdInt int mUserId;
- final @AppIdInt int mAppId;
+ @UserIdInt int mUserId;
+ @AppIdInt int mAppId;
+
+ int mPid;
+ int mUid;
+ String mProcessName;
+ @CurrentTimeMillisLong long mTimestampMs;
+ String mCrashReason;
boolean mPurged = false;
+ final IParcelFileDescriptorRetriever mRetriever = new ParcelFileDescriptorRetriever();
- TombstoneFile(ParcelFileDescriptor pfd, @UserIdInt int userId, @AppIdInt int appId) {
+ TombstoneFile(ParcelFileDescriptor pfd) {
mPfd = pfd;
- mUserId = userId;
- mAppId = appId;
}
public boolean matches(Optional<Integer> userId, Optional<Integer> appId) {
@@ -175,24 +343,90 @@ public final class NativeTombstoneManager {
return true;
}
+ public boolean matches(ApplicationExitInfo exitInfo) {
+ if (exitInfo.getReason() != REASON_CRASH_NATIVE) {
+ return false;
+ }
+
+ if (exitInfo.getPid() != mPid) {
+ return false;
+ }
+
+ if (exitInfo.getRealUid() != mUid) {
+ return false;
+ }
+
+ if (Math.abs(exitInfo.getTimestamp() - mTimestampMs) > 1000) {
+ return false;
+ }
+
+ return true;
+ }
+
public void dispose() {
IoUtils.closeQuietly(mPfd);
}
+ public void purge() {
+ if (!mPurged) {
+ // There's no way to atomically unlink a specific file for which we have an fd from
+ // a path, which means that we can't safely delete a tombstone without coordination
+ // with tombstoned (which has a risk of deadlock if for example, system_server hangs
+ // with a flock). Do the next best thing, and just truncate the file.
+ //
+ // We don't have to worry about inflicting a SIGBUS on a process that has the
+ // tombstone mmaped, because we only clear if the package has been removed, which
+ // means no one with access to the tombstone should be left.
+ try {
+ Os.ftruncate(mPfd.getFileDescriptor(), 0);
+ } catch (ErrnoException ex) {
+ Slog.e(TAG, "Failed to truncate tombstone", ex);
+ }
+ mPurged = true;
+ }
+ }
+
static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
final ProtoInputStream stream = new ProtoInputStream(is);
+ int pid = 0;
int uid = 0;
+ String processName = "";
+ String crashReason = "";
String selinuxLabel = "";
try {
while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
switch (stream.getFieldNumber()) {
+ case (int) Tombstone.PID:
+ pid = stream.readInt(Tombstone.PID);
+ break;
+
case (int) Tombstone.UID:
uid = stream.readInt(Tombstone.UID);
break;
+ case (int) Tombstone.PROCESS_NAME:
+ processName = stream.readString(Tombstone.PROCESS_NAME);
+ break;
+
+ case (int) Tombstone.CAUSE:
+ long token = stream.start(Tombstone.CAUSE);
+ cause:
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) Cause.HUMAN_READABLE:
+ crashReason = stream.readString(Cause.HUMAN_READABLE);
+ break cause;
+
+ default:
+ break;
+ }
+ }
+ stream.end(token);
+
+
case (int) Tombstone.SELINUX_LABEL:
selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
break;
@@ -201,7 +435,7 @@ public final class NativeTombstoneManager {
break;
}
}
- } catch (IOException ex) {
+ } catch (IOException | ProtoParseException ex) {
Slog.e(TAG, "Failed to parse tombstone", ex);
return Optional.empty();
}
@@ -211,6 +445,14 @@ public final class NativeTombstoneManager {
return Optional.empty();
}
+ long timestampMs = 0;
+ try {
+ StructStat stat = Os.fstat(pfd.getFileDescriptor());
+ timestampMs = stat.st_atim.tv_sec * 1000 + stat.st_atim.tv_nsec / 1000000;
+ } catch (ErrnoException ex) {
+ Slog.e(TAG, "Failed to get timestamp of tombstone", ex);
+ }
+
final int userId = UserHandle.getUserId(uid);
final int appId = UserHandle.getAppId(uid);
@@ -219,7 +461,74 @@ public final class NativeTombstoneManager {
return Optional.empty();
}
- return Optional.of(new TombstoneFile(pfd, userId, appId));
+ TombstoneFile result = new TombstoneFile(pfd);
+
+ result.mUserId = userId;
+ result.mAppId = appId;
+ result.mPid = pid;
+ result.mUid = uid;
+ result.mProcessName = processName;
+ result.mTimestampMs = timestampMs;
+ result.mCrashReason = crashReason;
+
+ return Optional.of(result);
+ }
+
+ public IParcelFileDescriptorRetriever getPfdRetriever() {
+ return mRetriever;
+ }
+
+ public ApplicationExitInfo toAppExitInfo() {
+ ApplicationExitInfo info = new ApplicationExitInfo();
+ info.setPid(mPid);
+ info.setRealUid(mUid);
+ info.setPackageUid(mUid);
+ info.setDefiningUid(mUid);
+ info.setProcessName(mProcessName);
+ info.setReason(ApplicationExitInfo.REASON_CRASH_NATIVE);
+
+ // Signal numbers are architecture-specific!
+ // We choose to provide nothing here, to avoid leading users astray.
+ info.setStatus(0);
+
+ // No way for us to find out.
+ info.setImportance(RunningAppProcessInfo.IMPORTANCE_GONE);
+ info.setPackageName("");
+ info.setProcessStateSummary(null);
+
+ // We could find out, but they didn't get OOM-killed...
+ info.setPss(0);
+ info.setRss(0);
+
+ info.setTimestamp(mTimestampMs);
+ info.setDescription(mCrashReason);
+
+ info.setSubReason(ApplicationExitInfo.SUBREASON_UNKNOWN);
+ info.setNativeTombstoneRetriever(mRetriever);
+
+ return info;
+ }
+
+
+ class ParcelFileDescriptorRetriever extends IParcelFileDescriptorRetriever.Stub {
+ ParcelFileDescriptorRetriever() {}
+
+ public @Nullable ParcelFileDescriptor getPfd() {
+ if (mPurged) {
+ return null;
+ }
+
+ // Reopen the file descriptor as read-only.
+ try {
+ final String path = "/proc/self/fd/" + mPfd.getFd();
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(path),
+ MODE_READ_ONLY);
+ return pfd;
+ } catch (FileNotFoundException ex) {
+ Slog.e(TAG, "failed to reopen file descriptor as read-only", ex);
+ return null;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index de85d9e25642..f31d1da84c35 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -43,6 +43,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Singleton;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -226,6 +227,12 @@ public abstract class ApexManager {
abstract ApexSessionInfo getStagedSessionInfo(int sessionId);
/**
+ * Returns array of all staged sessions known to apexd.
+ */
+ @NonNull
+ abstract SparseArray<ApexSessionInfo> getSessions();
+
+ /**
* Submit a staged session to apex service. This causes the apex service to perform some initial
* verification and accept or reject the session. Submitting a session successfully is not
* enough for it to be activated at the next boot, the caller needs to call
@@ -691,6 +698,21 @@ public abstract class ApexManager {
}
@Override
+ SparseArray<ApexSessionInfo> getSessions() {
+ try {
+ final ApexSessionInfo[] sessions = waitForApexService().getSessions();
+ final SparseArray<ApexSessionInfo> result = new SparseArray<>(sessions.length);
+ for (int i = 0; i < sessions.length; i++) {
+ result.put(sessions[i].sessionId, sessions[i]);
+ }
+ return result;
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Unable to contact apexservice", re);
+ throw new RuntimeException(re);
+ }
+ }
+
+ @Override
ApexInfoList submitStagedSession(ApexSessionParams params) throws PackageManagerException {
try {
final ApexInfoList apexInfoList = new ApexInfoList();
@@ -1083,6 +1105,11 @@ public abstract class ApexManager {
}
@Override
+ SparseArray<ApexSessionInfo> getSessions() {
+ return new SparseArray<>(0);
+ }
+
+ @Override
ApexInfoList submitStagedSession(ApexSessionParams params)
throws PackageManagerException {
throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 402f6467e82d..af0aa769f8cc 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -217,7 +217,7 @@ public class BackgroundDexOptService extends JobService {
// trade-off worth doing to save boot time work.
int result = pm.performDexOptWithStatus(new DexoptOptions(
pkg,
- PackageManagerService.REASON_BOOT,
+ PackageManagerService.REASON_POST_BOOT,
DexoptOptions.DEXOPT_BOOT_COMPLETE));
if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
updatedPackages.add(pkg);
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index 52fdc7983636..308e815d7659 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -27,6 +27,8 @@ import android.content.pm.IDataLoaderManager;
import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -45,12 +47,20 @@ import java.util.List;
public class DataLoaderManagerService extends SystemService {
private static final String TAG = "DataLoaderManager";
private final Context mContext;
+ private final HandlerThread mThread;
+ private final Handler mHandler;
private final DataLoaderManagerBinderService mBinderService;
private SparseArray<DataLoaderServiceConnection> mServiceConnections = new SparseArray<>();
public DataLoaderManagerService(Context context) {
super(context);
mContext = context;
+
+ mThread = new HandlerThread(TAG);
+ mThread.start();
+
+ mHandler = new Handler(mThread.getLooper());
+
mBinderService = new DataLoaderManagerBinderService();
}
@@ -62,7 +72,7 @@ public class DataLoaderManagerService extends SystemService {
final class DataLoaderManagerBinderService extends IDataLoaderManager.Stub {
@Override
public boolean bindToDataLoader(int dataLoaderId, DataLoaderParamsParcel params,
- IDataLoaderStatusListener listener) {
+ long bindDelayMs, IDataLoaderStatusListener listener) {
synchronized (mServiceConnections) {
if (mServiceConnections.get(dataLoaderId) != null) {
return true;
@@ -76,19 +86,21 @@ public class DataLoaderManagerService extends SystemService {
}
// Binds to the specific data loader service.
- DataLoaderServiceConnection connection = new DataLoaderServiceConnection(dataLoaderId,
- listener);
+ final DataLoaderServiceConnection connection = new DataLoaderServiceConnection(
+ dataLoaderId, listener);
- Intent intent = new Intent();
+ final Intent intent = new Intent();
intent.setComponent(dataLoaderComponent);
- if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
- UserHandle.of(UserHandle.getCallingUserId()))) {
- Slog.e(TAG,
- "Failed to bind to: " + dataLoaderComponent + " for ID=" + dataLoaderId);
- mContext.unbindService(connection);
- return false;
- }
- return true;
+
+ return mHandler.postDelayed(() -> {
+ if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
+ mHandler, UserHandle.of(UserHandle.getCallingUserId()))) {
+ Slog.e(TAG,
+ "Failed to bind to: " + dataLoaderComponent + " for ID="
+ + dataLoaderId);
+ mContext.unbindService(connection);
+ }
+ }, bindDelayMs);
}
/**
diff --git a/services/core/java/com/android/server/pm/DefaultAppProvider.java b/services/core/java/com/android/server/pm/DefaultAppProvider.java
index a17967fcc76e..c18d0e9ef35e 100644
--- a/services/core/java/com/android/server/pm/DefaultAppProvider.java
+++ b/services/core/java/com/android/server/pm/DefaultAppProvider.java
@@ -41,14 +41,18 @@ import java.util.function.Supplier;
public class DefaultAppProvider {
@NonNull
private final Supplier<RoleManager> mRoleManagerSupplier;
+ @NonNull
+ private final Supplier<UserManagerInternal> mUserManagerInternalSupplier;
/**
* Create a new instance of this class
*
* @param roleManagerSupplier the supplier for {@link RoleManager}
*/
- public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier) {
+ public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier,
+ @NonNull Supplier<UserManagerInternal> userManagerInternalSupplier) {
mRoleManagerSupplier = roleManagerSupplier;
+ mUserManagerInternalSupplier = userManagerInternalSupplier;
}
/**
@@ -132,7 +136,8 @@ public class DefaultAppProvider {
*/
@Nullable
public String getDefaultHome(@NonNull int userId) {
- return getRoleHolder(RoleManager.ROLE_HOME, userId);
+ return getRoleHolder(RoleManager.ROLE_HOME,
+ mUserManagerInternalSupplier.get().getProfileParentId(userId));
}
/**
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 15e1d5281bfa..7bf704299373 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -59,6 +59,7 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.utils.Snappable;
import com.android.server.utils.Watchable;
import com.android.server.utils.WatchableImpl;
+import com.android.server.utils.Watched;
import com.android.server.utils.WatchedSparseArray;
import com.android.server.utils.WatchedSparseBooleanArray;
import com.android.server.utils.Watcher;
@@ -123,6 +124,7 @@ class InstantAppRegistry implements Watchable, Snappable {
private final CookiePersistence mCookiePersistence;
/** State for uninstalled instant apps */
+ @Watched
@GuardedBy("mService.mLock")
private final WatchedSparseArray<List<UninstalledInstantAppState>> mUninstalledInstantApps;
@@ -132,10 +134,12 @@ class InstantAppRegistry implements Watchable, Snappable {
* The value is a set of instant app UIDs.
* UserID -> TargetAppId -> InstantAppId
*/
+ @Watched
@GuardedBy("mService.mLock")
private final WatchedSparseArray<WatchedSparseArray<WatchedSparseBooleanArray>> mInstantGrants;
/** The set of all installed instant apps. UserID -> AppID */
+ @Watched
@GuardedBy("mService.mLock")
private final WatchedSparseArray<WatchedSparseBooleanArray> mInstalledInstantAppUids;
@@ -189,6 +193,7 @@ class InstantAppRegistry implements Watchable, Snappable {
mUninstalledInstantApps.registerObserver(mObserver);
mInstantGrants.registerObserver(mObserver);
mInstalledInstantAppUids.registerObserver(mObserver);
+ Watchable.verifyWatchedAttributes(this, mObserver);
}
/**
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index f240d85572c4..d3a56c6f67c0 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -16,11 +16,13 @@
package com.android.server.pm;
+import static android.app.ActivityOptions.KEY_SPLASH_SCREEN_THEME;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.pm.LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS;
import static android.content.pm.LauncherApps.FLAG_CACHE_NOTIFICATION_SHORTCUTS;
+import static android.content.pm.LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -929,11 +931,23 @@ public class LauncherAppsService extends SystemService {
// Flag for bubble to make behaviour match documentLaunchMode=always.
intents[0].addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
intents[0].addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ intents[0].putExtra(Intent.EXTRA_IS_BUBBLED, true);
}
intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intents[0].setSourceBounds(sourceBounds);
+ // Replace theme for splash screen
+ final int splashScreenThemeResId =
+ mShortcutServiceInternal.getShortcutStartingThemeResId(getCallingUserId(),
+ callingPackage, packageName, shortcutId, targetUserId);
+ if (splashScreenThemeResId != 0) {
+ if (startActivityOptions == null) {
+ startActivityOptions = new Bundle();
+ }
+ startActivityOptions.putInt(KEY_SPLASH_SCREEN_THEME, splashScreenThemeResId);
+ }
+
return startShortcutIntentsAsPublisher(
intents, packageName, featureId, startActivityOptions, targetUserId);
}
@@ -1157,6 +1171,8 @@ public class LauncherAppsService extends SystemService {
ret = ShortcutInfo.FLAG_CACHED_NOTIFICATIONS;
} else if (cacheFlags == FLAG_CACHE_BUBBLE_SHORTCUTS) {
ret = ShortcutInfo.FLAG_CACHED_BUBBLES;
+ } else if (cacheFlags == FLAG_CACHE_PEOPLE_TILE_SHORTCUTS) {
+ ret = ShortcutInfo.FLAG_CACHED_PEOPLE_TILE;
}
Preconditions.checkArgumentPositive(ret, "Invalid cache owner");
@@ -1317,6 +1333,10 @@ public class LauncherAppsService extends SystemService {
mListeners.finishBroadcast();
}
super.onPackageAdded(packageName, uid);
+ PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ pmi.registerInstalledLoadingProgressCallback(packageName,
+ new PackageLoadingProgressCallback(packageName, user),
+ user.getIdentifier());
}
@Override
@@ -1538,5 +1558,38 @@ public class LauncherAppsService extends SystemService {
checkCallbackCount();
}
}
+
+ class PackageLoadingProgressCallback extends
+ PackageManagerInternal.InstalledLoadingProgressCallback {
+ private String mPackageName;
+ private UserHandle mUser;
+
+ PackageLoadingProgressCallback(String packageName, UserHandle user) {
+ super(mCallbackHandler);
+ mPackageName = packageName;
+ mUser = user;
+ }
+
+ @Override
+ public void onLoadingProgressChanged(float progress) {
+ final int n = mListeners.beginBroadcast();
+ try {
+ for (int i = 0; i < n; i++) {
+ IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+ BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(cookie.user, mUser, "onLoadingProgressChanged")) {
+ continue;
+ }
+ try {
+ listener.onPackageLoadingProgressChanged(mUser, mPackageName, progress);
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
+ }
+ } finally {
+ mListeners.finishBroadcast();
+ }
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 6485c0c42ab7..d851e6ced91b 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -401,11 +401,16 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
String[] abiList = (cpuAbiOverride != null)
? new String[]{cpuAbiOverride} : Build.SUPPORTED_ABIS;
- // Enable gross and lame hacks for apps that are built with old
- // SDK tools. We must scan their APKs for renderscript bitcode and
- // not launch them if it's present. Don't bother checking on devices
- // that don't have 64 bit support.
+ // If an app that contains RenderScript has target API level < 21, it needs to run
+ // with 32-bit ABI, and its APK file will contain a ".bc" file.
+ // If an app that contains RenderScript has target API level >= 21, it can run with
+ // either 32-bit or 64-bit ABI, and its APK file will not contain a ".bc" file.
+ // Therefore, on a device that supports both 32-bit and 64-bit ABIs, we scan the app
+ // APK to see if it has a ".bc" file. If so, we will run it with 32-bit ABI.
+ // However, if the device only supports 64-bit ABI but does not support 32-bit ABI,
+ // we will fail the installation for such an app because it won't be able to run.
boolean needsRenderScriptOverride = false;
+ // No need to check if the device only supports 32-bit
if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null
&& NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
@@ -414,7 +419,8 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
} else {
throw new PackageManagerException(
INSTALL_FAILED_CPU_ABI_INCOMPATIBLE,
- "Apks with renderscript are not supported on 64-bit only devices");
+ "Apps that contain RenderScript with target API level < 21 are not "
+ + "supported on 64-bit only platforms");
}
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index fc02b3439d16..b9e3e0f4450b 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -687,7 +687,8 @@ public class PackageDexOptimizer {
boolean generateCompactDex = true;
switch (compilationReason) {
case PackageManagerService.REASON_FIRST_BOOT:
- case PackageManagerService.REASON_BOOT:
+ case PackageManagerService.REASON_BOOT_AFTER_OTA:
+ case PackageManagerService.REASON_POST_BOOT:
case PackageManagerService.REASON_INSTALL:
generateCompactDex = false;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index c974bbc9daf1..44c63e236385 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -295,23 +295,29 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
synchronized (mSessions) {
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
- if (session.isStaged()) {
- stagedSessionsToRestore.add(session.mStagedSession);
+ if (!session.isStaged()) {
+ continue;
+ }
+ StagingManager.StagedSession stagedSession = session.mStagedSession;
+ if (!stagedSession.isInTerminalState() && stagedSession.hasParentSessionId()
+ && getSession(stagedSession.getParentSessionId()) == null) {
+ stagedSession.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+ "An orphan staged session " + stagedSession.sessionId() + " is found, "
+ + "parent " + stagedSession.getParentSessionId() + " is missing");
+ continue;
+ }
+ if (!stagedSession.hasParentSessionId() && stagedSession.isCommitted()
+ && !stagedSession.isInTerminalState()) {
+ // StagingManager.restoreSessions expects a list of committed, non-finalized
+ // parent staged sessions.
+ stagedSessionsToRestore.add(stagedSession);
}
}
}
- // Don't hold mSessions lock when calling restoreSession, since it might trigger an APK
+ // Don't hold mSessions lock when calling restoreSessions, since it might trigger an APK
// atomic install which needs to query sessions, which requires lock on mSessions.
- boolean isDeviceUpgrading = mPm.isDeviceUpgrading();
- for (StagingManager.StagedSession session : stagedSessionsToRestore) {
- if (!session.isInTerminalState() && session.hasParentSessionId()
- && getSession(session.getParentSessionId()) == null) {
- session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
- "An orphan staged session " + session.sessionId() + " is found, "
- + "parent " + session.getParentSessionId() + " is missing");
- }
- mStagingManager.restoreSession(session, isDeviceUpgrading);
- }
+ // Note: restoreSessions mutates content of stagedSessionsToRestore.
+ mStagingManager.restoreSessions(stagedSessionsToRestore, mPm.isDeviceUpgrading());
}
@GuardedBy("mSessions")
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 9ffaeec8eb6a..0f28b6144650 100755
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -70,6 +70,7 @@ import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageInstallerSession;
import android.content.pm.IPackageInstallerSessionFileSystemConnector;
+import android.content.pm.IPackageLoadingProgressCallback;
import android.content.pm.InstallationFile;
import android.content.pm.InstallationFileParcel;
import android.content.pm.PackageInfo;
@@ -329,6 +330,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private float mProgress = 0;
@GuardedBy("mLock")
private float mReportedProgress = -1;
+ @GuardedBy("mLock")
+ private float mIncrementalProgress = 0;
/** State of the session. */
@GuardedBy("mLock")
@@ -3795,7 +3798,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir,
inheritedDir, params, statusListener, healthCheckParams, healthListener,
- addedFiles, perUidReadTimeouts);
+ addedFiles, perUidReadTimeouts,
+ new IPackageLoadingProgressCallback.Stub() {
+ @Override
+ public void onPackageLoadingProgressChanged(float progress) {
+ synchronized (mLock) {
+ mIncrementalProgress = progress;
+ }
+ }
+ });
return false;
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
@@ -3803,7 +3814,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- if (!dataLoaderManager.bindToDataLoader(sessionId, params.getData(), statusListener)) {
+ final long bindDelayMs = 0;
+ if (!dataLoaderManager.bindToDataLoader(sessionId, params.getData(), bindDelayMs,
+ statusListener)) {
throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Failed to initialize data loader");
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index bff3659f6711..4b010c258852 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -234,6 +234,7 @@ import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.dex.IArtManager;
+import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.ParsingPackageUtils;
@@ -389,6 +390,7 @@ import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationService;
+import com.android.server.pm.verify.domain.DomainVerificationUtils;
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;
@@ -461,6 +463,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Predicate;
/**
@@ -779,17 +782,18 @@ public class PackageManagerService extends IPackageManager.Stub
// Compilation reasons.
public static final int REASON_UNKNOWN = -1;
public static final int REASON_FIRST_BOOT = 0;
- public static final int REASON_BOOT = 1;
- public static final int REASON_INSTALL = 2;
- public static final int REASON_INSTALL_FAST = 3;
- public static final int REASON_INSTALL_BULK = 4;
- public static final int REASON_INSTALL_BULK_SECONDARY = 5;
- public static final int REASON_INSTALL_BULK_DOWNGRADED = 6;
- public static final int REASON_INSTALL_BULK_SECONDARY_DOWNGRADED = 7;
- public static final int REASON_BACKGROUND_DEXOPT = 8;
- public static final int REASON_AB_OTA = 9;
- public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 10;
- public static final int REASON_SHARED = 11;
+ public static final int REASON_BOOT_AFTER_OTA = 1;
+ public static final int REASON_POST_BOOT = 2;
+ public static final int REASON_INSTALL = 3;
+ public static final int REASON_INSTALL_FAST = 4;
+ public static final int REASON_INSTALL_BULK = 5;
+ public static final int REASON_INSTALL_BULK_SECONDARY = 6;
+ public static final int REASON_INSTALL_BULK_DOWNGRADED = 7;
+ public static final int REASON_INSTALL_BULK_SECONDARY_DOWNGRADED = 8;
+ public static final int REASON_BACKGROUND_DEXOPT = 9;
+ public static final int REASON_AB_OTA = 10;
+ public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 11;
+ public static final int REASON_SHARED = 12;
public static final int REASON_LAST = REASON_SHARED;
@@ -1750,6 +1754,11 @@ public class PackageManagerService extends IPackageManager.Stub
public AndroidPackage getPackage(@NonNull String packageName) {
return getPackageLocked(packageName);
}
+
+ @Override
+ public boolean filterAppAccess(String packageName, int callingUid, int userId) {
+ return mPmInternal.filterAppAccess(packageName, callingUid, userId);
+ }
}
/**
@@ -2581,49 +2590,58 @@ public class PackageManagerService extends IPackageManager.Stub
Intent intent, int matchFlags, List<ResolveInfo> candidates,
CrossProfileDomainInfo xpDomainInfo, int userId, boolean debug) {
final ArrayList<ResolveInfo> result = new ArrayList<>();
- final ArrayList<ResolveInfo> alwaysList = new ArrayList<>();
- final ArrayList<ResolveInfo> undefinedList = new ArrayList<>();
final ArrayList<ResolveInfo> matchAllList = new ArrayList<>();
+ final ArrayList<ResolveInfo> undefinedList = new ArrayList<>();
+
final int count = candidates.size();
- // First, try to use linked apps. Partition the candidates into four lists:
- // one for the final results, one for the "do not use ever", one for "undefined status"
- // and finally one for "browser app type".
- for (int n=0; n<count; n++) {
+ // First, try to use approved apps.
+ for (int n = 0; n < count; n++) {
ResolveInfo info = candidates.get(n);
- String packageName = info.activityInfo.packageName;
- PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null) {
- // Add to the special match all list (Browser use case)
- if (info.handleAllWebDataURI) {
- matchAllList.add(info);
- continue;
- }
-
- boolean isAlways = mDomainVerificationManager
- .isApprovedForDomain(ps, intent, userId);
- if (isAlways) {
- alwaysList.add(info);
- } else {
- undefinedList.add(info);
- }
- continue;
+ // Add to the special match all list (Browser use case)
+ if (info.handleAllWebDataURI) {
+ matchAllList.add(info);
+ } else {
+ undefinedList.add(info);
}
}
// We'll want to include browser possibilities in a few cases
boolean includeBrowser = false;
- // First try to add the "always" resolution(s) for the current user, if any
- if (alwaysList.size() > 0) {
- result.addAll(alwaysList);
- } else {
- // Add all undefined apps as we want them to appear in the disambiguation dialog.
+ if (!DomainVerificationUtils.isDomainVerificationIntent(intent)) {
result.addAll(undefinedList);
// Maybe add one for the other profile.
- if (xpDomainInfo != null && xpDomainInfo.wereAnyDomainsVerificationApproved) {
+ if (xpDomainInfo != null && xpDomainInfo.highestApprovalLevel
+ > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE) {
result.add(xpDomainInfo.resolveInfo);
}
includeBrowser = true;
+ } else {
+ Pair<List<ResolveInfo>, Integer> infosAndLevel = mDomainVerificationManager
+ .filterToApprovedApp(intent, undefinedList, userId,
+ mSettings::getPackageLPr);
+ List<ResolveInfo> approvedInfos = infosAndLevel.first;
+ Integer highestApproval = infosAndLevel.second;
+
+ // If no apps are approved for the domain, resolve only to browsers
+ if (approvedInfos.isEmpty()) {
+ // If the other profile has a result, include that and delegate to
+ // ResolveActivity
+ if (xpDomainInfo != null && xpDomainInfo.highestApprovalLevel
+ > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE) {
+ result.add(xpDomainInfo.resolveInfo);
+ } else {
+ includeBrowser = true;
+ }
+ } else {
+ result.addAll(approvedInfos);
+
+ // If the other profile has an app that's of equal or higher approval, add it
+ if (xpDomainInfo != null
+ && xpDomainInfo.highestApprovalLevel >= highestApproval) {
+ result.add(xpDomainInfo.resolveInfo);
+ }
+ }
}
if (includeBrowser) {
@@ -2671,9 +2689,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- // If there is nothing selected, add all candidates and remove the ones that the
- //user
- // has explicitly put into the INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER state
+ // If there is nothing selected, add all candidates
if (result.size() == 0) {
result.addAll(candidates);
}
@@ -2775,10 +2791,12 @@ public class PackageManagerService extends IPackageManager.Stub
sourceUserId, parentUserId);
}
- result.wereAnyDomainsVerificationApproved |= mDomainVerificationManager
- .isApprovedForDomain(ps, intent, riTargetUser.targetUserId);
+ result.highestApprovalLevel = Math.max(mDomainVerificationManager
+ .approvalLevelForDomain(ps, intent, riTargetUser.targetUserId),
+ result.highestApprovalLevel);
}
- if (result != null && !result.wereAnyDomainsVerificationApproved) {
+ if (result != null && result.highestApprovalLevel
+ <= DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE) {
return null;
}
return result;
@@ -3021,9 +3039,10 @@ public class PackageManagerService extends IPackageManager.Stub
final String packageName = info.activityInfo.packageName;
final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps.getInstantApp(userId)) {
- if (mDomainVerificationManager.isApprovedForDomain(ps, intent, userId)) {
+ if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent,
+ userId)) {
if (DEBUG_INSTANT) {
- Slog.v(TAG, "Instant app approvd for intent; pkg: "
+ Slog.v(TAG, "Instant app approved for intent; pkg: "
+ packageName);
}
localInstantApp = info;
@@ -3619,8 +3638,6 @@ public class PackageManagerService extends IPackageManager.Stub
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
final int userId = UserHandle.getUserId(uid);
final int appId = UserHandle.getAppId(uid);
- enforceCrossUserPermission(callingUid, userId,
- /* requireFullPermission */ false, /* checkShell */ false, "getPackagesForUid");
return getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp);
}
@@ -3950,7 +3967,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (ps != null) {
// only check domain verification status if the app is not a browser
if (!info.handleAllWebDataURI) {
- if (mDomainVerificationManager.isApprovedForDomain(ps, intent, userId)) {
+ if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent,
+ userId)) {
if (DEBUG_INSTANT) {
Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName
+ ", approved");
@@ -5766,8 +5784,8 @@ public class PackageManagerService extends IPackageManager.Stub
(i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()),
(i, pm) -> (IncrementalManager)
i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),
- (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(
- RoleManager.class)),
+ (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class),
+ () -> LocalServices.getService(UserManagerInternal.class)),
(i, pm) -> new DisplayMetrics(),
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,
i.getDisplayMetrics(), pm.mCacheDir,
@@ -5943,6 +5961,21 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ // Link watchables to the class
+ private void registerObserver() {
+ mPackages.registerObserver(mWatcher);
+ mSharedLibraries.registerObserver(mWatcher);
+ mStaticLibsByDeclaringPackage.registerObserver(mWatcher);
+ mInstrumentation.registerObserver(mWatcher);
+ mWebInstantAppsDisabled.registerObserver(mWatcher);
+ mAppsFilter.registerObserver(mWatcher);
+ mInstantAppRegistry.registerObserver(mWatcher);
+ mSettings.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));
+ }
+
/**
* A extremely minimal constructor designed to start up a PackageManagerService instance for
* testing.
@@ -6027,15 +6060,7 @@ public class PackageManagerService extends IPackageManager.Stub
sSnapshotCorked = true;
mLiveComputer = createLiveComputer();
mSnapshotComputer = mLiveComputer;
-
- // Link up the watchers
- mPackages.registerObserver(mWatcher);
- mSharedLibraries.registerObserver(mWatcher);
- mStaticLibsByDeclaringPackage.registerObserver(mWatcher);
- mInstrumentation.registerObserver(mWatcher);
- mWebInstantAppsDisabled.registerObserver(mWatcher);
- mAppsFilter.registerObserver(mWatcher);
- Watchable.verifyWatchedAttributes(this, mWatcher);
+ registerObserver();
mPackages.putAll(testParams.packages);
mEnableFreeCacheV2 = testParams.enableFreeCacheV2;
@@ -6189,15 +6214,6 @@ public class PackageManagerService extends IPackageManager.Stub
mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
mDomainVerificationManager.setConnection(mDomainVerificationConnection);
- // Link up the watchers
- mPackages.registerObserver(mWatcher);
- mSharedLibraries.registerObserver(mWatcher);
- mStaticLibsByDeclaringPackage.registerObserver(mWatcher);
- mInstrumentation.registerObserver(mWatcher);
- mWebInstantAppsDisabled.registerObserver(mWatcher);
- mAppsFilter.registerObserver(mWatcher);
- Watchable.verifyWatchedAttributes(this, mWatcher);
-
// Create the computer as soon as the state objects have been installed. The
// cached computer is the same as the live computer until the end of the
// constructor, at which time the invalidation method updates it. The cache is
@@ -6206,6 +6222,7 @@ public class PackageManagerService extends IPackageManager.Stub
sSnapshotCorked = true;
mLiveComputer = createLiveComputer();
mSnapshotComputer = mLiveComputer;
+ registerObserver();
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
@@ -8975,7 +8992,6 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public List<String> getAllPackages() {
- enforceSystemOrRootOrShell("getAllPackages is limited to privileged callers");
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
synchronized (mLock) {
@@ -9398,8 +9414,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (ri.activityInfo.applicationInfo.isInstantApp()) {
final String packageName = ri.activityInfo.packageName;
final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null && mDomainVerificationManager
- .isApprovedForDomain(ps, intent, userId)) {
+ if (ps != null && hasAnyDomainApproval(mDomainVerificationManager, ps,
+ intent, userId)) {
return ri;
}
}
@@ -9449,6 +9465,19 @@ public class PackageManagerService extends IPackageManager.Stub
}
/**
+ * Do NOT use for intent resolution filtering. That should be done with
+ * {@link DomainVerificationManagerInternal#filterToApprovedApp(Intent, List, int, Function)}.
+ *
+ * @return if the package is approved at any non-zero level for the domain in the intent
+ */
+ private static boolean hasAnyDomainApproval(
+ @NonNull DomainVerificationManagerInternal manager, @NonNull PackageSetting pkgSetting,
+ @NonNull Intent intent, @UserIdInt int userId) {
+ return manager.approvalLevelForDomain(pkgSetting, intent, userId)
+ > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
+ }
+
+ /**
* Return true if the given list is not empty and all of its contents have
* an activityInfo with the given package name.
*/
@@ -9891,7 +9920,7 @@ public class PackageManagerService extends IPackageManager.Stub
private static class CrossProfileDomainInfo {
/* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */
ResolveInfo resolveInfo;
- boolean wereAnyDomainsVerificationApproved;
+ int highestApprovalLevel = DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
}
private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
@@ -11667,10 +11696,7 @@ public class PackageManagerService extends IPackageManager.Stub
// first boot, as they do not have profile data.
boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade;
- // We need to re-extract after a pruned cache, as AoT-ed files will be out of date.
- boolean causePrunedCache = VMRuntime.didPruneDalvikCache();
-
- if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) {
+ if (!causeUpgrade && !causeFirstBoot) {
return;
}
@@ -11687,7 +11713,7 @@ public class PackageManagerService extends IPackageManager.Stub
final long startTime = System.nanoTime();
final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
- causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
+ causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT_AFTER_OTA,
false /* bootComplete */);
final int elapsedTimeSeconds =
@@ -16192,8 +16218,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Deprecated
@Override
public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
- mDomainVerificationManager.setLegacyUserState(packageName, userId, status);
- return true;
+ return mDomainVerificationManager.setLegacyUserState(packageName, userId, status);
}
@Deprecated
@@ -18188,11 +18213,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (libPs == null) {
continue;
}
- final String[] overlayPaths = libPs.getOverlayPaths(currentUserId);
- if (overlayPaths != null) {
- ps.setOverlayPathsForLibrary(sharedLib.getName(),
- Arrays.asList(overlayPaths), currentUserId);
- }
+ ps.setOverlayPathsForLibrary(sharedLib.getName(),
+ libPs.getOverlayPaths(currentUserId), currentUserId);
}
}
}
@@ -24018,7 +24040,8 @@ public class PackageManagerService extends IPackageManager.Stub
writer.println("Domain verification status:");
writer.increaseIndent();
try {
- mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL);
+ mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL,
+ mSettings::getPackageLPr);
} catch (PackageManager.NameNotFoundException e) {
pw.println("Failure printing domain verification information");
Slog.e(TAG, "Failure printing domain verification information", e);
@@ -26004,6 +26027,17 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
+ public boolean isPackageDebuggable(String packageName) throws RemoteException {
+ int callingUser = UserHandle.getCallingUserId();
+ ApplicationInfo appInfo = getApplicationInfo(packageName, 0, callingUser);
+ if (appInfo != null) {
+ return (0 != (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE));
+ }
+
+ throw new RemoteException("Couldn't get debug flag for package " + packageName);
+ }
+
+ @Override
public boolean[] isAudioPlaybackCaptureAllowed(String[] packageNames)
throws RemoteException {
int callingUser = UserHandle.getUserId(Binder.getCallingUid());
@@ -26034,6 +26068,13 @@ public class PackageManagerService extends IPackageManager.Stub
public String getModuleMetadataPackageName() throws RemoteException {
return PackageManagerService.this.mModuleInfoProvider.getPackageName();
}
+
+ @Override
+ public boolean hasSha256SigningCertificate(String packageName, byte[] certificate)
+ throws RemoteException {
+ return PackageManagerService.this.hasSigningCertificate(
+ packageName, certificate, CERT_INPUT_SHA256);
+ }
}
private AndroidPackage getPackage(String packageName) {
@@ -26291,30 +26332,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void setKeepUninstalledPackages(final List<String> packageList) {
- Preconditions.checkNotNull(packageList);
- List<String> removedFromList = null;
- synchronized (mLock) {
- if (mKeepUninstalledPackages != null) {
- final int packagesCount = mKeepUninstalledPackages.size();
- for (int i = 0; i < packagesCount; i++) {
- String oldPackage = mKeepUninstalledPackages.get(i);
- if (packageList != null && packageList.contains(oldPackage)) {
- continue;
- }
- if (removedFromList == null) {
- removedFromList = new ArrayList<>();
- }
- removedFromList.add(oldPackage);
- }
- }
- mKeepUninstalledPackages = new ArrayList<>(packageList);
- if (removedFromList != null) {
- final int removedCount = removedFromList.size();
- for (int i = 0; i < removedCount; i++) {
- deletePackageIfUnusedLPr(removedFromList.get(i));
- }
- }
- }
+ PackageManagerService.this.setKeepUninstalledPackagesInternal(packageList);
}
@Override
@@ -26701,34 +26719,19 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName,
- @Nullable List<String> overlayPackageNames,
- @NonNull Collection<String> outUpdatedPackageNames) {
+ @Nullable OverlayPaths overlayPaths,
+ @NonNull Set<String> outUpdatedPackageNames) {
+ boolean modified = false;
synchronized (mLock) {
final AndroidPackage targetPkg = mPackages.get(targetPackageName);
if (targetPackageName == null || targetPkg == null) {
Slog.e(TAG, "failed to find package " + targetPackageName);
return false;
}
- ArrayList<String> overlayPaths = null;
- if (overlayPackageNames != null && overlayPackageNames.size() > 0) {
- final int N = overlayPackageNames.size();
- overlayPaths = new ArrayList<>(N);
- for (int i = 0; i < N; i++) {
- final String packageName = overlayPackageNames.get(i);
- final AndroidPackage pkg = mPackages.get(packageName);
- if (pkg == null) {
- Slog.e(TAG, "failed to find package " + packageName);
- return false;
- }
- overlayPaths.add(pkg.getBaseApkPath());
- }
- }
- ArraySet<String> updatedPackageNames = null;
if (targetPkg.getLibraryNames() != null) {
// Set the overlay paths for dependencies of the shared library.
- updatedPackageNames = new ArraySet<>();
- for (String libName : targetPkg.getLibraryNames()) {
+ for (final String libName : targetPkg.getLibraryNames()) {
final SharedLibraryInfo info = getSharedLibraryInfoLPr(libName,
SharedLibraryInfo.VERSION_UNDEFINED);
if (info == null) {
@@ -26739,28 +26742,30 @@ public class PackageManagerService extends IPackageManager.Stub
if (dependents == null) {
continue;
}
- for (VersionedPackage dependent : dependents) {
+ for (final VersionedPackage dependent : dependents) {
final PackageSetting ps = mSettings.getPackageLPr(
dependent.getPackageName());
if (ps == null) {
continue;
}
- ps.setOverlayPathsForLibrary(libName, overlayPaths, userId);
- updatedPackageNames.add(dependent.getPackageName());
+ if (ps.setOverlayPathsForLibrary(libName, overlayPaths, userId)) {
+ outUpdatedPackageNames.add(dependent.getPackageName());
+ modified = true;
+ }
}
}
}
final PackageSetting ps = mSettings.getPackageLPr(targetPackageName);
- ps.setOverlayPaths(overlayPaths, userId);
-
- outUpdatedPackageNames.add(targetPackageName);
- if (updatedPackageNames != null) {
- outUpdatedPackageNames.addAll(updatedPackageNames);
+ if (ps.setOverlayPaths(overlayPaths, userId)) {
+ outUpdatedPackageNames.add(targetPackageName);
+ modified = true;
}
}
- invalidatePackageInfoCache();
+ if (modified) {
+ invalidatePackageInfoCache();
+ }
return true;
}
@@ -27127,6 +27132,28 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
+ public boolean registerInstalledLoadingProgressCallback(String packageName,
+ PackageManagerInternal.InstalledLoadingProgressCallback callback, int userId) {
+ final PackageSetting ps = getPackageSettingForUser(packageName, Binder.getCallingUid(),
+ userId);
+ if (ps == null) {
+ return false;
+ }
+ if (!ps.isPackageLoading()) {
+ Slog.w(TAG,
+ "Failed registering loading progress callback. Package is fully loaded.");
+ return false;
+ }
+ if (mIncrementalManager == null) {
+ Slog.w(TAG,
+ "Failed registering loading progress callback. Incremental is not enabled");
+ return false;
+ }
+ return mIncrementalManager.registerLoadingProgressCallback(ps.getPathString(),
+ (IPackageLoadingProgressCallback) callback.getBinder());
+ }
+
+ @Override
public IncrementalStatesInfo getIncrementalStatesInfo(
@NonNull String packageName, int filterCallingUid, int userId) {
final PackageSetting ps = getPackageSettingForUser(packageName, filterCallingUid,
@@ -27803,6 +27830,43 @@ public class PackageManagerService extends IPackageManager.Stub
public DomainVerificationService.Connection getDomainVerificationConnection() {
return mDomainVerificationConnection;
}
+
+ @Override
+ public void setKeepUninstalledPackages(List<String> packageList) {
+ mContext.enforceCallingPermission(
+ Manifest.permission.KEEP_UNINSTALLED_PACKAGES,
+ "setKeepUninstalledPackages requires KEEP_UNINSTALLED_PACKAGES permission");
+ Objects.requireNonNull(packageList);
+
+ setKeepUninstalledPackagesInternal(packageList);
+ }
+
+ private void setKeepUninstalledPackagesInternal(List<String> packageList) {
+ Preconditions.checkNotNull(packageList);
+ List<String> removedFromList = null;
+ synchronized (mLock) {
+ if (mKeepUninstalledPackages != null) {
+ final int packagesCount = mKeepUninstalledPackages.size();
+ for (int i = 0; i < packagesCount; i++) {
+ String oldPackage = mKeepUninstalledPackages.get(i);
+ if (packageList != null && packageList.contains(oldPackage)) {
+ continue;
+ }
+ if (removedFromList == null) {
+ removedFromList = new ArrayList<>();
+ }
+ removedFromList.add(oldPackage);
+ }
+ }
+ mKeepUninstalledPackages = new ArrayList<>(packageList);
+ if (removedFromList != null) {
+ final int removedCount = removedFromList.size();
+ for (int i = 0; i < removedCount; i++) {
+ deletePackageIfUnusedLPr(removedFromList.get(i));
+ }
+ }
+ }
+ }
}
interface PackageSender {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 9cd55a6bb07e..636db111be88 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -29,7 +29,8 @@ public class PackageManagerServiceCompilerMapping {
// Names for compilation reasons.
public static final String REASON_STRINGS[] = {
"first-boot",
- "boot",
+ "boot-after-ota",
+ "post-boot",
"install",
"install-fast",
"install-bulk",
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 3a142837e063..a83a3f81bc00 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -31,6 +31,7 @@ import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.Signature;
import android.content.pm.SuspendDialogInfo;
+import android.content.pm.overlay.OverlayPaths;
import android.os.PersistableBundle;
import android.os.incremental.IncrementalManager;
import android.service.pm.PackageProto;
@@ -44,7 +45,6 @@ import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.File;
import java.util.Arrays;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -327,21 +327,20 @@ public abstract class PackageSettingBase extends SettingBase {
modifyUserState(userId).uninstallReason = uninstallReason;
}
- void setOverlayPaths(List<String> overlayPaths, int userId) {
- modifyUserState(userId).setOverlayPaths(overlayPaths == null ? null :
- overlayPaths.toArray(new String[overlayPaths.size()]));
+ boolean setOverlayPaths(OverlayPaths overlayPaths, int userId) {
+ return modifyUserState(userId).setOverlayPaths(overlayPaths);
}
- String[] getOverlayPaths(int userId) {
+ OverlayPaths getOverlayPaths(int userId) {
return readUserState(userId).getOverlayPaths();
}
- void setOverlayPathsForLibrary(String libName, List<String> overlayPaths, int userId) {
- modifyUserState(userId).setSharedLibraryOverlayPaths(libName,
- overlayPaths == null ? null : overlayPaths.toArray(new String[0]));
+ boolean setOverlayPathsForLibrary(String libName, OverlayPaths overlayPaths,
+ int userId) {
+ return modifyUserState(userId).setSharedLibraryOverlayPaths(libName, overlayPaths);
}
- Map<String, String[]> getOverlayPathsForLibrary(int userId) {
+ Map<String, OverlayPaths> getOverlayPathsForLibrary(int userId) {
return readUserState(userId).getSharedLibraryOverlayPaths();
}
@@ -784,6 +783,10 @@ public abstract class PackageSettingBase extends SettingBase {
incrementalStates.onStorageHealthStatusChanged(status);
}
+ public long getFirstInstallTime() {
+ return firstInstallTime;
+ }
+
protected PackageSettingBase updateFrom(PackageSettingBase other) {
super.copyFrom(other);
setPath(other.getPath());
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index fb033e6594b8..2112247650a5 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -40,6 +40,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.overlay.OverlayPaths;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageUserState;
@@ -49,6 +50,7 @@ import android.content.pm.Signature;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.component.ParsedComponent;
import android.content.pm.parsing.component.ParsedIntentInfo;
@@ -70,7 +72,6 @@ import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.incremental.IncrementalManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.service.pm.PackageServiceDumpProto;
@@ -105,9 +106,6 @@ import com.android.permission.persistence.RuntimePermissionsState;
import com.android.server.LocalServices;
import com.android.server.backup.PreferredActivityBackupHelper;
import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.verify.domain.DomainVerificationLegacySettings;
-import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
-import com.android.server.pm.verify.domain.DomainVerificationPersistence;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
@@ -115,6 +113,9 @@ import com.android.server.pm.permission.LegacyPermissionDataProvider;
import com.android.server.pm.permission.LegacyPermissionSettings;
import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.pm.permission.LegacyPermissionState.PermissionState;
+import com.android.server.pm.verify.domain.DomainVerificationLegacySettings;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationPersistence;
import com.android.server.utils.Snappable;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.utils.Watchable;
@@ -487,7 +488,7 @@ public final class Settings implements Watchable, Snappable {
// App-link priority tracking, per-user
@NonNull
@Watched
- final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray();
+ private final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray();
final StringBuilder mReadMessages = new StringBuilder();
@@ -552,6 +553,7 @@ public final class Settings implements Watchable, Snappable {
mAppIds.registerObserver(mObserver);
mOtherAppIds.registerObserver(mObserver);
mRenamedPackages.registerObserver(mObserver);
+ mNextAppLinkGeneration.registerObserver(mObserver);
mDefaultBrowserApp.registerObserver(mObserver);
Watchable.verifyWatchedAttributes(this, mObserver);
@@ -602,6 +604,7 @@ public final class Settings implements Watchable, Snappable {
mAppIds.registerObserver(mObserver);
mOtherAppIds.registerObserver(mObserver);
mRenamedPackages.registerObserver(mObserver);
+ mNextAppLinkGeneration.registerObserver(mObserver);
mDefaultBrowserApp.registerObserver(mObserver);
Watchable.verifyWatchedAttributes(this, mObserver);
@@ -649,6 +652,7 @@ public final class Settings implements Watchable, Snappable {
mPastSignatures.addAll(r.mPastSignatures);
mKeySetRefs.putAll(r.mKeySetRefs);
mRenamedPackages.snapshot(r.mRenamedPackages);
+ mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration);
mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp);
// mReadMessages
mPendingPackages.addAll(r.mPendingPackages);
@@ -2610,6 +2614,8 @@ public final class Settings implements Watchable, Snappable {
} else {
serializer.attributeInt(null, "sharedUserId", pkg.appId);
}
+ serializer.attributeFloat(null, "loadingProgress",
+ pkg.getIncrementalStates().getProgress());
writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
@@ -2707,7 +2713,6 @@ public final class Settings implements Watchable, Snappable {
writeSigningKeySetLPr(serializer, pkg.keySetData);
writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
writeKeySetAliasesLPr(serializer, pkg.keySetData);
- mDomainVerificationManager.writeLegacySettings(serializer, pkg.name);
writeMimeGroupLPr(serializer, pkg.mimeGroups);
serializer.endTag(null, "package");
@@ -3385,6 +3390,9 @@ public final class Settings implements Watchable, Snappable {
if (ps.appId <= 0) {
ps.appId = parser.getAttributeInt(null, "sharedUserId", 0);
}
+ final float loadingProgress =
+ parser.getAttributeFloat(null, "loadingProgress", 0);
+ ps.setLoadingProgress(loadingProgress);
int outerDepth = parser.getDepth();
int type;
@@ -4578,7 +4586,7 @@ public final class Settings implements Watchable, Snappable {
pw.print(prefix); pw.print(" installerAttributionTag=");
pw.println(ps.installSource.installerAttributionTag);
}
- if (IncrementalManager.isIncrementalPath(ps.getPathString())) {
+ if (ps.isPackageLoading()) {
pw.print(prefix); pw.println(" loadingProgress="
+ (int) (ps.getIncrementalStates().getProgress() * 100) + "%");
}
@@ -4686,26 +4694,58 @@ public final class Settings implements Watchable, Snappable {
}
}
- String[] overlayPaths = ps.getOverlayPaths(user.id);
- if (overlayPaths != null && overlayPaths.length > 0) {
- pw.print(prefix); pw.println(" overlay paths:");
- for (String path : overlayPaths) {
- pw.print(prefix); pw.print(" "); pw.println(path);
+ final OverlayPaths overlayPaths = ps.getOverlayPaths(user.id);
+ if (overlayPaths != null) {
+ if (!overlayPaths.getOverlayPaths().isEmpty()) {
+ pw.print(prefix);
+ pw.println(" overlay paths:");
+ for (String path : overlayPaths.getOverlayPaths()) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.println(path);
+ }
+ }
+ if (!overlayPaths.getResourceDirs().isEmpty()) {
+ pw.print(prefix);
+ pw.println(" legacy overlay paths:");
+ for (String path : overlayPaths.getResourceDirs()) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.println(path);
+ }
}
}
- Map<String, String[]> sharedLibraryOverlayPaths =
+ final Map<String, OverlayPaths> sharedLibraryOverlayPaths =
ps.getOverlayPathsForLibrary(user.id);
if (sharedLibraryOverlayPaths != null) {
- for (Map.Entry<String, String[]> libOverlayPaths :
+ for (Map.Entry<String, OverlayPaths> libOverlayPaths :
sharedLibraryOverlayPaths.entrySet()) {
- if (libOverlayPaths.getValue() == null) {
+ final OverlayPaths paths = libOverlayPaths.getValue();
+ if (paths == null) {
continue;
}
- pw.print(prefix); pw.print(" ");
- pw.print(libOverlayPaths.getKey()); pw.println(" overlay paths:");
- for (String path : libOverlayPaths.getValue()) {
- pw.print(prefix); pw.print(" "); pw.println(path);
+ if (!paths.getOverlayPaths().isEmpty()) {
+ pw.print(prefix);
+ pw.println(" ");
+ pw.print(libOverlayPaths.getKey());
+ pw.println(" overlay paths:");
+ for (String path : paths.getOverlayPaths()) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.println(path);
+ }
+ }
+ if (!paths.getResourceDirs().isEmpty()) {
+ pw.print(prefix);
+ pw.println(" ");
+ pw.print(libOverlayPaths.getKey());
+ pw.println(" legacy overlay paths:");
+ for (String path : paths.getResourceDirs()) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.println(path);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/SettingsXml.java b/services/core/java/com/android/server/pm/SettingsXml.java
index 9588a279ecec..ec643f598041 100644
--- a/services/core/java/com/android/server/pm/SettingsXml.java
+++ b/services/core/java/com/android/server/pm/SettingsXml.java
@@ -181,7 +181,10 @@ public class SettingsXml {
}
private void moveToFirstTag() throws IOException, XmlPullParserException {
- // Move to first tag
+ if (mParser.getEventType() == XmlPullParser.START_TAG) {
+ return;
+ }
+
int type;
//noinspection StatementWithEmptyBody
while ((type = mParser.next()) != XmlPullParser.START_TAG
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 9b092c000172..bb4ec16be0a8 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -112,6 +112,7 @@ class ShortcutPackage extends ShortcutPackageItem {
private static final String ATTR_BITMAP_PATH = "bitmap-path";
private static final String ATTR_ICON_URI = "icon-uri";
private static final String ATTR_LOCUS_ID = "locus-id";
+ private static final String ATTR_SPLASH_SCREEN_THEME_ID = "splash-screen-theme-id";
private static final String ATTR_PERSON_NAME = "name";
private static final String ATTR_PERSON_URI = "uri";
@@ -1654,6 +1655,7 @@ class ShortcutPackage extends ShortcutPackageItem {
ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle());
ShortcutService.writeAttr(out, ATTR_TITLE_RES_ID, si.getTitleResId());
ShortcutService.writeAttr(out, ATTR_TITLE_RES_NAME, si.getTitleResName());
+ ShortcutService.writeAttr(out, ATTR_SPLASH_SCREEN_THEME_ID, si.getStartingThemeResId());
ShortcutService.writeAttr(out, ATTR_TEXT, si.getText());
ShortcutService.writeAttr(out, ATTR_TEXT_RES_ID, si.getTextResId());
ShortcutService.writeAttr(out, ATTR_TEXT_RES_NAME, si.getTextResName());
@@ -1861,6 +1863,7 @@ class ShortcutPackage extends ShortcutPackageItem {
String bitmapPath;
String iconUri;
final String locusIdString;
+ int splashScreenThemeResId;
int backupVersionCode;
ArraySet<String> categories = null;
ArrayList<Person> persons = new ArrayList<>();
@@ -1871,6 +1874,8 @@ class ShortcutPackage extends ShortcutPackageItem {
title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE);
titleResId = ShortcutService.parseIntAttribute(parser, ATTR_TITLE_RES_ID);
titleResName = ShortcutService.parseStringAttribute(parser, ATTR_TITLE_RES_NAME);
+ splashScreenThemeResId = ShortcutService.parseIntAttribute(parser,
+ ATTR_SPLASH_SCREEN_THEME_ID);
text = ShortcutService.parseStringAttribute(parser, ATTR_TEXT);
textResId = ShortcutService.parseIntAttribute(parser, ATTR_TEXT_RES_ID);
textResName = ShortcutService.parseStringAttribute(parser, ATTR_TEXT_RES_NAME);
@@ -1964,7 +1969,8 @@ class ShortcutPackage extends ShortcutPackageItem {
intents.toArray(new Intent[intents.size()]),
rank, extras, lastChangedTimestamp, flags,
iconResId, iconResName, bitmapPath, iconUri,
- disabledReason, persons.toArray(new Person[persons.size()]), locusId);
+ disabledReason, persons.toArray(new Person[persons.size()]), locusId,
+ splashScreenThemeResId);
}
private static Intent parseIntent(TypedXmlPullParser parser)
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index d3aace19672a..c06f01a463ad 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -383,6 +383,8 @@ public class ShortcutParser {
final int textResId = sa.getResourceId(R.styleable.Shortcut_shortcutLongLabel, 0);
final int disabledMessageResId = sa.getResourceId(
R.styleable.Shortcut_shortcutDisabledMessage, 0);
+ final int splashScreenTheme = sa.getResourceId(
+ R.styleable.Shortcut_splashScreenTheme, 0);
if (TextUtils.isEmpty(id)) {
Log.w(TAG, "android:shortcutId must be provided. activity=" + activity);
@@ -404,7 +406,8 @@ public class ShortcutParser {
disabledMessageResId,
rank,
iconResId,
- enabled);
+ enabled,
+ splashScreenTheme);
} finally {
sa.recycle();
}
@@ -413,7 +416,7 @@ public class ShortcutParser {
private static ShortcutInfo createShortcutFromManifest(ShortcutService service,
@UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
int titleResId, int textResId, int disabledMessageResId,
- int rank, int iconResId, boolean enabled) {
+ int rank, int iconResId, boolean enabled, int splashScreenTheme) {
final int flags =
(enabled ? ShortcutInfo.FLAG_MANIFEST : ShortcutInfo.FLAG_DISABLED)
@@ -452,7 +455,8 @@ public class ShortcutParser {
null, // icon Url
disabledReason,
null /* persons */,
- null /* locusId */);
+ null /* locusId */,
+ splashScreenTheme);
}
private static String parseCategory(ShortcutService service, AttributeSet attrs) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 532e78c0f5a2..4d8abea8acd4 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -40,6 +40,7 @@ import android.content.IntentSender.SendIntentException;
import android.content.LocusId;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
import android.content.pm.IPackageManager;
import android.content.pm.IShortcutService;
import android.content.pm.LauncherApps;
@@ -3213,6 +3214,32 @@ public class ShortcutService extends IShortcutService.Stub {
}
@Override
+ public int getShortcutStartingThemeResId(int launcherUserId,
+ @NonNull String callingPackage, @NonNull String packageName,
+ @NonNull String shortcutId, int userId) {
+ Objects.requireNonNull(callingPackage, "callingPackage");
+ Objects.requireNonNull(packageName, "packageName");
+ Objects.requireNonNull(shortcutId, "shortcutId");
+
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
+ getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+ .attemptToRestoreIfNeededAndSave();
+
+ final ShortcutPackage p = getUserShortcutsLocked(userId)
+ .getPackageShortcutsIfExists(packageName);
+ if (p == null) {
+ return 0;
+ }
+
+ final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
+ return shortcutInfo != null ? shortcutInfo.getStartingThemeResId() : 0;
+ }
+ }
+
+ @Override
public ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName,
@NonNull String shortcutId, int userId) {
@@ -4619,6 +4646,9 @@ public class ShortcutService extends IShortcutService.Stub {
case "verify-states": // hidden command to verify various internal states.
handleVerifyStates();
break;
+ case "has-shortcut-access":
+ handleHasShortcutAccess();
+ break;
default:
return handleDefaultCommands(cmd);
}
@@ -4650,7 +4680,7 @@ public class ShortcutService extends IShortcutService.Stub {
pw.println("[Deprecated] cmd shortcut get-default-launcher [--user USER_ID]");
pw.println(" Show the default launcher");
pw.println(" Note: This command is deprecated. Callers should query the default"
- + " launcher directly from RoleManager instead.");
+ + " launcher from RoleManager instead.");
pw.println();
pw.println("cmd shortcut unload-user [--user USER_ID]");
pw.println(" Unload a user from the memory");
@@ -4662,6 +4692,10 @@ public class ShortcutService extends IShortcutService.Stub {
pw.println("cmd shortcut get-shortcuts [--user USER_ID] [--flags FLAGS] PACKAGE");
pw.println(" Show the shortcuts for a package that match the given flags");
pw.println();
+ pw.println("cmd shortcut has-shortcut-access [--user USER_ID] PACKAGE");
+ pw.println(" Prints \"true\" if the package can access shortcuts,"
+ + " \"false\" otherwise");
+ pw.println();
}
private void handleResetThrottling() throws CommandException {
@@ -4706,11 +4740,24 @@ public class ShortcutService extends IShortcutService.Stub {
private void handleGetDefaultLauncher() throws CommandException {
synchronized (mLock) {
parseOptionsLocked(/* takeUser =*/ true);
+
+ final String defaultLauncher = getDefaultLauncher(mUserId);
+ if (defaultLauncher == null) {
+ throw new CommandException(
+ "Failed to get the default launcher for user " + mUserId);
+ }
+
+ // Get the class name of the component from PM to keep the old behaviour.
final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
- // Default launcher from package manager.
- final ComponentName defaultLauncher = mPackageManagerInternal
- .getHomeActivitiesAsUser(allHomeCandidates, getParentOrSelfUserId(mUserId));
- getOutPrintWriter().println("Launcher: " + defaultLauncher);
+ mPackageManagerInternal.getHomeActivitiesAsUser(allHomeCandidates,
+ getParentOrSelfUserId(mUserId));
+ for (ResolveInfo ri : allHomeCandidates) {
+ final ComponentInfo ci = ri.getComponentInfo();
+ if (ci.packageName.equals(defaultLauncher)) {
+ getOutPrintWriter().println("Launcher: " + ci.getComponentName());
+ break;
+ }
+ }
}
}
@@ -4761,6 +4808,16 @@ public class ShortcutService extends IShortcutService.Stub {
throw new CommandException(th.getMessage() + "\n" + Log.getStackTraceString(th));
}
}
+
+ private void handleHasShortcutAccess() throws CommandException {
+ synchronized (mLock) {
+ parseOptionsLocked(/* takeUser =*/ true);
+ final String packageName = getNextArgRequired();
+
+ boolean shortcutAccess = hasShortcutHostPermissionInner(packageName, mUserId);
+ getOutPrintWriter().println(Boolean.toString(shortcutAccess));
+ }
+ }
}
// === Unit test support ===
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 545567c26972..0a74032ab214 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -50,8 +50,6 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.os.storage.IStorageManager;
-import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.IntArray;
@@ -63,6 +61,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageHelper;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
@@ -143,10 +142,16 @@ public class StagingManager {
}
StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier) {
+ this(context, packageParserSupplier, ApexManager.getInstance());
+ }
+
+ @VisibleForTesting
+ StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier,
+ ApexManager apexManager) {
mContext = context;
mPackageParserSupplier = packageParserSupplier;
- mApexManager = ApexManager.getInstance();
+ mApexManager = apexManager;
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mPreRebootVerificationHandler = new PreRebootVerificationHandler(
BackgroundThread.get().getLooper());
@@ -354,11 +359,11 @@ public class StagingManager {
}
// Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device.
- private void abortCheckpoint(int sessionId, String errorMsg) {
- String failureReason = "Failed to install sessionId: " + sessionId + " Error: " + errorMsg;
+ private void abortCheckpoint(String failureReason, boolean supportsCheckpoint,
+ boolean needsCheckpoint) {
Slog.e(TAG, failureReason);
try {
- if (supportsCheckpoint() && needsCheckpoint()) {
+ if (supportsCheckpoint && needsCheckpoint) {
// Store failure reason for next reboot
try (BufferedWriter writer =
new BufferedWriter(new FileWriter(mFailureReasonFile))) {
@@ -371,8 +376,9 @@ public class StagingManager {
if (mApexManager.isApexSupported()) {
mApexManager.revertActiveSessions();
}
+
PackageHelper.getStorageManager().abortChanges(
- "StagingManager initiated", false /*retry*/);
+ "abort-staged-install", false /*retry*/);
}
} catch (Exception e) {
Slog.wtf(TAG, "Failed to abort checkpoint", e);
@@ -384,14 +390,6 @@ public class StagingManager {
}
}
- private boolean supportsCheckpoint() throws RemoteException {
- return PackageHelper.getStorageManager().supportsCheckpoint();
- }
-
- private boolean needsCheckpoint() throws RemoteException {
- return PackageHelper.getStorageManager().needsCheckpoint();
- }
-
/**
* Utility function for extracting apex sessions out of multi-package/single session.
*/
@@ -517,96 +515,31 @@ public class StagingManager {
}
}
- private void resumeSession(@NonNull StagedSession session)
- throws PackageManagerException {
+ private void resumeSession(@NonNull StagedSession session, boolean supportsCheckpoint,
+ boolean needsCheckpoint) throws PackageManagerException {
Slog.d(TAG, "Resuming session " + session.sessionId());
final boolean hasApex = session.containsApexSession();
- ApexSessionInfo apexSessionInfo = null;
- if (hasApex) {
- // Check with apexservice whether the apex packages have been activated.
- apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId());
-
- // Prepare for logging a native crash during boot, if one occurred.
- if (apexSessionInfo != null && !TextUtils.isEmpty(
- apexSessionInfo.crashingNativeProcess)) {
- prepareForLoggingApexdRevert(session, apexSessionInfo.crashingNativeProcess);
- }
-
- if (apexSessionInfo != null && apexSessionInfo.isVerified) {
- // Session has been previously submitted to apexd, but didn't complete all the
- // pre-reboot verification, perhaps because the device rebooted in the meantime.
- // Greedily re-trigger the pre-reboot verification. We want to avoid marking it as
- // failed when not in checkpoint mode, hence it is being processed separately.
- Slog.d(TAG, "Found pending staged session " + session.sessionId() + " still to "
- + "be verified, resuming pre-reboot verification");
- mPreRebootVerificationHandler.startPreRebootVerification(session);
- return;
- }
- }
// Before we resume session, we check if revert is needed or not. Typically, we enter file-
// system checkpoint mode when we reboot first time in order to install staged sessions. We
// want to install staged sessions in this mode as rebooting now will revert user data. If
// something goes wrong, then we reboot again to enter fs-rollback mode. Rebooting now will
// have no effect on user data, so mark the sessions as failed instead.
- try {
- // If checkpoint is supported, then we only resume sessions if we are in checkpointing
- // mode. If not, we fail all sessions.
- if (supportsCheckpoint() && !needsCheckpoint()) {
- String revertMsg = "Reverting back to safe state. Marking "
- + session.sessionId() + " as failed.";
- final String reasonForRevert = getReasonForRevert();
- if (!TextUtils.isEmpty(reasonForRevert)) {
- revertMsg += " Reason for revert: " + reasonForRevert;
- }
- Slog.d(TAG, revertMsg);
- session.setSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, revertMsg);
- return;
+ // If checkpoint is supported, then we only resume sessions if we are in checkpointing mode.
+ // If not, we fail all sessions.
+ if (supportsCheckpoint && !needsCheckpoint) {
+ String revertMsg = "Reverting back to safe state. Marking " + session.sessionId()
+ + " as failed.";
+ final String reasonForRevert = getReasonForRevert();
+ if (!TextUtils.isEmpty(reasonForRevert)) {
+ revertMsg += " Reason for revert: " + reasonForRevert;
}
- } catch (RemoteException e) {
- // Cannot continue staged install without knowing if fs-checkpoint is supported
- Slog.e(TAG, "Checkpoint support unknown. Aborting staged install for session "
- + session.sessionId(), e);
- // TODO: Mark all staged sessions together and reboot only once
- session.setSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
- "Checkpoint support unknown. Aborting staged install.");
- if (hasApex) {
- mApexManager.revertActiveSessions();
- }
- mPowerManager.reboot("Checkpoint support unknown");
+ Slog.d(TAG, revertMsg);
+ session.setSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, revertMsg);
return;
}
- // Check if apex packages in the session failed to activate
- if (hasApex) {
- if (apexSessionInfo == null) {
- final String errorMsg = "apexd did not know anything about a staged session "
- + "supposed to be activated";
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
- }
- if (isApexSessionFailed(apexSessionInfo)) {
- String errorMsg = "APEX activation failed. Check logcat messages from apexd "
- + "for more information.";
- if (!TextUtils.isEmpty(mNativeFailureReason)) {
- errorMsg = "Session reverted due to crashing native process: "
- + mNativeFailureReason;
- }
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
- }
- if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) {
- // Apexd did not apply the session for some unknown reason. There is no
- // guarantee that apexd will install it next time. Safer to proactively mark
- // it as failed.
- final String errorMsg = "Staged session " + session.sessionId() + "at boot "
- + "didn't activate nor fail. Marking it as failed anyway.";
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
- }
- }
-
// Handle apk and apk-in-apex installation
if (hasApex) {
checkInstallationOfApkInApexSuccessful(session);
@@ -622,28 +555,24 @@ public class StagingManager {
Slog.d(TAG, "Marking session " + session.sessionId() + " as applied");
session.setSessionApplied();
if (hasApex) {
- try {
- if (supportsCheckpoint()) {
- // Store the session ID, which will be marked as successful by ApexManager
- // upon boot completion.
- synchronized (mSuccessfulStagedSessionIds) {
- mSuccessfulStagedSessionIds.add(session.sessionId());
- }
- } else {
- // Mark sessions as successful immediately on non-checkpointing devices.
- mApexManager.markStagedSessionSuccessful(session.sessionId());
+ if (supportsCheckpoint) {
+ // Store the session ID, which will be marked as successful by ApexManager upon
+ // boot completion.
+ synchronized (mSuccessfulStagedSessionIds) {
+ mSuccessfulStagedSessionIds.add(session.sessionId());
}
- } catch (RemoteException e) {
- Slog.w(TAG, "Checkpoint support unknown, marking session as successful "
- + "immediately.");
+ } else {
+ // Mark sessions as successful immediately on non-checkpointing devices.
mApexManager.markStagedSessionSuccessful(session.sessionId());
}
}
}
- void onInstallationFailure(StagedSession session, PackageManagerException e) {
+ void onInstallationFailure(StagedSession session, PackageManagerException e,
+ boolean supportsCheckpoint, boolean needsCheckpoint) {
session.setSessionFailed(e.error, e.getMessage());
- abortCheckpoint(session.sessionId(), e.getMessage());
+ abortCheckpoint("Failed to install sessionId: " + session.sessionId()
+ + " Error: " + e.getMessage(), supportsCheckpoint, needsCheckpoint);
// If checkpoint is not supported, we have to handle failure for one staged session.
if (!session.containsApexSession()) {
@@ -767,8 +696,13 @@ public class StagingManager {
"Cannot stage session " + session.sessionId() + " with package name null");
}
- boolean supportsCheckpoint = ((StorageManager) mContext.getSystemService(
- Context.STORAGE_SERVICE)).isCheckpointSupported();
+ boolean supportsCheckpoint;
+ try {
+ supportsCheckpoint = PackageHelper.getStorageManager().supportsCheckpoint();
+ } catch (RemoteException e) {
+ throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Can't query fs-checkpoint status : " + e);
+ }
final boolean isRollback = isRollback(session);
@@ -911,60 +845,166 @@ public class StagingManager {
|| apexSessionInfo.isRevertFailed;
}
- void restoreSession(@NonNull StagedSession session, boolean isDeviceUpgrading) {
- if (session.hasParentSessionId()) {
- // Only parent sessions can be restored
- return;
- }
- // Store this parent session which will be used to check overlapping later
- createSession(session);
- // The preconditions used during pre-reboot verification might have changed when device
- // is upgrading. Updated staged sessions to activation failed before we resume the session.
- StagedSession sessionToResume = session;
- if (isDeviceUpgrading && !sessionToResume.isInTerminalState()) {
- sessionToResume.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
- "Build fingerprint has changed");
- return;
+ private void handleNonReadyAndDestroyedSessions(List<StagedSession> sessions) {
+ int j = sessions.size();
+ for (int i = 0; i < j; ) {
+ // Maintain following invariant:
+ // * elements at positions [0, i) should be kept
+ // * elements at positions [j, n) should be remove.
+ // * n = sessions.size()
+ StagedSession session = sessions.get(i);
+ if (session.isDestroyed()) {
+ // Device rebooted before abandoned session was cleaned up.
+ session.abandon();
+ StagedSession session2 = sessions.set(j - 1, session);
+ sessions.set(i, session2);
+ j--;
+ } else if (!session.isSessionReady()) {
+ // The framework got restarted before the pre-reboot verification could complete,
+ // restart the verification.
+ mPreRebootVerificationHandler.startPreRebootVerification(session);
+ StagedSession session2 = sessions.set(j - 1, session);
+ sessions.set(i, session2);
+ j--;
+ } else {
+ i++;
+ }
}
- checkStateAndResume(sessionToResume);
+ // Delete last j elements.
+ sessions.subList(j, sessions.size()).clear();
}
- private void checkStateAndResume(@NonNull StagedSession session) {
- // Do not resume session if boot completed already
+ void restoreSessions(@NonNull List<StagedSession> sessions, boolean isDeviceUpgrading) {
+ // Do not resume sessions if boot completed already
if (SystemProperties.getBoolean("sys.boot_completed", false)) {
return;
}
- if (!session.isCommitted()) {
- // Session hasn't been committed yet, ignore.
+ for (int i = 0; i < sessions.size(); i++) {
+ StagedSession session = sessions.get(i);
+ // Quick check that PackageInstallerService gave us sessions we expected.
+ Preconditions.checkArgument(!session.hasParentSessionId(),
+ session.sessionId() + " is a child session");
+ Preconditions.checkArgument(session.isCommitted(),
+ session.sessionId() + " is not committed");
+ Preconditions.checkArgument(!session.isInTerminalState(),
+ session.sessionId() + " is in terminal state");
+ // Store this parent session which will be used to check overlapping later
+ createSession(session);
+ }
+
+ if (isDeviceUpgrading) {
+ // TODO(ioffe): check that corresponding apex sessions are failed.
+ // The preconditions used during pre-reboot verification might have changed when device
+ // is upgrading. Fail all the sessions and exit early.
+ for (int i = 0; i < sessions.size(); i++) {
+ StagedSession session = sessions.get(i);
+ session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+ "Build fingerprint has changed");
+ }
return;
}
- // Check the state of the session and decide what to do next.
- if (session.isSessionFailed() || session.isSessionApplied()) {
- // Final states, nothing to do.
+
+ boolean needsCheckpoint = false;
+ boolean supportsCheckpoint = false;
+ try {
+ supportsCheckpoint = PackageHelper.getStorageManager().supportsCheckpoint();
+ needsCheckpoint = PackageHelper.getStorageManager().needsCheckpoint();
+ } catch (RemoteException e) {
+ // This means that vold has crashed, and device is in a bad state.
+ throw new IllegalStateException("Failed to get checkpoint status", e);
+ }
+
+ if (sessions.size() > 1 && !supportsCheckpoint) {
+ throw new IllegalStateException("Detected multiple staged sessions on a device without "
+ + "fs-checkpoint support");
+ }
+
+ // Do a set of quick checks before resuming individual sessions:
+ // 1. Schedule a pre-reboot verification for non-ready sessions.
+ // 2. Abandon destroyed sessions.
+ handleNonReadyAndDestroyedSessions(sessions); // mutates |sessions|
+
+ // 3. Check state of apex sessions is consistent. All non-applied sessions will be marked
+ // as failed.
+ final SparseArray<ApexSessionInfo> apexSessions = mApexManager.getSessions();
+ boolean hasFailedApexSession = false;
+ boolean hasAppliedApexSession = false;
+ for (int i = 0; i < sessions.size(); i++) {
+ StagedSession session = sessions.get(i);
+ if (!session.containsApexSession()) {
+ // At this point we are only interested in apex sessions.
+ continue;
+ }
+ final ApexSessionInfo apexSession = apexSessions.get(session.sessionId());
+ if (apexSession == null || apexSession.isUnknown) {
+ hasFailedApexSession = true;
+ session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "apexd did "
+ + "not know anything about a staged session supposed to be activated");
+ continue;
+ } else if (isApexSessionFailed(apexSession)) {
+ hasFailedApexSession = true;
+ String errorMsg = "APEX activation failed. Check logcat messages from apexd "
+ + "for more information.";
+ if (!TextUtils.isEmpty(apexSession.crashingNativeProcess)) {
+ prepareForLoggingApexdRevert(session, apexSession.crashingNativeProcess);
+ errorMsg = "Session reverted due to crashing native process: "
+ + apexSession.crashingNativeProcess;
+ }
+ session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg);
+ continue;
+ } else if (apexSession.isActivated || apexSession.isSuccess) {
+ hasAppliedApexSession = true;
+ continue;
+ } else if (apexSession.isStaged) {
+ // Apexd did not apply the session for some unknown reason. There is no guarantee
+ // that apexd will install it next time. Safer to proactively mark it as failed.
+ hasFailedApexSession = true;
+ session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+ "Staged session " + session.sessionId() + " at boot didn't activate nor "
+ + "fail. Marking it as failed anyway.");
+ } else {
+ Slog.w(TAG, "Apex session " + session.sessionId() + " is in impossible state");
+ hasFailedApexSession = true;
+ session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+ "Impossible state");
+ }
+ }
+
+ if (hasAppliedApexSession && hasFailedApexSession) {
+ abortCheckpoint("Found both applied and failed apex sessions", supportsCheckpoint,
+ needsCheckpoint);
return;
}
- if (session.isDestroyed()) {
- // Device rebooted before abandoned session was cleaned up.
- session.abandon();
+
+ if (hasFailedApexSession) {
+ // Either of those means that we failed at least one apex session, hence we should fail
+ // all other sessions.
+ for (int i = 0; i < sessions.size(); i++) {
+ StagedSession session = sessions.get(i);
+ if (session.isSessionFailed()) {
+ // Session has been already failed in the loop above.
+ continue;
+ }
+ session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+ "Another apex session failed");
+ }
return;
}
- if (!session.isSessionReady()) {
- // The framework got restarted before the pre-reboot verification could complete,
- // restart the verification.
- mPreRebootVerificationHandler.startPreRebootVerification(session);
- } else {
- // Session had already being marked ready. Start the checks to verify if there is any
- // follow-up work.
+
+ // Time to resume sessions.
+ for (int i = 0; i < sessions.size(); i++) {
+ StagedSession session = sessions.get(i);
try {
- resumeSession(session);
+ resumeSession(session, supportsCheckpoint, needsCheckpoint);
} catch (PackageManagerException e) {
- onInstallationFailure(session, e);
+ onInstallationFailure(session, e, supportsCheckpoint, needsCheckpoint);
} catch (Exception e) {
Slog.e(TAG, "Staged install failed due to unhandled exception", e);
onInstallationFailure(session, new PackageManagerException(
SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
- "Staged install failed due to unhandled exception: " + e));
+ "Staged install failed due to unhandled exception: " + e),
+ supportsCheckpoint, needsCheckpoint);
}
}
}
@@ -992,9 +1032,7 @@ public class StagingManager {
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context ctx, Intent intent) {
- mPreRebootVerificationHandler.readyToStart();
- BackgroundThread.getExecutor().execute(
- () -> logFailedApexSessionsIfNecessary());
+ onBootCompletedBroadcastReceived();
ctx.unregisterReceiver(this);
}
}, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
@@ -1002,6 +1040,12 @@ public class StagingManager {
mFailureReasonFile.delete();
}
+ @VisibleForTesting
+ void onBootCompletedBroadcastReceived() {
+ mPreRebootVerificationHandler.readyToStart();
+ BackgroundThread.getExecutor().execute(() -> logFailedApexSessionsIfNecessary());
+ }
+
private static class LocalIntentReceiverSync {
private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
@@ -1286,9 +1330,8 @@ public class StagingManager {
private void handlePreRebootVerification_End(@NonNull StagedSession session) {
// Before marking the session as ready, start checkpoint service if available
try {
- IStorageManager storageManager = PackageHelper.getStorageManager();
- if (storageManager.supportsCheckpoint()) {
- storageManager.startCheckpoint(2);
+ if (PackageHelper.getStorageManager().supportsCheckpoint()) {
+ PackageHelper.getStorageManager().startCheckpoint(2);
}
} catch (Exception e) {
// Failed to get hold of StorageManager
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 753f22da2b9d..24c27bedb9f7 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1522,6 +1522,14 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
+ public boolean isUserForeground() {
+ int callingUserId = Binder.getCallingUserHandle().getIdentifier();
+ int currentUser = Binder.withCleanCallingIdentity(() -> ActivityManager.getCurrentUser());
+ // TODO(b/179163496): should return true for profile users of the current user as well
+ return currentUser == callingUserId;
+ }
+
+ @Override
public String getUserName() {
if (!hasManageUsersOrPermission(android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED)) {
throw new SecurityException("You need MANAGE_USERS or GET_ACCOUNTS_PRIVILEGED "
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 139654ef4561..3576950bd3a6 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -587,7 +587,7 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
private static final int TRON_COMPILATION_REASON_ERROR = 0;
private static final int TRON_COMPILATION_REASON_UNKNOWN = 1;
private static final int TRON_COMPILATION_REASON_FIRST_BOOT = 2;
- private static final int TRON_COMPILATION_REASON_BOOT = 3;
+ private static final int TRON_COMPILATION_REASON_BOOT_DEPRECATED_SINCE_S = 3;
private static final int TRON_COMPILATION_REASON_INSTALL = 4;
private static final int TRON_COMPILATION_REASON_BG_DEXOPT = 5;
private static final int TRON_COMPILATION_REASON_AB_OTA = 6;
@@ -605,6 +605,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
private static final int TRON_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED_WITH_DM = 18;
private static final int
TRON_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED_WITH_DM = 19;
+ private static final int TRON_COMPILATION_REASON_BOOT_AFTER_OTA = 20;
+ private static final int TRON_COMPILATION_REASON_POST_BOOT = 21;
// The annotation to add as a suffix to the compilation reason when dexopt was
// performed with dex metadata.
@@ -618,7 +620,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
case "unknown" : return TRON_COMPILATION_REASON_UNKNOWN;
case "error" : return TRON_COMPILATION_REASON_ERROR;
case "first-boot" : return TRON_COMPILATION_REASON_FIRST_BOOT;
- case "boot" : return TRON_COMPILATION_REASON_BOOT;
+ case "boot-after-ota": return TRON_COMPILATION_REASON_BOOT_AFTER_OTA;
+ case "post-boot" : return TRON_COMPILATION_REASON_POST_BOOT;
case "install" : return TRON_COMPILATION_REASON_INSTALL;
case "bg-dexopt" : return TRON_COMPILATION_REASON_BG_DEXOPT;
case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA;
diff --git a/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java b/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java
new file mode 100644
index 000000000000..6cdd4df824a8
--- /dev/null
+++ b/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java
@@ -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 com.android.server.pm.parsing.library;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+/**
+ * Updates a package to remove dependency on android.net.ipsec.ike library.
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class AndroidNetIpSecIkeUpdater extends PackageSharedLibraryUpdater {
+
+ private static final String LIBRARY_NAME = "android.net.ipsec.ike";
+
+ @Override
+ public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
+ removeLibrary(parsedPackage, LIBRARY_NAME);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
index 1405a7d613f1..8a8a302734b1 100644
--- a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
+++ b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
@@ -45,6 +45,9 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater {
static {
final List<PackageSharedLibraryUpdater> packageUpdaters = new ArrayList<>();
+ // Remove android.net.ipsec.ike library, it is added to boot classpath since Android S.
+ packageUpdaters.add(new AndroidNetIpSecIkeUpdater());
+
// Remove com.google.android.maps library.
packageUpdaters.add(new ComGoogleAndroidMapsUpdater());
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index 37dfea4ee0f3..5ee612b6d55f 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -125,8 +125,10 @@ public class AndroidPackageUtils {
public static void validatePackageDexMetadata(AndroidPackage pkg)
throws PackageParserException {
Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values();
+ String packageName = pkg.getPackageName();
+ long versionCode = pkg.toAppInfoWithoutState().longVersionCode;
for (String dexMetadata : apkToDexMetadataList) {
- DexMetadataHelper.validateDexMetadataFile(dexMetadata);
+ DexMetadataHelper.validateDexMetadataFile(dexMetadata, packageName, versionCode);
}
}
diff --git a/services/core/java/com/android/server/pm/permission/OWNERS b/services/core/java/com/android/server/pm/permission/OWNERS
index 0e88862e01b1..e05ef482ec08 100644
--- a/services/core/java/com/android/server/pm/permission/OWNERS
+++ b/services/core/java/com/android/server/pm/permission/OWNERS
@@ -1,4 +1,3 @@
-moltmann@google.com
zhanghai@google.com
per-file DefaultPermissionGrantPolicy.java = hackbod@android.com
per-file DefaultPermissionGrantPolicy.java = jsharkey@android.com
@@ -7,5 +6,4 @@ per-file DefaultPermissionGrantPolicy.java = toddke@google.com
per-file DefaultPermissionGrantPolicy.java = yamasani@google.com
per-file DefaultPermissionGrantPolicy.java = patb@google.com
per-file DefaultPermissionGrantPolicy.java = eugenesusla@google.com
-per-file DefaultPermissionGrantPolicy.java = moltmann@google.com
per-file DefaultPermissionGrantPolicy.java = zhanghai@google.com
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index 30c334d22b6a..32bee5809b11 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -38,6 +38,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collection;
import java.util.Objects;
+import java.util.Set;
/**
* Permission definition.
@@ -345,6 +346,14 @@ public final class Permission {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_ROLE) != 0;
}
+ public boolean isKnownSigner() {
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER) != 0;
+ }
+
+ public Set<String> getKnownCerts() {
+ return mPermissionInfo.knownCerts;
+ }
+
public void transfer(@NonNull String oldPackageName, @NonNull String newPackageName) {
if (!oldPackageName.equals(mPermissionInfo.packageName)) {
return;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index aff871118a34..71e53d9f1f40 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2590,8 +2590,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
boolean runtimePermissionsRevoked = false;
int[] updatedUserIds = EMPTY_INT_ARRAY;
+ ArraySet<String> isPrivilegedPermissionAllowlisted = null;
ArraySet<String> shouldGrantSignaturePermission = null;
ArraySet<String> shouldGrantInternalPermission = null;
+ ArraySet<String> shouldGrantPrivilegedPermissionIfWasGranted = new ArraySet<>();
final List<String> requestedPermissions = pkg.getRequestedPermissions();
final int requestedPermissionsSize = requestedPermissions.size();
for (int i = 0; i < requestedPermissionsSize; i++) {
@@ -2604,15 +2606,24 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (permission == null) {
continue;
}
- if (permission.isSignature() && (shouldGrantSignaturePermission(pkg, permission)
- || shouldGrantPermissionByProtectionFlags(pkg, ps, permission))) {
+ if (permission.isPrivileged()
+ && checkPrivilegedPermissionAllowlist(pkg, ps, permission)) {
+ if (isPrivilegedPermissionAllowlisted == null) {
+ isPrivilegedPermissionAllowlisted = new ArraySet<>();
+ }
+ isPrivilegedPermissionAllowlisted.add(permissionName);
+ }
+ if (permission.isSignature() && (shouldGrantPermissionBySignature(pkg, permission)
+ || shouldGrantPermissionByProtectionFlags(pkg, ps, permission,
+ shouldGrantPrivilegedPermissionIfWasGranted))) {
if (shouldGrantSignaturePermission == null) {
shouldGrantSignaturePermission = new ArraySet<>();
}
shouldGrantSignaturePermission.add(permissionName);
}
if (permission.isInternal()
- && shouldGrantPermissionByProtectionFlags(pkg, ps, permission)) {
+ && shouldGrantPermissionByProtectionFlags(pkg, ps, permission,
+ shouldGrantPrivilegedPermissionIfWasGranted)) {
if (shouldGrantInternalPermission == null) {
shouldGrantInternalPermission = new ArraySet<>();
}
@@ -2830,14 +2841,22 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if ((bp.isNormal() && shouldGrantNormalPermission)
|| (bp.isSignature()
- && ((shouldGrantSignaturePermission != null
- && shouldGrantSignaturePermission.contains(permName))
- || ((bp.isDevelopment() || bp.isRole())
+ && (!bp.isPrivileged() || CollectionUtils.contains(
+ isPrivilegedPermissionAllowlisted, permName))
+ && (CollectionUtils.contains(shouldGrantSignaturePermission,
+ permName)
+ || (((bp.isPrivileged() && CollectionUtils.contains(
+ shouldGrantPrivilegedPermissionIfWasGranted,
+ permName)) || bp.isDevelopment() || bp.isRole())
&& origState.isPermissionGranted(permName))))
|| (bp.isInternal()
- && ((shouldGrantInternalPermission != null
- && shouldGrantInternalPermission.contains(permName))
- || ((bp.isDevelopment() || bp.isRole())
+ && (!bp.isPrivileged() || CollectionUtils.contains(
+ isPrivilegedPermissionAllowlisted, permName))
+ && (CollectionUtils.contains(shouldGrantInternalPermission,
+ permName)
+ || (((bp.isPrivileged() && CollectionUtils.contains(
+ shouldGrantPrivilegedPermissionIfWasGranted,
+ permName)) || bp.isDevelopment() || bp.isRole())
&& origState.isPermissionGranted(permName))))) {
// Grant an install permission.
if (uidState.grantPermission(bp)) {
@@ -3343,7 +3362,100 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return allowed;
}
- private boolean shouldGrantSignaturePermission(@NonNull AndroidPackage pkg,
+ private boolean checkPrivilegedPermissionAllowlist(@NonNull AndroidPackage pkg,
+ @NonNull PackageSetting packageSetting, @NonNull Permission permission) {
+ if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) {
+ return true;
+ }
+ final String packageName = pkg.getPackageName();
+ if (Objects.equals(packageName, PLATFORM_PACKAGE_NAME)) {
+ return true;
+ }
+ if (!pkg.isPrivileged()) {
+ return true;
+ }
+ if (!Objects.equals(permission.getPackageName(), PLATFORM_PACKAGE_NAME)) {
+ return true;
+ }
+ final String permissionName = permission.getName();
+ if (isInSystemConfigPrivAppPermissions(pkg, permissionName)) {
+ return true;
+ }
+ if (isInSystemConfigPrivAppDenyPermissions(pkg, permissionName)) {
+ return false;
+ }
+ // Updated system apps do not need to be allowlisted
+ if (packageSetting.getPkgState().isUpdatedSystemApp()) {
+ // Let shouldGrantPermissionByProtectionFlags() decide whether the privileged permission
+ // can be granted, because an updated system app may be in a shared UID, and in case a
+ // new privileged permission is requested by the updated system app but not the factory
+ // app, although this app and permission combination isn't in the allowlist and can't
+ // get the permission this way, other apps in the shared UID may still get it. A proper
+ // fix for this would be to perform the reconciliation by UID, but for now let's keep
+ // the old workaround working, which is to keep granted privileged permissions still
+ // granted.
+ return true;
+ }
+ // Only enforce the allowlist on boot
+ if (!mSystemReady) {
+ final ApexManager apexManager = ApexManager.getInstance();
+ final String containingApexPackageName =
+ apexManager.getActiveApexPackageNameContainingPackage(packageName);
+ final boolean isInUpdatedApex = containingApexPackageName != null
+ && !apexManager.isFactory(apexManager.getPackageInfo(containingApexPackageName,
+ MATCH_ACTIVE_PACKAGE));
+ // Apps that are in updated apexs' do not need to be allowlisted
+ if (!isInUpdatedApex) {
+ Slog.w(TAG, "Privileged permission " + permissionName + " for package "
+ + packageName + " (" + pkg.getPath()
+ + ") not in privapp-permissions allowlist");
+ if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
+ synchronized (mLock) {
+ if (mPrivappPermissionsViolations == null) {
+ mPrivappPermissionsViolations = new ArraySet<>();
+ }
+ mPrivappPermissionsViolations.add(packageName + " (" + pkg.getPath() + "): "
+ + permissionName);
+ }
+ }
+ }
+ }
+ return !RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE;
+ }
+
+ private boolean isInSystemConfigPrivAppPermissions(@NonNull AndroidPackage pkg,
+ @NonNull String permission) {
+ final SystemConfig systemConfig = SystemConfig.getInstance();
+ final Set<String> permissions;
+ if (pkg.isVendor()) {
+ permissions = systemConfig.getVendorPrivAppPermissions(pkg.getPackageName());
+ } else if (pkg.isProduct()) {
+ permissions = systemConfig.getProductPrivAppPermissions(pkg.getPackageName());
+ } else if (pkg.isSystemExt()) {
+ permissions = systemConfig.getSystemExtPrivAppPermissions(pkg.getPackageName());
+ } else {
+ permissions = systemConfig.getPrivAppPermissions(pkg.getPackageName());
+ }
+ return CollectionUtils.contains(permissions, permission);
+ }
+
+ private boolean isInSystemConfigPrivAppDenyPermissions(@NonNull AndroidPackage pkg,
+ @NonNull String permission) {
+ final SystemConfig systemConfig = SystemConfig.getInstance();
+ final Set<String> permissions;
+ if (pkg.isVendor()) {
+ permissions = systemConfig.getVendorPrivAppDenyPermissions(pkg.getPackageName());
+ } else if (pkg.isProduct()) {
+ permissions = systemConfig.getProductPrivAppDenyPermissions(pkg.getPackageName());
+ } else if (pkg.isSystemExt()) {
+ permissions = systemConfig.getSystemExtPrivAppDenyPermissions(pkg.getPackageName());
+ } else {
+ permissions = systemConfig.getPrivAppDenyPermissions(pkg.getPackageName());
+ }
+ return CollectionUtils.contains(permissions, permission);
+ }
+
+ private boolean shouldGrantPermissionBySignature(@NonNull AndroidPackage pkg,
@NonNull Permission bp) {
// expect single system package
String systemPackageName = ArrayUtils.firstOrNull(mPackageManagerInt.getKnownPackageNames(
@@ -3371,10 +3483,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
private boolean shouldGrantPermissionByProtectionFlags(@NonNull AndroidPackage pkg,
- @NonNull PackageSetting pkgSetting, @NonNull Permission bp) {
+ @NonNull PackageSetting pkgSetting, @NonNull Permission bp,
+ @NonNull ArraySet<String> shouldGrantPrivilegedPermissionIfWasGranted) {
boolean allowed = false;
- final boolean isVendorPrivilegedPermission = bp.isVendorPrivileged();
- final boolean isPrivilegedPermission = bp.isPrivileged() || isVendorPrivilegedPermission;
+ final boolean isPrivilegedPermission = bp.isPrivileged();
final boolean isOemPermission = bp.isOem();
if (!allowed && (isPrivilegedPermission || isOemPermission) && pkg.isSystem()) {
final String permissionName = bp.getName();
@@ -3384,21 +3496,27 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final PackageSetting disabledPs = mPackageManagerInt
.getDisabledSystemPackage(pkg.getPackageName());
final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.pkg;
- if (disabledPkg != null && disabledPkg.getRequestedPermissions().contains(
- permissionName)) {
- allowed = (isPrivilegedPermission && canGrantPrivilegedPermission(disabledPkg,
- true, bp)) || (isOemPermission && canGrantOemPermission(disabledPkg,
- permissionName));
+ if (disabledPkg != null
+ && ((isPrivilegedPermission && disabledPkg.isPrivileged())
+ || (isOemPermission && canGrantOemPermission(disabledPkg,
+ permissionName)))) {
+ if (disabledPkg.getRequestedPermissions().contains(permissionName)) {
+ allowed = true;
+ } else {
+ // If the original was granted this permission, we take
+ // that grant decision as read and propagate it to the
+ // update.
+ shouldGrantPrivilegedPermissionIfWasGranted.add(permissionName);
+ }
}
} else {
- allowed = (isPrivilegedPermission && canGrantPrivilegedPermission(pkg, false, bp))
+ allowed = (isPrivilegedPermission && pkg.isPrivileged())
|| (isOemPermission && canGrantOemPermission(pkg, permissionName));
}
// In any case, don't grant a privileged permission to privileged vendor apps, if
// the permission's protectionLevel does not have the extra 'vendorPrivileged'
// flag.
- if (allowed && isPrivilegedPermission && !isVendorPrivilegedPermission
- && pkg.isVendor()) {
+ if (allowed && isPrivilegedPermission && !bp.isVendorPrivileged() && pkg.isVendor()) {
Slog.w(TAG, "Permission " + permissionName
+ " cannot be granted to privileged vendor apk " + pkg.getPackageName()
+ " because it isn't a 'vendorPrivileged' permission.");
@@ -3438,6 +3556,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Any pre-installed system app is allowed to get this permission.
allowed = true;
}
+ if (!allowed && bp.isKnownSigner()) {
+ // If the permission is to be granted to a known signer then check if any of this
+ // app's signing certificates are in the trusted certificate digest Set.
+ allowed = pkg.getSigningDetails().hasAncestorOrSelfWithDigest(bp.getKnownCerts());
+ }
// Deferred to be checked under permission data lock inside restorePermissionState().
//if (!allowed && bp.isDevelopment()) {
// // For development permissions, a development permission
@@ -3536,90 +3659,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return mPackageManagerInt.getPackageSetting(sourcePackageName);
}
- private boolean canGrantPrivilegedPermission(@NonNull AndroidPackage pkg,
- boolean isUpdatedSystemApp, @NonNull Permission permission) {
- if (!pkg.isPrivileged()) {
- return false;
- }
- final boolean isPlatformPermission = PLATFORM_PACKAGE_NAME.equals(
- permission.getPackageName());
- if (!isPlatformPermission) {
- return true;
- }
- if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) {
- return true;
- }
- final String permissionName = permission.getName();
- if (isInSystemConfigPrivAppPermissions(pkg, permissionName)) {
- return true;
- }
- // Only enforce the allowlist on boot
- if (!mSystemReady
- // Updated system apps do not need to be allowlisted
- && !isUpdatedSystemApp) {
- final ApexManager apexManager = ApexManager.getInstance();
- final String packageName = pkg.getPackageName();
- final String containingApexPackageName =
- apexManager.getActiveApexPackageNameContainingPackage(packageName);
- final boolean isInUpdatedApex = containingApexPackageName != null
- && !apexManager.isFactory(apexManager.getPackageInfo(containingApexPackageName,
- MATCH_ACTIVE_PACKAGE));
- // Apps that are in updated apexs' do not need to be allowlisted
- if (!isInUpdatedApex) {
- // it's only a reportable violation if the permission isn't explicitly
- // denied
- if (isInSystemConfigPrivAppDenyPermissions(pkg, permissionName)) {
- return false;
- }
- Slog.w(TAG, "Privileged permission " + permissionName + " for package "
- + packageName + " (" + pkg.getPath()
- + ") not in privapp-permissions allowlist");
- if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
- synchronized (mLock) {
- if (mPrivappPermissionsViolations == null) {
- mPrivappPermissionsViolations = new ArraySet<>();
- }
- mPrivappPermissionsViolations.add(packageName + " (" + pkg.getPath() + "): "
- + permissionName);
- }
- }
- }
- }
- return !RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE;
- }
-
- private boolean isInSystemConfigPrivAppPermissions(@NonNull AndroidPackage pkg,
- @NonNull String permission) {
- final SystemConfig systemConfig = SystemConfig.getInstance();
- final Set<String> permissions;
- if (pkg.isVendor()) {
- permissions = systemConfig.getVendorPrivAppPermissions(pkg.getPackageName());
- } else if (pkg.isProduct()) {
- permissions = systemConfig.getProductPrivAppPermissions(pkg.getPackageName());
- } else if (pkg.isSystemExt()) {
- permissions = systemConfig.getSystemExtPrivAppPermissions(pkg.getPackageName());
- } else {
- permissions = systemConfig.getPrivAppPermissions(pkg.getPackageName());
- }
- return permissions != null && permissions.contains(permission);
- }
-
- private boolean isInSystemConfigPrivAppDenyPermissions(@NonNull AndroidPackage pkg,
- @NonNull String permission) {
- final SystemConfig systemConfig = SystemConfig.getInstance();
- final Set<String> permissions;
- if (pkg.isVendor()) {
- permissions = systemConfig.getVendorPrivAppDenyPermissions(pkg.getPackageName());
- } else if (pkg.isProduct()) {
- permissions = systemConfig.getProductPrivAppDenyPermissions(pkg.getPackageName());
- } else if (pkg.isSystemExt()) {
- permissions = systemConfig.getSystemExtPrivAppDenyPermissions(pkg.getPackageName());
- } else {
- permissions = systemConfig.getPrivAppDenyPermissions(pkg.getPackageName());
- }
- return permissions != null && permissions.contains(permission);
- }
-
private static boolean canGrantOemPermission(AndroidPackage pkg, String permission) {
if (!pkg.isOem()) {
return false;
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
index 36efb39909a6..080de73ff933 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
@@ -21,11 +21,8 @@ import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedIntentInfo;
-import android.os.Binder;
import android.os.Build;
import android.util.ArraySet;
import android.util.Patterns;
@@ -36,19 +33,31 @@ import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.util.List;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class DomainVerificationCollector {
+ // The default domain name matcher doesn't account for wildcards, so prefix with *.
+ private static final Pattern DOMAIN_NAME_WITH_WILDCARD =
+ Pattern.compile("(\\*\\.)?" + Patterns.DOMAIN_NAME.pattern());
+
@NonNull
private final PlatformCompat mPlatformCompat;
@NonNull
private final SystemConfig mSystemConfig;
+ @NonNull
+ private final Matcher mDomainMatcher;
+
public DomainVerificationCollector(@NonNull PlatformCompat platformCompat,
@NonNull SystemConfig systemConfig) {
mPlatformCompat = platformCompat;
mSystemConfig = systemConfig;
+
+ // Cache the matcher to avoid calling into native on each check
+ mDomainMatcher = DOMAIN_NAME_WITH_WILDCARD.matcher("");
}
/**
@@ -144,7 +153,10 @@ public class DomainVerificationCollector {
if (intent.handlesWebUris(false)) {
int authorityCount = intent.countDataAuthorities();
for (int index = 0; index < authorityCount; index++) {
- domains.add(intent.getDataAuthority(index).getHost());
+ String host = intent.getDataAuthority(index).getHost();
+ if (isValidHost(host)) {
+ domains.add(host);
+ }
}
}
}
@@ -188,13 +200,22 @@ public class DomainVerificationCollector {
int authorityCount = intent.countDataAuthorities();
for (int index = 0; index < authorityCount; index++) {
String host = intent.getDataAuthority(index).getHost();
- // It's easy to misconfigure autoVerify intent filters, so to avoid
- // adding unintended hosts, check if the host is an HTTP domain.
- if (Patterns.DOMAIN_NAME.matcher(host).matches()) {
+ if (isValidHost(host)) {
domains.add(host);
}
}
}
}
}
+
+ /**
+ * It's easy to mis-configure autoVerify intent filters, so to avoid adding unintended hosts,
+ * check if the host is an HTTP domain. This applies for both legacy and modern versions of
+ * the API, which will strip invalid hosts from the legacy parsing result. This is done to
+ * improve the reliability of any legacy verifiers.
+ */
+ private boolean isValidHost(String host) {
+ mDomainMatcher.reset(host);
+ return mDomainMatcher.matches();
+ }
}
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 af9978b91e48..b3108c58a11e 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
@@ -32,15 +32,28 @@ import android.util.SparseArray;
import com.android.internal.util.CollectionUtils;
import com.android.server.pm.PackageSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.util.Arrays;
+import java.util.function.Function;
+@SuppressWarnings("PointlessBooleanExpression")
public class DomainVerificationDebug {
+ // Disable to turn off all logging. This is used to allow a "basic" set of debug flags to be
+ // enabled and checked in, without having everything be on or off.
+ public static final boolean DEBUG_ANY = false;
+
+ // Enable to turn on all logging. Requires enabling DEBUG_ANY.
+ public static final boolean DEBUG_ALL = false;
+
+ public static final boolean DEBUG_APPROVAL = DEBUG_ANY && (DEBUG_ALL || true);
+ public static final boolean DEBUG_BROADCASTS = DEBUG_ANY && (DEBUG_ALL || false);
+ public static final boolean DEBUG_PROXIES = DEBUG_ANY && (DEBUG_ALL || false);
+
@NonNull
private final DomainVerificationCollector mCollector;
@@ -50,7 +63,7 @@ public class DomainVerificationDebug {
public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
@Nullable @UserIdInt Integer userId,
- @NonNull DomainVerificationService.Connection connection,
+ @NonNull Function<String, PackageSetting> pkgSettingFunction,
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> stateMap)
throws NameNotFoundException {
ArrayMap<String, Integer> reusedMap = new ArrayMap<>();
@@ -61,7 +74,7 @@ public class DomainVerificationDebug {
for (int index = 0; index < size; index++) {
DomainVerificationPkgState pkgState = stateMap.valueAt(index);
String pkgName = pkgState.getPackageName();
- PackageSetting pkgSetting = connection.getPackageSettingLocked(pkgName);
+ PackageSetting pkgSetting = pkgSettingFunction.apply(pkgName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
continue;
}
@@ -77,7 +90,7 @@ public class DomainVerificationDebug {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
- PackageSetting pkgSetting = connection.getPackageSettingLocked(packageName);
+ PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
@@ -187,8 +200,7 @@ public class DomainVerificationDebug {
printedHeader = true;
}
- boolean isLinkHandlingAllowed = userState == null
- || !userState.isDisallowLinkHandling();
+ boolean isLinkHandlingAllowed = userState == null || userState.isLinkHandlingAllowed();
writer.increaseIndent();
writer.print("User ");
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 c521f828ade9..275dd053fdde 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
@@ -18,8 +18,10 @@ package com.android.server.pm.verify.domain;
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Process;
@@ -30,10 +32,17 @@ public class DomainVerificationEnforcer {
@NonNull
private final Context mContext;
+ @NonNull
+ private Callback mCallback;
+
public DomainVerificationEnforcer(@NonNull Context context) {
mContext = context;
}
+ public void setCallback(@NonNull Callback callback) {
+ mCallback = callback;
+ }
+
/**
* Enforced when mutating any state from shell or internally in the system process.
*/
@@ -67,6 +76,11 @@ public class DomainVerificationEnforcer {
"Caller " + callingUid
+ " is not allowed to query domain verification state");
}
+
+ mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES,
+ Binder.getCallingPid(), callingUid,
+ "Caller " + callingUid + " does not hold "
+ + android.Manifest.permission.QUERY_ALL_PACKAGES);
break;
}
}
@@ -84,28 +98,42 @@ public class DomainVerificationEnforcer {
isAllowed = true;
break;
default:
- // TODO(b/159952358): Remove permission check? The component package should
- // have been checked when the verifier component was first scanned in PMS.
- mContext.enforcePermission(
- android.Manifest.permission.DOMAIN_VERIFICATION_AGENT,
- Binder.getCallingPid(), callingUid,
- "Caller " + callingUid + " does not hold DOMAIN_VERIFICATION_AGENT");
+ final int callingPid = Binder.getCallingPid();
+ boolean isLegacyVerificationAgent = false;
+ if (mContext.checkPermission(
+ android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, callingPid,
+ callingUid) != PackageManager.PERMISSION_GRANTED) {
+ isLegacyVerificationAgent = mContext.checkPermission(
+ android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
+ callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
+ if (!isLegacyVerificationAgent) {
+ throw new SecurityException("Caller " + callingUid + " does not hold "
+ + android.Manifest.permission.DOMAIN_VERIFICATION_AGENT);
+ }
+ }
+
+ // If the caller isn't a legacy verifier, it needs the QUERY_ALL permission
+ if (!isLegacyVerificationAgent) {
+ mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES,
+ callingPid, callingUid, "Caller " + callingUid + " does not hold "
+ + android.Manifest.permission.QUERY_ALL_PACKAGES);
+ }
+
isAllowed = proxy.isCallerVerifier(callingUid);
break;
}
if (!isAllowed) {
throw new SecurityException("Caller " + callingUid
- + " is not the approved domain verification agent, isVerifier = "
- + proxy.isCallerVerifier(callingUid));
+ + " is not the approved domain verification agent");
}
}
/**
* Enforced when mutating user selection state inside an exposed API method.
*/
- public void assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId,
- @UserIdInt int targetUserId) throws SecurityException {
+ public boolean assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId,
+ @Nullable String packageName, @UserIdInt int targetUserId) throws SecurityException {
if (callingUserId != targetUserId) {
mContext.enforcePermission(
Manifest.permission.INTERACT_ACROSS_USERS,
@@ -117,12 +145,51 @@ public class DomainVerificationEnforcer {
android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
Binder.getCallingPid(), callingUid,
"Caller is not allowed to edit user selections");
+
+ if (packageName == null) {
+ return true;
+ }
+
+ return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
}
- public void callerIsLegacyUserSelector(int callingUid) {
+ public boolean callerIsLegacyUserSelector(int callingUid, @UserIdInt int callingUserId,
+ @NonNull String packageName, @UserIdInt int targetUserId) {
mContext.enforcePermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS,
Binder.getCallingPid(), callingUid,
"Caller is not allowed to edit user state");
+
+ if (callingUserId != targetUserId) {
+ if (mContext.checkPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS,
+ Binder.getCallingPid(), callingUid) != PackageManager.PERMISSION_GRANTED) {
+ // Legacy API did not enforce this, so for backwards compatibility, fail silently
+ return false;
+ }
+ }
+
+ return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
+ }
+
+ public boolean callerIsLegacyUserQuerent(int callingUid, @UserIdInt int callingUserId,
+ @NonNull String packageName, @UserIdInt int targetUserId) {
+ if (callingUserId != targetUserId) {
+ // The legacy API enforces the _FULL variant, so maintain that here
+ mContext.enforcePermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ Binder.getCallingPid(), callingUid,
+ "Caller is not allowed to edit other users");
+ }
+
+ return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
+ }
+
+ public interface Callback {
+ /**
+ * @return true if access to the given package should be filtered and the method failed as
+ * if the package was not installed
+ */
+ boolean filterAppAccess(@NonNull String packageName, int callingUid, @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 7ad275a6f351..5d4370ae5dd4 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
@@ -16,37 +16,115 @@
package com.android.server.pm.verify.domain;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.content.Intent;
import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
import android.content.pm.verify.domain.DomainVerificationInfo;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.os.Binder;
import android.os.UserHandle;
import android.util.IndentingPrintWriter;
+import android.util.Pair;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import com.android.server.pm.PackageSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
+import java.util.function.Function;
public interface DomainVerificationManagerInternal extends DomainVerificationManager {
UUID DISABLED_ID = new UUID(0, 0);
/**
+ * The app has not been approved for this domain and should never be able to open it through
+ * an implicit web intent.
+ */
+ int APPROVAL_LEVEL_NONE = 0;
+
+ /**
+ * The app has been approved through the legacy
+ * {@link PackageManager#updateIntentVerificationStatusAsUser(String, int, int)} API, which has
+ * been preserved for migration purposes, but is otherwise ignored. Corresponds to
+ * {@link PackageManager#INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK} and
+ * {@link PackageManager#INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK}.
+ *
+ * This should be used as the cutoff for showing a picker if no better approved app exists
+ * during the legacy transition period.
+ *
+ * TODO(b/177923646): The legacy values can be removed once the Settings API changes are
+ * shipped. These values are not stable, so just deleting the constant and shifting others is
+ * fine.
+ */
+ int APPROVAL_LEVEL_LEGACY_ASK = 1;
+
+ /**
+ * The app has been approved through the legacy
+ * {@link PackageManager#updateIntentVerificationStatusAsUser(String, int, int)} API, which has
+ * 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;
+
+ /**
+ * The app has been chosen by the user through
+ * {@link #setDomainVerificationUserSelection(UUID, Set, boolean)}, indictag an explicit
+ * choice to use this app to open an unverified domain.
+ */
+ int APPROVAL_LEVEL_SELECTION = 2;
+
+ /**
+ * The app is approved through the digital asset link statement being hosted at the domain
+ * it is capturing. This is set through {@link #setDomainVerificationStatus(UUID, Set, int)} by
+ * the domain verification agent on device.
+ */
+ int APPROVAL_LEVEL_VERIFIED = 3;
+
+ /**
+ * The app has been installed as an instant app, which grants it total authority on the domains
+ * that it declares. It is expected that the package installer validate the domains the app
+ * declares against the digital asset link statements before allowing it to be installed.
+ *
+ * The user is still able to disable instant app link handling through
+ * {@link #setDomainVerificationLinkHandlingAllowed(String, boolean)}.
+ */
+ int APPROVAL_LEVEL_INSTANT_APP = 4;
+
+ /**
+ * Defines the possible values for {@link #approvalLevelForDomain(PackageSetting, Intent, int)}
+ * which sorts packages by approval priority. A higher numerical value means the package should
+ * override all lower values. This means that comparison using less/greater than IS valid.
+ *
+ * Negative values are possible, although not implemented, reserved if explicit disable of a
+ * package for a domain needs to be tracked.
+ */
+ @IntDef({
+ APPROVAL_LEVEL_NONE,
+ APPROVAL_LEVEL_LEGACY_ASK,
+ APPROVAL_LEVEL_LEGACY_ALWAYS,
+ APPROVAL_LEVEL_SELECTION,
+ APPROVAL_LEVEL_VERIFIED,
+ APPROVAL_LEVEL_INSTANT_APP
+ })
+ @interface ApprovalLevel{}
+
+ /**
* Generate a new domain set ID to be used for attaching new packages.
*/
@NonNull
@@ -173,8 +251,10 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
* Set aside a legacy user selection that will be restored to a pending
* {@link DomainVerificationPkgState} once it's added through
* {@link #addPackage(PackageSetting)}.
+ *
+ * @return true if state changed successfully
*/
- void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state);
+ boolean setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state);
/**
* Until the legacy APIs are entirely removed, returns the legacy state from the previously
@@ -183,20 +263,23 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
int getLegacyState(@NonNull String packageName, @UserIdInt int userId);
/**
- * Serialize a legacy setting that wasn't attached yet.
- * TODO: Does this even matter? Should consider for removal.
- */
- void writeLegacySettings(TypedXmlSerializer serializer, String name);
-
- /**
* Print the verification state and user selection state of a package.
*
- * @param packageName the package whose state to change, or all packages if none is specified
- * @param userId the specific user to print, or null to skip printing user selection
- * states, supports {@link android.os.UserHandle#USER_ALL}
+ * @param packageName the package whose state to change, or all packages if none is
+ * specified
+ * @param userId the specific user to print, or null to skip printing user selection
+ * states, supports {@link android.os.UserHandle#USER_ALL}
+ * @param pkgSettingFunction the method by which to retrieve package data; if this is called
+ * from {@link com.android.server.pm.PackageManagerService}, it is
+ * expected to pass in the snapshot of {@link PackageSetting} objects,
+ * or if null is passed, the manager may decide to lock {@link
+ * com.android.server.pm.PackageManagerService} through {@link
+ * Connection#getPackageSettingLocked(String)}
*/
void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
- @Nullable @UserIdInt Integer userId) throws NameNotFoundException;
+ @Nullable @UserIdInt Integer userId,
+ @Nullable Function<String, PackageSetting> pkgSettingFunction)
+ throws NameNotFoundException;
@NonNull
DomainVerificationShell getShell();
@@ -205,11 +288,28 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
DomainVerificationCollector getCollector();
/**
- * Check if a resolving URI is approved to takeover the domain as the sole resolved target.
+ * Filters the provided list down to the {@link ResolveInfo} objects that should be allowed
+ * to open the domain inside the {@link Intent}. It is possible for no packages represented in
+ * the list to be approved, in which case an empty list will be returned.
+ *
+ * @return the filtered list and the corresponding approval level
+ */
+ @NonNull
+ Pair<List<ResolveInfo>, Integer> filterToApprovedApp(@NonNull Intent intent,
+ @NonNull List<ResolveInfo> infos, @UserIdInt int userId,
+ @NonNull Function<String, PackageSetting> pkgSettingFunction);
+
+ /**
+ * Check at what precedence a package resolving a URI is approved to takeover the domain.
* This can be because the domain was auto-verified for the package, or if the user manually
- * chose to enable the domain for the package.
+ * chose to enable the domain for the package. If an app is auto-verified, it will be
+ * preferred over apps that were manually selected.
+ *
+ * NOTE: This should not be used for filtering intent resolution. See
+ * {@link #filterToApprovedApp(Intent, List, int, Function)} for that.
*/
- boolean isApprovedForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
+ @ApprovalLevel
+ int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
@UserIdInt int userId);
/**
@@ -225,7 +325,7 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
throws IllegalArgumentException, NameNotFoundException;
- interface Connection {
+ interface Connection extends DomainVerificationEnforcer.Callback {
/**
* Notify that a settings change has been made and that eventually
@@ -249,6 +349,10 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
*/
void schedule(int code, @Nullable Object object);
+ // TODO(b/178733426): Make DomainVerificationService PMS snapshot aware so it can avoid
+ // locking package state at all. This can be as simple as removing this method in favor of
+ // accepting a PackageSetting function in at every method call, although should probably
+ // be abstracted to a wrapper class.
@Nullable
PackageSetting getPackageSettingLocked(@NonNull String pkgName);
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java
index 679f948bb3de..c864b2937f6b 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java
@@ -53,7 +53,7 @@ public class DomainVerificationPersistence {
public static final String TAG_USER_STATE = "user-state";
public static final String ATTR_USER_ID = "userId";
- public static final String ATTR_DISALLOW_LINK_HANDLING = "disallowLinkHandling";
+ public static final String ATTR_ALLOW_LINK_HANDLING = "allowLinkHandling";
public static final String TAG_ENABLED_HOSTS = "enabled-hosts";
public static final String TAG_HOST = "host";
@@ -252,7 +252,7 @@ public class DomainVerificationPersistence {
return null;
}
- boolean disallowLinkHandling = section.getBoolean(ATTR_DISALLOW_LINK_HANDLING);
+ boolean allowLinkHandling = section.getBoolean(ATTR_ALLOW_LINK_HANDLING, true);
ArraySet<String> enabledHosts = new ArraySet<>();
SettingsXml.ChildSection child = section.children();
@@ -260,7 +260,7 @@ public class DomainVerificationPersistence {
readEnabledHosts(child, enabledHosts);
}
- return new DomainVerificationUserState(userId, enabledHosts, disallowLinkHandling);
+ return new DomainVerificationUserState(userId, enabledHosts, allowLinkHandling);
}
private static void readEnabledHosts(@NonNull SettingsXml.ReadSection section,
@@ -279,8 +279,8 @@ public class DomainVerificationPersistence {
try (SettingsXml.WriteSection section =
parentSection.startSection(TAG_USER_STATE)
.attribute(ATTR_USER_ID, userState.getUserId())
- .attribute(ATTR_DISALLOW_LINK_HANDLING,
- userState.isDisallowLinkHandling())) {
+ .attribute(ATTR_ALLOW_LINK_HANDLING,
+ userState.isLinkHandlingAllowed())) {
ArraySet<String> enabledHosts = userState.getEnabledHosts();
if (!enabledHosts.isEmpty()) {
try (SettingsXml.WriteSection enabledHostsSection =
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 53540c8e0d4f..e5ed774fcaa8 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
@@ -16,6 +16,8 @@
package com.android.server.pm.verify.domain;
+import static java.util.Collections.emptyList;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -26,6 +28,8 @@ import android.content.Intent;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.verify.domain.DomainVerificationInfo;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationState;
@@ -35,6 +39,7 @@ import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -47,12 +52,12 @@ import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.PackageSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
import com.android.server.pm.verify.domain.models.DomainVerificationUserState;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyUnavailable;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
import org.xmlpull.v1.XmlPullParserException;
@@ -62,15 +67,17 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.UUID;
+import java.util.function.Function;
public class DomainVerificationService extends SystemService
implements DomainVerificationManagerInternal, DomainVerificationShell.Callback {
private static final String TAG = "DomainVerificationService";
- public static final boolean DEBUG_APPROVAL = true;
+ public static final boolean DEBUG_APPROVAL = DomainVerificationDebug.DEBUG_APPROVAL;
/**
* The new user preference API for verifying domains marked autoVerify=true in
@@ -91,9 +98,9 @@ public class DomainVerificationService extends SystemService
* immediately attached once its available.
* <p>
* Generally this should be not accessed directly. Prefer calling {@link
- * #getAndValidateAttachedLocked(UUID, Set, boolean)}.
+ * #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer)}.
*
- * @see #getAndValidateAttachedLocked(UUID, Set, boolean)
+ * @see #getAndValidateAttachedLocked(UUID, Set, boolean, int, Integer)
**/
@GuardedBy("mLock")
@NonNull
@@ -159,6 +166,7 @@ public class DomainVerificationService extends SystemService
@Override
public void setConnection(@NonNull Connection connection) {
mConnection = connection;
+ mEnforcer.setCallback(mConnection);
}
@NonNull
@@ -284,7 +292,7 @@ public class DomainVerificationService extends SystemService
mEnforcer.assertApprovedVerifier(callingUid, mProxy);
synchronized (mLock) {
DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
- true /* forAutoVerify */);
+ true /* forAutoVerify */, callingUid, null /* userId */);
ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
for (String domain : domains) {
Integer previousState = stateMap.get(domain);
@@ -388,8 +396,10 @@ public class DomainVerificationService extends SystemService
public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
boolean allowed, @UserIdInt int userId) throws NameNotFoundException {
- mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
- mConnection.getCallingUserId(), userId);
+ if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+ mConnection.getCallingUserId(), packageName, userId)) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
synchronized (mLock) {
DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
if (pkgState == null) {
@@ -397,7 +407,7 @@ public class DomainVerificationService extends SystemService
}
pkgState.getOrCreateUserSelectionState(userId)
- .setDisallowLinkHandling(!allowed);
+ .setLinkHandlingAllowed(allowed);
}
mConnection.scheduleWriteSettings();
@@ -419,11 +429,11 @@ public class DomainVerificationService extends SystemService
for (int userStateIndex = 0; userStateIndex < userStatesSize;
userStateIndex++) {
userStates.valueAt(userStateIndex)
- .setDisallowLinkHandling(!allowed);
+ .setLinkHandlingAllowed(allowed);
}
} else {
pkgState.getOrCreateUserSelectionState(userId)
- .setDisallowLinkHandling(!allowed);
+ .setLinkHandlingAllowed(allowed);
}
}
@@ -436,7 +446,7 @@ public class DomainVerificationService extends SystemService
}
pkgState.getOrCreateUserSelectionState(userId)
- .setDisallowLinkHandling(!allowed);
+ .setLinkHandlingAllowed(allowed);
}
}
@@ -454,11 +464,29 @@ public class DomainVerificationService extends SystemService
public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
@NonNull Set<String> domains, boolean enabled, @UserIdInt int userId)
throws InvalidDomainSetException, NameNotFoundException {
- mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
- mConnection.getCallingUserId(), userId);
synchronized (mLock) {
+ final int callingUid = mConnection.getCallingUid();
+ // Pass null for package name here and do the app visibility enforcement inside
+ // getAndValidateAttachedLocked instead, since this has to fail with the same invalid
+ // 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);
+ }
+
+ if (enabled) {
+ for (String domain : domains) {
+ if (!getApprovedPackages(domain, userId, APPROVAL_LEVEL_LEGACY_ALWAYS + 1,
+ mConnection::getPackageSettingLocked).first.isEmpty()) {
+ throw new InvalidDomainSetException(domainSetId, null,
+ InvalidDomainSetException.REASON_UNABLE_TO_APPROVE);
+ }
+ }
+ }
+
DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
- false /* forAutoVerify */);
+ false /* forAutoVerify */, callingUid, userId);
DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId);
if (enabled) {
userState.addHosts(domains);
@@ -541,8 +569,6 @@ public class DomainVerificationService extends SystemService
userState.removeHosts(domains);
}
}
-
- mConnection.scheduleWriteSettings();
}
@Nullable
@@ -557,8 +583,10 @@ public class DomainVerificationService extends SystemService
@Override
public DomainVerificationUserSelection getDomainVerificationUserSelection(
@NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException {
- mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
- mConnection.getCallingUserId(), userId);
+ if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(),
+ mConnection.getCallingUserId(), packageName, userId)) {
+ throw DomainVerificationUtils.throwPackageUnavailable(packageName);
+ }
synchronized (mLock) {
DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
if (pkgState == null) {
@@ -578,10 +606,10 @@ public class DomainVerificationService extends SystemService
hostToUserSelectionMap.put(domains.valueAt(index), false);
}
- boolean openVerifiedLinks = false;
DomainVerificationUserState userState = pkgState.getUserSelectionState(userId);
+ boolean linkHandlingAllowed = true;
if (userState != null) {
- openVerifiedLinks = !userState.isDisallowLinkHandling();
+ linkHandlingAllowed = userState.isLinkHandlingAllowed();
ArraySet<String> enabledHosts = userState.getEnabledHosts();
int hostsSize = enabledHosts.size();
for (int index = 0; index < hostsSize; index++) {
@@ -590,7 +618,7 @@ public class DomainVerificationService extends SystemService
}
return new DomainVerificationUserSelection(pkgState.getId(), packageName,
- UserHandle.of(userId), openVerifiedLinks, hostToUserSelectionMap);
+ UserHandle.of(userId), linkHandlingAllowed, hostToUserSelectionMap);
}
}
@@ -672,7 +700,7 @@ public class DomainVerificationService extends SystemService
ArraySet<String> newEnabledHosts = new ArraySet<>(oldEnabledHosts);
newEnabledHosts.retainAll(newWebDomains);
DomainVerificationUserState newUserState = new DomainVerificationUserState(
- userId, newEnabledHosts, oldUserState.isDisallowLinkHandling());
+ userId, newEnabledHosts, oldUserState.isLinkHandlingAllowed());
newUserStates.put(userId, newUserState);
}
}
@@ -845,22 +873,27 @@ public class DomainVerificationService extends SystemService
}
@Override
- public void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state) {
- mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid());
+ public boolean setLegacyUserState(@NonNull String packageName, @UserIdInt int userId,
+ int state) {
+ if (!mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid(),
+ mConnection.getCallingUserId(), packageName, userId)) {
+ return false;
+ }
mLegacySettings.add(packageName, userId, state);
+ mConnection.scheduleWriteSettings();
+ return true;
}
@Override
public int getLegacyState(@NonNull String packageName, @UserIdInt int userId) {
+ if (!mEnforcer.callerIsLegacyUserQuerent(mConnection.getCallingUid(),
+ mConnection.getCallingUserId(), packageName, userId)) {
+ return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ }
return mLegacySettings.getUserState(packageName, userId);
}
@Override
- public void writeLegacySettings(TypedXmlSerializer serializer, String name) {
-
- }
-
- @Override
public void clearPackage(@NonNull String packageName) {
synchronized (mLock) {
mAttachedPkgStates.remove(packageName);
@@ -890,9 +923,24 @@ public class DomainVerificationService extends SystemService
@Override
public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
- @Nullable @UserIdInt Integer userId) throws NameNotFoundException {
+ @Nullable Integer userId) throws NameNotFoundException {
+ // This method is only used by DomainVerificationShell, which doesn't lock PMS, so it's
+ // safe to pass mConnection directly here and lock PMS. This method is not exposed
+ // to the general system server/PMS.
+ printState(writer, packageName, userId, mConnection::getPackageSettingLocked);
+ }
+
+ @Override
+ public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
+ @Nullable @UserIdInt Integer userId,
+ @Nullable Function<String, PackageSetting> pkgSettingFunction)
+ throws NameNotFoundException {
+ if (pkgSettingFunction == null) {
+ pkgSettingFunction = mConnection::getPackageSettingLocked;
+ }
+
synchronized (mLock) {
- mDebug.printState(writer, packageName, userId, mConnection, mAttachedPkgStates);
+ mDebug.printState(writer, packageName, userId, pkgSettingFunction, mAttachedPkgStates);
}
}
@@ -920,10 +968,14 @@ public class DomainVerificationService extends SystemService
* Validates parameters provided by an external caller. Checks that an ID is still live and that
* any provided domains are valid. Should be called at the beginning of each API that takes in a
* {@link UUID} domain set ID.
+ *
+ * @param userIdForFilter which user to filter app access to, or null if the caller has already
+ * validated package visibility
*/
@GuardedBy("mLock")
private DomainVerificationPkgState getAndValidateAttachedLocked(@NonNull UUID domainSetId,
- @NonNull Set<String> domains, boolean forAutoVerify)
+ @NonNull Set<String> domains, boolean forAutoVerify, int callingUid,
+ @Nullable Integer userIdForFilter)
throws InvalidDomainSetException, NameNotFoundException {
if (domainSetId == null) {
throw new InvalidDomainSetException(null, null,
@@ -937,6 +989,13 @@ public class DomainVerificationService extends SystemService
}
String pkgName = pkgState.getPackageName();
+
+ if (userIdForFilter != null
+ && mConnection.filterAppAccess(pkgName, callingUid, userIdForFilter)) {
+ throw new InvalidDomainSetException(domainSetId, null,
+ InvalidDomainSetException.REASON_ID_INVALID);
+ }
+
PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
throw DomainVerificationUtils.throwPackageUnavailable(pkgName);
@@ -1051,6 +1110,8 @@ public class DomainVerificationService extends SystemService
}
}
}
+
+ mConnection.scheduleWriteSettings();
}
/**
@@ -1108,48 +1169,216 @@ public class DomainVerificationService extends SystemService
}
}
}
+
+ mConnection.scheduleWriteSettings();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Resolving an Intent to an approved app happens in stages:
+ * <ol>
+ * <li>Find all non-zero approved packages for the {@link Intent}'s domain</li>
+ * <li>Filter to packages with the highest approval level, see {@link ApprovalLevel}</li>
+ * <li>Filter out {@link ResolveInfo}s that don't match that approved packages</li>
+ * <li>Take the approved packages with the latest install time</li>
+ * <li>Take the ResolveInfo representing the Activity declared last in the manifest</li>
+ * <li>Return remaining results if any exist</li>
+ * </ol>
+ */
+ @NonNull
+ @Override
+ public Pair<List<ResolveInfo>, Integer> filterToApprovedApp(@NonNull Intent intent,
+ @NonNull List<ResolveInfo> infos, @UserIdInt int userId,
+ @NonNull Function<String, PackageSetting> pkgSettingFunction) {
+ String domain = intent.getData().getHost();
+
+ // Collect package names
+ ArrayMap<String, Integer> packageApprovals = new ArrayMap<>();
+ int infosSize = infos.size();
+ for (int index = 0; index < infosSize; index++) {
+ packageApprovals.put(infos.get(index).getComponentInfo().packageName,
+ APPROVAL_LEVEL_NONE);
+ }
+
+ // Find all approval levels
+ int highestApproval = fillMapWithApprovalLevels(packageApprovals, 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));
+ }
+ }
+
+ ArraySet<String> filteredPackages = new ArraySet<>();
+ 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);
+ }
+ }
+ }
+
+ // 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);
+ }
+ }
+
+ 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 {
+ // Find the last declared ResolveInfo per package
+ finalList = filterToLastDeclared(approvedInfos, pkgSettingFunction);
+ }
+
+ return Pair.create(finalList, highestApproval);
+ }
+
+ /**
+ * @return highest approval level found
+ */
+ private int fillMapWithApprovalLevels(@NonNull ArrayMap<String, 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);
+ PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
+ if (pkgSetting == null) {
+ inputMap.setValueAt(index, APPROVAL_LEVEL_NONE);
+ continue;
+ }
+ int approval = approvalLevelForDomain(pkgSetting, domain, userId, domain);
+ highestApproval = Math.max(highestApproval, approval);
+ inputMap.setValueAt(index, approval);
+ }
+
+ return highestApproval;
+ }
+
+ @NonNull
+ private List<ResolveInfo> filterToLastDeclared(
+ @NonNull ArrayMap<String, List<ResolveInfo>> 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);
+ PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
+ 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;
+ }
+ }
+ }
+
+ // Shouldn't be null, but might as well be safe
+ if (result != null) {
+ finalList.add(result);
+ }
+ }
+
+ return finalList;
}
@Override
- public boolean isApprovedForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
+ public int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
@UserIdInt int userId) {
String packageName = pkgSetting.name;
if (!DomainVerificationUtils.isDomainVerificationIntent(intent)) {
if (DEBUG_APPROVAL) {
debugApproval(packageName, intent, userId, false, "not valid intent");
}
- return false;
+ return APPROVAL_LEVEL_NONE;
}
- String host = intent.getData().getHost();
+ return approvalLevelForDomain(pkgSetting, intent.getData().getHost(), userId, intent);
+ }
+
+ /**
+ * @param debugObject Should be an {@link Intent} if checking for resolution or a {@link String}
+ * otherwise.
+ */
+ private int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull String host,
+ @UserIdInt int userId, @NonNull Object debugObject) {
+ String packageName = pkgSetting.name;
final AndroidPackage pkg = pkgSetting.getPkg();
// Should never be null, but if it is, skip this and assume that v2 is enabled
- if (pkg != null) {
- // To allow an instant app to immediately open domains after being installed by the
- // user, auto approve them for any declared autoVerify domains.
- if (pkgSetting.getInstantApp(userId)
- && mCollector.collectAutoVerifyDomains(pkg).contains(host)) {
- return true;
- }
-
- if (!DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg, SETTINGS_API_V2)) {
- int legacyState = mLegacySettings.getUserState(packageName, userId);
- switch (legacyState) {
- case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
- // If nothing specifically set, assume v2 rules
- break;
- case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
- case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
- case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK:
- // With v2 split into 2 lists, always and undefined, the concept of whether
- // or not to ask is irrelevant. Assume the user wants this application to
- // open the domain.
- return true;
- case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
- // Never has the same semantics are before
- return false;
- }
+ if (pkg != null && !DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg,
+ SETTINGS_API_V2)) {
+ switch (mLegacySettings.getUserState(packageName, userId)) {
+ case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+ // If nothing specifically set, assume v2 rules
+ break;
+ case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
+ return APPROVAL_LEVEL_NONE;
+ case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+ case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK:
+ return APPROVAL_LEVEL_LEGACY_ASK;
+ case PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
+ return APPROVAL_LEVEL_LEGACY_ALWAYS;
}
}
@@ -1157,59 +1386,76 @@ public class DomainVerificationService extends SystemService
DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
if (pkgState == null) {
if (DEBUG_APPROVAL) {
- debugApproval(packageName, intent, userId, false, "pkgState unavailable");
+ debugApproval(packageName, debugObject, userId, false, "pkgState unavailable");
}
- return false;
+ return APPROVAL_LEVEL_NONE;
}
- ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
DomainVerificationUserState userState = pkgState.getUserSelectionState(userId);
- // Only allow autoVerify approval if the user hasn't disabled it
- if (userState == null || !userState.isDisallowLinkHandling()) {
- // Check if the exact host matches
- Integer state = stateMap.get(host);
- if (state != null && DomainVerificationManager.isStateVerified(state)) {
- if (DEBUG_APPROVAL) {
- debugApproval(packageName, intent, userId, true, "host verified exactly");
- }
- return true;
+ if (userState != null && !userState.isLinkHandlingAllowed()) {
+ if (DEBUG_APPROVAL) {
+ debugApproval(packageName, debugObject, userId, false,
+ "link handling not allowed");
}
+ return APPROVAL_LEVEL_NONE;
+ }
- // 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))) {
- continue;
- }
+ // The instant app branch must be run after the link handling check,
+ // since that should also disable instant apps if toggled
+ if (pkg != null) {
+ // To allow an instant app to immediately open domains after being installed by the
+ // user, auto approve them for any declared autoVerify domains.
+ if (pkgSetting.getInstantApp(userId)
+ && mCollector.collectAutoVerifyDomains(pkg).contains(host)) {
+ return APPROVAL_LEVEL_INSTANT_APP;
+ }
+ }
- String domain = stateMap.keyAt(index);
- if (domain.startsWith("*.") && host.endsWith(domain.substring(2))) {
- if (DEBUG_APPROVAL) {
- debugApproval(packageName, intent, userId, true,
- "host verified by wildcard");
- }
- return true;
+ ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+ // Check if the exact host matches
+ Integer state = stateMap.get(host);
+ if (state != null && DomainVerificationManager.isStateVerified(state)) {
+ if (DEBUG_APPROVAL) {
+ debugApproval(packageName, debugObject, userId, true,
+ "host verified exactly");
+ }
+ return APPROVAL_LEVEL_VERIFIED;
+ }
+
+ // 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))) {
+ continue;
+ }
+
+ String domain = stateMap.keyAt(index);
+ if (domain.startsWith("*.") && host.endsWith(domain.substring(2))) {
+ if (DEBUG_APPROVAL) {
+ debugApproval(packageName, debugObject, userId, true,
+ "host verified by wildcard");
}
+ return APPROVAL_LEVEL_VERIFIED;
}
}
// Check user state if available
if (userState == null) {
if (DEBUG_APPROVAL) {
- debugApproval(packageName, intent, userId, false, "userState unavailable");
+ debugApproval(packageName, debugObject, userId, false, "userState unavailable");
}
- return false;
+ return APPROVAL_LEVEL_NONE;
}
// See if the user has approved the exact host
ArraySet<String> enabledHosts = userState.getEnabledHosts();
if (enabledHosts.contains(host)) {
if (DEBUG_APPROVAL) {
- debugApproval(packageName, intent, userId, true,
+ debugApproval(packageName, debugObject, userId, true,
"host enabled by user exactly");
}
- return true;
+ return APPROVAL_LEVEL_SELECTION;
}
// See if the host matches a user selection by wildcard
@@ -1218,24 +1464,85 @@ public class DomainVerificationService extends SystemService
String domain = enabledHosts.valueAt(index);
if (domain.startsWith("*.") && host.endsWith(domain.substring(2))) {
if (DEBUG_APPROVAL) {
- debugApproval(packageName, intent, userId, true,
+ debugApproval(packageName, debugObject, userId, true,
"host enabled by user through wildcard");
}
- return true;
+ return APPROVAL_LEVEL_SELECTION;
}
}
if (DEBUG_APPROVAL) {
- debugApproval(packageName, intent, userId, false, "not approved");
+ debugApproval(packageName, debugObject, userId, false, "not approved");
+ }
+ return APPROVAL_LEVEL_NONE;
+ }
+ }
+
+ /**
+ * @return the filtered list paired with the corresponding approval level
+ */
+ @NonNull
+ private Pair<List<String>, Integer> getApprovedPackages(@NonNull String domain,
+ @UserIdInt int userId, int minimumApproval,
+ @NonNull Function<String, PackageSetting> pkgSettingFunction) {
+ int highestApproval = minimumApproval;
+ List<String> approvedPackages = emptyList();
+
+ synchronized (mLock) {
+ final int size = mAttachedPkgStates.size();
+ for (int index = 0; index < size; index++) {
+ DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+ String packageName = pkgState.getPackageName();
+ PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
+ if (pkgSetting == null) {
+ continue;
+ }
+
+ int level = approvalLevelForDomain(pkgSetting, domain, userId, domain);
+ if (level < minimumApproval) {
+ continue;
+ }
+
+ if (level > highestApproval) {
+ approvedPackages.clear();
+ approvedPackages = CollectionUtils.add(approvedPackages, packageName);
+ highestApproval = level;
+ } else if (level == highestApproval) {
+ approvedPackages = CollectionUtils.add(approvedPackages, packageName);
+ }
}
- return false;
}
+
+ if (approvedPackages.isEmpty()) {
+ return Pair.create(approvedPackages, APPROVAL_LEVEL_NONE);
+ }
+
+ List<String> filteredPackages = new ArrayList<>();
+ long latestInstall = Long.MIN_VALUE;
+ final int approvedSize = approvedPackages.size();
+ for (int index = 0; index < approvedSize; index++) {
+ String packageName = approvedPackages.get(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);
+ }
+ }
+
+ return Pair.create(filteredPackages, highestApproval);
}
- private void debugApproval(@NonNull String packageName, @NonNull Intent intent,
+ private void debugApproval(@NonNull String packageName, @NonNull Object debugObject,
@UserIdInt int userId, boolean approved, @NonNull String reason) {
String approvalString = approved ? "approved" : "denied";
- Slog.d(TAG + "Approval", packageName + " was " + approvalString + " for " + intent
- + " for user " + userId + ": " + reason);
+ Slog.d(TAG + "Approval", packageName + " was " + approvalString + " for "
+ + debugObject + " for user " + userId + ": " + reason);
}
}
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 073967e00134..a8e937cf2b90 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
@@ -23,7 +23,6 @@ import android.content.pm.verify.domain.DomainVerificationState;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Pair;
import android.util.SparseArray;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -229,14 +228,14 @@ class DomainVerificationSettings {
DomainVerificationUserState oldUserState =
oldSelectionStates.get(UserHandle.USER_SYSTEM);
- boolean disallowLinkHandling = newUserState.isDisallowLinkHandling();
+ boolean linkHandlingAllowed = newUserState.isLinkHandlingAllowed();
if (oldUserState == null) {
oldUserState = new DomainVerificationUserState(UserHandle.USER_SYSTEM,
- newEnabledHosts, disallowLinkHandling);
+ newEnabledHosts, linkHandlingAllowed);
oldSelectionStates.put(UserHandle.USER_SYSTEM, oldUserState);
} else {
oldUserState.addHosts(newEnabledHosts)
- .setDisallowLinkHandling(disallowLinkHandling);
+ .setLinkHandlingAllowed(linkHandlingAllowed);
}
}
}
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 474f822d6a73..475d3a87b427 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
@@ -28,7 +28,7 @@ import com.android.server.compat.PlatformCompat;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-final class DomainVerificationUtils {
+public final class DomainVerificationUtils {
/**
* Consolidates package exception messages. A generic unavailable message is included since the
@@ -40,7 +40,7 @@ final class DomainVerificationUtils {
throw new NameNotFoundException("Package " + packageName + " unavailable");
}
- static boolean isDomainVerificationIntent(Intent intent) {
+ public static boolean isDomainVerificationIntent(Intent intent) {
return intent.isWebIntent()
&& intent.hasCategory(Intent.CATEGORY_BROWSABLE)
&& intent.hasCategory(Intent.CATEGORY_DEFAULT);
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java
index 8e8260899a48..22468640800e 100644
--- a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java
@@ -25,10 +25,10 @@ import com.android.internal.util.DataClass;
import java.util.Set;
/**
- * Tracks which domains have been explicitly enabled by the user, allowing it to automatically open
- * that domain when a web URL Intent is sent ft.
+ * Tracks which domains have been explicitly enabled by the user, allowing it to open that domain
+ * when a web URL Intent is sent and the application is the highest priority for that domain.
*/
-@DataClass(genSetters = true, genEqualsHashCode = true, genToString = true)
+@DataClass(genSetters = true, genEqualsHashCode = true, genToString = true, genBuilder = false)
public class DomainVerificationUserState {
@UserIdInt
@@ -38,8 +38,10 @@ public class DomainVerificationUserState {
@NonNull
private final ArraySet<String> mEnabledHosts;
- /** Whether to disallow this package from automatically opening links by auto verification. */
- private boolean mDisallowLinkHandling;
+ /**
+ * Whether to allow this package to automatically open links by auto verification.
+ */
+ private boolean mLinkHandlingAllowed = true;
public DomainVerificationUserState(@UserIdInt int userId) {
mUserId = userId;
@@ -67,13 +69,15 @@ public class DomainVerificationUserState {
}
+
// Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm
+ // /verify/domain/models/DomainVerificationUserState.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -85,19 +89,21 @@ public class DomainVerificationUserState {
*
* @param enabledHosts
* List of domains which have been enabled by the user. *
+ * @param linkHandlingAllowed
+ * Whether to allow this package to automatically open links by auto verification.
*/
@DataClass.Generated.Member
public DomainVerificationUserState(
@UserIdInt int userId,
@NonNull ArraySet<String> enabledHosts,
- boolean disallowLinkHandling) {
+ boolean linkHandlingAllowed) {
this.mUserId = userId;
com.android.internal.util.AnnotationValidations.validate(
UserIdInt.class, null, mUserId);
this.mEnabledHosts = enabledHosts;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mEnabledHosts);
- this.mDisallowLinkHandling = disallowLinkHandling;
+ this.mLinkHandlingAllowed = linkHandlingAllowed;
// onConstructed(); // You can define this method to get a callback
}
@@ -115,14 +121,20 @@ public class DomainVerificationUserState {
return mEnabledHosts;
}
+ /**
+ * Whether to allow this package to automatically open links by auto verification.
+ */
@DataClass.Generated.Member
- public boolean isDisallowLinkHandling() {
- return mDisallowLinkHandling;
+ public boolean isLinkHandlingAllowed() {
+ return mLinkHandlingAllowed;
}
+ /**
+ * Whether to allow this package to automatically open links by auto verification.
+ */
@DataClass.Generated.Member
- public @NonNull DomainVerificationUserState setDisallowLinkHandling( boolean value) {
- mDisallowLinkHandling = value;
+ public @NonNull DomainVerificationUserState setLinkHandlingAllowed( boolean value) {
+ mLinkHandlingAllowed = value;
return this;
}
@@ -135,7 +147,7 @@ public class DomainVerificationUserState {
return "DomainVerificationUserState { " +
"userId = " + mUserId + ", " +
"enabledHosts = " + mEnabledHosts + ", " +
- "disallowLinkHandling = " + mDisallowLinkHandling +
+ "linkHandlingAllowed = " + mLinkHandlingAllowed +
" }";
}
@@ -154,7 +166,7 @@ public class DomainVerificationUserState {
return true
&& mUserId == that.mUserId
&& java.util.Objects.equals(mEnabledHosts, that.mEnabledHosts)
- && mDisallowLinkHandling == that.mDisallowLinkHandling;
+ && mLinkHandlingAllowed == that.mLinkHandlingAllowed;
}
@Override
@@ -166,15 +178,15 @@ public class DomainVerificationUserState {
int _hash = 1;
_hash = 31 * _hash + mUserId;
_hash = 31 * _hash + java.util.Objects.hashCode(mEnabledHosts);
- _hash = 31 * _hash + Boolean.hashCode(mDisallowLinkHandling);
+ _hash = 31 * _hash + Boolean.hashCode(mLinkHandlingAllowed);
return _hash;
}
@DataClass.Generated(
- time = 1608234273324L,
+ time = 1612894390039L,
codegenVersion = "1.0.22",
- sourceFile = "frameworks/base/services/core/java/com/android/server/pm/domain/verify/models/DomainVerificationUserState.java",
- inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledHosts\nprivate boolean mDisallowLinkHandling\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState addHosts(android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState addHosts(java.util.Set<java.lang.String>)\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState removeHosts(android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState removeHosts(java.util.Set<java.lang.String>)\nclass DomainVerificationUserState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genEqualsHashCode=true, genToString=true)")
+ sourceFile = "frameworks/base/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java",
+ inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledHosts\nprivate boolean mLinkHandlingAllowed\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState addHosts(android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState addHosts(java.util.Set<java.lang.String>)\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState removeHosts(android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.verify.domain.models.DomainVerificationUserState removeHosts(java.util.Set<java.lang.String>)\nclass DomainVerificationUserState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genEqualsHashCode=true, genToString=true, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
index 715d8fb0fc2d..09abdd092648 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
@@ -23,9 +23,10 @@ import android.content.Context;
import android.util.Slog;
import com.android.server.DeviceIdleInternal;
-import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
import com.android.server.pm.verify.domain.DomainVerificationCollector;
+import com.android.server.pm.verify.domain.DomainVerificationDebug;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
import java.util.Objects;
import java.util.Set;
@@ -35,7 +36,7 @@ public interface DomainVerificationProxy {
String TAG = "DomainVerificationProxy";
- boolean DEBUG_PROXIES = false;
+ boolean DEBUG_PROXIES = DomainVerificationDebug.DEBUG_PROXIES;
static <ConnectionType extends DomainVerificationProxyV1.Connection
& DomainVerificationProxyV2.Connection> DomainVerificationProxy makeProxy(
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 eab89e987885..a80406548719 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
@@ -30,20 +30,21 @@ import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationState;
import android.os.Process;
import android.os.UserHandle;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.verify.domain.DomainVerificationCollector;
+import com.android.server.pm.verify.domain.DomainVerificationDebug;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
@@ -52,7 +53,7 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy {
private static final String TAG = "DomainVerificationProxyV1";
- private static final boolean DEBUG_BROADCASTS = false;
+ private static final boolean DEBUG_BROADCASTS = DomainVerificationDebug.DEBUG_BROADCASTS;
@NonNull
private final Context mContext;
@@ -152,33 +153,73 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy {
UUID domainSetId = pair.first;
String packageName = pair.second;
- DomainVerificationInfo set;
+ DomainVerificationInfo info;
try {
- set = mManager.getDomainVerificationInfo(packageName);
+ info = mManager.getDomainVerificationInfo(packageName);
} catch (PackageManager.NameNotFoundException ignored) {
return true;
}
- if (!Objects.equals(domainSetId, set.getIdentifier())) {
+ if (info == null) {
+ return true;
+ }
+
+ if (!Objects.equals(domainSetId, info.getIdentifier())) {
return true;
}
- Set<String> successfulDomains = new ArraySet<>(set.getHostToStateMap().keySet());
- successfulDomains.removeAll(response.failedDomains);
+ AndroidPackage pkg = mConnection.getPackage(packageName);
+ if (pkg == null) {
+ return true;
+ }
+
+ ArraySet<String> failedDomains = new ArraySet<>(response.failedDomains);
+ Map<String, Integer> hostToStateMap = info.getHostToStateMap();
+ Set<String> hostKeySet = hostToStateMap.keySet();
+ ArraySet<String> successfulDomains = new ArraySet<>(hostKeySet);
+ successfulDomains.removeAll(failedDomains);
+
+ // v1 doesn't handle wildcard domains, so check them here for the verifier
+ int size = successfulDomains.size();
+ for (int index = size - 1; index >= 0; index--) {
+ String domain = successfulDomains.valueAt(index);
+ if (domain.startsWith("*.")) {
+ String nonWildcardDomain = domain.substring(2);
+ if (failedDomains.contains(nonWildcardDomain)) {
+ failedDomains.add(domain);
+ successfulDomains.removeAt(index);
+
+ // It's possible to declare a wildcard without declaring its
+ // non-wildcard equivalent, so if it wasn't originally declared,
+ // remove the transformed domain from the failed set. Otherwise the
+ // manager will not accept the failed set as it contains an undeclared
+ // domain.
+ if (!hostKeySet.contains(nonWildcardDomain)) {
+ failedDomains.remove(nonWildcardDomain);
+ }
+ }
+ }
+ }
int callingUid = response.callingUid;
- try {
- mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
- successfulDomains, DomainVerificationState.STATE_SUCCESS);
- } catch (DomainVerificationManager.InvalidDomainSetException
- | PackageManager.NameNotFoundException ignored) {
+ if (!successfulDomains.isEmpty()) {
+ try {
+ mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+ successfulDomains, DomainVerificationState.STATE_SUCCESS);
+ } catch (DomainVerificationManager.InvalidDomainSetException
+ | PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Failure reporting successful domains for " + packageName, e);
+ }
}
- try {
- mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
- new ArraySet<>(response.failedDomains),
- DomainVerificationState.STATE_LEGACY_FAILURE);
- } catch (DomainVerificationManager.InvalidDomainSetException
- | PackageManager.NameNotFoundException ignored) {
+
+ if (!failedDomains.isEmpty()) {
+ try {
+ mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+ failedDomains, DomainVerificationState.STATE_LEGACY_FAILURE);
+ } catch (DomainVerificationManager.InvalidDomainSetException
+ | PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Failure reporting failed domains for " + packageName, e);
+ }
}
return true;
@@ -230,7 +271,21 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy {
// The collector itself handles the v1 vs v2 behavior, which is based on targetSdkVersion,
// not the version of the verification agent on device.
ArraySet<String> domains = mCollector.collectAutoVerifyDomains(pkg);
- return TextUtils.join(" ", domains);
+
+ // v1 doesn't handle wildcard domains, so transform them here to the root
+ StringBuilder builder = new StringBuilder();
+ int size = domains.size();
+ for (int index = 0; index < size; index++) {
+ if (index > 0) {
+ builder.append(" ");
+ }
+ String domain = domains.valueAt(index);
+ if (domain.startsWith("*.")) {
+ domain = domain.substring(2);
+ }
+ builder.append(domain);
+ }
+ return builder.toString();
}
private static class Response {
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
index 9fcbce2ad055..1ef06036021e 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
@@ -28,6 +28,7 @@ import android.os.Process;
import android.os.UserHandle;
import android.util.Slog;
+import com.android.server.pm.verify.domain.DomainVerificationDebug;
import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
import java.util.Set;
@@ -36,7 +37,7 @@ public class DomainVerificationProxyV2 implements DomainVerificationProxy {
private static final String TAG = "DomainVerificationProxyV2";
- private static final boolean DEBUG_BROADCASTS = true;
+ private static final boolean DEBUG_BROADCASTS = DomainVerificationDebug.DEBUG_BROADCASTS;
@NonNull
private final Context mContext;
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index ac358db51939..4e1065a9d3af 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -17,6 +17,7 @@
package com.android.server.policy;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -84,7 +85,8 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
private static final BooleanSupplier TRUE_BOOLEAN_SUPPLIER = () -> true;
@VisibleForTesting
- static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(0, "DEFAULT");
+ static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(MINIMUM_DEVICE_STATE,
+ "DEFAULT");
private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/";
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 9d934725f39e..44e823429e90 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -56,13 +56,11 @@ import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
@@ -346,8 +344,21 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/** Amount of time (in milliseconds) to wait for windows drawn before powering on. */
static final int WAITING_FOR_DRAWN_TIMEOUT = 1000;
- /** Amount of time (in milliseconds) a toast window can be shown. */
- public static final int TOAST_WINDOW_TIMEOUT = 3500; // 3.5 seconds
+ /**
+ * Extra time for additional SystemUI animations.
+ * <p>Since legacy apps can add Toast windows directly instead of using Toast APIs,
+ * {@link DisplayPolicy} ensures that the window manager removes toast windows after
+ * TOAST_WINDOW_TIMEOUT. We increase this timeout by TOAST_WINDOW_ANIM_BUFFER to account for
+ * SystemUI's in/out toast animations, so that the toast text is still shown for a minimum
+ * of 3.5 seconds and the animations are finished before window manager removes the window.
+ */
+ public static final int TOAST_WINDOW_ANIM_BUFFER = 600;
+
+ /**
+ * Amount of time (in milliseconds) a toast window can be shown before it's automatically
+ * removed by window manager.
+ */
+ public static final int TOAST_WINDOW_TIMEOUT = 3500 + TOAST_WINDOW_ANIM_BUFFER;
/**
* Lock protecting internal state. Must not call out into window
@@ -2288,25 +2299,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return attrs.type == TYPE_NOTIFICATION_SHADE;
}
- @Override
- public boolean canBeHiddenByKeyguardLw(WindowState win) {
-
- // Keyguard visibility of window from activities are determined over activity visibility.
- if (win.getAppToken() != null) {
- return false;
- }
- switch (win.getAttrs().type) {
- case TYPE_NOTIFICATION_SHADE:
- case TYPE_STATUS_BAR:
- case TYPE_NAVIGATION_BAR:
- case TYPE_WALLPAPER:
- return false;
- default:
- // Hide only windows below the keyguard host window.
- return getWindowLayerLw(win) < getWindowLayerFromTypeLw(TYPE_NOTIFICATION_SHADE);
- }
- }
-
/** {@inheritDoc} */
@Override
public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index db33e750d803..b5a9acacec83 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -672,7 +672,22 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
/**
* @return whether {@param win} can be hidden by Keyguard
*/
- public boolean canBeHiddenByKeyguardLw(WindowState win);
+ default boolean canBeHiddenByKeyguardLw(WindowState win) {
+ // Keyguard visibility of window from activities are determined over activity visibility.
+ if (win.getAppToken() != null) {
+ return false;
+ }
+ switch (win.getAttrs().type) {
+ case TYPE_NOTIFICATION_SHADE:
+ case TYPE_STATUS_BAR:
+ case TYPE_NAVIGATION_BAR:
+ case TYPE_WALLPAPER:
+ return false;
+ default:
+ // Hide only windows below the keyguard host window.
+ return getWindowLayerLw(win) < getWindowLayerFromTypeLw(TYPE_NOTIFICATION_SHADE);
+ }
+ }
/**
* Called when the system would like to show a UI to indicate that an
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8e0d632dd1a8..c0b820293649 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -90,11 +90,11 @@ import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.KeyEvent;
-import com.android.internal.BrightnessSynchronizer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
@@ -5138,10 +5138,15 @@ public final class PowerManagerService extends SystemService
// Get current time before acquiring the lock so that the calculated end time is as
// accurate as possible.
final long nowElapsed = SystemClock.elapsedRealtime();
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.BATTERY_PREDICTION)
+ != PackageManager.PERMISSION_GRANTED) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, "setBatteryDischargePrediction");
+ }
final long timeRemainingMs = timeRemaining.getDuration().toMillis();
- // A non-positive number means the battery should be dead right now...
+ // A non-positive number means the battery should be dead right now...
Preconditions.checkArgumentPositive(timeRemainingMs,
"Given time remaining is not positive: " + timeRemainingMs);
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
index 6d9cb7522bef..2a95416747a6 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
@@ -47,7 +47,8 @@ public class PowerStatsDataStorage {
private static final long DELETE_AGE_MILLIS = 48 * MILLISECONDS_PER_HOUR;
private final ReentrantLock mLock = new ReentrantLock();
- private File mDataStorageDir;
+ private final File mDataStorageDir;
+ private final String mDataStorageFilename;
private final FileRotator mFileRotator;
private static class DataElement {
@@ -168,6 +169,7 @@ public class PowerStatsDataStorage {
public PowerStatsDataStorage(Context context, File dataStoragePath,
String dataStorageFilename) {
mDataStorageDir = dataStoragePath;
+ mDataStorageFilename = dataStorageFilename;
if (!mDataStorageDir.exists() && !mDataStorageDir.mkdirs()) {
Slog.wtf(TAG, "mDataStorageDir does not exist: " + mDataStorageDir.getPath());
@@ -177,33 +179,35 @@ public class PowerStatsDataStorage {
// filename, so any files that don't match the current version number can be deleted.
File[] files = mDataStorageDir.listFiles();
for (int i = 0; i < files.length; i++) {
- // Meter and model files are stored in the same directory.
+ // Meter, model, and residency files are stored in the same directory.
//
// The format of filenames on disk is:
// log.powerstats.meter.version.timestamp
// log.powerstats.model.version.timestamp
+ // log.powerstats.residency.version.timestamp
//
// The format of dataStorageFilenames is:
// log.powerstats.meter.version
// log.powerstats.model.version
+ // log.powerstats.residency.version
//
- // A PowerStatsDataStorage object is created for meter and model data. Strip off
- // the version and check that the current file we're checking starts with the stem
- // (log.powerstats.meter or log.powerstats.model). If the stem matches and the
- // version number is different, delete the old file.
- int versionDot = dataStorageFilename.lastIndexOf('.');
- String beforeVersionDot = dataStorageFilename.substring(0, versionDot);
+ // A PowerStatsDataStorage object is created for meter, model, and residency data.
+ // Strip off the version and check that the current file we're checking starts with
+ // the stem (log.powerstats.meter, log.powerstats.model, log.powerstats.residency).
+ // If the stem matches and the version number is different, delete the old file.
+ int versionDot = mDataStorageFilename.lastIndexOf('.');
+ String beforeVersionDot = mDataStorageFilename.substring(0, versionDot);
// Check that the stems match.
if (files[i].getName().startsWith(beforeVersionDot)) {
// Check that the version number matches. If not, delete the old file.
- if (!files[i].getName().startsWith(dataStorageFilename)) {
+ if (!files[i].getName().startsWith(mDataStorageFilename)) {
files[i].delete();
}
}
}
mFileRotator = new FileRotator(mDataStorageDir,
- dataStorageFilename,
+ mDataStorageFilename,
ROTATE_AGE_MILLIS,
DELETE_AGE_MILLIS);
}
@@ -242,4 +246,19 @@ public class PowerStatsDataStorage {
public void read(DataElementReadCallback callback) throws IOException {
mFileRotator.readMatching(new DataReader(callback), Long.MIN_VALUE, Long.MAX_VALUE);
}
+
+ /**
+ * Deletes all stored log data.
+ */
+ public void deleteLogs() {
+ File[] files = mDataStorageDir.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ int versionDot = mDataStorageFilename.lastIndexOf('.');
+ String beforeVersionDot = mDataStorageFilename.substring(0, versionDot);
+ // Check that the stems before the version match.
+ if (files[i].getName().startsWith(beforeVersionDot)) {
+ files[i].delete();
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index 37fc5a0f68fa..c4f29ea69218 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -26,6 +26,7 @@ import android.hardware.power.stats.StateResidencyResult;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.util.AtomicFile;
import android.util.Slog;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
@@ -41,14 +42,17 @@ import com.android.server.powerstats.ProtoStreamUtils.StateResidencyResultUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.Arrays;
/**
- * PowerStatsLogger is responsible for logging model and meter energy data to on-device storage.
- * Messages are sent to its message handler to request that energy data be logged, at which time it
- * queries the PowerStats HAL and logs the data to on-device storage. The on-device storage is
- * dumped to file by calling writeModelDataToFile, writeMeterDataToFile, or writeResidencyDataToFile
- * with a file descriptor that points to the output file.
+ * PowerStatsLogger is responsible for logging model, meter, and residency data to on-device
+ * storage. Messages are sent to its message handler to request that energy data be logged, at
+ * which time it queries the PowerStats HAL and logs the data to on-device storage. The on-device
+ * storage is dumped to file by calling writeModelDataToFile, writeMeterDataToFile, or
+ * writeResidencyDataToFile with a file descriptor that points to the output file.
*/
public final class PowerStatsLogger extends Handler {
private static final String TAG = PowerStatsLogger.class.getSimpleName();
@@ -61,6 +65,10 @@ public final class PowerStatsLogger extends Handler {
private final PowerStatsDataStorage mPowerStatsModelStorage;
private final PowerStatsDataStorage mPowerStatsResidencyStorage;
private final IPowerStatsHALWrapper mPowerStatsHALWrapper;
+ private File mDataStoragePath;
+ private boolean mDeleteMeterDataOnBoot;
+ private boolean mDeleteModelDataOnBoot;
+ private boolean mDeleteResidencyDataOnBoot;
@Override
public void handleMessage(Message msg) {
@@ -230,16 +238,99 @@ public final class PowerStatsLogger extends Handler {
pos.flush();
}
- public PowerStatsLogger(Context context, File dataStoragePath, String meterFilename,
- String modelFilename, String residencyFilename,
+ private boolean dataChanged(String cachedFilename, byte[] dataCurrent) {
+ boolean dataChanged = false;
+
+ if (mDataStoragePath.exists() || mDataStoragePath.mkdirs()) {
+ final File cachedFile = new File(mDataStoragePath, cachedFilename);
+
+ if (cachedFile.exists()) {
+ // Get the byte array for the cached data.
+ final byte[] dataCached = new byte[(int) cachedFile.length()];
+
+ // Get the cached data from file.
+ try {
+ final FileInputStream fis = new FileInputStream(cachedFile.getPath());
+ fis.read(dataCached);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read cached data from file");
+ }
+
+ // If the cached and current data are different, delete the data store.
+ dataChanged = !Arrays.equals(dataCached, dataCurrent);
+ } else {
+ // Either the cached file was somehow deleted, or this is the first
+ // boot of the device and we're creating the file for the first time.
+ // In either case, delete the log files.
+ dataChanged = true;
+ }
+ }
+
+ return dataChanged;
+ }
+
+ private void updateCacheFile(String cacheFilename, byte[] data) {
+ try {
+ final AtomicFile atomicCachedFile =
+ new AtomicFile(new File(mDataStoragePath, cacheFilename));
+ final FileOutputStream fos = atomicCachedFile.startWrite();
+ fos.write(data);
+ atomicCachedFile.finishWrite(fos);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write current data to cached file");
+ }
+ }
+
+ public boolean getDeleteMeterDataOnBoot() {
+ return mDeleteMeterDataOnBoot;
+ }
+
+ public boolean getDeleteModelDataOnBoot() {
+ return mDeleteModelDataOnBoot;
+ }
+
+ public boolean getDeleteResidencyDataOnBoot() {
+ return mDeleteResidencyDataOnBoot;
+ }
+
+ public PowerStatsLogger(Context context, File dataStoragePath,
+ String meterFilename, String meterCacheFilename,
+ String modelFilename, String modelCacheFilename,
+ String residencyFilename, String residencyCacheFilename,
IPowerStatsHALWrapper powerStatsHALWrapper) {
super(Looper.getMainLooper());
mPowerStatsHALWrapper = powerStatsHALWrapper;
- mPowerStatsMeterStorage = new PowerStatsDataStorage(context, dataStoragePath,
+ mDataStoragePath = dataStoragePath;
+
+ mPowerStatsMeterStorage = new PowerStatsDataStorage(context, mDataStoragePath,
meterFilename);
- mPowerStatsModelStorage = new PowerStatsDataStorage(context, dataStoragePath,
+ mPowerStatsModelStorage = new PowerStatsDataStorage(context, mDataStoragePath,
modelFilename);
- mPowerStatsResidencyStorage = new PowerStatsDataStorage(context, dataStoragePath,
+ mPowerStatsResidencyStorage = new PowerStatsDataStorage(context, mDataStoragePath,
residencyFilename);
+
+ final Channel[] channels = mPowerStatsHALWrapper.getEnergyMeterInfo();
+ final byte[] channelBytes = ChannelUtils.getProtoBytes(channels);
+ mDeleteMeterDataOnBoot = dataChanged(meterCacheFilename, channelBytes);
+ if (mDeleteMeterDataOnBoot) {
+ mPowerStatsMeterStorage.deleteLogs();
+ updateCacheFile(meterCacheFilename, channelBytes);
+ }
+
+ final EnergyConsumer[] energyConsumers = mPowerStatsHALWrapper.getEnergyConsumerInfo();
+ final byte[] energyConsumerBytes = EnergyConsumerUtils.getProtoBytes(energyConsumers);
+ mDeleteModelDataOnBoot = dataChanged(modelCacheFilename, energyConsumerBytes);
+ if (mDeleteModelDataOnBoot) {
+ mPowerStatsModelStorage.deleteLogs();
+ updateCacheFile(modelCacheFilename, energyConsumerBytes);
+ }
+
+ final PowerEntity[] powerEntities = mPowerStatsHALWrapper.getPowerEntityInfo();
+ final byte[] powerEntityBytes = PowerEntityUtils.getProtoBytes(powerEntities);
+ mDeleteResidencyDataOnBoot = dataChanged(residencyCacheFilename, powerEntityBytes);
+ if (mDeleteResidencyDataOnBoot) {
+ mPowerStatsResidencyStorage.deleteLogs();
+ updateCacheFile(residencyCacheFilename, powerEntityBytes);
+ }
}
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index ea41980c02b1..bb52c1dc1a29 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -61,8 +61,12 @@ public class PowerStatsService extends SystemService {
private static final String MODEL_FILENAME = "log.powerstats.model." + DATA_STORAGE_VERSION;
private static final String RESIDENCY_FILENAME =
"log.powerstats.residency." + DATA_STORAGE_VERSION;
+ private static final String METER_CACHE_FILENAME = "meterCache";
+ private static final String MODEL_CACHE_FILENAME = "modelCache";
+ private static final String RESIDENCY_CACHE_FILENAME = "residencyCache";
private final Injector mInjector;
+ private File mDataStoragePath;
private Context mContext;
@Nullable
@@ -73,6 +77,8 @@ public class PowerStatsService extends SystemService {
private TimerTrigger mTimerTrigger;
@Nullable
private StatsPullAtomCallbackImpl mPullAtomCallback;
+ @Nullable
+ private PowerStatsInternal mPowerStatsInternal;
@VisibleForTesting
static class Injector {
@@ -96,6 +102,18 @@ public class PowerStatsService extends SystemService {
return RESIDENCY_FILENAME;
}
+ String createMeterCacheFilename() {
+ return METER_CACHE_FILENAME;
+ }
+
+ String createModelCacheFilename() {
+ return MODEL_CACHE_FILENAME;
+ }
+
+ String createResidencyCacheFilename() {
+ return RESIDENCY_CACHE_FILENAME;
+ }
+
IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() {
return PowerStatsHALWrapper.getPowerStatsHalImpl();
}
@@ -110,10 +128,15 @@ public class PowerStatsService extends SystemService {
}
PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
- String meterFilename, String modelFilename, String residencyFilename,
+ String meterFilename, String meterCacheFilename,
+ String modelFilename, String modelCacheFilename,
+ String residencyFilename, String residencyCacheFilename,
IPowerStatsHALWrapper powerStatsHALWrapper) {
- return new PowerStatsLogger(context, dataStoragePath, meterFilename,
- modelFilename, residencyFilename, powerStatsHALWrapper);
+ return new PowerStatsLogger(context, dataStoragePath,
+ meterFilename, meterCacheFilename,
+ modelFilename, modelCacheFilename,
+ residencyFilename, residencyCacheFilename,
+ powerStatsHALWrapper);
}
BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) {
@@ -125,8 +148,8 @@ public class PowerStatsService extends SystemService {
}
StatsPullAtomCallbackImpl createStatsPullerImpl(Context context,
- IPowerStatsHALWrapper powerStatsHALWrapper) {
- return new StatsPullAtomCallbackImpl(context, powerStatsHALWrapper);
+ PowerStatsInternal powerStatsInternal) {
+ return new StatsPullAtomCallbackImpl(context, powerStatsInternal);
}
}
@@ -175,31 +198,41 @@ public class PowerStatsService extends SystemService {
@Override
public void onStart() {
if (getPowerStatsHal().isInitialized()) {
- // Only create internal service if PowerStatsHal is available.
- publishLocalService(PowerStatsInternal.class, new LocalService());
+ mPowerStatsInternal = new LocalService();
+ publishLocalService(PowerStatsInternal.class, mPowerStatsInternal);
}
publishBinderService(Context.POWER_STATS_SERVICE, new BinderService());
}
private void onSystemServicesReady() {
- if (getPowerStatsHal().isInitialized()) {
- if (DEBUG) Slog.d(TAG, "Starting PowerStatsService statsd pullers");
+ mPullAtomCallback = mInjector.createStatsPullerImpl(mContext, mPowerStatsInternal);
+ }
- // Only start statsd pullers if initialization is successful.
- mPullAtomCallback = mInjector.createStatsPullerImpl(mContext, getPowerStatsHal());
- } else {
- Slog.e(TAG, "Failed to start PowerStatsService statsd pullers");
- }
+ @VisibleForTesting
+ public boolean getDeleteMeterDataOnBoot() {
+ return mPowerStatsLogger.getDeleteMeterDataOnBoot();
+ }
+
+ @VisibleForTesting
+ public boolean getDeleteModelDataOnBoot() {
+ return mPowerStatsLogger.getDeleteModelDataOnBoot();
+ }
+
+ @VisibleForTesting
+ public boolean getDeleteResidencyDataOnBoot() {
+ return mPowerStatsLogger.getDeleteResidencyDataOnBoot();
}
private void onBootCompleted() {
if (getPowerStatsHal().isInitialized()) {
if (DEBUG) Slog.d(TAG, "Starting PowerStatsService loggers");
+ mDataStoragePath = mInjector.createDataStoragePath();
// Only start logger and triggers if initialization is successful.
- mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext,
- mInjector.createDataStoragePath(), mInjector.createMeterFilename(),
- mInjector.createModelFilename(), mInjector.createResidencyFilename(),
+ mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, mDataStoragePath,
+ mInjector.createMeterFilename(), mInjector.createMeterCacheFilename(),
+ mInjector.createModelFilename(), mInjector.createModelCacheFilename(),
+ mInjector.createResidencyFilename(), mInjector.createResidencyCacheFilename(),
getPowerStatsHal());
mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger);
mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger);
diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
index bd003d3ac2dd..11b22a574476 100644
--- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
+++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
@@ -49,6 +49,12 @@ public class ProtoStreamUtils {
private static final String TAG = ProtoStreamUtils.class.getSimpleName();
static class PowerEntityUtils {
+ public static byte[] getProtoBytes(PowerEntity[] powerEntity) {
+ ProtoOutputStream pos = new ProtoOutputStream();
+ packProtoMessage(powerEntity, pos);
+ return pos.getBytes();
+ }
+
public static void packProtoMessage(PowerEntity[] powerEntity,
ProtoOutputStream pos) {
if (powerEntity == null) return;
@@ -260,6 +266,12 @@ public class ProtoStreamUtils {
}
static class ChannelUtils {
+ public static byte[] getProtoBytes(Channel[] channel) {
+ ProtoOutputStream pos = new ProtoOutputStream();
+ packProtoMessage(channel, pos);
+ return pos.getBytes();
+ }
+
public static void packProtoMessage(Channel[] channel, ProtoOutputStream pos) {
if (channel == null) return;
@@ -396,6 +408,12 @@ public class ProtoStreamUtils {
}
static class EnergyConsumerUtils {
+ public static byte[] getProtoBytes(EnergyConsumer[] energyConsumer) {
+ ProtoOutputStream pos = new ProtoOutputStream();
+ packProtoMessage(energyConsumer, pos);
+ return pos.getBytes();
+ }
+
public static void packProtoMessage(EnergyConsumer[] energyConsumer,
ProtoOutputStream pos) {
if (energyConsumer == null) return;
@@ -410,6 +428,72 @@ public class ProtoStreamUtils {
}
}
+ public static EnergyConsumer[] unpackProtoMessage(byte[] data) throws IOException {
+ final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data));
+ List<EnergyConsumer> energyConsumerList = new ArrayList<EnergyConsumer>();
+
+ while (true) {
+ try {
+ int nextField = pis.nextField();
+ EnergyConsumer energyConsumer = new EnergyConsumer();
+
+ if (nextField == (int) PowerStatsServiceModelProto.ENERGY_CONSUMER) {
+ long token = pis.start(PowerStatsServiceModelProto.ENERGY_CONSUMER);
+ energyConsumerList.add(unpackEnergyConsumerProto(pis));
+ pis.end(token);
+ } else if (nextField == ProtoInputStream.NO_MORE_FIELDS) {
+ return energyConsumerList.toArray(
+ new EnergyConsumer[energyConsumerList.size()]);
+ } else {
+ Slog.e(TAG, "Unhandled field in proto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Slog.e(TAG, "Wire Type mismatch in proto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
+ private static EnergyConsumer unpackEnergyConsumerProto(ProtoInputStream pis)
+ throws IOException {
+ final EnergyConsumer energyConsumer = new EnergyConsumer();
+
+ while (true) {
+ try {
+ switch (pis.nextField()) {
+ case (int) EnergyConsumerProto.ID:
+ energyConsumer.id = pis.readInt(EnergyConsumerProto.ID);
+ break;
+
+ case (int) EnergyConsumerProto.ORDINAL:
+ energyConsumer.ordinal = pis.readInt(EnergyConsumerProto.ORDINAL);
+ break;
+
+ case (int) EnergyConsumerProto.TYPE:
+ energyConsumer.type = (byte) pis.readInt(EnergyConsumerProto.TYPE);
+ break;
+
+ case (int) EnergyConsumerProto.NAME:
+ energyConsumer.name = pis.readString(EnergyConsumerProto.NAME);
+ break;
+
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return energyConsumer;
+
+ default:
+ Slog.e(TAG, "Unhandled field in EnergyConsumerProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ break;
+
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Slog.e(TAG, "Wire Type mismatch in EnergyConsumerProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
public static void print(EnergyConsumer[] energyConsumer) {
if (energyConsumer == null) return;
diff --git a/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java b/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java
index 7c6999acc666..bdabefbbeee6 100644
--- a/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java
+++ b/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java
@@ -24,26 +24,31 @@ import android.hardware.power.stats.PowerEntity;
import android.hardware.power.stats.State;
import android.hardware.power.stats.StateResidency;
import android.hardware.power.stats.StateResidencyResult;
+import android.power.PowerStatsInternal;
+import android.util.Slog;
import android.util.StatsEvent;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
/**
* StatsPullAtomCallbackImpl is responsible implementing the stats pullers for
* SUBSYSTEM_SLEEP_STATE and ON_DEVICE_POWER_MEASUREMENT statsd atoms.
*/
public class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
+ private static final String TAG = StatsPullAtomCallbackImpl.class.getSimpleName();
private Context mContext;
- private IPowerStatsHALWrapper mPowerStatsHALWrapper;
+ private PowerStatsInternal mPowerStatsInternal;
private Map<Integer, Channel> mChannels = new HashMap();
private Map<Integer, String> mEntityNames = new HashMap();
- private Map<Integer, Map<Integer, String>> mStateNames = new HashMap();;
+ private Map<Integer, Map<Integer, String>> mStateNames = new HashMap();
+ private static final int STATS_PULL_TIMEOUT_MILLIS = 2000;
+ private static final boolean DEBUG = false;
@Override
public int onPullAtom(int atomTag, List<StatsEvent> data) {
@@ -57,21 +62,28 @@ public class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCall
}
}
- private void initPullOnDevicePowerMeasurement() {
- Channel[] channels = mPowerStatsHALWrapper.getEnergyMeterInfo();
- if (channels == null) {
- return;
+ private boolean initPullOnDevicePowerMeasurement() {
+ Channel[] channels = mPowerStatsInternal.getEnergyMeterInfo();
+ if (channels == null || channels.length == 0) {
+ Slog.e(TAG, "Failed to init OnDevicePowerMeasurement puller");
+ return false;
}
for (int i = 0; i < channels.length; i++) {
final Channel channel = channels[i];
mChannels.put(channel.id, channel);
}
+
+ return true;
}
private int pullOnDevicePowerMeasurement(int atomTag, List<StatsEvent> events) {
- EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeter(new int[0]);
- if (energyMeasurements == null) {
+ final EnergyMeasurement[] energyMeasurements;
+ try {
+ energyMeasurements = mPowerStatsInternal.readEnergyMeterAsync(new int[0])
+ .get(STATS_PULL_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to readEnergyMeterAsync", e);
return StatsManager.PULL_SKIP;
}
@@ -91,10 +103,11 @@ public class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCall
return StatsManager.PULL_SUCCESS;
}
- private void initSubsystemSleepState() {
- PowerEntity[] entities = mPowerStatsHALWrapper.getPowerEntityInfo();
- if (entities == null) {
- return;
+ private boolean initSubsystemSleepState() {
+ PowerEntity[] entities = mPowerStatsInternal.getPowerEntityInfo();
+ if (entities == null || entities.length == 0) {
+ Slog.e(TAG, "Failed to init SubsystemSleepState puller");
+ return false;
}
for (int i = 0; i < entities.length; i++) {
@@ -108,13 +121,20 @@ public class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCall
mEntityNames.put(entity.id, entity.name);
mStateNames.put(entity.id, states);
}
+
+ return true;
}
private int pullSubsystemSleepState(int atomTag, List<StatsEvent> events) {
- StateResidencyResult[] results = mPowerStatsHALWrapper.getStateResidency(new int[0]);
- if (results == null) {
+ final StateResidencyResult[] results;
+ try {
+ results = mPowerStatsInternal.getStateResidencyAsync(new int[0])
+ .get(STATS_PULL_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to getStateResidencyAsync", e);
return StatsManager.PULL_SKIP;
}
+
for (int i = 0; i < results.length; i++) {
final StateResidencyResult result = results[i];
for (int j = 0; j < result.stateResidencyData.length; j++) {
@@ -131,22 +151,33 @@ public class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCall
return StatsManager.PULL_SUCCESS;
}
- public StatsPullAtomCallbackImpl(Context context, IPowerStatsHALWrapper powerStatsHALWrapper) {
+ public StatsPullAtomCallbackImpl(Context context, PowerStatsInternal powerStatsInternal) {
+ if (DEBUG) Slog.d(TAG, "Starting PowerStatsService statsd pullers");
+
mContext = context;
- mPowerStatsHALWrapper = powerStatsHALWrapper;
- initPullOnDevicePowerMeasurement();
- initSubsystemSleepState();
+ mPowerStatsInternal = powerStatsInternal;
+
+ if (powerStatsInternal == null) {
+ Slog.e(TAG, "Failed to start PowerStatsService statsd pullers");
+ return;
+ }
StatsManager manager = mContext.getSystemService(StatsManager.class);
- manager.setPullAtomCallback(
- FrameworkStatsLog.SUBSYSTEM_SLEEP_STATE,
- null, // use default PullAtomMetadata values
- ConcurrentUtils.DIRECT_EXECUTOR,
- this);
- manager.setPullAtomCallback(
- FrameworkStatsLog.ON_DEVICE_POWER_MEASUREMENT,
- null, // use default PullAtomMetadata values
- ConcurrentUtils.DIRECT_EXECUTOR,
- this);
+
+ if (initPullOnDevicePowerMeasurement()) {
+ manager.setPullAtomCallback(
+ FrameworkStatsLog.ON_DEVICE_POWER_MEASUREMENT,
+ null, // use default PullAtomMetadata values
+ ConcurrentUtils.DIRECT_EXECUTOR,
+ this);
+ }
+
+ if (initSubsystemSleepState()) {
+ manager.setPullAtomCallback(
+ FrameworkStatsLog.SUBSYSTEM_SLEEP_STATE,
+ null, // use default PullAtomMetadata values
+ ConcurrentUtils.DIRECT_EXECUTOR,
+ this);
+ }
}
}
diff --git a/services/core/java/com/android/server/role/OWNERS b/services/core/java/com/android/server/role/OWNERS
index b94d98827d71..31e3549d9111 100644
--- a/services/core/java/com/android/server/role/OWNERS
+++ b/services/core/java/com/android/server/role/OWNERS
@@ -1,5 +1,4 @@
svetoslavganov@google.com
-moltmann@google.com
zhanghai@google.com
evanseverson@google.com
eugenesusla@google.com
diff --git a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
index 57e39b6c6829..b995b19c5841 100644
--- a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
+++ b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
@@ -45,6 +45,8 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.ServiceConnector;
+import java.lang.ref.WeakReference;
+
/** Manages the connection to the remote rotation resolver service. */
class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResolverService> {
@@ -128,13 +130,20 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
mProposedRotation = proposedRotation;
mCurrentRotation = currentRotation;
mPackageName = packageName;
- mIRotationResolverCallback = new RotationResolverCallback();
+ mIRotationResolverCallback = new RotationResolverCallback(this);
mCancellationSignalInternal = cancellationSignal;
mRequestStartTimeMillis = SystemClock.elapsedRealtime();
}
void cancelInternal() {
+ synchronized (mLock) {
+ if (mIsFulfilled) {
+ Slog.v(TAG, "Trying to cancel the request that has been already fulfilled.");
+ return;
+ }
+ mIsFulfilled = true;
+ }
Handler.getMain().post(() -> {
synchronized (mLock) {
try {
@@ -147,9 +156,6 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
}
}
});
- synchronized (mLock) {
- mIsFulfilled = true;
- }
mCallbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
}
@@ -160,44 +166,53 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
ipw.decreaseIndent();
}
- private class RotationResolverCallback extends IRotationResolverCallback.Stub {
+ private static class RotationResolverCallback extends IRotationResolverCallback.Stub {
+ private WeakReference<RotationRequest> mRequestWeakReference;
+
+ RotationResolverCallback(RotationRequest request) {
+ this.mRequestWeakReference = new WeakReference<>(request);
+ }
+
@Override
public void onSuccess(int rotation) {
- synchronized (mLock) {
- if (mIsFulfilled) {
+ final RotationRequest request = mRequestWeakReference.get();
+ synchronized (request.mLock) {
+ if (request.mIsFulfilled) {
Slog.w(TAG, "Callback received after the rotation request is fulfilled.");
return;
}
- mIsFulfilled = true;
- mCallbackInternal.onSuccess(rotation);
+ request.mIsFulfilled = true;
+ request.mCallbackInternal.onSuccess(rotation);
final long timeToCalculate =
- SystemClock.elapsedRealtime() - mRequestStartTimeMillis;
- logRotationStats(mProposedRotation, mCurrentRotation, rotation,
+ SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis;
+ logRotationStats(request.mProposedRotation, request.mCurrentRotation, rotation,
timeToCalculate);
}
}
@Override
public void onFailure(int error) {
- synchronized (mLock) {
- if (mIsFulfilled) {
+ final RotationRequest request = mRequestWeakReference.get();
+ synchronized (request.mLock) {
+ if (request.mIsFulfilled) {
Slog.w(TAG, "Callback received after the rotation request is fulfilled.");
return;
}
- mIsFulfilled = true;
- mCallbackInternal.onFailure(error);
+ request.mIsFulfilled = true;
+ request.mCallbackInternal.onFailure(error);
final long timeToCalculate =
- SystemClock.elapsedRealtime() - mRequestStartTimeMillis;
- logRotationStats(mProposedRotation, mCurrentRotation, RESOLUTION_FAILURE,
- timeToCalculate);
+ SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis;
+ logRotationStats(request.mProposedRotation, request.mCurrentRotation,
+ RESOLUTION_FAILURE, timeToCalculate);
}
}
@Override
public void onCancellable(@NonNull ICancellationSignal cancellation) {
- synchronized (mLock) {
- mCancellation = cancellation;
- if (mCancellationSignalInternal.isCanceled()) {
+ final RotationRequest request = mRequestWeakReference.get();
+ synchronized (request.mLock) {
+ request.mCancellation = cancellation;
+ if (request.mCancellationSignalInternal.isCanceled()) {
// Dispatch the cancellation signal if the client has cancelled the request.
try {
cancellation.cancel();
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
index 3dbc32ad54d8..6f7c016cb3f6 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
@@ -122,14 +122,9 @@ final class RotationResolverManagerPerUserService extends
}
});
- if (mRemoteService != null) {
- mRemoteService.resolveRotationLocked(mCurrentRequest);
- mCurrentRequest.mIsDispatched = true;
- } else {
- Slog.w(TAG, "Remote service is not available at this moment.");
- callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
- cancelLocked();
- }
+
+ mRemoteService.resolveRotationLocked(mCurrentRequest);
+ mCurrentRequest.mIsDispatched = true;
}
@GuardedBy("mLock")
@@ -198,15 +193,6 @@ final class RotationResolverManagerPerUserService extends
if (mCurrentRequest == null) {
return;
}
-
- if (mCurrentRequest.mIsFulfilled) {
- if (isVerbose()) {
- Slog.d(TAG, "Trying to cancel the request that has been already fulfilled.");
- }
- mCurrentRequest = null;
- return;
- }
-
mCurrentRequest.cancelInternal();
mCurrentRequest = null;
}
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
index 4a37e7960912..e57d4ce9ec31 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
@@ -66,7 +66,7 @@ public class RotationResolverManagerService extends
private static final String KEY_SERVICE_ENABLED = "service_enabled";
/** Default value in absence of {@link DeviceConfig} override. */
- private static final boolean DEFAULT_SERVICE_ENABLED = false;
+ private static final boolean DEFAULT_SERVICE_ENABLED = true;
static final int ORIENTATION_UNKNOWN =
FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__UNKNOWN;
@@ -150,7 +150,7 @@ public class RotationResolverManagerService extends
@Override
public void resolveRotation(
@NonNull RotationResolverCallbackInternal callbackInternal, int proposedRotation,
- int currentRotation, String packageName, long timeout,
+ int currentRotation, long timeout,
@NonNull CancellationSignal cancellationSignalInternal) {
Objects.requireNonNull(callbackInternal);
Objects.requireNonNull(cancellationSignalInternal);
@@ -159,7 +159,8 @@ public class RotationResolverManagerService extends
final RotationResolverManagerPerUserService service = getServiceForUserLocked(
UserHandle.getCallingUserId());
service.resolveRotationLocked(callbackInternal, proposedRotation,
- currentRotation, packageName, timeout, cancellationSignalInternal);
+ currentRotation, /* packageName */ "", timeout,
+ cancellationSignalInternal);
} else {
Slog.w(TAG, "Rotation Resolver service is disabled.");
callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
@@ -191,7 +192,7 @@ public class RotationResolverManagerService extends
TAG);
final RotationResolverManagerPerUserService service = getServiceForUserLocked(
UserHandle.getCallingUserId());
- new RotationResolverShellCommend(service).exec(this, in, out, err, args, callback,
+ new RotationResolverShellCommand(service).exec(this, in, out, err, args, callback,
resultReceiver);
}
}
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
index 0a873892b5bf..54a9edba4e03 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
@@ -26,13 +26,13 @@ import android.view.Surface;
import java.io.PrintWriter;
-final class RotationResolverShellCommend extends ShellCommand {
+final class RotationResolverShellCommand extends ShellCommand {
private static final int INITIAL_RESULT_CODE = -1;
@NonNull
private final RotationResolverManagerPerUserService mService;
- RotationResolverShellCommend(@NonNull RotationResolverManagerPerUserService service) {
+ RotationResolverShellCommand(@NonNull RotationResolverManagerPerUserService service) {
mService = service;
}
diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java
index 6ec71b717ec6..74bb99351a6d 100644
--- a/services/core/java/com/android/server/security/FileIntegrityService.java
+++ b/services/core/java/com/android/server/security/FileIntegrityService.java
@@ -23,10 +23,8 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
-import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.security.IFileIntegrityService;
import android.util.Slog;
@@ -60,7 +58,7 @@ public class FileIntegrityService extends SystemService {
private final IBinder mService = new IFileIntegrityService.Stub() {
@Override
public boolean isApkVeritySupported() {
- return FileIntegrityService.isApkVeritySupported();
+ return VerityUtils.isFsVeritySupported();
}
@Override
@@ -69,7 +67,7 @@ public class FileIntegrityService extends SystemService {
checkCallerPermission(packageName);
try {
- if (!isApkVeritySupported()) {
+ if (!VerityUtils.isFsVeritySupported()) {
return false;
}
if (certificateBytes == null) {
@@ -110,11 +108,6 @@ public class FileIntegrityService extends SystemService {
}
};
- public static boolean isApkVeritySupported() {
- return Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.R
- || SystemProperties.getInt("ro.apk_verity.mode", 0) == 2;
- }
-
public FileIntegrityService(final Context context) {
super(context);
try {
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 09ee001a5544..48a60387fee7 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -17,7 +17,9 @@
package com.android.server.security;
import android.annotation.NonNull;
+import android.os.Build;
import android.os.SharedMemory;
+import android.os.SystemProperties;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -57,6 +59,11 @@ abstract public class VerityUtils {
private static final boolean DEBUG = false;
+ public static boolean isFsVeritySupported() {
+ return Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.R
+ || SystemProperties.getInt("ro.apk_verity.mode", 0) == 2;
+ }
+
/** Returns true if the given file looks like containing an fs-verity signature. */
public static boolean isFsveritySignatureFile(File file) {
return file.getName().endsWith(FSVERITY_SIGNATURE_FILE_EXTENSION);
diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
index 96248c3711e3..3f203026a219 100644
--- a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
+++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
@@ -18,22 +18,65 @@ package com.android.server.speech;
import static com.android.internal.infra.AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
import android.os.RemoteException;
import android.speech.IRecognitionListener;
import android.speech.IRecognitionService;
import android.speech.RecognitionService;
+import android.speech.SpeechRecognizer;
+import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.ServiceConnector;
final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecognitionService> {
private static final String TAG = RemoteSpeechRecognitionService.class.getSimpleName();
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
- RemoteSpeechRecognitionService(Context context, ComponentName serviceName, int userId) {
+ private static final String APP_OP_MESSAGE = "Recording audio for speech recognition";
+ private static final String RECORD_AUDIO_APP_OP =
+ AppOpsManager.permissionToOp(android.Manifest.permission.RECORD_AUDIO);
+
+ private final Object mLock = new Object();
+
+ private boolean mConnected = false;
+
+ @Nullable
+ private IRecognitionListener mListener;
+
+ @Nullable
+ @GuardedBy("mLock")
+ private String mPackageName;
+
+ @Nullable
+ @GuardedBy("mLock")
+ private String mFeatureId;
+
+ @Nullable
+ @GuardedBy("mLock")
+ private DelegatingListener mDelegatingListener;
+
+ // Makes sure we can block startListening() if session is still in progress.
+ @GuardedBy("mLock")
+ private boolean mSessionInProgress = false;
+
+ // Makes sure we call startProxyOp / finishProxyOp at right times and only once per session.
+ @GuardedBy("mLock")
+ private boolean mRecordingInProgress = false;
+
+ private final int mCallingUid;
+ private final AppOpsManager mAppOpsManager;
+ private final ComponentName mComponentName;
+
+ RemoteSpeechRecognitionService(
+ Context context, ComponentName serviceName, int userId, int callingUid) {
super(context,
new Intent(RecognitionService.SERVICE_INTERFACE).setComponent(serviceName),
Context.BIND_AUTO_CREATE
@@ -43,46 +86,197 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
userId,
IRecognitionService.Stub::asInterface);
+ mCallingUid = callingUid;
+ mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+ mComponentName = serviceName;
+
if (DEBUG) {
Slog.i(TAG, "Bound to recognition service at: " + serviceName.flattenToString());
}
}
+ ComponentName getServiceComponentName() {
+ return mComponentName;
+ }
+
void startListening(Intent recognizerIntent, IRecognitionListener listener, String packageName,
- String featureId) throws RemoteException {
+ String featureId) {
if (DEBUG) {
- Slog.i(TAG, "#startListening for package: " + packageName + ", feature=" + featureId);
+ Slog.i(TAG, String.format("#startListening for package: %s, feature=%s, callingUid=%d",
+ packageName, featureId, mCallingUid));
+ }
+
+ if (listener == null) {
+ Log.w(TAG, "#startListening called with no preceding #setListening - ignoring");
+ return;
+ }
+
+ if (!mConnected) {
+ tryRespondWithError(listener, SpeechRecognizer.ERROR_SERVER_DISCONNECTED);
+ return;
+ }
+
+ synchronized (mLock) {
+ if (mSessionInProgress) {
+ Slog.i(TAG, "#startListening called while listening is in progress.");
+ tryRespondWithError(listener, SpeechRecognizer.ERROR_RECOGNIZER_BUSY);
+ return;
+ }
+
+ if (startProxyOp(packageName, featureId) != AppOpsManager.MODE_ALLOWED) {
+ tryRespondWithError(listener, SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS);
+ return;
+ }
+ mSessionInProgress = true;
+ mRecordingInProgress = true;
+
+ mListener = listener;
+ mDelegatingListener = new DelegatingListener(listener, () -> {
+ // To be invoked in terminal calls of the callback: results() or error()
+ if (DEBUG) {
+ Slog.i(TAG, "Recognition session complete");
+ }
+
+ synchronized (mLock) {
+ resetStateLocked();
+ }
+ });
+ mPackageName = packageName;
+ mFeatureId = featureId;
+
+ run(service ->
+ service.startListening(
+ recognizerIntent,
+ mDelegatingListener,
+ packageName,
+ featureId,
+ mCallingUid));
}
- run(service -> service.startListening(recognizerIntent, listener, packageName, featureId));
}
- void stopListening(IRecognitionListener listener, String packageName, String featureId)
- throws RemoteException {
+ void stopListening(
+ IRecognitionListener listener, String packageName, String featureId) {
if (DEBUG) {
Slog.i(TAG, "#stopListening for package: " + packageName + ", feature=" + featureId);
}
- run(service -> service.stopListening(listener, packageName, featureId));
+
+ if (!mConnected) {
+ tryRespondWithError(listener, SpeechRecognizer.ERROR_SERVER_DISCONNECTED);
+ return;
+ }
+
+ synchronized (mLock) {
+ if (mListener == null) {
+ Log.w(TAG, "#stopListening called with no preceding #startListening - ignoring");
+ tryRespondWithError(listener, SpeechRecognizer.ERROR_CLIENT);
+ return;
+ }
+
+ if (mListener.asBinder() != listener.asBinder()) {
+ Log.w(TAG, "#stopListening called with an unexpected listener");
+ tryRespondWithError(listener, SpeechRecognizer.ERROR_CLIENT);
+ return;
+ }
+
+ if (!mRecordingInProgress) {
+ Slog.i(TAG, "#stopListening called while listening isn't in progress, ignoring.");
+ return;
+ }
+ mRecordingInProgress = false;
+
+ finishProxyOp(packageName, featureId);
+
+ run(service -> service.stopListening(mDelegatingListener, packageName, featureId));
+ }
}
- void cancel(IRecognitionListener listener, String packageName, String featureId)
- throws RemoteException {
+ void cancel(
+ IRecognitionListener listener,
+ String packageName,
+ String featureId,
+ boolean isShutdown) {
if (DEBUG) {
Slog.i(TAG, "#cancel for package: " + packageName + ", feature=" + featureId);
}
- run(service -> service.cancel(listener, packageName, featureId));
+
+ if (!mConnected) {
+ tryRespondWithError(listener, SpeechRecognizer.ERROR_SERVER_DISCONNECTED);
+ }
+
+ synchronized (mLock) {
+ if (mListener == null) {
+ if (DEBUG) {
+ Log.w(TAG, "#cancel called with no preceding #startListening - ignoring");
+ }
+ return;
+ }
+
+ if (mListener.asBinder() != listener.asBinder()) {
+ Log.w(TAG, "#cancel called with an unexpected listener");
+ tryRespondWithError(listener, SpeechRecognizer.ERROR_CLIENT);
+ return;
+ }
+
+ // Temporary reference to allow for resetting the hard link mDelegatingListener to null.
+ IRecognitionListener delegatingListener = mDelegatingListener;
+
+ run(service -> service.cancel(delegatingListener, packageName, featureId, isShutdown));
+
+ if (mRecordingInProgress) {
+ finishProxyOp(packageName, featureId);
+ }
+ mRecordingInProgress = false;
+ mSessionInProgress = false;
+
+ mDelegatingListener = null;
+ mListener = null;
+
+ // Schedule to unbind after cancel is delivered.
+ if (isShutdown) {
+ run(service -> unbind());
+ }
+ }
+ }
+
+ void shutdown() {
+ synchronized (mLock) {
+ if (this.mListener == null) {
+ if (DEBUG) {
+ Slog.i(TAG, "Package died, but session wasn't initialized. "
+ + "Not invoking #cancel");
+ }
+ return;
+ }
+ }
+
+ cancel(mListener, mPackageName, mFeatureId, true /* isShutdown */);
}
@Override // from ServiceConnector.Impl
protected void onServiceConnectionStatusChanged(
IRecognitionService service, boolean connected) {
- if (!DEBUG) {
- return;
+ mConnected = connected;
+
+ if (DEBUG) {
+ if (connected) {
+ Slog.i(TAG, "Connected to speech recognition service");
+ } else {
+ Slog.w(TAG, "Disconnected from speech recognition service");
+ }
}
- if (connected) {
- Slog.i(TAG, "Connected to ASR service");
- } else {
- Slog.w(TAG, "Disconnected from ASR service");
+ synchronized (mLock) {
+ if (!connected) {
+ if (mListener == null) {
+ Slog.i(TAG, "Connection to speech recognition service lost, but no "
+ + "#startListening has been invoked yet.");
+ return;
+ }
+
+ tryRespondWithError(mListener, SpeechRecognizer.ERROR_SERVER_DISCONNECTED);
+
+ resetStateLocked();
+ }
}
}
@@ -90,4 +284,119 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
protected long getAutoDisconnectTimeoutMs() {
return PERMANENT_BOUND_TIMEOUT_MS;
}
+
+ private void resetStateLocked() {
+ if (mRecordingInProgress && mPackageName != null) {
+ finishProxyOp(mPackageName, mFeatureId);
+ }
+
+ mListener = null;
+ mDelegatingListener = null;
+ mSessionInProgress = false;
+ mRecordingInProgress = false;
+ }
+
+ private int startProxyOp(String packageName, String featureId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mAppOpsManager.startProxyOp(
+ RECORD_AUDIO_APP_OP,
+ mCallingUid,
+ packageName,
+ featureId,
+ APP_OP_MESSAGE);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private void finishProxyOp(String packageName, String featureId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mAppOpsManager.finishProxyOp(
+ RECORD_AUDIO_APP_OP, mCallingUid, packageName, featureId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private static void tryRespondWithError(IRecognitionListener listener, int errorCode) {
+ if (DEBUG) {
+ Slog.i(TAG, "Responding with error " + errorCode);
+ }
+
+ try {
+ if (listener != null) {
+ listener.onError(errorCode);
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG,
+ String.format("Failed to respond with an error %d to the client", errorCode),
+ e);
+ }
+ }
+
+ private static class DelegatingListener extends IRecognitionListener.Stub {
+
+ private final IRecognitionListener mRemoteListener;
+ private final Runnable mOnSessionComplete;
+
+ DelegatingListener(IRecognitionListener listener, Runnable onSessionComplete) {
+ mRemoteListener = listener;
+ mOnSessionComplete = onSessionComplete;
+ }
+
+ @Override
+ public void onReadyForSpeech(Bundle params) throws RemoteException {
+ mRemoteListener.onReadyForSpeech(params);
+ }
+
+ @Override
+ public void onBeginningOfSpeech() throws RemoteException {
+ mRemoteListener.onBeginningOfSpeech();
+ }
+
+ @Override
+ public void onRmsChanged(float rmsdB) throws RemoteException {
+ mRemoteListener.onRmsChanged(rmsdB);
+ }
+
+ @Override
+ public void onBufferReceived(byte[] buffer) throws RemoteException {
+ mRemoteListener.onBufferReceived(buffer);
+ }
+
+ @Override
+ public void onEndOfSpeech() throws RemoteException {
+ mRemoteListener.onEndOfSpeech();
+ }
+
+ @Override
+ public void onError(int error) throws RemoteException {
+ if (DEBUG) {
+ Slog.i(TAG, String.format("Error %d during recognition session", error));
+ }
+ mOnSessionComplete.run();
+ mRemoteListener.onError(error);
+ }
+
+ @Override
+ public void onResults(Bundle results) throws RemoteException {
+ if (DEBUG) {
+ Slog.i(TAG, "#onResults invoked for a recognition session");
+ }
+ mOnSessionComplete.run();
+ mRemoteListener.onResults(results);
+ }
+
+ @Override
+ public void onPartialResults(Bundle results) throws RemoteException {
+ mRemoteListener.onPartialResults(results);
+ }
+
+ @Override
+ public void onEvent(int eventType, Bundle params) throws RemoteException {
+ mRemoteListener.onEvent(eventType, params);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
index 592ba9e616b8..dbe73546d748 100644
--- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
@@ -18,7 +18,9 @@ package com.android.server.speech;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.content.ComponentName;
import android.content.Context;
+import android.os.IBinder;
import android.os.UserHandle;
import android.speech.IRecognitionServiceManager;
import android.speech.IRecognitionServiceManagerCallback;
@@ -42,6 +44,7 @@ public final class SpeechRecognitionManagerService extends
public SpeechRecognitionManagerService(@NonNull Context context) {
super(context,
+ // TODO(b/176578753): think if we want to favor the particular service here.
new FrameworkResourcesServiceNameResolver(
context,
R.string.config_defaultOnDeviceSpeechRecognitionService),
@@ -63,11 +66,15 @@ public final class SpeechRecognitionManagerService extends
final class SpeechRecognitionManagerServiceStub extends IRecognitionServiceManager.Stub {
@Override
- public void createSession(IRecognitionServiceManagerCallback callback) {
+ public void createSession(
+ ComponentName componentName,
+ IBinder clientToken,
+ boolean onDevice,
+ IRecognitionServiceManagerCallback callback) {
int userId = UserHandle.getCallingUserId();
synchronized (mLock) {
SpeechRecognitionManagerServiceImpl service = getServiceForUserLocked(userId);
- service.createSessionLocked(callback);
+ service.createSessionLocked(componentName, clientToken, onDevice, callback);
}
}
}
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
index bcaf174b1d92..2656a3d32555 100644
--- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
@@ -24,30 +24,44 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
import android.speech.IRecognitionListener;
import android.speech.IRecognitionService;
import android.speech.IRecognitionServiceManagerCallback;
+import android.speech.SpeechRecognizer;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.infra.AbstractPerUserSystemService;
+import com.google.android.collect.Sets;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
final class SpeechRecognitionManagerServiceImpl extends
AbstractPerUserSystemService<SpeechRecognitionManagerServiceImpl,
SpeechRecognitionManagerService> {
-
private static final String TAG = SpeechRecognitionManagerServiceImpl.class.getSimpleName();
+ private static final int MAX_CONCURRENT_CONNECTIONS_BY_CLIENT = 10;
+
+ private final Object mLock = new Object();
+
+ @NonNull
@GuardedBy("mLock")
- @Nullable
- private RemoteSpeechRecognitionService mRemoteService;
+ private final Map<Integer, Set<RemoteSpeechRecognitionService>> mRemoteServicesByUid =
+ new HashMap<>();
SpeechRecognitionManagerServiceImpl(
@NonNull SpeechRecognitionManagerService master,
@NonNull Object lock, @UserIdInt int userId, boolean disabled) {
super(master, lock, userId);
- updateRemoteServiceLocked();
}
@GuardedBy("mLock")
@@ -67,92 +81,196 @@ final class SpeechRecognitionManagerServiceImpl extends
@Override // from PerUserSystemService
protected boolean updateLocked(boolean disabled) {
final boolean enabledChanged = super.updateLocked(disabled);
- updateRemoteServiceLocked();
return enabledChanged;
}
- /**
- * Updates the reference to the remote service.
- */
- @GuardedBy("mLock")
- private void updateRemoteServiceLocked() {
- if (mRemoteService != null) {
- if (mMaster.debug) {
- Slog.d(TAG, "updateRemoteService(): destroying old remote service");
- }
- mRemoteService.unbind();
- mRemoteService = null;
+ void createSessionLocked(
+ ComponentName componentName,
+ IBinder clientToken,
+ boolean onDevice,
+ IRecognitionServiceManagerCallback callback) {
+ if (mMaster.debug) {
+ Slog.i(TAG, String.format("#createSessionLocked, component=%s, onDevice=%s",
+ componentName, onDevice));
+ }
+
+ ComponentName serviceComponent = componentName;
+ if (onDevice) {
+ serviceComponent = getOnDeviceComponentNameLocked();
}
- }
- void createSessionLocked(IRecognitionServiceManagerCallback callback) {
- // TODO(b/176578753): check clients have record audio permission.
- // TODO(b/176578753): verify caller package is the one supplied
+ if (serviceComponent == null) {
+ tryRespondWithError(callback, SpeechRecognizer.ERROR_CLIENT);
+ return;
+ }
- RemoteSpeechRecognitionService service = ensureRemoteServiceLocked();
+ final int creatorCallingUid = Binder.getCallingUid();
+ Set<String> creatorPackageNames =
+ Sets.newArraySet(
+ getContext().getPackageManager().getPackagesForUid(creatorCallingUid));
+
+ RemoteSpeechRecognitionService service = createService(creatorCallingUid, serviceComponent);
if (service == null) {
- tryRespondWithError(callback);
+ tryRespondWithError(callback, SpeechRecognizer.ERROR_TOO_MANY_REQUESTS);
return;
}
+ IBinder.DeathRecipient deathRecipient =
+ () -> handleClientDeath(creatorCallingUid, service, true /* invoke #cancel */);
+
+ try {
+ clientToken.linkToDeath(deathRecipient, 0);
+ } catch (RemoteException e) {
+ // RemoteException == binder already died, schedule disconnect anyway.
+ handleClientDeath(creatorCallingUid, service, true /* invoke #cancel */);
+ }
+
service.connect().thenAccept(binderService -> {
if (binderService != null) {
try {
callback.onSuccess(new IRecognitionService.Stub() {
@Override
- public void startListening(Intent recognizerIntent,
+ public void startListening(
+ Intent recognizerIntent,
IRecognitionListener listener,
- String packageName, String featureId) throws RemoteException {
+ String packageName,
+ String featureId,
+ int callingUid) throws RemoteException {
+ verifyCallerIdentity(
+ creatorCallingUid, packageName, creatorPackageNames, listener);
+ if (callingUid != creatorCallingUid) {
+ listener.onError(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS);
+ return;
+ }
+
service.startListening(
recognizerIntent, listener, packageName, featureId);
}
@Override
- public void stopListening(IRecognitionListener listener,
+ public void stopListening(
+ IRecognitionListener listener,
String packageName,
String featureId) throws RemoteException {
+ verifyCallerIdentity(
+ creatorCallingUid, packageName, creatorPackageNames, listener);
+
service.stopListening(listener, packageName, featureId);
}
@Override
- public void cancel(IRecognitionListener listener,
+ public void cancel(
+ IRecognitionListener listener,
String packageName,
- String featureId) throws RemoteException {
- service.cancel(listener, packageName, featureId);
+ String featureId,
+ boolean isShutdown) throws RemoteException {
+ verifyCallerIdentity(
+ creatorCallingUid, packageName, creatorPackageNames, listener);
+
+ service.cancel(listener, packageName, featureId, isShutdown);
+
+ if (isShutdown) {
+ handleClientDeath(
+ creatorCallingUid,
+ service,
+ false /* invoke #cancel */);
+ clientToken.unlinkToDeath(deathRecipient, 0);
+ }
}
});
} catch (RemoteException e) {
Slog.e(TAG, "Error creating a speech recognition session", e);
- tryRespondWithError(callback);
+ tryRespondWithError(callback, SpeechRecognizer.ERROR_CLIENT);
}
} else {
- tryRespondWithError(callback);
+ tryRespondWithError(callback, SpeechRecognizer.ERROR_CLIENT);
}
});
}
+ private void verifyCallerIdentity(
+ int creatorCallingUid,
+ String packageName,
+ Set<String> creatorPackageNames,
+ IRecognitionListener listener) throws RemoteException {
+ if (creatorCallingUid != Binder.getCallingUid()
+ || !creatorPackageNames.contains(packageName)) {
+ listener.onError(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS);
+ }
+ }
+
+ private void handleClientDeath(
+ int callingUid,
+ RemoteSpeechRecognitionService service, boolean invokeCancel) {
+ if (invokeCancel) {
+ service.shutdown();
+ }
+ removeService(callingUid, service);
+ }
+
@GuardedBy("mLock")
@Nullable
- private RemoteSpeechRecognitionService ensureRemoteServiceLocked() {
- if (mRemoteService == null) {
- final String serviceName = getComponentNameLocked();
- if (serviceName == null) {
- if (mMaster.verbose) {
- Slog.v(TAG, "ensureRemoteServiceLocked(): no service component name.");
- }
+ private ComponentName getOnDeviceComponentNameLocked() {
+ final String serviceName = getComponentNameLocked();
+ if (serviceName == null) {
+ if (mMaster.verbose) {
+ Slog.v(TAG, "ensureRemoteServiceLocked(): no service component name.");
+ }
+ return null;
+ }
+ return ComponentName.unflattenFromString(serviceName);
+ }
+
+ private RemoteSpeechRecognitionService createService(
+ int callingUid, ComponentName serviceComponent) {
+ synchronized (mLock) {
+ Set<RemoteSpeechRecognitionService> servicesForClient =
+ mRemoteServicesByUid.get(callingUid);
+
+ if (servicesForClient != null
+ && servicesForClient.size() >= MAX_CONCURRENT_CONNECTIONS_BY_CLIENT) {
return null;
}
- final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
- mRemoteService =
- new RemoteSpeechRecognitionService(getContext(), serviceComponent, mUserId);
+
+ if (servicesForClient != null) {
+ Optional<RemoteSpeechRecognitionService> existingService =
+ servicesForClient
+ .stream()
+ .filter(service ->
+ service.getServiceComponentName().equals(serviceComponent))
+ .findFirst();
+ if (existingService.isPresent()) {
+ return existingService.get();
+ }
+ }
+
+ RemoteSpeechRecognitionService service =
+ new RemoteSpeechRecognitionService(
+ getContext(), serviceComponent, getUserId(), callingUid);
+
+ Set<RemoteSpeechRecognitionService> valuesByCaller =
+ mRemoteServicesByUid.computeIfAbsent(callingUid, key -> new HashSet<>());
+ valuesByCaller.add(service);
+
+ return service;
+ }
+ }
+
+ private void removeService(int callingUid, RemoteSpeechRecognitionService service) {
+ synchronized (mLock) {
+ Set<RemoteSpeechRecognitionService> valuesByCaller =
+ mRemoteServicesByUid.get(callingUid);
+ if (valuesByCaller != null) {
+ valuesByCaller.remove(service);
+ }
}
- return mRemoteService;
}
- private static void tryRespondWithError(IRecognitionServiceManagerCallback callback) {
+ private static void tryRespondWithError(IRecognitionServiceManagerCallback callback,
+ int errorCode) {
try {
- callback.onError();
+ callback.onError(errorCode);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to respond with error");
}
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 263776c63db6..342dbfa3f59d 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -90,6 +90,7 @@ import android.net.NetworkTemplate;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.BatteryStats;
+import android.os.BatteryStatsInternal;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -138,6 +139,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BinderCallsStats.ExportedCallStat;
+import com.android.internal.os.KernelCpuBpfTracking;
import com.android.internal.os.KernelCpuThreadReader;
import com.android.internal.os.KernelCpuThreadReaderDiff;
import com.android.internal.os.KernelCpuThreadReaderSettingsObserver;
@@ -152,6 +154,7 @@ import com.android.internal.os.LooperStats;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.StoragedUidIoStatsReader;
+import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.role.RoleManagerLocal;
@@ -457,6 +460,8 @@ public class StatsPullAtomService extends SystemService {
synchronized (mCpuTimePerUidFreqLock) {
return pullCpuTimePerUidFreqLocked(atomTag, data);
}
+ case FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER:
+ return pullCpuCyclesPerThreadGroupCluster(atomTag, data);
case FrameworkStatsLog.CPU_ACTIVE_TIME:
synchronized (mCpuActiveTimeLock) {
return pullCpuActiveTimeLocked(atomTag, data);
@@ -781,6 +786,7 @@ public class StatsPullAtomService extends SystemService {
registerCpuTimePerUid();
registerCpuCyclesPerUidCluster();
registerCpuTimePerUidFreq();
+ registerCpuCyclesPerThreadGroupCluster();
registerCpuActiveTime();
registerCpuClusterTime();
registerWifiActivityInfo();
@@ -1450,7 +1456,7 @@ public class StatsPullAtomService extends SystemService {
}
private void registerCpuTimePerClusterFreq() {
- if (KernelCpuTotalBpfMapReader.isSupported()) {
+ if (KernelCpuBpfTracking.isSupported()) {
int tagId = FrameworkStatsLog.CPU_TIME_PER_CLUSTER_FREQ;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {3})
@@ -1510,6 +1516,7 @@ public class StatsPullAtomService extends SystemService {
}
int pullCpuCyclesPerUidClusterLocked(int atomTag, List<StatsEvent> pulledData) {
+ // TODO(b/179485697): Remove power profile dependency.
PowerProfile powerProfile = new PowerProfile(mContext);
// Frequency index to frequency mapping.
long[] freqs = mCpuUidFreqTimeReader.readFreqs(powerProfile);
@@ -1606,9 +1613,6 @@ public class StatsPullAtomService extends SystemService {
// Aggregate times for the same uids.
SparseArray<long[]> aggregated = new SparseArray<>();
mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
- // For uids known to be aggregated from many entries allow mutating in place to avoid
- // many copies. Otherwise, copy before aggregating.
- boolean mutateInPlace = false;
if (UserHandle.isIsolated(uid)) {
// Skip individual isolated uids because they are recycled and quickly removed from
// the underlying data source.
@@ -1616,26 +1620,18 @@ public class StatsPullAtomService extends SystemService {
} else if (UserHandle.isSharedAppGid(uid)) {
// All shared app gids are accounted together.
uid = LAST_SHARED_APPLICATION_GID;
- mutateInPlace = true;
} else {
// Everything else is accounted under their base uid.
uid = UserHandle.getAppId(uid);
}
long[] aggCpuFreqTimeMs = aggregated.get(uid);
- if (aggCpuFreqTimeMs != null) {
- if (!mutateInPlace) {
- aggCpuFreqTimeMs = Arrays.copyOf(aggCpuFreqTimeMs, cpuFreqTimeMs.length);
- aggregated.put(uid, aggCpuFreqTimeMs);
- }
- for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
- aggCpuFreqTimeMs[freqIndex] += cpuFreqTimeMs[freqIndex];
- }
- } else {
- if (mutateInPlace) {
- cpuFreqTimeMs = Arrays.copyOf(cpuFreqTimeMs, cpuFreqTimeMs.length);
- }
- aggregated.put(uid, cpuFreqTimeMs);
+ if (aggCpuFreqTimeMs == null) {
+ aggCpuFreqTimeMs = new long[cpuFreqTimeMs.length];
+ aggregated.put(uid, aggCpuFreqTimeMs);
+ }
+ for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
+ aggCpuFreqTimeMs[freqIndex] += cpuFreqTimeMs[freqIndex];
}
});
@@ -1653,6 +1649,82 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
+ private void registerCpuCyclesPerThreadGroupCluster() {
+ if (KernelCpuBpfTracking.isSupported()) {
+ int tagId = FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER;
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
+ .setAdditiveFields(new int[] {3, 4})
+ .build();
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ metadata,
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl
+ );
+ }
+ }
+
+ int pullCpuCyclesPerThreadGroupCluster(int atomTag, List<StatsEvent> pulledData) {
+ // TODO(b/179485697): Remove power profile dependency.
+ PowerProfile powerProfile = new PowerProfile(mContext);
+ // Frequency index to frequency mapping.
+ long[] freqs = mCpuUidFreqTimeReader.readFreqs(powerProfile);
+ if (freqs == null) {
+ return StatsManager.PULL_SKIP;
+ }
+ // Frequency index to cluster mapping.
+ int[] freqClusters = new int[freqs.length];
+ // Number of clusters.
+ int clusters;
+
+ // Initialize frequency mappings.
+ {
+ int cluster = 0;
+ long lastFreq = -1;
+ for (int freqIndex = 0; freqIndex < freqs.length; ++freqIndex) {
+ long currFreq = freqs[freqIndex];
+ if (currFreq <= lastFreq) {
+ cluster++;
+ }
+ freqClusters[freqIndex] = cluster;
+ lastFreq = currFreq;
+ }
+
+ clusters = cluster + 1;
+ }
+
+ SystemServiceCpuThreadTimes times = LocalServices.getService(BatteryStatsInternal.class)
+ .getSystemServiceCpuThreadTimes();
+ if (times == null) {
+ return StatsManager.PULL_SKIP;
+ }
+
+ addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData,
+ FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER,
+ times.threadCpuTimesUs, clusters, freqs, freqClusters);
+ addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData,
+ FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER_BINDER,
+ times.binderThreadCpuTimesUs, clusters, freqs, freqClusters);
+
+ return StatsManager.PULL_SUCCESS;
+ }
+
+ private static void addCpuCyclesPerThreadGroupClusterAtoms(
+ int atomTag, List<StatsEvent> pulledData, int threadGroup, long[] cpuTimesUs,
+ int clusters, long[] freqs, int[] freqClusters) {
+ long[] aggregatedCycles = new long[clusters];
+ long[] aggregatedTimesUs = new long[clusters];
+ for (int i = 0; i < cpuTimesUs.length; ++i) {
+ aggregatedCycles[freqClusters[i]] += freqs[i] * cpuTimesUs[i] / 1_000;
+ aggregatedTimesUs[freqClusters[i]] += cpuTimesUs[i];
+ }
+ for (int cluster = 0; cluster < clusters; ++cluster) {
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ atomTag, threadGroup, cluster, aggregatedCycles[cluster] / 1_000_000L,
+ aggregatedTimesUs[cluster] / 1_000));
+ }
+ }
+
private void registerCpuActiveTime() {
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
@@ -2062,7 +2134,9 @@ public class StatsPullAtomService extends SystemService {
metrics.kernelStackKb,
metrics.totalIonKb,
metrics.unaccountedKb,
- metrics.gpuTotalUsageKb));
+ metrics.gpuTotalUsageKb,
+ metrics.gpuPrivateAllocationsKb,
+ metrics.dmaBufTotalExportedKb));
return StatsManager.PULL_SUCCESS;
}
@@ -2509,7 +2583,6 @@ public class StatsPullAtomService extends SystemService {
try {
// force procstats to flush & combine old files into one store
long lastHighWaterMark = readProcStatsHighWaterMark(section);
- List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS];
for (int i = 0; i < protoStreams.length; i++) {
@@ -2519,7 +2592,7 @@ public class StatsPullAtomService extends SystemService {
ProcessStats procStats = new ProcessStats(false);
// Force processStatsService to aggregate all in-storage and in-memory data.
long highWaterMark = processStatsService.getCommittedStatsMerged(
- lastHighWaterMark, section, true, statsFiles, procStats);
+ lastHighWaterMark, section, true, null, procStats);
procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE);
for (int i = 0; i < protoStreams.length; i++) {
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 1e80c4fe89fb..628c1d61ce9a 100644
--- a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
@@ -28,6 +28,8 @@ final class SystemMemoryUtil {
static Metrics getMetrics() {
int totalIonKb = (int) Debug.getIonHeapsSizeKb();
int gpuTotalUsageKb = (int) Debug.getGpuTotalUsageKb();
+ int gpuDmaBufUsageKb = (int) Debug.getGpuDmaBufUsageKb();
+ int dmaBufTotalExportedKb = (int) Debug.getDmabufTotalExportedKb();
long[] mInfos = new long[Debug.MEMINFO_COUNT];
Debug.getMemInfo(mInfos);
@@ -49,14 +51,36 @@ final class SystemMemoryUtil {
+ mInfos[Debug.MEMINFO_SLAB_UNRECLAIMABLE]
+ kReclaimableKb
+ mInfos[Debug.MEMINFO_VM_ALLOC_USED]
- + mInfos[Debug.MEMINFO_PAGE_TABLES]
- + Math.max(totalIonKb, 0);
+ + mInfos[Debug.MEMINFO_PAGE_TABLES];
if (!Debug.isVmapStack()) {
// See b/146088882
accountedKb += mInfos[Debug.MEMINFO_KERNEL_STACK];
}
+ int gpuPrivateAllocationsKb = -1;
+ if (gpuTotalUsageKb >= 0 && gpuDmaBufUsageKb >= 0) {
+ gpuPrivateAllocationsKb = gpuTotalUsageKb - gpuDmaBufUsageKb;
+ }
+ // If we can distinguish gpu private allocs it means the dmabuf metrics
+ // are supported already.
+ if (dmaBufTotalExportedKb >= 0 && gpuPrivateAllocationsKb >= 0) {
+ // If we can calculate the overlap between dma memory and gpu
+ // drivers we can do more accurate tracking. But this is only
+ // available on 5.4+ kernels.
+ accountedKb += dmaBufTotalExportedKb + gpuPrivateAllocationsKb;
+ } else {
+ // If we cannot distinguish, accept that we will double count the
+ // dma buffers also used by the gpu driver.
+ accountedKb += Math.max(0, gpuTotalUsageKb);
+ if (dmaBufTotalExportedKb >= 0) {
+ accountedKb += dmaBufTotalExportedKb;
+ } else if (totalIonKb >= 0) {
+ // ION is a subset of total exported dmabuf memory.
+ accountedKb += totalIonKb;
+ }
+ }
+
Metrics result = new Metrics();
result.unreclaimableSlabKb = (int) mInfos[Debug.MEMINFO_SLAB_UNRECLAIMABLE];
result.vmallocUsedKb = (int) mInfos[Debug.MEMINFO_VM_ALLOC_USED];
@@ -64,6 +88,8 @@ final class SystemMemoryUtil {
result.kernelStackKb = (int) mInfos[Debug.MEMINFO_KERNEL_STACK];
result.totalIonKb = totalIonKb;
result.gpuTotalUsageKb = gpuTotalUsageKb;
+ result.gpuPrivateAllocationsKb = gpuPrivateAllocationsKb;
+ result.dmaBufTotalExportedKb = dmaBufTotalExportedKb;
result.unaccountedKb = (int) (mInfos[Debug.MEMINFO_TOTAL] - accountedKb);
return result;
}
@@ -75,6 +101,8 @@ final class SystemMemoryUtil {
public int kernelStackKb;
public int totalIonKb;
public int gpuTotalUsageKb;
+ public int gpuPrivateAllocationsKb;
+ public int dmaBufTotalExportedKb;
public int unaccountedKb;
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 4a346732ebe7..ae0150c10a08 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1502,6 +1502,18 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
+ public void onNotificationFeedbackReceived(String key, Bundle feedback) {
+ enforceStatusBarService();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mNotificationDelegate.onNotificationFeedbackReceived(key, feedback);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+
+ @Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
(new StatusBarShellCommand(this, mContext)).exec(
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index eb4a0501a953..0087c0c29853 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -158,6 +158,29 @@ public final class StorageSessionController {
}
/**
+ * Called when {@code packageName} is about to ANR
+ *
+ * @return ANR dialog delay in milliseconds
+ */
+ public long getAnrDelayMillis(String packageName, int uid)
+ throws ExternalStorageServiceException {
+ synchronized (mLock) {
+ int size = mConnections.size();
+ for (int i = 0; i < size; i++) {
+ int key = mConnections.keyAt(i);
+ StorageUserConnection connection = mConnections.get(key);
+ if (connection != null) {
+ long delay = connection.getAnrDelayMillis(packageName, uid);
+ if (delay > 0) {
+ return delay;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
* Removes and returns the {@link StorageUserConnection} for {@code vol}.
*
* Does nothing if {@link #shouldHandle} is {@code false}
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index 13cceeed84e6..709d558ea0bc 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -16,6 +16,7 @@
package com.android.server.storage;
+import static android.service.storage.ExternalStorageService.EXTRA_ANR_TIMEOUT_MS;
import static android.service.storage.ExternalStorageService.EXTRA_ERROR;
import static android.service.storage.ExternalStorageService.FLAG_SESSION_ATTRIBUTE_INDEXABLE;
import static android.service.storage.ExternalStorageService.FLAG_SESSION_TYPE_FUSE;
@@ -143,6 +144,24 @@ public final class StorageUserConnection {
}
/**
+ * Called when {@code packageName} is about to ANR
+ *
+ * @return ANR dialog delay in milliseconds
+ */
+ public long getAnrDelayMillis(String packageName, int uid)
+ throws ExternalStorageServiceException {
+ synchronized (mSessionsLock) {
+ for (String sessionId : mSessions.keySet()) {
+ long delay = mActiveConnection.getAnrDelayMillis(packageName, uid);
+ if (delay > 0) {
+ return delay;
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
* Removes a session without ending it or waiting for exit.
*
* This should only be used if the session has certainly been ended because the volume was
@@ -234,6 +253,9 @@ public final class StorageUserConnection {
@GuardedBy("mLock")
private final ArrayList<CompletableFuture<Void>> mOutstandingOps = new ArrayList<>();
+ @GuardedBy("mLock")
+ private final ArrayList<CompletableFuture<Long>> mOutstandingTimeoutOps = new ArrayList<>();
+
@Override
public void close() {
ServiceConnection oldConnection = null;
@@ -250,6 +272,9 @@ public final class StorageUserConnection {
for (CompletableFuture<Void> op : mOutstandingOps) {
op.cancel(true);
}
+ for (CompletableFuture<Long> op : mOutstandingTimeoutOps) {
+ op.cancel(true);
+ }
mOutstandingOps.clear();
}
@@ -264,27 +289,44 @@ public final class StorageUserConnection {
}
}
- private void waitForAsync(AsyncStorageServiceCall asyncCall) throws Exception {
- CompletableFuture<IExternalStorageService> serviceFuture = connectIfNeeded();
+ private void waitForAsyncVoid(AsyncStorageServiceCall asyncCall) throws Exception {
CompletableFuture<Void> opFuture = new CompletableFuture<>();
+ RemoteCallback callback = new RemoteCallback(result -> setResult(result, opFuture));
+
+ waitForAsync(asyncCall, callback, opFuture, mOutstandingOps,
+ DEFAULT_REMOTE_TIMEOUT_SECONDS);
+ }
+
+ private long waitForAsyncLong(AsyncStorageServiceCall asyncCall) throws Exception {
+ CompletableFuture<Long> opFuture = new CompletableFuture<>();
+ RemoteCallback callback =
+ new RemoteCallback(result -> setTimeoutResult(result, opFuture));
+
+ return waitForAsync(asyncCall, callback, opFuture, mOutstandingTimeoutOps,
+ 1 /* timeoutSeconds */);
+ }
+
+ private <T> T waitForAsync(AsyncStorageServiceCall asyncCall, RemoteCallback callback,
+ CompletableFuture<T> opFuture, ArrayList<CompletableFuture<T>> outstandingOps,
+ long timeoutSeconds) throws Exception {
+ CompletableFuture<IExternalStorageService> serviceFuture = connectIfNeeded();
try {
synchronized (mLock) {
- mOutstandingOps.add(opFuture);
+ outstandingOps.add(opFuture);
}
- serviceFuture.thenCompose(service -> {
+ return serviceFuture.thenCompose(service -> {
try {
- asyncCall.run(service,
- new RemoteCallback(result -> setResult(result, opFuture)));
+ asyncCall.run(service, callback);
} catch (RemoteException e) {
opFuture.completeExceptionally(e);
}
return opFuture;
- }).get(DEFAULT_REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ }).get(timeoutSeconds, TimeUnit.SECONDS);
} finally {
synchronized (mLock) {
- mOutstandingOps.remove(opFuture);
+ outstandingOps.remove(opFuture);
}
}
}
@@ -292,9 +334,9 @@ public final class StorageUserConnection {
public void startSession(Session session, ParcelFileDescriptor fd)
throws ExternalStorageServiceException {
try {
- waitForAsync((service, callback) -> service.startSession(session.sessionId,
+ waitForAsyncVoid((service, callback) -> service.startSession(session.sessionId,
FLAG_SESSION_TYPE_FUSE | FLAG_SESSION_ATTRIBUTE_INDEXABLE,
- fd, session.upperPath, session.lowerPath, callback));
+ fd, session.upperPath, session.lowerPath, callback));
} catch (Exception e) {
throw new ExternalStorageServiceException("Failed to start session: " + session, e);
} finally {
@@ -308,7 +350,7 @@ public final class StorageUserConnection {
public void endSession(Session session) throws ExternalStorageServiceException {
try {
- waitForAsync((service, callback) ->
+ waitForAsyncVoid((service, callback) ->
service.endSession(session.sessionId, callback));
} catch (Exception e) {
throw new ExternalStorageServiceException("Failed to end session: " + session, e);
@@ -319,7 +361,7 @@ public final class StorageUserConnection {
public void notifyVolumeStateChanged(String sessionId, StorageVolume vol) throws
ExternalStorageServiceException {
try {
- waitForAsync((service, callback) ->
+ waitForAsyncVoid((service, callback) ->
service.notifyVolumeStateChanged(sessionId, vol, callback));
} catch (Exception e) {
throw new ExternalStorageServiceException("Failed to notify volume state changed "
@@ -330,7 +372,7 @@ public final class StorageUserConnection {
public void freeCache(String sessionId, String volumeUuid, long bytes)
throws ExternalStorageServiceException {
try {
- waitForAsync((service, callback) ->
+ waitForAsyncVoid((service, callback) ->
service.freeCache(sessionId, volumeUuid, bytes, callback));
} catch (Exception e) {
throw new ExternalStorageServiceException("Failed to free " + bytes
@@ -338,6 +380,27 @@ public final class StorageUserConnection {
}
}
+ public long getAnrDelayMillis(String packgeName, int uid)
+ throws ExternalStorageServiceException {
+ try {
+ return waitForAsyncLong((service, callback) ->
+ service.getAnrDelayMillis(packgeName, uid, callback));
+ } catch (Exception e) {
+ throw new ExternalStorageServiceException("Failed to notify app not responding: "
+ + packgeName, e);
+ }
+ }
+
+ private void setTimeoutResult(Bundle result, CompletableFuture<Long> future) {
+ ParcelableException ex = result.getParcelable(EXTRA_ERROR);
+ if (ex != null) {
+ future.completeExceptionally(ex);
+ } else {
+ long timeoutMs = result.getLong(EXTRA_ANR_TIMEOUT_MS);
+ future.complete(timeoutMs);
+ }
+ }
+
private void setResult(Bundle result, CompletableFuture<Void> future) {
ParcelableException ex = result.getParcelable(EXTRA_ERROR);
if (ex != null) {
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index bbbd19f7a49e..b210339adf79 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -18,7 +18,7 @@ package com.android.server.timedetector;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.timedetector.ExternalTimeSuggestion;
+import android.app.time.ExternalTimeSuggestion;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ITimeDetectorService;
import android.app.timedetector.ManualTimeSuggestion;
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index 6a4c276de2dd..792f372b0c49 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -18,7 +18,7 @@ package com.android.server.timedetector;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.app.timedetector.ExternalTimeSuggestion;
+import android.app.time.ExternalTimeSuggestion;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 7cd4184f2bc9..c4c620c41918 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -23,7 +23,7 @@ import static java.util.stream.Collectors.joining;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlarmManager;
-import android.app.timedetector.ExternalTimeSuggestion;
+import android.app.time.ExternalTimeSuggestion;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
index a54288f73018..e463ee22452d 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
@@ -33,9 +33,11 @@ import java.util.Objects;
*/
class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment {
- private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
+ // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented
+ private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(1);
+ // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented
private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
- Duration.ofMinutes(1);
+ Duration.ofSeconds(20);
private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
@NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal;
diff --git a/services/core/java/com/android/server/tracing/OWNERS b/services/core/java/com/android/server/tracing/OWNERS
new file mode 100644
index 000000000000..f5de4eb05c54
--- /dev/null
+++ b/services/core/java/com/android/server/tracing/OWNERS
@@ -0,0 +1,2 @@
+cfijalkovich@google.com
+carmenjackson@google.com
diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
new file mode 100644
index 000000000000..8f227489740f
--- /dev/null
+++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.tracing;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.UserHandle;
+import android.tracing.ITracingServiceProxy;
+import android.util.Log;
+
+import com.android.server.SystemService;
+
+/**
+ * TracingServiceProxy is the system_server intermediary between the Perfetto tracing daemon and the
+ * system tracing app Traceur.
+ *
+ * @hide
+ */
+public class TracingServiceProxy extends SystemService {
+ private static final String TAG = "TracingServiceProxy";
+
+ public static final String TRACING_SERVICE_PROXY_BINDER_NAME = "tracing.proxy";
+
+ private static final String TRACING_APP_PACKAGE_NAME = "com.android.traceur";
+ private static final String TRACING_APP_ACTIVITY = "com.android.traceur.StopTraceService";
+
+ // Keep this in sync with the definitions in TraceService
+ private static final String INTENT_ACTION_NOTIFY_SESSION_STOPPED =
+ "com.android.traceur.NOTIFY_SESSION_STOPPED";
+ private static final String INTENT_ACTION_NOTIFY_SESSION_STOLEN =
+ "com.android.traceur.NOTIFY_SESSION_STOLEN";
+
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+
+ private final ITracingServiceProxy.Stub mTracingServiceProxy = new ITracingServiceProxy.Stub() {
+ /**
+ * Notifies system tracing app that a tracing session has ended. If a session is repurposed
+ * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
+ * there is no buffer available to dump.
+ */
+ @Override
+ public void notifyTraceSessionEnded(boolean sessionStolen) {
+ notifyTraceur(sessionStolen);
+ }
+ };
+
+ public TracingServiceProxy(Context context) {
+ super(context);
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(TRACING_SERVICE_PROXY_BINDER_NAME, mTracingServiceProxy);
+ }
+
+ private void notifyTraceur(boolean sessionStolen) {
+ final Intent intent = new Intent();
+
+ try {
+ // Validate that Traceur is a system app.
+ PackageInfo info = mPackageManager.getPackageInfo(TRACING_APP_PACKAGE_NAME,
+ PackageManager.MATCH_SYSTEM_ONLY);
+
+ intent.setClassName(info.packageName, TRACING_APP_ACTIVITY);
+ if (sessionStolen) {
+ intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOLEN);
+ } else {
+ intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOPPED);
+ }
+
+ try {
+ mContext.startForegroundServiceAsUser(intent, UserHandle.SYSTEM);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Failed to notifyTraceSessionEnded", e);
+ }
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Failed to locate Traceur", e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index da3e48ad2c0d..4fbc79507b95 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -703,7 +703,7 @@ public class TrustManagerService extends SystemService {
if (changed) {
dispatchDeviceLocked(userId, locked);
- mAuthorizationService.onLockScreenEvent(locked, userId, null);
+ Authorization.onLockScreenEvent(locked, userId, null);
KeyStore.getInstance().onUserLockedStateChanged(userId, locked);
// Also update the user's profiles who have unified challenge, since they
// share the same unlocked state (see {@link #isDeviceLocked(int)})
@@ -1261,7 +1261,7 @@ public class TrustManagerService extends SystemService {
mDeviceLockedForUser.put(userId, locked);
}
- mAuthorizationService.onLockScreenEvent(locked, userId, null);
+ Authorization.onLockScreenEvent(locked, userId, null);
KeyStore.getInstance().onUserLockedStateChanged(userId, locked);
if (locked) {
diff --git a/services/core/java/com/android/server/utils/Watchable.java b/services/core/java/com/android/server/utils/Watchable.java
index f936693bd621..44a74593cda2 100644
--- a/services/core/java/com/android/server/utils/Watchable.java
+++ b/services/core/java/com/android/server/utils/Watchable.java
@@ -19,8 +19,8 @@ package com.android.server.utils;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Build;
+import android.util.Log;
-import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
/**
@@ -61,40 +61,54 @@ public interface Watchable {
public void dispatchChange(@Nullable Watchable what);
/**
- * Return true if the field is tagged with @Watched
- */
- private static boolean isWatched(Field f) {
- for (Annotation a : f.getDeclaredAnnotations()) {
- if (a.annotationType().equals(Watched.class)) {
- return true;
- }
- }
- return false;
- }
-
- /**
* Verify that all @Watched {@link Watchable} attributes are being watched by this
* class. This requires reflection and only runs in engineering or user debug
* builds.
+ * @param base The object that contains watched attributes.
+ * @param observer The {@link Watcher} that should be watching these attributes.
+ * @param logOnly If true then log errors; if false then throw an RuntimeExecption on error.
*/
- static void verifyWatchedAttributes(Object base, Watcher observer) {
- if (Build.IS_ENG || Build.IS_USERDEBUG) {
- for (Field f : base.getClass().getDeclaredFields()) {
+ static void verifyWatchedAttributes(Object base, Watcher observer, boolean logOnly) {
+ if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
+ return;
+ }
+ for (Field f : base.getClass().getDeclaredFields()) {
+ if (f.getAnnotation(Watched.class) != null) {
+ final String fn = base.getClass().getName() + "." + f.getName();
try {
- final boolean flagged = isWatched(f);
+ f.setAccessible(true);
final Object o = f.get(base);
- final boolean watchable = o instanceof Watchable;
- if (flagged && watchable) {
- Watchable attr = (Watchable) f.get(base);
+ if (o instanceof Watchable) {
+ Watchable attr = (Watchable) (o);
if (attr != null && !attr.isRegisteredObserver(observer)) {
- throw new RuntimeException(f.getName() + " missing an observer");
+ if (logOnly) {
+ Log.e("Watchable", fn + " missing an observer");
+ } else {
+ throw new RuntimeException("Watchable " + fn
+ + " missing an observer");
+ }
}
}
} catch (IllegalAccessException e) {
// The field is protected; ignore it. Other exceptions that may be thrown by
// Field.get() are allowed to roll up.
+ if (logOnly) {
+ Log.e("Watchable", fn + " not visible");
+ } else {
+ throw new RuntimeException("Watchable " + fn + " not visible");
+ }
}
}
}
}
+
+ /**
+ * Verify that all @Watched {@link Watchable} attributes are being watched by this
+ * class. This calls verifyWatchedAttributes() with logOnly set to false.
+ * @param base The object that contains watched attributes.
+ * @param observer The {@link Watcher} that should be watching these attributes.
+ */
+ static void verifyWatchedAttributes(Object base, Watcher observer) {
+ verifyWatchedAttributes(base, observer, false);
+ }
}
diff --git a/services/core/java/com/android/server/vcn/Android.bp b/services/core/java/com/android/server/vcn/Android.bp
index 5ed204fd7640..ab5da3ee45b4 100644
--- a/services/core/java/com/android/server/vcn/Android.bp
+++ b/services/core/java/com/android/server/vcn/Android.bp
@@ -1,4 +1,13 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "framework-vcn-util-sources",
srcs: ["util/**/*.java"],
-} \ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 5ec527a7d6c4..6ad30b544257 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -16,12 +16,15 @@
package com.android.server.vcn;
+import static com.android.server.VcnManagementService.VDBG;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnManager.VcnErrorCode;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
@@ -29,7 +32,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
-import com.android.server.VcnManagementService.VcnSafemodeCallback;
+import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import java.util.Collections;
@@ -85,18 +88,18 @@ public class Vcn extends Handler {
private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
/**
- * Causes this VCN to immediately enter Safemode.
+ * Causes this VCN to immediately enter safe mode.
*
- * <p>Upon entering Safemode, the VCN will unregister its RequestListener, tear down all of its
- * VcnGatewayConnections, and notify VcnManagementService that it is in Safemode.
+ * <p>Upon entering safe mode, the VCN will unregister its RequestListener, tear down all of its
+ * VcnGatewayConnections, and notify VcnManagementService that it is in safe mode.
*/
- private static final int MSG_CMD_ENTER_SAFEMODE = MSG_CMD_BASE + 1;
+ private static final int MSG_CMD_ENTER_SAFE_MODE = MSG_CMD_BASE + 1;
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final Dependencies mDeps;
@NonNull private final VcnNetworkRequestListener mRequestListener;
- @NonNull private final VcnSafemodeCallback mVcnSafemodeCallback;
+ @NonNull private final VcnCallback mVcnCallback;
@NonNull
private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
@@ -124,14 +127,8 @@ public class Vcn extends Handler {
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot,
- @NonNull VcnSafemodeCallback vcnSafemodeCallback) {
- this(
- vcnContext,
- subscriptionGroup,
- config,
- snapshot,
- vcnSafemodeCallback,
- new Dependencies());
+ @NonNull VcnCallback vcnCallback) {
+ this(vcnContext, subscriptionGroup, config, snapshot, vcnCallback, new Dependencies());
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -140,13 +137,12 @@ public class Vcn extends Handler {
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot,
- @NonNull VcnSafemodeCallback vcnSafemodeCallback,
+ @NonNull VcnCallback vcnCallback,
@NonNull Dependencies deps) {
super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
- mVcnSafemodeCallback =
- Objects.requireNonNull(vcnSafemodeCallback, "Missing vcnSafemodeCallback");
+ mVcnCallback = Objects.requireNonNull(vcnCallback, "Missing vcnCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps");
mRequestListener = new VcnNetworkRequestListener();
@@ -215,8 +211,8 @@ public class Vcn extends Handler {
case MSG_CMD_TEARDOWN:
handleTeardown();
break;
- case MSG_CMD_ENTER_SAFEMODE:
- handleEnterSafemode();
+ case MSG_CMD_ENTER_SAFE_MODE:
+ handleEnterSafeMode();
break;
default:
Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
@@ -225,7 +221,7 @@ public class Vcn extends Handler {
private void handleConfigUpdated(@NonNull VcnConfig config) {
// TODO: Add a dump function in VcnConfig that omits PII. Until then, use hashCode()
- Slog.v(getLogTag(), String.format("Config updated: config = %s", config.hashCode()));
+ Slog.v(getLogTag(), "Config updated: config = " + config.hashCode());
mConfig = config;
@@ -242,26 +238,38 @@ public class Vcn extends Handler {
mIsActive.set(false);
}
- private void handleEnterSafemode() {
+ private void handleEnterSafeMode() {
handleTeardown();
- mVcnSafemodeCallback.onEnteredSafemode();
+ mVcnCallback.onEnteredSafeMode();
}
private void handleNetworkRequested(
@NonNull NetworkRequest request, int score, int providerId) {
if (score > getNetworkScore()) {
- Slog.v(getLogTag(),
- "Request already satisfied by higher-scoring (" + score + ") network from "
- + "provider " + providerId + ": " + request);
+ if (VDBG) {
+ Slog.v(
+ getLogTag(),
+ "Request already satisfied by higher-scoring ("
+ + score
+ + ") network from "
+ + "provider "
+ + providerId
+ + ": "
+ + request);
+ }
return;
}
// If preexisting VcnGatewayConnection(s) satisfy request, return
for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
- Slog.v(getLogTag(),
- "Request already satisfied by existing VcnGatewayConnection: " + request);
+ if (VDBG) {
+ Slog.v(
+ getLogTag(),
+ "Request already satisfied by existing VcnGatewayConnection: "
+ + request);
+ }
return;
}
}
@@ -308,7 +316,7 @@ public class Vcn extends Handler {
}
private String getLogTag() {
- return String.format("%s [%d]", TAG, mSubscriptionGroup.hashCode());
+ return TAG + " [" + mSubscriptionGroup.hashCode() + "]";
}
/** Retrieves the network score for a VCN Network */
@@ -322,14 +330,31 @@ public class Vcn extends Handler {
/** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
@VisibleForTesting(visibility = Visibility.PACKAGE)
public interface VcnGatewayStatusCallback {
- /** Called by a VcnGatewayConnection to indicate that it has entered Safemode. */
- void onEnteredSafemode();
+ /** Called by a VcnGatewayConnection to indicate that it has entered safe mode. */
+ void onEnteredSafeMode();
+
+ /** Callback by a VcnGatewayConnection to indicate that an error occurred. */
+ void onGatewayConnectionError(
+ @NonNull int[] networkCapabilities,
+ @VcnErrorCode int errorCode,
+ @Nullable String exceptionClass,
+ @Nullable String exceptionMessage);
}
private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
@Override
- public void onEnteredSafemode() {
- sendMessage(obtainMessage(MSG_CMD_ENTER_SAFEMODE));
+ public void onEnteredSafeMode() {
+ sendMessage(obtainMessage(MSG_CMD_ENTER_SAFE_MODE));
+ }
+
+ @Override
+ public void onGatewayConnectionError(
+ @NonNull int[] networkCapabilities,
+ @VcnErrorCode int errorCode,
+ @Nullable String exceptionClass,
+ @Nullable String exceptionMessage) {
+ mVcnCallback.onGatewayConnectionError(
+ networkCapabilities, errorCode, exceptionClass, exceptionMessage);
}
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 9ecdf1b48789..9ee072ee7ce5 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -22,11 +22,15 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
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;
+import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR;
+import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR;
import static com.android.server.VcnManagementService.VDBG;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.IpSecManager;
@@ -37,10 +41,12 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkAgent;
+import android.net.NetworkAgent.ValidationStatus;
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.RouteInfo;
import android.net.TelephonyNetworkSpecifier;
+import android.net.Uri;
import android.net.annotations.PolicyDirection;
import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.ChildSessionConfiguration;
@@ -49,7 +55,9 @@ import android.net.ipsec.ike.IkeSession;
import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionConfiguration;
import android.net.ipsec.ike.IkeSessionParams;
+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.VcnGatewayConnectionConfig;
import android.net.vcn.VcnTransportInfo;
@@ -58,6 +66,9 @@ import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Message;
import android.os.ParcelUuid;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.SystemClock;
import android.util.ArraySet;
import android.util.Slog;
@@ -65,6 +76,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.internal.util.WakeupMessage;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
@@ -75,6 +87,7 @@ import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -119,15 +132,37 @@ import java.util.concurrent.TimeUnit;
* +----------------------------+
* </pre>
*
+ * <p>All messages in VcnGatewayConnection <b>should</b> be enqueued using {@link
+ * #sendMessageAndAcquireWakeLock}. Careful consideration should be given to any uses of {@link
+ * #sendMessage} directly, as they are not guaranteed to be processed in a timely manner (due to the
+ * lack of WakeLocks).
+ *
+ * <p>Any attempt to remove messages from the Handler should be done using {@link
+ * #removeEqualMessages}. This is necessary to ensure that the WakeLock is correctly released when
+ * no messages remain in the Handler queue.
+ *
* @hide
*/
public class VcnGatewayConnection extends StateMachine {
private static final String TAG = VcnGatewayConnection.class.getSimpleName();
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0");
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final String TEARDOWN_TIMEOUT_ALARM = TAG + "_TEARDOWN_TIMEOUT_ALARM";
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final String DISCONNECT_REQUEST_ALARM = TAG + "_DISCONNECT_REQUEST_ALARM";
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final String RETRY_TIMEOUT_ALARM = TAG + "_RETRY_TIMEOUT_ALARM";
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final String SAFEMODE_TIMEOUT_ALARM = TAG + "_SAFEMODE_TIMEOUT_ALARM";
+
private static final int[] MERGED_CAPABILITIES =
new int[] {NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_ROAMING};
-
- private static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0");
private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE;
private static final String DISCONNECT_REASON_INTERNAL_ERROR = "Uncaught exception: ";
@@ -136,11 +171,15 @@ public class VcnGatewayConnection extends StateMachine {
private static final String DISCONNECT_REASON_TEARDOWN = "teardown() called on VcnTunnel";
private static final int TOKEN_ALL = Integer.MIN_VALUE;
- private static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30;
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30;
@VisibleForTesting(visibility = Visibility.PRIVATE)
static final int TEARDOWN_TIMEOUT_SECONDS = 5;
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int SAFEMODE_TIMEOUT_SECONDS = 30;
+
private interface EventInfo {}
/**
@@ -297,9 +336,9 @@ public class VcnGatewayConnection extends StateMachine {
private static final int EVENT_SETUP_COMPLETED = 6;
private static class EventSetupCompletedInfo implements EventInfo {
- @NonNull public final ChildSessionConfiguration childSessionConfig;
+ @NonNull public final VcnChildSessionConfiguration childSessionConfig;
- EventSetupCompletedInfo(@NonNull ChildSessionConfiguration childSessionConfig) {
+ EventSetupCompletedInfo(@NonNull VcnChildSessionConfiguration childSessionConfig) {
this.childSessionConfig = Objects.requireNonNull(childSessionConfig);
}
@@ -383,6 +422,23 @@ public class VcnGatewayConnection extends StateMachine {
// TODO(b/178426520): implement handling of this event
private static final int EVENT_SUBSCRIPTIONS_CHANGED = 9;
+ /**
+ * Sent when this VcnGatewayConnection has entered safe mode.
+ *
+ * <p>A VcnGatewayConnection enters safe mode when it takes over {@link
+ * #SAFEMODE_TIMEOUT_SECONDS} to enter {@link ConnectedState}.
+ *
+ * <p>When a VcnGatewayConnection enters safe mode, it will fire {@link
+ * VcnGatewayStatusCallback#onEnteredSafeMode()} to notify its Vcn. The Vcn will then shut down
+ * its VcnGatewayConnectin(s).
+ *
+ * <p>Relevant in DisconnectingState, ConnectingState, ConnectedState (if the Vcn Network is not
+ * validated yet), and RetryTimeoutState.
+ *
+ * @param arg1 The "all" token; this signal is always honored.
+ */
+ private static final int EVENT_SAFE_MODE_TIMEOUT_EXCEEDED = 10;
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
@NonNull
final DisconnectedState mDisconnectedState = new DisconnectedState();
@@ -411,11 +467,26 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
@NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull private final Dependencies mDeps;
-
@NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
@NonNull private final IpSecManager mIpSecManager;
- @NonNull private final IpSecTunnelInterface mTunnelIface;
+
+ @Nullable private IpSecTunnelInterface mTunnelIface = null;
+
+ /**
+ * WakeLock to be held when processing messages on the Handler queue.
+ *
+ * <p>Used to prevent the device from going to sleep while there are VCN-related events to
+ * process for this VcnGatewayConnection.
+ *
+ * <p>Obtain a WakeLock when enquing messages onto the Handler queue. Once all messages in the
+ * Handler queue have been processed, the WakeLock can be released and cleared.
+ *
+ * <p>This WakeLock is also used for handling delayed messages by using WakeupMessages to send
+ * delayed messages to the Handler. When the WakeupMessage fires, it will obtain the WakeLock
+ * before enquing the delayed event to the Handler.
+ */
+ @NonNull private final VcnWakeLock mWakeLock;
/** Running state of this VcnGatewayConnection. */
private boolean mIsRunning = true;
@@ -471,7 +542,7 @@ public class VcnGatewayConnection extends StateMachine {
* <p>Set in Connected and Migrating states, always @NonNull in Connected, Migrating
* states, @Nullable otherwise.
*/
- private ChildSessionConfiguration mChildConfig;
+ private VcnChildSessionConfiguration mChildConfig;
/**
* The active network agent.
@@ -479,7 +550,13 @@ public class VcnGatewayConnection extends StateMachine {
* <p>Set in Connected state, always @NonNull in Connected, Migrating states, @Nullable
* otherwise.
*/
- private NetworkAgent mNetworkAgent;
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ NetworkAgent mNetworkAgent;
+
+ @Nullable private WakeupMessage mTeardownTimeoutAlarm;
+ @Nullable private WakeupMessage mDisconnectRequestAlarm;
+ @Nullable private WakeupMessage mRetryTimeoutAlarm;
+ @Nullable private WakeupMessage mSafeModeTimeoutAlarm;
public VcnGatewayConnection(
@NonNull VcnContext vcnContext,
@@ -516,6 +593,9 @@ public class VcnGatewayConnection extends StateMachine {
mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback();
+ mWakeLock =
+ mDeps.newWakeLock(mVcnContext.getContext(), PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
mUnderlyingNetworkTracker =
mDeps.newUnderlyingNetworkTracker(
mVcnContext,
@@ -525,20 +605,6 @@ public class VcnGatewayConnection extends StateMachine {
mUnderlyingNetworkTrackerCallback);
mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
- IpSecTunnelInterface iface;
- try {
- iface =
- mIpSecManager.createIpSecTunnelInterface(
- DUMMY_ADDR, DUMMY_ADDR, new Network(-1));
- } catch (IOException | ResourceUnavailableException e) {
- teardownAsynchronously();
- mTunnelIface = null;
-
- return;
- }
-
- mTunnelIface = iface;
-
addState(mDisconnectedState);
addState(mDisconnectingState);
addState(mConnectingState);
@@ -556,7 +622,7 @@ public class VcnGatewayConnection extends StateMachine {
* <p>Once torn down, this VcnTunnel CANNOT be started again.
*/
public void teardownAsynchronously() {
- sendMessage(
+ sendMessageAndAcquireWakeLock(
EVENT_DISCONNECT_REQUESTED,
TOKEN_ALL,
new EventDisconnectRequestedInfo(DISCONNECT_REASON_TEARDOWN));
@@ -572,6 +638,13 @@ public class VcnGatewayConnection extends StateMachine {
mTunnelIface.close();
}
+ releaseWakeLock();
+
+ cancelTeardownTimeoutAlarm();
+ cancelDisconnectRequestAlarm();
+ cancelRetryTimeoutAlarm();
+ cancelSafeModeAlarm();
+
mUnderlyingNetworkTracker.teardown();
}
@@ -588,79 +661,377 @@ public class VcnGatewayConnection extends StateMachine {
mLastSnapshot = snapshot;
mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
- sendMessage(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
+ sendMessageAndAcquireWakeLock(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
}
private class VcnUnderlyingNetworkTrackerCallback implements UnderlyingNetworkTrackerCallback {
@Override
public void onSelectedUnderlyingNetworkChanged(
@Nullable UnderlyingNetworkRecord underlying) {
+ // TODO(b/180132994): explore safely removing this Thread check
+ mVcnContext.ensureRunningOnLooperThread();
+
// TODO(b/179091925): Move the delayed-message handling to BaseState
// If underlying is null, all underlying networks have been lost. Disconnect VCN after a
// timeout.
if (underlying == null) {
- sendMessageDelayed(
- EVENT_DISCONNECT_REQUESTED,
- TOKEN_ALL,
- new EventDisconnectRequestedInfo(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST),
- TimeUnit.SECONDS.toMillis(NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS));
- } else if (getHandler() != null) {
- // Cancel any existing disconnect due to loss of underlying network
- // getHandler() can return null if the state machine has already quit. Since this is
- // called from other classes, this condition must be verified.
-
- getHandler()
- .removeEqualMessages(
- EVENT_DISCONNECT_REQUESTED,
- new EventDisconnectRequestedInfo(
- DISCONNECT_REASON_UNDERLYING_NETWORK_LOST));
+ setDisconnectRequestAlarm();
+ } else {
+ // Received a new Network so any previous alarm is irrelevant - cancel + clear it,
+ // and cancel any queued EVENT_DISCONNECT_REQUEST messages
+ cancelDisconnectRequestAlarm();
}
- sendMessage(
+ sendMessageAndAcquireWakeLock(
EVENT_UNDERLYING_NETWORK_CHANGED,
TOKEN_ALL,
new EventUnderlyingNetworkChangedInfo(underlying));
}
}
- private void sendMessage(int what, int token, EventInfo data) {
+ private void acquireWakeLock() {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (mIsRunning) {
+ mWakeLock.acquire();
+ }
+ }
+
+ private void releaseWakeLock() {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ mWakeLock.release();
+ }
+
+ /**
+ * Attempt to release mWakeLock - this can only be done if the Handler is null (meaning the
+ * StateMachine has been shutdown and thus has no business keeping the WakeLock) or if there are
+ * no more messags left to process in the Handler queue (at which point the WakeLock can be
+ * released until more messages must be processed).
+ */
+ private void maybeReleaseWakeLock() {
+ final Handler handler = getHandler();
+ if (handler == null || !handler.hasMessagesOrCallbacks()) {
+ releaseWakeLock();
+ }
+ }
+
+ @Override
+ public void sendMessage(int what) {
+ Slog.wtf(
+ TAG,
+ "sendMessage should not be used in VcnGatewayConnection. See"
+ + " sendMessageAndAcquireWakeLock()");
+ super.sendMessage(what);
+ }
+
+ @Override
+ public void sendMessage(int what, Object obj) {
+ Slog.wtf(
+ TAG,
+ "sendMessage should not be used in VcnGatewayConnection. See"
+ + " sendMessageAndAcquireWakeLock()");
+ super.sendMessage(what, obj);
+ }
+
+ @Override
+ public void sendMessage(int what, int arg1) {
+ Slog.wtf(
+ TAG,
+ "sendMessage should not be used in VcnGatewayConnection. See"
+ + " sendMessageAndAcquireWakeLock()");
+ super.sendMessage(what, arg1);
+ }
+
+ @Override
+ public void sendMessage(int what, int arg1, int arg2) {
+ Slog.wtf(
+ TAG,
+ "sendMessage should not be used in VcnGatewayConnection. See"
+ + " sendMessageAndAcquireWakeLock()");
+ super.sendMessage(what, arg1, arg2);
+ }
+
+ @Override
+ public void sendMessage(int what, int arg1, int arg2, Object obj) {
+ Slog.wtf(
+ TAG,
+ "sendMessage should not be used in VcnGatewayConnection. See"
+ + " sendMessageAndAcquireWakeLock()");
+ super.sendMessage(what, arg1, arg2, obj);
+ }
+
+ @Override
+ public void sendMessage(Message msg) {
+ Slog.wtf(
+ TAG,
+ "sendMessage should not be used in VcnGatewayConnection. See"
+ + " sendMessageAndAcquireWakeLock()");
+ super.sendMessage(msg);
+ }
+
+ // TODO(b/180146061): also override and Log.wtf() other Message handling methods
+ // In mind are sendMessageDelayed(), sendMessageAtFrontOfQueue, removeMessages, and
+ // removeDeferredMessages
+
+ /**
+ * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not
+ * go to sleep before processing the sent message.
+ */
+ private void sendMessageAndAcquireWakeLock(int what, int token) {
+ acquireWakeLock();
+ super.sendMessage(what, token);
+ }
+
+ /**
+ * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not
+ * go to sleep before processing the sent message.
+ */
+ private void sendMessageAndAcquireWakeLock(int what, int token, EventInfo data) {
+ acquireWakeLock();
super.sendMessage(what, token, ARG_NOT_PRESENT, data);
}
- private void sendMessage(int what, int token, int arg2, EventInfo data) {
+ /**
+ * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not
+ * go to sleep before processing the sent message.
+ */
+ private void sendMessageAndAcquireWakeLock(int what, int token, int arg2, EventInfo data) {
+ acquireWakeLock();
super.sendMessage(what, token, arg2, data);
}
- private void sendMessageDelayed(int what, int token, EventInfo data, long timeout) {
- super.sendMessageDelayed(what, token, ARG_NOT_PRESENT, data, timeout);
+ /**
+ * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not
+ * go to sleep before processing the sent message.
+ */
+ private void sendMessageAndAcquireWakeLock(Message msg) {
+ acquireWakeLock();
+ super.sendMessage(msg);
+ }
+
+ /**
+ * Removes all messages matching the given parameters, and attempts to release mWakeLock if the
+ * Handler is empty.
+ *
+ * @param what the Message.what value to be removed
+ */
+ private void removeEqualMessages(int what) {
+ removeEqualMessages(what, null /* obj */);
+ }
+
+ /**
+ * Removes all messages matching the given parameters, and attempts to release mWakeLock if the
+ * Handler is empty.
+ *
+ * @param what the Message.what value to be removed
+ * @param obj the Message.obj to to be removed, or null if all messages matching Message.what
+ * should be removed
+ */
+ private void removeEqualMessages(int what, @Nullable Object obj) {
+ final Handler handler = getHandler();
+ if (handler != null) {
+ handler.removeEqualMessages(what, obj);
+ }
+
+ maybeReleaseWakeLock();
+ }
+
+ private WakeupMessage createScheduledAlarm(
+ @NonNull String cmdName, Message delayedMessage, long delay) {
+ // WakeupMessage uses Handler#dispatchMessage() to immediately handle the specified Runnable
+ // at the scheduled time. dispatchMessage() immediately executes and there may be queued
+ // events that resolve the scheduled alarm pending in the queue. So, use the Runnable to
+ // place the alarm event at the end of the queue with sendMessageAndAcquireWakeLock (which
+ // guarantees the device will stay awake).
+ final WakeupMessage alarm =
+ mDeps.newWakeupMessage(
+ mVcnContext,
+ getHandler(),
+ cmdName,
+ () -> sendMessageAndAcquireWakeLock(delayedMessage));
+ alarm.schedule(mDeps.getElapsedRealTime() + delay);
+ return alarm;
+ }
+
+ private void setTeardownTimeoutAlarm() {
+ // Safe to assign this alarm because it is either 1) already null, or 2) already fired. In
+ // either case, there is nothing to cancel.
+ if (mTeardownTimeoutAlarm != null) {
+ Slog.wtf(TAG, "mTeardownTimeoutAlarm should be null before being set");
+ }
+
+ final Message delayedMessage = obtainMessage(EVENT_TEARDOWN_TIMEOUT_EXPIRED, mCurrentToken);
+ mTeardownTimeoutAlarm =
+ createScheduledAlarm(
+ TEARDOWN_TIMEOUT_ALARM,
+ delayedMessage,
+ TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
}
- private void sendMessageDelayed(int what, int token, int arg2, EventInfo data, long timeout) {
- super.sendMessageDelayed(what, token, arg2, data, timeout);
+ private void cancelTeardownTimeoutAlarm() {
+ if (mTeardownTimeoutAlarm != null) {
+ mTeardownTimeoutAlarm.cancel();
+ mTeardownTimeoutAlarm = null;
+ }
+
+ // Cancel any existing teardown timeouts
+ removeEqualMessages(EVENT_TEARDOWN_TIMEOUT_EXPIRED);
+ }
+
+ private void setDisconnectRequestAlarm() {
+ // Only schedule a NEW alarm if none is already set.
+ if (mDisconnectRequestAlarm != null) {
+ return;
+ }
+
+ final Message delayedMessage =
+ obtainMessage(
+ EVENT_DISCONNECT_REQUESTED,
+ TOKEN_ALL,
+ 0 /* arg2 */,
+ new EventDisconnectRequestedInfo(
+ DISCONNECT_REASON_UNDERLYING_NETWORK_LOST));
+ mDisconnectRequestAlarm =
+ createScheduledAlarm(
+ DISCONNECT_REQUEST_ALARM,
+ delayedMessage,
+ TimeUnit.SECONDS.toMillis(NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS));
+ }
+
+ private void cancelDisconnectRequestAlarm() {
+ if (mDisconnectRequestAlarm != null) {
+ mDisconnectRequestAlarm.cancel();
+ mDisconnectRequestAlarm = null;
+ }
+
+ // Cancel any existing disconnect due to previous loss of underlying network
+ removeEqualMessages(
+ EVENT_DISCONNECT_REQUESTED,
+ new EventDisconnectRequestedInfo(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST));
+ }
+
+ private void setRetryTimeoutAlarm(long delay) {
+ // Safe to assign this alarm because it is either 1) already null, or 2) already fired. In
+ // either case, there is nothing to cancel.
+ if (mRetryTimeoutAlarm != null) {
+ Slog.wtf(TAG, "mRetryTimeoutAlarm should be null before being set");
+ }
+
+ final Message delayedMessage = obtainMessage(EVENT_RETRY_TIMEOUT_EXPIRED, mCurrentToken);
+ mRetryTimeoutAlarm = createScheduledAlarm(RETRY_TIMEOUT_ALARM, delayedMessage, delay);
+ }
+
+ private void cancelRetryTimeoutAlarm() {
+ if (mRetryTimeoutAlarm != null) {
+ mRetryTimeoutAlarm.cancel();
+ mRetryTimeoutAlarm = null;
+ }
+
+ removeEqualMessages(EVENT_RETRY_TIMEOUT_EXPIRED);
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setSafeModeAlarm() {
+ // Only schedule a NEW alarm if none is already set.
+ if (mSafeModeTimeoutAlarm != null) {
+ return;
+ }
+
+ final Message delayedMessage = obtainMessage(EVENT_SAFE_MODE_TIMEOUT_EXCEEDED, TOKEN_ALL);
+ mSafeModeTimeoutAlarm =
+ createScheduledAlarm(
+ SAFEMODE_TIMEOUT_ALARM,
+ delayedMessage,
+ TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS));
+ }
+
+ private void cancelSafeModeAlarm() {
+ if (mSafeModeTimeoutAlarm != null) {
+ mSafeModeTimeoutAlarm.cancel();
+ mSafeModeTimeoutAlarm = null;
+ }
+
+ removeEqualMessages(EVENT_SAFE_MODE_TIMEOUT_EXCEEDED);
+ }
+
+ private void sessionLostWithoutCallback(int token, @Nullable Exception exception) {
+ sendMessageAndAcquireWakeLock(
+ EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception));
}
private void sessionLost(int token, @Nullable Exception exception) {
- sendMessage(EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception));
+ // Only notify mGatewayStatusCallback if the session was lost with an error. All
+ // authentication and DNS failures are sent through
+ // IkeSessionCallback.onClosedExceptionally(), which calls sessionClosed()
+ if (exception != null) {
+ mGatewayStatusCallback.onGatewayConnectionError(
+ mConnectionConfig.getRequiredUnderlyingCapabilities(),
+ VCN_ERROR_CODE_INTERNAL_ERROR,
+ "java.lang.RuntimeException",
+ "Received "
+ + exception.getClass().getSimpleName()
+ + " with message: "
+ + exception.getMessage());
+ }
+
+ sessionLostWithoutCallback(token, exception);
+ }
+
+ private void notifyStatusCallbackForSessionClosed(@NonNull Exception exception) {
+ final int errorCode;
+ final String exceptionClass;
+ final String exceptionMessage;
+
+ if (exception instanceof AuthenticationFailedException) {
+ errorCode = VCN_ERROR_CODE_CONFIG_ERROR;
+ exceptionClass = exception.getClass().getName();
+ exceptionMessage = exception.getMessage();
+ } else if (exception instanceof IkeInternalException
+ && exception.getCause() instanceof IOException) {
+ errorCode = VCN_ERROR_CODE_NETWORK_ERROR;
+ exceptionClass = "java.io.IOException";
+ exceptionMessage = exception.getCause().getMessage();
+ } else {
+ errorCode = VCN_ERROR_CODE_INTERNAL_ERROR;
+ exceptionClass = "java.lang.RuntimeException";
+ exceptionMessage =
+ "Received "
+ + exception.getClass().getSimpleName()
+ + " with message: "
+ + exception.getMessage();
+ }
+
+ mGatewayStatusCallback.onGatewayConnectionError(
+ mConnectionConfig.getRequiredUnderlyingCapabilities(),
+ errorCode,
+ exceptionClass,
+ exceptionMessage);
}
private void sessionClosed(int token, @Nullable Exception exception) {
+ if (exception != null) {
+ notifyStatusCallbackForSessionClosed(exception);
+ }
+
// SESSION_LOST MUST be sent before SESSION_CLOSED to ensure that the SM moves to the
// Disconnecting state.
- sessionLost(token, exception);
- sendMessage(EVENT_SESSION_CLOSED, token);
+ sessionLostWithoutCallback(token, exception);
+ sendMessageAndAcquireWakeLock(EVENT_SESSION_CLOSED, token);
}
private void childTransformCreated(
int token, @NonNull IpSecTransform transform, int direction) {
- sendMessage(
+ sendMessageAndAcquireWakeLock(
EVENT_TRANSFORM_CREATED,
token,
new EventTransformCreatedInfo(direction, transform));
}
- private void childOpened(int token, @NonNull ChildSessionConfiguration childConfig) {
- sendMessage(EVENT_SETUP_COMPLETED, token, new EventSetupCompletedInfo(childConfig));
+ private void childOpened(int token, @NonNull VcnChildSessionConfiguration childConfig) {
+ sendMessageAndAcquireWakeLock(
+ EVENT_SETUP_COMPLETED, token, new EventSetupCompletedInfo(childConfig));
}
private abstract class BaseState extends State {
@@ -670,7 +1041,7 @@ public class VcnGatewayConnection extends StateMachine {
enterState();
} catch (Exception e) {
Slog.wtf(TAG, "Uncaught exception", e);
- sendMessage(
+ sendMessageAndAcquireWakeLock(
EVENT_DISCONNECT_REQUESTED,
TOKEN_ALL,
new EventDisconnectRequestedInfo(
@@ -681,22 +1052,47 @@ public class VcnGatewayConnection extends StateMachine {
protected void enterState() throws Exception {}
/**
+ * Returns whether the given token is valid.
+ *
+ * <p>By default, States consider any and all token to be 'valid'.
+ *
+ * <p>States should override this method if they want to restrict message handling to
+ * specific tokens.
+ */
+ protected boolean isValidToken(int token) {
+ return true;
+ }
+
+ /**
* Top-level processMessage with safeguards to prevent crashing the System Server on non-eng
* builds.
+ *
+ * <p>Here be dragons: processMessage() is final to ensure that mWakeLock is released once
+ * the Handler queue is empty. Future changes (or overrides) to processMessage() to MUST
+ * ensure that mWakeLock is correctly released.
*/
@Override
- public boolean processMessage(Message msg) {
+ public final boolean processMessage(Message msg) {
+ final int token = msg.arg1;
+ if (!isValidToken(token)) {
+ Slog.v(TAG, "Message called with obsolete token: " + token + "; what: " + msg.what);
+ return HANDLED;
+ }
+
try {
processStateMsg(msg);
} catch (Exception e) {
Slog.wtf(TAG, "Uncaught exception", e);
- sendMessage(
+ sendMessageAndAcquireWakeLock(
EVENT_DISCONNECT_REQUESTED,
TOKEN_ALL,
new EventDisconnectRequestedInfo(
DISCONNECT_REASON_INTERNAL_ERROR + e.toString()));
}
+ // Attempt to release the WakeLock - only possible if the Handler queue is empty
+ maybeReleaseWakeLock();
+
return HANDLED;
}
@@ -708,7 +1104,7 @@ public class VcnGatewayConnection extends StateMachine {
exitState();
} catch (Exception e) {
Slog.wtf(TAG, "Uncaught exception", e);
- sendMessage(
+ sendMessageAndAcquireWakeLock(
EVENT_DISCONNECT_REQUESTED,
TOKEN_ALL,
new EventDisconnectRequestedInfo(
@@ -746,6 +1142,8 @@ public class VcnGatewayConnection extends StateMachine {
}
protected void handleDisconnectRequested(String msg) {
+ // TODO(b/180526152): notify VcnStatusCallback for Network loss
+
Slog.v(TAG, "Tearing down. Cause: " + msg);
mIsRunning = false;
@@ -786,6 +1184,8 @@ public class VcnGatewayConnection extends StateMachine {
if (mIkeSession != null || mNetworkAgent != null) {
Slog.wtf(TAG, "Active IKE Session or NetworkAgent in DisconnectedState");
}
+
+ cancelSafeModeAlarm();
}
@Override
@@ -809,27 +1209,16 @@ public class VcnGatewayConnection extends StateMachine {
break;
}
}
- }
- private abstract class ActiveBaseState extends BaseState {
- /**
- * Handles all incoming messages, discarding messages for previous networks.
- *
- * <p>States that handle mobility events may need to override this method to receive
- * messages for all underlying networks.
- */
@Override
- public boolean processMessage(Message msg) {
- final int token = msg.arg1;
- // Only process if a valid token is presented.
- if (isValidToken(token)) {
- return super.processMessage(msg);
- }
-
- Slog.v(TAG, "Message called with obsolete token: " + token + "; what: " + msg.what);
- return HANDLED;
+ protected void exitState() {
+ // Safe to blindly set up, as it is cancelled and cleared on entering this state
+ setSafeModeAlarm();
}
+ }
+ private abstract class ActiveBaseState extends BaseState {
+ @Override
protected boolean isValidToken(int token) {
return (token == TOKEN_ALL || token == mCurrentToken);
}
@@ -860,7 +1249,7 @@ public class VcnGatewayConnection extends StateMachine {
protected void enterState() throws Exception {
if (mIkeSession == null) {
Slog.wtf(TAG, "IKE session was already closed when entering Disconnecting state.");
- sendMessage(EVENT_SESSION_CLOSED, mCurrentToken);
+ sendMessageAndAcquireWakeLock(EVENT_SESSION_CLOSED, mCurrentToken);
return;
}
@@ -872,10 +1261,9 @@ public class VcnGatewayConnection extends StateMachine {
}
mIkeSession.close();
- sendMessageDelayed(
- EVENT_TEARDOWN_TIMEOUT_EXPIRED,
- mCurrentToken,
- TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+
+ // Safe to blindly set up, as it is cancelled and cleared on exiting this state
+ setTeardownTimeoutAlarm();
}
@Override
@@ -900,6 +1288,8 @@ public class VcnGatewayConnection extends StateMachine {
String reason = ((EventDisconnectRequestedInfo) msg.obj).reason;
if (reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) {
+ // TODO(b/180526152): notify VcnStatusCallback for Network loss
+
// Will trigger EVENT_SESSION_CLOSED immediately.
mIkeSession.kill();
break;
@@ -917,6 +1307,10 @@ public class VcnGatewayConnection extends StateMachine {
transitionTo(mDisconnectedState);
}
break;
+ case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
+ mGatewayStatusCallback.onEnteredSafeMode();
+ mSafeModeTimeoutAlarm = null;
+ break;
default:
logUnhandledMessage(msg);
break;
@@ -926,6 +1320,8 @@ public class VcnGatewayConnection extends StateMachine {
@Override
protected void exitState() throws Exception {
mSkipRetryTimeout = false;
+
+ cancelTeardownTimeoutAlarm();
}
}
@@ -997,6 +1393,10 @@ public class VcnGatewayConnection extends StateMachine {
case EVENT_DISCONNECT_REQUESTED:
handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
break;
+ case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
+ mGatewayStatusCallback.onEnteredSafeMode();
+ mSafeModeTimeoutAlarm = null;
+ break;
default:
logUnhandledMessage(msg);
break;
@@ -1008,7 +1408,7 @@ public class VcnGatewayConnection extends StateMachine {
protected void updateNetworkAgent(
@NonNull IpSecTunnelInterface tunnelIface,
@NonNull NetworkAgent agent,
- @NonNull ChildSessionConfiguration childConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig) {
final NetworkCapabilities caps =
buildNetworkCapabilities(mConnectionConfig, mUnderlying);
final LinkProperties lp =
@@ -1020,7 +1420,7 @@ public class VcnGatewayConnection extends StateMachine {
protected NetworkAgent buildNetworkAgent(
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull ChildSessionConfiguration childConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig) {
final NetworkCapabilities caps =
buildNetworkCapabilities(mConnectionConfig, mUnderlying);
final LinkProperties lp =
@@ -1040,6 +1440,14 @@ public class VcnGatewayConnection extends StateMachine {
public void unwanted() {
teardownAsynchronously();
}
+
+ @Override
+ public void onValidationStatus(
+ @ValidationStatus int status, @Nullable Uri redirectUri) {
+ if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
+ clearFailedAttemptCounterAndSafeModeAlarm();
+ }
+ }
};
agent.register();
@@ -1048,6 +1456,14 @@ public class VcnGatewayConnection extends StateMachine {
return agent;
}
+ protected void clearFailedAttemptCounterAndSafeModeAlarm() {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ // Validated connection, clear failed attempt counter
+ mFailedAttempts = 0;
+ cancelSafeModeAlarm();
+ }
+
protected void applyTransform(
int token,
@NonNull IpSecTunnelInterface tunnelIface,
@@ -1055,7 +1471,7 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull IpSecTransform transform,
int direction) {
try {
- // TODO: Set underlying network of tunnel interface
+ // TODO(b/180163196): Set underlying network of tunnel interface
// Transforms do not need to be persisted; the IkeSession will keep them alive
mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform);
@@ -1068,15 +1484,15 @@ public class VcnGatewayConnection extends StateMachine {
protected void setupInterface(
int token,
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull ChildSessionConfiguration childConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig) {
setupInterface(token, tunnelIface, childConfig, null);
}
protected void setupInterface(
int token,
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull ChildSessionConfiguration childConfig,
- @Nullable ChildSessionConfiguration oldChildConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig,
+ @Nullable VcnChildSessionConfiguration oldChildConfig) {
try {
final Set<LinkAddress> newAddrs =
new ArraySet<>(childConfig.getInternalAddresses());
@@ -1116,8 +1532,17 @@ public class VcnGatewayConnection extends StateMachine {
class ConnectedState extends ConnectedStateBase {
@Override
protected void enterState() throws Exception {
- // Successful connection, clear failed attempt counter
- mFailedAttempts = 0;
+ if (mTunnelIface == null) {
+ try {
+ // Requires a real Network object in order to be created; doing this any earlier
+ // means not having a real Network object, or picking an incorrect Network.
+ mTunnelIface =
+ mIpSecManager.createIpSecTunnelInterface(
+ DUMMY_ADDR, DUMMY_ADDR, mUnderlying.network);
+ } catch (IOException | ResourceUnavailableException e) {
+ teardownAsynchronously();
+ }
+ }
}
@Override
@@ -1154,6 +1579,10 @@ public class VcnGatewayConnection extends StateMachine {
case EVENT_DISCONNECT_REQUESTED:
handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
break;
+ case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
+ mGatewayStatusCallback.onEnteredSafeMode();
+ mSafeModeTimeoutAlarm = null;
+ break;
default:
logUnhandledMessage(msg);
break;
@@ -1173,6 +1602,7 @@ public class VcnGatewayConnection extends StateMachine {
// mUnderlying assumed non-null, given check above.
// If network changed, migrate. Otherwise, update any existing networkAgent.
if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) {
+ Slog.v(TAG, "Migrating to new network: " + mUnderlying.network);
mIkeSession.setNetwork(mUnderlying.network);
} else {
// oldUnderlying is non-null & underlying network itself has not changed
@@ -1189,15 +1619,25 @@ public class VcnGatewayConnection extends StateMachine {
protected void setupInterfaceAndNetworkAgent(
int token,
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull ChildSessionConfiguration childConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig) {
setupInterface(token, tunnelIface, childConfig);
if (mNetworkAgent == null) {
mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig);
} else {
updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig);
+
+ // mNetworkAgent not null, so the VCN Network has already been established. Clear
+ // the failed attempt counter and safe mode alarm since this transition is complete.
+ clearFailedAttemptCounterAndSafeModeAlarm();
}
}
+
+ @Override
+ protected void exitState() {
+ // Will only set a new alarm if no safe mode alarm is currently scheduled.
+ setSafeModeAlarm();
+ }
}
/**
@@ -1207,7 +1647,69 @@ public class VcnGatewayConnection extends StateMachine {
*/
class RetryTimeoutState extends ActiveBaseState {
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() throws Exception {
+ // Reset upon entry to ConnectedState
+ mFailedAttempts++;
+
+ if (mUnderlying == null) {
+ Slog.wtf(TAG, "Underlying network was null in retry state");
+ transitionTo(mDisconnectedState);
+ } else {
+ // Safe to blindly set up, as it is cancelled and cleared on exiting this state
+ setRetryTimeoutAlarm(getNextRetryIntervalsMs());
+ }
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED:
+ final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ // If new underlying is null, all networks were lost; go back to disconnected.
+ if (mUnderlying == null) {
+ transitionTo(mDisconnectedState);
+ return;
+ } else if (oldUnderlying != null
+ && mUnderlying.network.equals(oldUnderlying.network)) {
+ // If the network has not changed, do nothing.
+ return;
+ }
+
+ // Fallthrough
+ case EVENT_RETRY_TIMEOUT_EXPIRED:
+ transitionTo(mConnectingState);
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+ break;
+ case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
+ mGatewayStatusCallback.onEnteredSafeMode();
+ mSafeModeTimeoutAlarm = null;
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
+
+ @Override
+ public void exitState() {
+ cancelRetryTimeoutAlarm();
+ }
+
+ private long getNextRetryIntervalsMs() {
+ final int retryDelayIndex = mFailedAttempts - 1;
+ final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMs();
+
+ // Repeatedly use last item in retry timeout list.
+ if (retryDelayIndex >= retryIntervalsMs.length) {
+ return retryIntervalsMs[retryIntervalsMs.length - 1];
+ }
+
+ return retryIntervalsMs[retryDelayIndex];
+ }
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -1269,15 +1771,13 @@ public class VcnGatewayConnection extends StateMachine {
}
}
- // TODO: Make a VcnNetworkSpecifier, and match all underlying subscription IDs.
-
return builder.build();
}
private static LinkProperties buildConnectedLinkProperties(
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull ChildSessionConfiguration childConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig) {
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(tunnelIface.getInterfaceName());
@@ -1328,20 +1828,28 @@ public class VcnGatewayConnection extends StateMachine {
}
}
- private class ChildSessionCallbackImpl implements ChildSessionCallback {
+ /** Implementation of ChildSessionCallback, exposed for testing. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public class VcnChildSessionCallback implements ChildSessionCallback {
private final int mToken;
- ChildSessionCallbackImpl(int token) {
+ VcnChildSessionCallback(int token) {
mToken = token;
}
- @Override
- public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
+ /** Internal proxy method for injecting of mocked ChildSessionConfiguration */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void onOpened(@NonNull VcnChildSessionConfiguration childConfig) {
Slog.v(TAG, "ChildOpened for token " + mToken);
childOpened(mToken, childConfig);
}
@Override
+ public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
+ onOpened(new VcnChildSessionConfiguration(childConfig));
+ }
+
+ @Override
public void onClosed() {
Slog.v(TAG, "ChildClosed for token " + mToken);
sessionLost(mToken, null);
@@ -1360,6 +1868,15 @@ public class VcnGatewayConnection extends StateMachine {
}
@Override
+ public void onIpSecTransformsMigrated(
+ @NonNull IpSecTransform inIpSecTransform,
+ @NonNull IpSecTransform outIpSecTransform) {
+ Slog.v(TAG, "ChildTransformsMigrated; token " + mToken);
+ onIpSecTransformCreated(inIpSecTransform, IpSecManager.DIRECTION_IN);
+ onIpSecTransformCreated(outIpSecTransform, IpSecManager.DIRECTION_OUT);
+ }
+
+ @Override
public void onIpSecTransformDeleted(@NonNull IpSecTransform transform, int direction) {
// Nothing to be done; no references to the IpSecTransform are held, and this transform
// will be closed by the IKE library.
@@ -1368,6 +1885,11 @@ public class VcnGatewayConnection extends StateMachine {
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setTunnelInterface(IpSecTunnelInterface tunnelIface) {
+ mTunnelIface = tunnelIface;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
UnderlyingNetworkTrackerCallback getUnderlyingNetworkTrackerCallback() {
return mUnderlyingNetworkTrackerCallback;
}
@@ -1421,7 +1943,7 @@ public class VcnGatewayConnection extends StateMachine {
buildIkeParams(),
buildChildParams(),
new IkeSessionCallbackImpl(token),
- new ChildSessionCallbackImpl(token));
+ new VcnChildSessionCallback(token));
}
/** External dependencies used by VcnGatewayConnection, for injection in tests */
@@ -1456,6 +1978,55 @@ public class VcnGatewayConnection extends StateMachine {
ikeSessionCallback,
childSessionCallback);
}
+
+ /** Builds a new WakeLock. */
+ public VcnWakeLock newWakeLock(
+ @NonNull Context context, int wakeLockFlag, @NonNull String wakeLockTag) {
+ return new VcnWakeLock(context, wakeLockFlag, wakeLockTag);
+ }
+
+ /** Builds a new WakeupMessage. */
+ public WakeupMessage newWakeupMessage(
+ @NonNull VcnContext vcnContext,
+ @NonNull Handler handler,
+ @NonNull String tag,
+ @NonNull Runnable runnable) {
+ return new WakeupMessage(vcnContext.getContext(), handler, tag, runnable);
+ }
+
+ /** Gets the elapsed real time since boot, in millis. */
+ public long getElapsedRealTime() {
+ return SystemClock.elapsedRealtime();
+ }
+ }
+
+ /**
+ * Proxy implementation of Child Session Configuration, used for testing.
+ *
+ * <p>This wrapper allows mocking of the final, parcelable ChildSessionConfiguration object for
+ * testing purposes. This is the unfortunate result of mockito-inline (for mocking final
+ * classes) not working properly with system services & associated classes.
+ *
+ * <p>This class MUST EXCLUSIVELY be a passthrough, proxying calls directly to the actual
+ * ChildSessionConfiguration.
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class VcnChildSessionConfiguration {
+ private final ChildSessionConfiguration mChildConfig;
+
+ public VcnChildSessionConfiguration(ChildSessionConfiguration childConfig) {
+ mChildConfig = childConfig;
+ }
+
+ /** Retrieves the addresses to be used inside the tunnel. */
+ public List<LinkAddress> getInternalAddresses() {
+ return mChildConfig.getInternalAddresses();
+ }
+
+ /** Retrieves the DNS servers to be used inside the tunnel. */
+ public List<InetAddress> getInternalDnsServers() {
+ return mChildConfig.getInternalDnsServers();
+ }
}
/** Proxy implementation of IKE session, used for testing. */
@@ -1506,4 +2077,34 @@ public class VcnGatewayConnection extends StateMachine {
mImpl.setNetwork(network);
}
}
+
+ /** Proxy Implementation of WakeLock, used for testing. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class VcnWakeLock {
+ private final WakeLock mImpl;
+
+ public VcnWakeLock(@NonNull Context context, int flags, @NonNull String tag) {
+ final PowerManager powerManager = context.getSystemService(PowerManager.class);
+ mImpl = powerManager.newWakeLock(flags, tag);
+ mImpl.setReferenceCounted(false /* isReferenceCounted */);
+ }
+
+ /**
+ * Acquire this WakeLock.
+ *
+ * <p>Synchronize this action to minimize locking around WakeLock use.
+ */
+ public synchronized void acquire() {
+ mImpl.acquire();
+ }
+
+ /**
+ * Release this Wakelock.
+ *
+ * <p>Synchronize this action to minimize locking around WakeLock use.
+ */
+ public synchronized void release() {
+ mImpl.release();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index fe4ea303610f..bfeec011a2c9 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -16,6 +16,8 @@
package com.android.server.vcn;
+import static com.android.server.VcnManagementService.VDBG;
+
import android.annotation.NonNull;
import android.content.Context;
import android.net.NetworkProvider;
@@ -83,11 +85,16 @@ public class VcnNetworkProvider extends NetworkProvider {
@Override
public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
- Slog.v(
- TAG,
- String.format(
- "Network requested: Request = %s, score = %d, providerId = %d",
- request, score, providerId));
+ if (VDBG) {
+ Slog.v(
+ TAG,
+ "Network requested: Request = "
+ + request
+ + ", score = "
+ + score
+ + ", providerId = "
+ + providerId);
+ }
final NetworkRequestEntry entry = new NetworkRequestEntry(request, score, providerId);
diff --git a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
index 39687231c249..685dce4683d7 100644
--- a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
+++ b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
@@ -21,16 +21,14 @@ import android.hardware.input.InputManager;
import android.os.CombinedVibrationEffect;
import android.os.Handler;
import android.os.VibrationAttributes;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
+import android.os.VibratorManager;
import android.util.SparseArray;
import android.view.InputDevice;
import com.android.internal.annotations.GuardedBy;
-/** Delegates vibrations to all connected {@link InputDevice} with available {@link Vibrator}. */
-// TODO(b/159207608): Make this package-private once vibrator services are moved to this package
-public final class InputDeviceDelegate implements InputManager.InputDeviceListener {
+/** Delegates vibrations to all connected {@link InputDevice} with one or more vibrators. */
+final class InputDeviceDelegate implements InputManager.InputDeviceListener {
private static final String TAG = "InputDeviceDelegate";
private final Object mLock = new Object();
@@ -38,7 +36,7 @@ public final class InputDeviceDelegate implements InputManager.InputDeviceListen
private final InputManager mInputManager;
@GuardedBy("mLock")
- private final SparseArray<Vibrator> mInputDeviceVibrators = new SparseArray<>();
+ private final SparseArray<VibratorManager> mInputDeviceVibrators = new SparseArray<>();
/**
* Flag updated via {@link #updateInputDeviceVibrators(boolean)}, holding the value of {@link
@@ -47,7 +45,7 @@ public final class InputDeviceDelegate implements InputManager.InputDeviceListen
@GuardedBy("mLock")
private boolean mShouldVibrateInputDevices;
- public InputDeviceDelegate(Context context, Handler handler) {
+ InputDeviceDelegate(Context context, Handler handler) {
mHandler = handler;
mInputManager = context.getSystemService(InputManager.class);
}
@@ -81,32 +79,22 @@ public final class InputDeviceDelegate implements InputManager.InputDeviceListen
}
/**
- * Vibrate all {@link InputDevice} with {@link Vibrator} available using given effect.
+ * Vibrate all {@link InputDevice} with vibrators using given effect.
*
* @return {@link #isAvailable()}
*/
public boolean vibrateIfAvailable(int uid, String opPkg, CombinedVibrationEffect effect,
String reason, VibrationAttributes attrs) {
synchronized (mLock) {
- // TODO(b/159207608): Pass on the combined vibration once InputManager is merged
- if (effect instanceof CombinedVibrationEffect.Mono) {
- VibrationEffect e = ((CombinedVibrationEffect.Mono) effect).getEffect();
- if (e instanceof VibrationEffect.Prebaked) {
- VibrationEffect fallback = ((VibrationEffect.Prebaked) e).getFallbackEffect();
- if (fallback != null) {
- e = fallback;
- }
- }
- for (int i = 0; i < mInputDeviceVibrators.size(); i++) {
- mInputDeviceVibrators.valueAt(i).vibrate(uid, opPkg, e, reason, attrs);
- }
+ for (int i = 0; i < mInputDeviceVibrators.size(); i++) {
+ mInputDeviceVibrators.valueAt(i).vibrate(uid, opPkg, effect, reason, attrs);
}
return mInputDeviceVibrators.size() > 0;
}
}
/**
- * Cancel vibration on all {@link InputDevice} with {@link Vibrator} available.
+ * Cancel vibration on all {@link InputDevice} with vibrators.
*
* @return {@link #isAvailable()}
*/
@@ -147,9 +135,9 @@ public final class InputDeviceDelegate implements InputManager.InputDeviceListen
if (device == null) {
continue;
}
- Vibrator vibrator = device.getVibrator();
- if (vibrator.hasVibrator()) {
- mInputDeviceVibrators.put(device.getId(), vibrator);
+ VibratorManager vibratorManager = device.getVibratorManager();
+ if (vibratorManager.getVibratorIds().length > 0) {
+ mInputDeviceVibrators.put(device.getId(), vibratorManager);
}
}
} else {
@@ -171,9 +159,9 @@ public final class InputDeviceDelegate implements InputManager.InputDeviceListen
mInputDeviceVibrators.remove(deviceId);
return;
}
- Vibrator vibrator = device.getVibrator();
- if (vibrator.hasVibrator()) {
- mInputDeviceVibrators.put(deviceId, vibrator);
+ VibratorManager vibratorManager = device.getVibratorManager();
+ if (vibratorManager.getVibratorIds().length > 0) {
+ mInputDeviceVibrators.put(device.getId(), vibratorManager);
} else {
mInputDeviceVibrators.remove(deviceId);
}
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index e0f5408a1537..607da3ce6fe2 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -25,25 +25,16 @@ import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.util.proto.ProtoOutputStream;
-import com.android.server.ComposedProto;
-import com.android.server.OneShotProto;
-import com.android.server.PrebakedProto;
-import com.android.server.VibrationAttributesProto;
-import com.android.server.VibrationEffectProto;
-import com.android.server.VibrationProto;
-import com.android.server.WaveformProto;
-
import java.text.SimpleDateFormat;
import java.util.Date;
/** Represents a vibration request to the vibrator service. */
-// TODO(b/159207608): Make this package-private once vibrator services are moved to this package
-public class Vibration {
+final class Vibration {
private static final String TAG = "Vibration";
private static final SimpleDateFormat DEBUG_DATE_FORMAT =
new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
- public enum Status {
+ enum Status {
RUNNING,
FINISHED,
FORWARDED_TO_INPUT_DEVICES,
@@ -91,7 +82,7 @@ public class Vibration {
private long mEndTimeDebug;
private Status mStatus;
- public Vibration(IBinder token, int id, CombinedVibrationEffect effect,
+ Vibration(IBinder token, int id, CombinedVibrationEffect effect,
VibrationAttributes attrs, int uid, String opPkg, String reason) {
this.token = token;
this.mEffect = effect;
@@ -157,7 +148,7 @@ public class Vibration {
}
/** Debug information about vibrations. */
- public static final class DebugInfo {
+ static final class DebugInfo {
private final long mStartTimeDebug;
private final long mEndTimeDebug;
private final CombinedVibrationEffect mEffect;
@@ -169,7 +160,7 @@ public class Vibration {
private final String mReason;
private final Status mStatus;
- public DebugInfo(long startTimeDebug, long endTimeDebug, CombinedVibrationEffect effect,
+ DebugInfo(long startTimeDebug, long endTimeDebug, CombinedVibrationEffect effect,
CombinedVibrationEffect originalEffect, float scale, VibrationAttributes attrs,
int uid, String opPkg, String reason, Status status) {
mStartTimeDebug = startTimeDebug;
@@ -235,21 +226,49 @@ public class Vibration {
}
private void dumpEffect(
- ProtoOutputStream proto, long fieldId, CombinedVibrationEffect combinedEffect) {
- VibrationEffect effect;
- // TODO(b/177805090): add proper support for dumping combined effects to proto
- if (combinedEffect instanceof CombinedVibrationEffect.Mono) {
- effect = ((CombinedVibrationEffect.Mono) combinedEffect).getEffect();
- } else if (combinedEffect instanceof CombinedVibrationEffect.Stereo) {
- effect = ((CombinedVibrationEffect.Stereo) combinedEffect).getEffects().valueAt(0);
- } else if (combinedEffect instanceof CombinedVibrationEffect.Sequential) {
- dumpEffect(proto, fieldId,
- ((CombinedVibrationEffect.Sequential) combinedEffect).getEffects().get(0));
- return;
- } else {
- // Unknown combined effect, skip dump.
- return;
+ ProtoOutputStream proto, long fieldId, CombinedVibrationEffect effect) {
+ dumpEffect(proto, fieldId,
+ (CombinedVibrationEffect.Sequential) CombinedVibrationEffect.startSequential()
+ .addNext(effect)
+ .combine());
+ }
+
+ private void dumpEffect(
+ ProtoOutputStream proto, long fieldId, CombinedVibrationEffect.Sequential effect) {
+ final long token = proto.start(fieldId);
+ for (int i = 0; i < effect.getEffects().size(); i++) {
+ CombinedVibrationEffect nestedEffect = effect.getEffects().get(i);
+ if (nestedEffect instanceof CombinedVibrationEffect.Mono) {
+ dumpEffect(proto, CombinedVibrationEffectProto.EFFECTS,
+ (CombinedVibrationEffect.Mono) nestedEffect);
+ } else if (nestedEffect instanceof CombinedVibrationEffect.Stereo) {
+ dumpEffect(proto, CombinedVibrationEffectProto.EFFECTS,
+ (CombinedVibrationEffect.Stereo) nestedEffect);
+ }
+ proto.write(CombinedVibrationEffectProto.DELAYS, effect.getDelays().get(i));
+ }
+ proto.end(token);
+ }
+
+ private void dumpEffect(
+ ProtoOutputStream proto, long fieldId, CombinedVibrationEffect.Mono effect) {
+ final long token = proto.start(fieldId);
+ dumpEffect(proto, SyncVibrationEffectProto.EFFECTS, effect.getEffect());
+ proto.end(token);
+ }
+
+ private void dumpEffect(
+ ProtoOutputStream proto, long fieldId, CombinedVibrationEffect.Stereo effect) {
+ final long token = proto.start(fieldId);
+ for (int i = 0; i < effect.getEffects().size(); i++) {
+ proto.write(SyncVibrationEffectProto.VIBRATOR_IDS, effect.getEffects().keyAt(i));
+ dumpEffect(proto, SyncVibrationEffectProto.EFFECTS, effect.getEffects().valueAt(i));
}
+ proto.end(token);
+ }
+
+ private void dumpEffect(
+ ProtoOutputStream proto, long fieldId, VibrationEffect effect) {
final long token = proto.start(fieldId);
if (effect instanceof VibrationEffect.OneShot) {
dumpEffect(proto, VibrationEffectProto.ONESHOT, (VibrationEffect.OneShot) effect);
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index 0fa4fe16e1ba..10393f682279 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -29,8 +29,7 @@ import java.util.List;
import java.util.Objects;
/** Controls vibration scaling. */
-// TODO(b/159207608): Make this package-private once vibrator services are moved to this package
-public final class VibrationScaler {
+final class VibrationScaler {
private static final String TAG = "VibrationScaler";
// Scale levels. Each level, except MUTE, is defined as the delta between the current setting
@@ -56,7 +55,7 @@ public final class VibrationScaler {
private final VibrationSettings mSettingsController;
private final int mDefaultVibrationAmplitude;
- public VibrationScaler(Context context, VibrationSettings settingsController) {
+ VibrationScaler(Context context, VibrationSettings settingsController) {
mSettingsController = settingsController;
mDefaultVibrationAmplitude = context.getResources().getInteger(
com.android.internal.R.integer.config_defaultVibrationAmplitude);
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 8910bdfa1c05..334129d6bde9 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -39,20 +39,18 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
-import com.android.server.VibratorServiceDumpProto;
import java.util.ArrayList;
import java.util.List;
/** Controls all the system settings related to vibration. */
-// TODO(b/159207608): Make this package-private once vibrator services are moved to this package
-public final class VibrationSettings {
+final class VibrationSettings {
private static final String TAG = "VibrationSettings";
private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = {0, 30, 100, 30};
/** Listener for changes on vibration settings. */
- public interface OnVibratorSettingsChanged {
+ interface OnVibratorSettingsChanged {
/** Callback triggered when any of the vibrator settings change. */
void onChange();
}
@@ -86,7 +84,7 @@ public final class VibrationSettings {
@GuardedBy("mLock")
private boolean mLowPowerMode;
- public VibrationSettings(Context context, Handler handler) {
+ VibrationSettings(Context context, Handler handler) {
mContext = context;
mVibrator = context.getSystemService(Vibrator.class);
mAudioManager = context.getSystemService(AudioManager.class);
@@ -345,17 +343,17 @@ public final class VibrationSettings {
/** Write current settings into given {@link ProtoOutputStream}. */
public void dumpProto(ProtoOutputStream proto) {
synchronized (mLock) {
- proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
+ proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
mHapticFeedbackIntensity);
- proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
+ proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
mVibrator.getDefaultHapticFeedbackIntensity());
- proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY,
+ proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_INTENSITY,
mNotificationIntensity);
- proto.write(VibratorServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
+ proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
mVibrator.getDefaultNotificationVibrationIntensity());
- proto.write(VibratorServiceDumpProto.RING_INTENSITY,
+ proto.write(VibratorManagerServiceDumpProto.RING_INTENSITY,
mRingIntensity);
- proto.write(VibratorServiceDumpProto.RING_DEFAULT_INTENSITY,
+ proto.write(VibratorManagerServiceDumpProto.RING_DEFAULT_INTENSITY,
mVibrator.getDefaultRingVibrationIntensity());
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 4f2fc86df06c..389326769096 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -17,6 +17,7 @@
package com.android.server.vibrator;
import android.annotation.Nullable;
+import android.hardware.vibrator.IVibratorManager;
import android.os.CombinedVibrationEffect;
import android.os.IBinder;
import android.os.PowerManager;
@@ -41,8 +42,7 @@ import java.util.List;
import java.util.PriorityQueue;
/** Plays a {@link Vibration} in dedicated thread. */
-// TODO(b/159207608): Make this package-private once vibrator services are moved to this package
-public final class VibrationThread extends Thread implements IBinder.DeathRecipient {
+final class VibrationThread extends Thread implements IBinder.DeathRecipient {
private static final String TAG = "VibrationThread";
private static final boolean DEBUG = false;
@@ -53,7 +53,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
private static final long CALLBACKS_EXTRA_TIMEOUT = 100;
/** Callbacks for playing a {@link Vibration}. */
- public interface VibrationCallbacks {
+ interface VibrationCallbacks {
/**
* Callback triggered before starting a synchronized vibration step. This will be called
@@ -65,10 +65,13 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
* IVibratorManager.CAP_MIXED_TRIGGER_*.
* @param vibratorIds The id of the vibrators to be prepared.
*/
- void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds);
+ boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds);
/** Callback triggered after synchronized vibrations were prepared. */
- void triggerSyncedVibration(long vibrationId);
+ boolean triggerSyncedVibration(long vibrationId);
+
+ /** Callback triggered to cancel a prepared synced vibration. */
+ void cancelSyncedVibration();
/** Callback triggered when vibration thread is complete. */
void onVibrationEnded(long vibrationId, Vibration.Status status);
@@ -88,14 +91,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
@GuardedBy("mLock")
private boolean mForceStop;
- // TODO(b/159207608): Remove this constructor once VibratorService is removed
- public VibrationThread(Vibration vib, VibratorController vibrator,
- PowerManager.WakeLock wakeLock, IBatteryStats batteryStatsService,
- VibrationCallbacks callbacks) {
- this(vib, toSparseArray(vibrator), wakeLock, batteryStatsService, callbacks);
- }
-
- public VibrationThread(Vibration vib, SparseArray<VibratorController> availableVibrators,
+ VibrationThread(Vibration vib, SparseArray<VibratorController> availableVibrators,
PowerManager.WakeLock wakeLock, IBatteryStats batteryStatsService,
VibrationCallbacks callbacks) {
mVibration = vib;
@@ -146,6 +142,20 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
}
}
+ /** Notify current vibration that a synced step has completed. */
+ public void syncedVibrationComplete() {
+ synchronized (mLock) {
+ 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));
+ }
+ }
+ }
+ }
+
/** Notify current vibration that a step has completed on given vibrator. */
public void vibratorComplete(int vibratorId) {
synchronized (mLock) {
@@ -158,7 +168,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
}
}
- public Vibration getVibration() {
+ Vibration getVibration() {
return mVibration;
}
@@ -268,12 +278,6 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
return filteredEffects;
}
- private static SparseArray<VibratorController> toSparseArray(VibratorController controller) {
- SparseArray<VibratorController> array = new SparseArray<>(1);
- array.put(controller.getVibratorInfo().getId(), controller);
- return array;
- }
-
/**
* Get the duration the vibrator will be on for given {@code waveform}, starting at {@code
* startIndex} until the next time it's vibrating amplitude is zero.
@@ -530,7 +534,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
/** Represent a synchronized vibration step on multiple vibrators. */
private final class SyncedVibrateStep implements VibrateStep {
private final SparseArray<VibrationEffect> mEffects;
- private final int mRequiredCapabilities;
+ private final long mRequiredCapabilities;
private final int[] mVibratorIds;
@GuardedBy("mLock")
@@ -539,8 +543,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
SyncedVibrateStep(SparseArray<VibrationEffect> effects) {
mEffects = effects;
mActiveVibratorCounter = mEffects.size();
- // TODO(b/159207608): Calculate required capabilities for syncing this step.
- mRequiredCapabilities = 0;
+ mRequiredCapabilities = calculateRequiredSyncCapabilities(effects);
mVibratorIds = new int[effects.size()];
for (int i = 0; i < effects.size(); i++) {
mVibratorIds[i] = effects.keyAt(i);
@@ -574,18 +577,21 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
@Override
public Vibration.Status play() {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "SyncedVibrateStep");
- long timeout = -1;
+ long duration = -1;
try {
if (DEBUG) {
Slog.d(TAG, "SyncedVibrateStep starting...");
}
final PriorityQueue<AmplitudeStep> nextSteps = new PriorityQueue<>(mEffects.size());
long startTime = SystemClock.uptimeMillis();
- mCallbacks.prepareSyncedVibration(mRequiredCapabilities, mVibratorIds);
- timeout = startVibrating(startTime, nextSteps);
- mCallbacks.triggerSyncedVibration(mVibration.id);
- noteVibratorOn(timeout);
+ duration = startVibratingSynced(startTime, nextSteps);
+
+ if (duration <= 0) {
+ // Vibrate step failed, vibrator could not be turned on for this step.
+ return Vibration.Status.IGNORED;
+ }
+ noteVibratorOn(duration);
while (!nextSteps.isEmpty()) {
AmplitudeStep step = nextSteps.poll();
if (!waitUntil(step.startTime)) {
@@ -607,7 +613,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
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 + timeout + CALLBACKS_EXTRA_TIMEOUT;
+ final long wakeUpTime = startTime + duration + CALLBACKS_EXTRA_TIMEOUT;
if (mActiveVibratorCounter <= 0 || waitForVibrationComplete(this, wakeUpTime)) {
return Vibration.Status.FINISHED;
}
@@ -617,7 +623,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
}
} finally {
- if (timeout > 0) {
+ if (duration > 0) {
noteVibratorOff();
}
if (DEBUG) {
@@ -628,14 +634,48 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
}
/**
+ * 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.
+ */
+ private long startVibratingSynced(long startTime, PriorityQueue<AmplitudeStep> 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.
+ synchronized (mLock) {
+ boolean hasPrepared = false;
+ boolean hasTriggered = false;
+ try {
+ hasPrepared = mCallbacks.prepareSyncedVibration(mRequiredCapabilities,
+ mVibratorIds);
+ long timeout = startVibrating(startTime, nextSteps);
+
+ // Check if preparation was successful, otherwise devices area already vibrating
+ if (hasPrepared) {
+ hasTriggered = mCallbacks.triggerSyncedVibration(mVibration.id);
+ }
+ return timeout;
+ } finally {
+ if (hasPrepared && !hasTriggered) {
+ mCallbacks.cancelSyncedVibration();
+ return 0;
+ }
+ }
+ }
+ }
+
+ /**
* 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 duration, in millis, to wait for the completion of all vibrations. This ignores
- * any repeating waveform duration and returns the duration of a single run.
+ * @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;
@@ -651,9 +691,9 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
/**
* Play a single effect on a single vibrator.
*
- * @return A duration, in millis, to wait for the completion of this effect. This ignores
- * any repeating waveform duration and returns the duration of a single run to be used as
- * timeout for callbacks.
+ * @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(VibratorController controller, VibrationEffect effect,
long startTime, PriorityQueue<AmplitudeStep> nextSteps) {
@@ -698,6 +738,49 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
}
}
}
+
+ /**
+ * 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;
+ }
+ 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;
+ }
}
/** Represent a step to set amplitude on a single vibrator. */
@@ -794,12 +877,12 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
// Waveform has ended, no more steps to run.
return null;
}
- long nextWakeUpTime = startTime + waveform.getTimings()[currentIndex];
+ long nextStartTime = startTime + waveform.getTimings()[currentIndex];
int nextIndex = currentIndex + 1;
if (nextIndex >= waveform.getTimings().length) {
nextIndex = waveform.getRepeatIndex();
}
- return new AmplitudeStep(vibratorId, waveform, nextIndex, nextWakeUpTime,
+ return new AmplitudeStep(vibratorId, waveform, nextIndex, nextStartTime,
nextVibratorStopTime());
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 9dcf12caa55b..95f6059c482e 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -32,8 +32,7 @@ import com.android.internal.annotations.VisibleForTesting;
import libcore.util.NativeAllocationRegistry;
/** Controls a single vibrator. */
-// TODO(b/159207608): Make this package-private once vibrator services are moved to this package
-public final class VibratorController {
+final class VibratorController {
private static final String TAG = "VibratorController";
private final Object mLock = new Object();
@@ -99,12 +98,12 @@ public final class VibratorController {
static native void vibratorAlwaysOnDisable(long nativePtr, long id);
- public VibratorController(int vibratorId, OnVibrationCompleteListener listener) {
+ VibratorController(int vibratorId, OnVibrationCompleteListener listener) {
this(vibratorId, listener, new NativeWrapper());
}
@VisibleForTesting
- public VibratorController(int vibratorId, OnVibrationCompleteListener listener,
+ VibratorController(int vibratorId, OnVibrationCompleteListener listener,
NativeWrapper nativeWrapper) {
mNativeWrapper = nativeWrapper;
mNativeWrapper.init(vibratorId, listener);
@@ -142,11 +141,6 @@ public final class VibratorController {
}
}
- @VisibleForTesting
- public NativeWrapper getNativeWrapper() {
- return mNativeWrapper;
- }
-
/** Return the {@link VibratorInfo} representing the vibrator controlled by this instance. */
public VibratorInfo getVibratorInfo() {
return mVibratorInfo;
diff --git a/services/core/java/com/android/server/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 6a816af68959..175085475b6c 100644
--- a/services/core/java/com/android/server/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.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.
@@ -14,13 +14,18 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.vibrator;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.hardware.vibrator.IVibrator;
import android.os.BatteryStats;
import android.os.Binder;
@@ -51,12 +56,8 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
-import com.android.server.vibrator.InputDeviceDelegate;
-import com.android.server.vibrator.Vibration;
-import com.android.server.vibrator.VibrationScaler;
-import com.android.server.vibrator.VibrationSettings;
-import com.android.server.vibrator.VibrationThread;
-import com.android.server.vibrator.VibratorController;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
import libcore.util.NativeAllocationRegistry;
@@ -74,6 +75,7 @@ import java.util.function.Function;
/** System implementation of {@link IVibratorManagerService}. */
public class VibratorManagerService extends IVibratorManagerService.Stub {
private static final String TAG = "VibratorManagerService";
+ private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
private static final boolean DEBUG = false;
private static final VibrationAttributes DEFAULT_ATTRIBUTES =
new VibrationAttributes.Builder().build();
@@ -89,7 +91,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@Override
public void onStart() {
mService = new VibratorManagerService(getContext(), new Injector());
- publishBinderService("vibrator_manager", mService);
+ publishBinderService(Context.VIBRATOR_MANAGER_SERVICE, mService);
}
@Override
@@ -105,12 +107,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private final Object mLock = new Object();
private final Context mContext;
+ private final String mSystemUiPackage;
private final PowerManager.WakeLock mWakeLock;
private final IBatteryStats mBatteryStatsService;
private final Handler mHandler;
private final AppOpsManager mAppOps;
private final NativeWrapper mNativeWrapper;
private final VibratorManagerRecords mVibratorManagerRecords;
+ private final long mCapabilities;
private final int[] mVibratorIds;
private final SparseArray<VibratorController> mVibrators;
private final VibrationCallbacks mVibrationCallbacks = new VibrationCallbacks();
@@ -127,23 +131,56 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private VibrationScaler mVibrationScaler;
private InputDeviceDelegate mInputDeviceDelegate;
- static native long nativeInit();
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+ synchronized (mLock) {
+ // When the system is entering a non-interactive state, we want
+ // to cancel vibrations in case a misbehaving app has abandoned
+ // them. However it may happen that the system is currently playing
+ // haptic feedback as part of the transition. So we don't cancel
+ // system vibrations.
+ if (mCurrentVibration != null
+ && !isSystemHapticFeedback(mCurrentVibration.getVibration())) {
+ mNextVibration = null;
+ mCurrentVibration.cancel();
+ }
+ }
+ }
+ }
+ };
+
+ static native long nativeInit(OnSyncedVibrationCompleteListener listener);
static native long nativeGetFinalizer();
+ static native long nativeGetCapabilities(long nativeServicePtr);
+
static native int[] nativeGetVibratorIds(long nativeServicePtr);
+ static native boolean nativePrepareSynced(long nativeServicePtr, int[] vibratorIds);
+
+ static native boolean nativeTriggerSynced(long nativeServicePtr, long vibrationId);
+
+ static native void nativeCancelSynced(long nativeServicePtr);
+
@VisibleForTesting
VibratorManagerService(Context context, Injector injector) {
mContext = context;
mHandler = injector.createHandler(Looper.myLooper());
+
+ VibrationCompleteListener listener = new VibrationCompleteListener(this);
mNativeWrapper = injector.getNativeWrapper();
- mNativeWrapper.init();
+ mNativeWrapper.init(listener);
int dumpLimit = mContext.getResources().getInteger(
com.android.internal.R.integer.config_previousVibrationsDumpLimit);
mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit);
+ mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
+ .getSystemUiServiceComponent().getPackageName();
+
mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
BatteryStats.SERVICE_NAME));
@@ -153,6 +190,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
mWakeLock.setReferenceCounted(true);
+ mCapabilities = mNativeWrapper.getCapabilities();
int[] vibratorIds = mNativeWrapper.getVibratorIds();
if (vibratorIds == null) {
mVibratorIds = new int[0];
@@ -161,11 +199,23 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// Keep original vibrator id order, which might be meaningful.
mVibratorIds = vibratorIds;
mVibrators = new SparseArray<>(mVibratorIds.length);
- VibrationCompleteListener listener = new VibrationCompleteListener(this);
for (int vibratorId : vibratorIds) {
mVibrators.put(vibratorId, injector.createVibratorController(vibratorId, listener));
}
}
+
+ // Reset the hardware to a default state, in case this is a runtime restart instead of a
+ // fresh boot.
+ mNativeWrapper.cancelSynced();
+ for (int i = 0; i < mVibrators.size(); i++) {
+ mVibrators.valueAt(i).off();
+ }
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ context.registerReceiver(mIntentReceiver, filter);
+
+ injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
}
/** Finish initialization at boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}. */
@@ -302,10 +352,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return;
}
- VibrationThread vibThread = new VibrationThread(vib, mVibrators, mWakeLock,
- mBatteryStatsService, mVibrationCallbacks);
-
- ignoreStatus = shouldIgnoreVibrationForCurrentLocked(vibThread);
+ ignoreStatus = shouldIgnoreVibrationForCurrentLocked(vib);
if (ignoreStatus != null) {
endVibrationLocked(vib, ignoreStatus);
return;
@@ -316,7 +363,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (mCurrentVibration != null) {
mCurrentVibration.cancel();
}
- Vibration.Status status = startVibrationLocked(vibThread);
+ Vibration.Status status = startVibrationLocked(vib);
if (status != Vibration.Status.RUNNING) {
endVibrationLocked(vib, status);
}
@@ -353,7 +400,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mVibratorManagerRecords.record(mCurrentExternalVibration);
mCurrentExternalVibration.externalVibration.mute();
mCurrentExternalVibration = null;
- // TODO(b/167946816): set external control to false
+ setExternalControl(false);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -414,6 +461,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ private void setExternalControl(boolean externalControl) {
+ for (int i = 0; i < mVibrators.size(); i++) {
+ mVibrators.valueAt(i).setExternalControl(externalControl);
+ }
+ }
+
@GuardedBy("mLock")
private void updateAlwaysOnLocked(AlwaysOnVibration vib) {
for (int i = 0; i < vib.effects.size(); i++) {
@@ -435,19 +488,19 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
@GuardedBy("mLock")
- private Vibration.Status startVibrationLocked(VibrationThread vibThread) {
+ private Vibration.Status startVibrationLocked(Vibration vib) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
try {
- Vibration vib = vibThread.getVibration();
vib.updateEffect(mVibrationScaler.scale(vib.getEffect(), vib.attrs.getUsage()));
-
boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable(
vib.uid, vib.opPkg, vib.getEffect(), vib.reason, vib.attrs);
-
if (inputDevicesAvailable) {
return Vibration.Status.FORWARDED_TO_INPUT_DEVICES;
}
+ VibrationThread vibThread = new VibrationThread(vib, mVibrators, mWakeLock,
+ mBatteryStatsService, mVibrationCallbacks);
+
if (mCurrentVibration == null) {
return startVibrationThreadLocked(vibThread);
}
@@ -489,6 +542,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
@GuardedBy("mLock")
+ private void endVibrationLocked(ExternalVibrationHolder vib, Vibration.Status status) {
+ vib.end(status);
+ mVibratorManagerRecords.record(vib);
+ }
+
+ @GuardedBy("mLock")
private void reportFinishedVibrationLocked(Vibration.Status status) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
@@ -502,6 +561,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ private void onSyncedVibrationComplete(long vibrationId) {
+ synchronized (mLock) {
+ if (mCurrentVibration != null && mCurrentVibration.getVibration().id == vibrationId) {
+ if (DEBUG) {
+ Slog.d(TAG, "Synced vibration " + vibrationId + " complete, notifying thread");
+ }
+ mCurrentVibration.syncedVibrationComplete();
+ }
+ }
+ }
+
private void onVibrationComplete(int vibratorId, long vibrationId) {
synchronized (mLock) {
if (mCurrentVibration != null && mCurrentVibration.getVibration().id == vibrationId) {
@@ -522,8 +592,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
*/
@GuardedBy("mLock")
@Nullable
- private Vibration.Status shouldIgnoreVibrationForCurrentLocked(VibrationThread vibThread) {
- if (vibThread.getVibration().isRepeating()) {
+ private Vibration.Status shouldIgnoreVibrationForCurrentLocked(Vibration vibration) {
+ if (vibration.isRepeating()) {
// Repeating vibrations always take precedence.
return null;
}
@@ -798,6 +868,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
== PackageManager.PERMISSION_GRANTED;
}
+ private boolean isSystemHapticFeedback(Vibration vib) {
+ if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) {
+ return false;
+ }
+ return vib.uid == Process.SYSTEM_UID || vib.uid == 0 || mSystemUiPackage.equals(vib.opPkg);
+ }
+
@GuardedBy("mLock")
private void onAllVibratorsLocked(Consumer<VibratorController> consumer) {
for (int i = 0; i < mVibrators.size(); i++) {
@@ -830,6 +907,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
VibratorController.OnVibrationCompleteListener listener) {
return new VibratorController(vibratorId, listener);
}
+
+ void addService(String name, IBinder service) {
+ ServiceManager.addService(name, service);
+ }
}
/**
@@ -839,13 +920,22 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private final class VibrationCallbacks implements VibrationThread.VibrationCallbacks {
@Override
- public void prepareSyncedVibration(int requiredCapabilities, int[] vibratorIds) {
- // TODO(b/167946816): call IVibratorManager to prepare
+ public boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds) {
+ if ((mCapabilities & requiredCapabilities) != requiredCapabilities) {
+ // This sync step requires capabilities this device doesn't have, skipping sync...
+ return false;
+ }
+ return mNativeWrapper.prepareSynced(vibratorIds);
+ }
+
+ @Override
+ public boolean triggerSyncedVibration(long vibrationId) {
+ return mNativeWrapper.triggerSynced(vibrationId);
}
@Override
- public void triggerSyncedVibration(long vibrationId) {
- // TODO(b/167946816): call IVibratorManager to trigger
+ public void cancelSyncedVibration() {
+ mNativeWrapper.cancelSynced();
}
@Override
@@ -868,12 +958,19 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ /** Listener for synced vibration completion callbacks from native. */
+ @VisibleForTesting
+ interface OnSyncedVibrationCompleteListener {
+
+ /** Callback triggered when synced vibration is complete. */
+ void onComplete(long vibrationId);
+ }
+
/**
- * Implementation of {@link VibratorController.OnVibrationCompleteListener} with a weak
- * reference to this service.
+ * Implementation of listeners to native vibrators with a weak reference to this service.
*/
private static final class VibrationCompleteListener implements
- VibratorController.OnVibrationCompleteListener {
+ VibratorController.OnVibrationCompleteListener, OnSyncedVibrationCompleteListener {
private WeakReference<VibratorManagerService> mServiceRef;
VibrationCompleteListener(VibratorManagerService service) {
@@ -881,6 +978,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
@Override
+ public void onComplete(long vibrationId) {
+ VibratorManagerService service = mServiceRef.get();
+ if (service != null) {
+ service.onSyncedVibrationComplete(vibrationId);
+ }
+ }
+
+ @Override
public void onComplete(int vibratorId, long vibrationId) {
VibratorManagerService service = mServiceRef.get();
if (service != null) {
@@ -952,9 +1057,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private long mNativeServicePtr = 0;
/** Returns native pointer to newly created controller and connects with HAL service. */
- public void init() {
- mNativeServicePtr = VibratorManagerService.nativeInit();
- long finalizerPtr = VibratorManagerService.nativeGetFinalizer();
+ public void init(OnSyncedVibrationCompleteListener listener) {
+ mNativeServicePtr = nativeInit(listener);
+ long finalizerPtr = nativeGetFinalizer();
if (finalizerPtr != 0) {
NativeAllocationRegistry registry =
@@ -964,9 +1069,29 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ /** Returns manager capabilities. */
+ public long getCapabilities() {
+ return nativeGetCapabilities(mNativeServicePtr);
+ }
+
/** Returns vibrator ids. */
public int[] getVibratorIds() {
- return VibratorManagerService.nativeGetVibratorIds(mNativeServicePtr);
+ return nativeGetVibratorIds(mNativeServicePtr);
+ }
+
+ /** Prepare vibrators for triggering vibrations in sync. */
+ public boolean prepareSynced(@NonNull int[] vibratorIds) {
+ return nativePrepareSynced(mNativeServicePtr, vibratorIds);
+ }
+
+ /** Trigger prepared synced vibration. */
+ public boolean triggerSynced(long vibrationId) {
+ return nativeTriggerSynced(mNativeServicePtr, vibrationId);
+ }
+
+ /** Cancel prepared synced vibration. */
+ public void cancelSynced() {
+ nativeCancelSynced(mNativeServicePtr);
}
}
@@ -1015,14 +1140,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
pw.println();
pw.println(" mCurrentVibration:");
- pw.println(" " + mCurrentVibration == null
- ? null : mCurrentVibration.getVibration().getDebugInfo());
+ pw.println(" " + (mCurrentVibration == null
+ ? null : mCurrentVibration.getVibration().getDebugInfo()));
pw.println(" mNextVibration:");
- pw.println(" " + mNextVibration == null
- ? null : mNextVibration.getVibration().getDebugInfo());
+ pw.println(" " + (mNextVibration == null
+ ? null : mNextVibration.getVibration().getDebugInfo()));
pw.println(" mCurrentExternalVibration:");
- pw.println(" " + mCurrentExternalVibration == null
- ? null : mCurrentExternalVibration.getDebugInfo());
+ pw.println(" " + (mCurrentExternalVibration == null
+ ? null : mCurrentExternalVibration.getDebugInfo()));
pw.println();
pw.println(" mVibrationSettings=" + mVibrationSettings);
for (int i = 0; i < mPreviousVibrations.size(); i++) {
@@ -1035,6 +1160,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ pw.println();
pw.println(" Previous external vibrations:");
for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
pw.println(" " + info);
@@ -1049,49 +1175,190 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mVibrationSettings.dumpProto(proto);
if (mCurrentVibration != null) {
mCurrentVibration.getVibration().getDebugInfo().dumpProto(proto,
- VibratorServiceDumpProto.CURRENT_VIBRATION);
+ VibratorManagerServiceDumpProto.CURRENT_VIBRATION);
}
if (mCurrentExternalVibration != null) {
mCurrentExternalVibration.getDebugInfo().dumpProto(proto,
- VibratorServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
+ VibratorManagerServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
}
boolean isVibrating = false;
boolean isUnderExternalControl = false;
for (int i = 0; i < mVibrators.size(); i++) {
+ proto.write(VibratorManagerServiceDumpProto.VIBRATOR_IDS, mVibrators.keyAt(i));
isVibrating |= mVibrators.valueAt(i).isVibrating();
isUnderExternalControl |= mVibrators.valueAt(i).isUnderExternalControl();
}
- proto.write(VibratorServiceDumpProto.IS_VIBRATING, isVibrating);
- proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
+ proto.write(VibratorManagerServiceDumpProto.IS_VIBRATING, isVibrating);
+ proto.write(VibratorManagerServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
isUnderExternalControl);
- for (Vibration.DebugInfo info : mPreviousVibrations.get(
- VibrationAttributes.USAGE_RINGTONE)) {
- info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
+ for (int i = 0; i < mPreviousVibrations.size(); i++) {
+ long fieldId;
+ switch (mPreviousVibrations.keyAt(i)) {
+ case VibrationAttributes.USAGE_RINGTONE:
+ fieldId = VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS;
+ break;
+ case VibrationAttributes.USAGE_NOTIFICATION:
+ fieldId = VibratorManagerServiceDumpProto
+ .PREVIOUS_NOTIFICATION_VIBRATIONS;
+ break;
+ case VibrationAttributes.USAGE_ALARM:
+ fieldId = VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS;
+ break;
+ default:
+ fieldId = VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS;
+ }
+ for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
+ info.dumpProto(proto, fieldId);
+ }
}
- for (Vibration.DebugInfo info : mPreviousVibrations.get(
- VibrationAttributes.USAGE_NOTIFICATION)) {
+ for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
info.dumpProto(proto,
- VibratorServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS);
+ VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
}
+ }
+ proto.flush();
+ }
+ }
+
+ /** Implementation of {@link IExternalVibratorService} to be triggered on external control. */
+ private final class ExternalVibratorService extends IExternalVibratorService.Stub {
+ ExternalVibrationDeathRecipient mCurrentExternalDeathRecipient;
- for (Vibration.DebugInfo info : mPreviousVibrations.get(
- VibrationAttributes.USAGE_ALARM)) {
- info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS);
+ @Override
+ public int onExternalVibrationStart(ExternalVibration vib) {
+ if (!hasExternalControlCapability()) {
+ return IExternalVibratorService.SCALE_MUTE;
+ }
+ if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
+ vib.getUid(), -1 /*owningUid*/, true /*exported*/)
+ != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
+ + " tried to play externally controlled vibration"
+ + " without VIBRATE permission, ignoring.");
+ return IExternalVibratorService.SCALE_MUTE;
+ }
+
+ int mode = checkAppOpModeLocked(vib.getUid(), vib.getPackage(),
+ vib.getVibrationAttributes());
+ if (mode != AppOpsManager.MODE_ALLOWED) {
+ ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
+ vibHolder.scale = IExternalVibratorService.SCALE_MUTE;
+ if (mode == AppOpsManager.MODE_ERRORED) {
+ Slog.w(TAG, "Would be an error: external vibrate from uid " + vib.getUid());
+ endVibrationLocked(vibHolder, Vibration.Status.IGNORED_ERROR_APP_OPS);
+ } else {
+ endVibrationLocked(vibHolder, Vibration.Status.IGNORED_APP_OPS);
}
+ return vibHolder.scale;
+ }
- for (Vibration.DebugInfo info : mPreviousVibrations.get(
- VibrationAttributes.USAGE_UNKNOWN)) {
- info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_VIBRATIONS);
+ VibrationThread cancelingVibration = null;
+ int scale;
+ synchronized (mLock) {
+ if (mCurrentExternalVibration != null
+ && mCurrentExternalVibration.externalVibration.equals(vib)) {
+ // We are already playing this external vibration, so we can return the same
+ // scale calculated in the previous call to this method.
+ return mCurrentExternalVibration.scale;
}
+ if (mCurrentExternalVibration == null) {
+ // If we're not under external control right now, then cancel any normal
+ // vibration that may be playing and ready the vibrator for external control.
+ if (mCurrentVibration != null) {
+ mNextVibration = null;
+ mCurrentVibration.cancel();
+ cancelingVibration = mCurrentVibration;
+ }
+ } else {
+ endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED);
+ }
+ // At this point we either have an externally controlled vibration playing, or
+ // no vibration playing. Since the interface defines that only one externally
+ // controlled vibration can play at a time, by returning something other than
+ // SCALE_MUTE from this function we can be assured that if we are currently
+ // playing vibration, it will be muted in favor of the new vibration.
+ //
+ // Note that this doesn't support multiple concurrent external controls, as we
+ // would need to mute the old one still if it came from a different controller.
+ mCurrentExternalVibration = new ExternalVibrationHolder(vib);
+ mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
+ vib.linkToDeath(mCurrentExternalDeathRecipient);
+ mCurrentExternalVibration.scale = mVibrationScaler.getExternalVibrationScale(
+ vib.getVibrationAttributes().getUsage());
+ scale = mCurrentExternalVibration.scale;
+ }
- for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
- info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
+ if (cancelingVibration != null) {
+ try {
+ cancelingVibration.join();
+ } catch (InterruptedException e) {
+ Slog.w("Interrupted while waiting for vibration to finish before starting "
+ + "external control", e);
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Vibrator going under external control.");
+ }
+ setExternalControl(true);
+ if (DEBUG) {
+ Slog.e(TAG, "Playing external vibration: " + vib);
+ }
+ return scale;
+ }
+
+ @Override
+ public void onExternalVibrationStop(ExternalVibration vib) {
+ synchronized (mLock) {
+ if (mCurrentExternalVibration != null
+ && mCurrentExternalVibration.externalVibration.equals(vib)) {
+ if (DEBUG) {
+ Slog.e(TAG, "Stopping external vibration" + vib);
+ }
+ stopExternalVibrateLocked(Vibration.Status.FINISHED);
+ }
+ }
+ }
+
+ private void stopExternalVibrateLocked(Vibration.Status status) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "stopExternalVibrateLocked");
+ try {
+ if (mCurrentExternalVibration == null) {
+ return;
+ }
+ endVibrationLocked(mCurrentExternalVibration, status);
+ mCurrentExternalVibration.externalVibration.unlinkToDeath(
+ mCurrentExternalDeathRecipient);
+ mCurrentExternalDeathRecipient = null;
+ mCurrentExternalVibration = null;
+ setExternalControl(false);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
+ private boolean hasExternalControlCapability() {
+ for (int i = 0; i < mVibrators.size(); i++) {
+ if (mVibrators.valueAt(i).hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private class ExternalVibrationDeathRecipient implements IBinder.DeathRecipient {
+ public void binderDied() {
+ synchronized (mLock) {
+ if (mCurrentExternalVibration != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "External vibration finished because binder died");
+ }
+ stopExternalVibrateLocked(Vibration.Status.CANCELLED);
+ }
}
}
- proto.flush();
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 5697564ce93f..370d921de2af 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -31,6 +31,7 @@ import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.ILocalWallpaperColorConsumer;
import android.app.IWallpaperManager;
import android.app.IWallpaperManagerCallback;
import android.app.PendingIntent;
@@ -59,6 +60,7 @@ import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Color;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.Bundle;
@@ -85,7 +87,10 @@ import android.service.wallpaper.IWallpaperService;
import android.service.wallpaper.WallpaperService;
import android.system.ErrnoException;
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;
@@ -105,7 +110,6 @@ import com.android.server.EventLogTags;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.WindowManagerInternal;
@@ -136,6 +140,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private static final String TAG = "WallpaperManagerService";
private static final boolean DEBUG = false;
private static final boolean DEBUG_LIVE = true;
+ private static final @NonNull RectF LOCAL_COLOR_BOUNDS =
+ new RectF(0, 0, 1, 1);
public static class Lifecycle extends SystemService {
private IWallpaperManagerService mService;
@@ -866,6 +872,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
private final SparseBooleanArray mUserRestorecon = new SparseBooleanArray();
private int mCurrentUserId = UserHandle.USER_NULL;
private boolean mInAmbientMode;
+ private ArrayMap<IBinder, ArraySet<RectF>> mLocalColorCallbackAreas =
+ new ArrayMap<>();
+ private ArrayMap<RectF, RemoteCallbackList<ILocalWallpaperColorConsumer>>
+ mLocalColorAreaCallbacks = new ArrayMap<>();
+ private ArrayMap<Integer, ArraySet<RectF>> mLocalColorDisplayIdAreas = new ArrayMap<>();
+ private ArrayMap<IBinder, Integer> mLocalColorCallbackDisplayId = new ArrayMap<>();
static class WallpaperData {
@@ -1276,6 +1288,32 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
@Override
+ public void onLocalWallpaperColorsChanged(RectF area, WallpaperColors colors,
+ int displayId) {
+ forEachDisplayConnector(displayConnector -> {
+ if (displayConnector.mDisplayId == displayId) {
+ RemoteCallbackList<ILocalWallpaperColorConsumer> callbacks;
+ ArrayMap<IBinder, Integer> callbackDisplayIds;
+ synchronized (mLock) {
+ callbacks = mLocalColorAreaCallbacks.get(area);
+ callbackDisplayIds = new ArrayMap<>(mLocalColorCallbackDisplayId);
+ }
+ if (callbacks == null) return;
+ callbacks.broadcast(c -> {
+ try {
+ int targetDisplayId =
+ callbackDisplayIds.get(c.asBinder());
+ if (targetDisplayId == displayId) c.onColorsChanged(area, colors);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ });
+ }
+ });
+ }
+
+
+ @Override
public void onServiceDisconnected(ComponentName name) {
synchronized (mLock) {
Slog.w(TAG, "Wallpaper service gone: " + name);
@@ -1437,6 +1475,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
} catch (RemoteException e) {
Slog.w(TAG, "Failed to request wallpaper colors", e);
}
+
+ ArraySet<RectF> areas = mLocalColorDisplayIdAreas.get(displayId);
+ if (areas != null && areas.size() != 0) {
+ try {
+ connector.mEngine.addLocalColorsAreas(new ArrayList<>(areas));
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to register local colors areas", e);
+ }
+ }
}
}
@@ -2340,6 +2387,115 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return true;
}
+ private IWallpaperEngine getEngine(int which, int userId, int displayId) {
+ WallpaperData wallpaperData = findWallpaperAtDisplay(userId, displayId);
+ if (wallpaperData == null) return null;
+ IWallpaperEngine engine = null;
+ synchronized (mLock) {
+ for (int i = 0; i < wallpaperData.connection.mDisplayConnector.size(); i++) {
+ int id = wallpaperData.connection.mDisplayConnector.get(i).mDisplayId;
+ int currentWhich = wallpaperData.connection.mDisplayConnector.get(i).mDisplayId;
+ if (id != displayId && currentWhich != which) continue;
+ engine = wallpaperData.connection.mDisplayConnector.get(i).mEngine;
+ break;
+ }
+ }
+ return engine;
+ }
+
+ @Override
+ public void addOnLocalColorsChangedListener(@NonNull ILocalWallpaperColorConsumer callback,
+ @NonNull List<RectF> regions, 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");
+ }
+ IWallpaperEngine engine = getEngine(which, userId, displayId);
+ if (engine == null) return;
+ ArrayList<RectF> validAreas = new ArrayList<>(regions.size());
+ synchronized (mLock) {
+ ArraySet<RectF> areas = mLocalColorCallbackAreas.get(callback);
+ if (areas == null) areas = new ArraySet<>(regions.size());
+ areas.addAll(regions);
+ mLocalColorCallbackAreas.put(callback.asBinder(), areas);
+ }
+ for (int i = 0; i < regions.size(); i++) {
+ if (!LOCAL_COLOR_BOUNDS.contains(regions.get(i))) {
+ continue;
+ }
+ RemoteCallbackList callbacks;
+ synchronized (mLock) {
+ callbacks = mLocalColorAreaCallbacks.get(
+ regions.get(i));
+ if (callbacks == null) {
+ callbacks = new RemoteCallbackList();
+ mLocalColorAreaCallbacks.put(regions.get(i), callbacks);
+ }
+ mLocalColorCallbackDisplayId.put(callback.asBinder(), displayId);
+ ArraySet<RectF> displayAreas = mLocalColorDisplayIdAreas.get(displayId);
+ if (displayAreas == null) {
+ displayAreas = new ArraySet<>(1);
+ mLocalColorDisplayIdAreas.put(displayId, displayAreas);
+ }
+ displayAreas.add(regions.get(i));
+ }
+ validAreas.add(regions.get(i));
+ callbacks.register(callback);
+ }
+ engine.addLocalColorsAreas(validAreas);
+ }
+
+ @Override
+ public void removeOnLocalColorsChangedListener(
+ @NonNull ILocalWallpaperColorConsumer callback, 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");
+ }
+ final UserHandle callingUser = Binder.getCallingUserHandle();
+ if (callingUser.getIdentifier() != userId) {
+ 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<>();
+ 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);
+ }
+ }
+ }
+ 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;
+ IWallpaperEngine engine = getEngine(which, userId, displayId);
+ if (engine == null) return;
+ engine.removeLocalColorsAreas(removeAreas);
+ }
+
@Override
public WallpaperColors getWallpaperColors(int which, int userId, int displayId)
throws RemoteException {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 771b712cf480..904bbe83e020 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -43,6 +43,7 @@ import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IActivityClientController;
+import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.EnterPipRequestedItem;
@@ -50,6 +51,8 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.os.Binder;
import android.os.Bundle;
@@ -134,10 +137,10 @@ class ActivityClientController extends IActivityClientController.Stub {
}
@Override
- public void activityResumed(IBinder token) {
+ public void activityResumed(IBinder token, boolean handleSplashScreenExit) {
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
- ActivityRecord.activityResumedLocked(token);
+ ActivityRecord.activityResumedLocked(token, handleSplashScreenExit);
}
Binder.restoreCallingIdentity(origId);
}
@@ -692,6 +695,18 @@ class ActivityClientController extends IActivityClientController.Stub {
}
/**
+ * Splash screen view is attached to activity.
+ */
+ @Override
+ public void splashScreenAttached(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ synchronized (mGlobalLock) {
+ ActivityRecord.splashScreenAttachedLocked(token);
+ }
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ /**
* Checks the state of the system and the activity associated with the given {@param token} to
* verify that picture-in-picture is supported for that activity.
*
@@ -1112,24 +1127,78 @@ class ActivityClientController extends IActivityClientController.Stub {
}
@Override
- public void onBackPressedOnTaskRoot(IBinder token) {
+ public void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) {
final long origId = Binder.clearCallingIdentity();
try {
+ final Intent baseActivityIntent;
+ final boolean launchedFromHome;
+
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
- if (r == null) {
- return;
- }
+ if (r == null) return;
+
if (mService.mWindowOrganizerController.mTaskOrganizerController
.handleInterceptBackPressedOnTaskRoot(r.getRootTask())) {
// This task is handled by a task organizer that has requested the back pressed
// callback.
- } else {
- moveActivityTaskToBack(token, false /* nonRoot */);
+ return;
}
+
+ final Intent baseIntent = r.getTask().getBaseIntent();
+ final boolean activityIsBaseActivity = baseIntent != null
+ && r.mActivityComponent.equals(baseIntent.getComponent());
+ baseActivityIntent = activityIsBaseActivity ? r.intent : null;
+ launchedFromHome = r.launchedFromHomeProcess;
+ }
+
+ // If the activity is one of the main entry points for the application, then we should
+ // refrain from finishing the activity and instead move it to the back to keep it in
+ // memory. The requirements for this are:
+ // 1. The current activity is the base activity for the task.
+ // 2. a. If the activity was launched by the home process, we trust that its intent
+ // was resolved, so we check if the it is a main intent for the application.
+ // b. Otherwise, we query Package Manager to verify whether the activity is a
+ // launcher activity for the application.
+ if (baseActivityIntent != null
+ && ((launchedFromHome && ActivityRecord.isMainIntent(baseActivityIntent))
+ || isLauncherActivity(baseActivityIntent.getComponent()))) {
+ moveActivityTaskToBack(token, false /* nonRoot */);
+ return;
+ }
+
+ // The default option for handling the back button is to finish the Activity.
+ try {
+ callback.requestFinish();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to invoke request finish callback", e);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
+
+ /**
+ * Queries PackageManager to see if the given activity is one of the main entry point for the
+ * application. This should not be called with the WM lock held.
+ */
+ @SuppressWarnings("unchecked")
+ private boolean isLauncherActivity(@NonNull ComponentName activity) {
+ final Intent queryIntent = new Intent(Intent.ACTION_MAIN);
+ queryIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ queryIntent.setPackage(activity.getPackageName());
+ try {
+ final ParceledListSlice<ResolveInfo> resolved =
+ mService.getPackageManager().queryIntentActivities(
+ queryIntent, null, 0, mContext.getUserId());
+ if (resolved == null) return false;
+ for (final ResolveInfo ri : resolved.getList()) {
+ if (ri.getComponentInfo().getComponentName().equals(activity)) {
+ return true;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to query intent activities", e);
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 83e0b4463fe1..c327d703a5db 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -1012,6 +1012,7 @@ class ActivityMetricsLogger {
final TransitionInfoSnapshot infoSnapshot =
new TransitionInfoSnapshot(info, r, (int) startupTimeMs);
BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
+ mLastTransitionInfo.remove(r);
if (!info.isInterestingToLoggerAndObserver()) {
return infoSnapshot;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2b96805aeee1..860a2c34b2a8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -42,6 +42,8 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.servertransaction.TransferSplashScreenViewStateItem.ATTACH_TO;
+import static android.app.servertransaction.TransferSplashScreenViewStateItem.HANDOVER_TO;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_HOME;
import static android.content.Intent.CATEGORY_LAUNCHER;
@@ -244,6 +246,7 @@ import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.StartActivityItem;
import android.app.servertransaction.StopActivityItem;
import android.app.servertransaction.TopResumedActivityChangeItem;
+import android.app.servertransaction.TransferSplashScreenViewStateItem;
import android.app.usage.UsageEvents.Event;
import android.content.ComponentName;
import android.content.Intent;
@@ -302,6 +305,7 @@ import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.TransitionOldType;
import android.view.animation.Animation;
import android.window.IRemoteTransition;
+import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
@@ -438,6 +442,7 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
final int launchedFromUid; // always the uid who started the activity.
final String launchedFromPackage; // always the package who started the activity.
final @Nullable String launchedFromFeatureId; // always the feature in launchedFromPackage
+ final boolean launchedFromHomeProcess; // as per original caller
final Intent intent; // the original intent that generated us
final String shortComponentName; // the short component name of the intent
final String resolvedType; // as per original caller;
@@ -680,6 +685,30 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
boolean startingDisplayed;
boolean startingMoved;
+ boolean mHandleExitSplashScreen;
+ @TransferSplashScreenState int mTransferringSplashScreenState =
+ TRANSFER_SPLASH_SCREEN_IDLE;
+
+ // Idle, can be triggered to do transfer if needed.
+ static final int TRANSFER_SPLASH_SCREEN_IDLE = 0;
+ // requesting a copy from shell.
+ static final int TRANSFER_SPLASH_SCREEN_COPYING = 1;
+ // attach the splash screen view to activity.
+ static final int TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT = 2;
+ // client has taken over splash screen view.
+ static final int TRANSFER_SPLASH_SCREEN_FINISH = 3;
+
+ @IntDef(prefix = { "TRANSFER_SPLASH_SCREEN_" }, value = {
+ TRANSFER_SPLASH_SCREEN_IDLE,
+ TRANSFER_SPLASH_SCREEN_COPYING,
+ TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT,
+ TRANSFER_SPLASH_SCREEN_FINISH,
+ })
+ @interface TransferSplashScreenState {}
+
+ // How long we wait until giving up transfer splash screen.
+ private static final int TRANSFER_SPLASH_SCREEN_TIMEOUT = 2000;
+
// TODO: Have a WindowContainer state for tracking exiting/deferred removal.
boolean mIsExiting;
// Force an app transition to be ran in the case the visibility of the app did not change.
@@ -1650,6 +1679,7 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
launchedFromUid = _launchedFromUid;
launchedFromPackage = _launchedFromPackage;
launchedFromFeatureId = _launchedFromFeature;
+ launchedFromHomeProcess = _caller != null && _caller.isHomeProcess();
shortComponentName = _intent.getComponent().flattenToShortString();
resolvedType = _resolvedType;
componentSpecified = _componentSpecified;
@@ -1739,6 +1769,7 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
if (_createTime > 0) {
createTime = _createTime;
}
+ mAtmService.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, packageName);
if (mPerf == null)
mPerf = new BoostFramework();
@@ -1822,7 +1853,85 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
return hasProcess() && app.hasThread();
}
- boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
+ /**
+ * Evaluate the theme for a starting window.
+ * @param originalTheme The original theme which read from activity or application.
+ * @param replaceTheme The replace theme which requested from starter.
+ * @return Resolved theme.
+ */
+ private int evaluateStartingWindowTheme(String pkg, int originalTheme, int replaceTheme) {
+ // Skip if the package doesn't want a starting window.
+ if (!validateStartingWindowTheme(pkg, originalTheme)) {
+ return 0;
+ }
+ int selectedTheme = originalTheme;
+ if (replaceTheme != 0 && validateStartingWindowTheme(pkg, replaceTheme)) {
+ // allow to replace theme
+ selectedTheme = replaceTheme;
+ }
+ return selectedTheme;
+ }
+
+ private boolean validateStartingWindowTheme(String pkg, int theme) {
+ // If this is a translucent window, then don't show a starting window -- the current
+ // effect (a full-screen opaque starting window that fades away to the real contents
+ // when it is ready) does not work for this.
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Checking theme of starting window: 0x%x", theme);
+ if (theme != 0) {
+ AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
+ com.android.internal.R.styleable.Window,
+ mWmService.mCurrentUserId);
+ if (ent == null) {
+ // Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't
+ // see that.
+ return false;
+ }
+ final boolean windowIsTranslucent = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowIsTranslucent, false);
+ final boolean windowIsFloating = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowIsFloating, false);
+ final boolean windowShowWallpaper = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowShowWallpaper, false);
+ final boolean windowDisableStarting = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowDisablePreview, false);
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
+ "Translucent=%s Floating=%s ShowWallpaper=%s Disable=%s",
+ windowIsTranslucent, windowIsFloating, windowShowWallpaper,
+ windowDisableStarting);
+ if (windowIsTranslucent || windowIsFloating || windowDisableStarting) {
+ return false;
+ }
+ if (windowShowWallpaper
+ && getDisplayContent().mWallpaperController.getWallpaperTarget() != null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void applyStartingWindowTheme(String pkg, int theme) {
+ if (theme != 0) {
+ AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
+ com.android.internal.R.styleable.Window,
+ mWmService.mCurrentUserId);
+ if (ent == null) {
+ return;
+ }
+ final boolean windowShowWallpaper = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowShowWallpaper, false);
+ if (windowShowWallpaper && getDisplayContent().mWallpaperController
+ .getWallpaperTarget() == null) {
+ // If this theme is requesting a wallpaper, and the wallpaper
+ // is not currently visible, then this effectively serves as
+ // an opaque window and our starting window transition animation
+ // can still work. We just need to make sure the starting window
+ // is also showing the wallpaper.
+ windowFlags |= FLAG_SHOW_WALLPAPER;
+ }
+ }
+ }
+
+ boolean addStartingWindow(String pkg, int resolvedTheme, CompatibilityInfo compatInfo,
CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
boolean allowTaskSnapshot, boolean activityCreated) {
@@ -1865,49 +1974,12 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
return createSnapshot(snapshot, typeParameter);
}
- // If this is a translucent window, then don't show a starting window -- the current
- // effect (a full-screen opaque starting window that fades away to the real contents
- // when it is ready) does not work for this.
- ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Checking theme of starting window: 0x%x", theme);
- if (theme != 0) {
- AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
- com.android.internal.R.styleable.Window,
- mWmService.mCurrentUserId);
- if (ent == null) {
- // Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't
- // see that.
- return false;
- }
- final boolean windowIsTranslucent = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsTranslucent, false);
- final boolean windowIsFloating = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsFloating, false);
- final boolean windowShowWallpaper = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowShowWallpaper, false);
- final boolean windowDisableStarting = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowDisablePreview, false);
- ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Translucent=%s Floating=%s ShowWallpaper=%s",
- windowIsTranslucent, windowIsFloating, windowShowWallpaper);
- if (windowIsTranslucent) {
- return false;
- }
- if (windowIsFloating || windowDisableStarting) {
- return false;
- }
- if (windowShowWallpaper) {
- if (getDisplayContent().mWallpaperController
- .getWallpaperTarget() == null) {
- // If this theme is requesting a wallpaper, and the wallpaper
- // is not currently visible, then this effectively serves as
- // an opaque window and our starting window transition animation
- // can still work. We just need to make sure the starting window
- // is also showing the wallpaper.
- windowFlags |= FLAG_SHOW_WALLPAPER;
- } else {
- return false;
- }
- }
+ // Original theme can be 0 if developer doesn't request any theme. So if resolved theme is 0
+ // but original theme is not 0, means this package doesn't want a starting window.
+ if (resolvedTheme == 0 && theme != 0) {
+ return false;
}
+ applyStartingWindowTheme(pkg, resolvedTheme);
if (transferStartingWindow(transferFrom)) {
return true;
@@ -1921,7 +1993,7 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SplashScreenStartingData");
mStartingData = new SplashScreenStartingData(mWmService, pkg,
- theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+ resolvedTheme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
getMergedOverrideConfiguration(), typeParameter);
scheduleAddStartingWindow();
return true;
@@ -2046,7 +2118,118 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
return snapshot.getRotation() == targetRotation;
}
+ /**
+ * See {@link SplashScreen#setOnExitAnimationListener}.
+ */
+ void setCustomizeSplashScreenExitAnimation(boolean enable) {
+ if (mHandleExitSplashScreen == enable) {
+ return;
+ }
+ mHandleExitSplashScreen = enable;
+ }
+
+ private final Runnable mTransferSplashScreenTimeoutRunnable = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mAtmService.mGlobalLock) {
+ Slog.w(TAG, "Activity transferring splash screen timeout for "
+ + ActivityRecord.this + " state " + mTransferringSplashScreenState);
+ if (isTransferringSplashScreen()) {
+ mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
+ // TODO show default exit splash screen animation
+ removeStartingWindow();
+ }
+ }
+ }
+ };
+
+ private void scheduleTransferSplashScreenTimeout() {
+ mAtmService.mH.postDelayed(mTransferSplashScreenTimeoutRunnable,
+ TRANSFER_SPLASH_SCREEN_TIMEOUT);
+ }
+
+ private void removeTransferSplashScreenTimeout() {
+ mAtmService.mH.removeCallbacks(mTransferSplashScreenTimeoutRunnable);
+ }
+
+ private boolean transferSplashScreenIfNeeded() {
+ if (!mHandleExitSplashScreen || mStartingSurface == null || mStartingWindow == null
+ || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH) {
+ return false;
+ }
+ if (isTransferringSplashScreen()) {
+ return true;
+ }
+ requestCopySplashScreen();
+ return isTransferringSplashScreen();
+ }
+
+ private boolean isTransferringSplashScreen() {
+ return mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT
+ || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_COPYING;
+ }
+
+ private void requestCopySplashScreen() {
+ mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_COPYING;
+ if (!mAtmService.mTaskOrganizerController.copySplashScreenView(getTask())) {
+ mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
+ removeStartingWindow();
+ }
+ scheduleTransferSplashScreenTimeout();
+ }
+
+ /**
+ * Receive the splash screen data from shell, sending to client.
+ * @param parcelable The data to reconstruct the splash screen view, null mean unable to copy.
+ */
+ void onCopySplashScreenFinish(SplashScreenViewParcelable parcelable) {
+ removeTransferSplashScreenTimeout();
+ // unable to copy from shell, maybe it's not a splash screen. or something went wrong.
+ // either way, abort and reset the sequence.
+ if (parcelable == null
+ || mTransferringSplashScreenState != TRANSFER_SPLASH_SCREEN_COPYING) {
+ if (parcelable != null) {
+ parcelable.clearIfNeeded();
+ }
+ mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
+ removeStartingWindow();
+ return;
+ }
+ // schedule attach splashScreen to client
+ try {
+ mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT;
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ TransferSplashScreenViewStateItem.obtain(ATTACH_TO, parcelable));
+ scheduleTransferSplashScreenTimeout();
+ } catch (Exception e) {
+ Slog.w(TAG, "onCopySplashScreenComplete fail: " + this);
+ parcelable.clearIfNeeded();
+ mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
+ }
+ }
+
+ private void onSplashScreenAttachComplete() {
+ removeTransferSplashScreenTimeout();
+ // Client has draw the splash screen, so we can remove the starting window.
+ if (mStartingWindow != null) {
+ mStartingWindow.hide(false, false);
+ }
+ try {
+ mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+ TransferSplashScreenViewStateItem.obtain(HANDOVER_TO, null));
+ } catch (Exception e) {
+ Slog.w(TAG, "onSplashScreenAttachComplete fail: " + this);
+ }
+ // no matter what, remove the starting window.
+ mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
+ removeStartingWindow();
+ }
+
void removeStartingWindow() {
+ if (transferSplashScreenIfNeeded()) {
+ return;
+ }
+ mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
if (mStartingWindow == null) {
if (mStartingData != null) {
// Starting window has not been added yet, but it is scheduled to be added.
@@ -5190,7 +5373,7 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
}
}
- static void activityResumedLocked(IBinder token) {
+ static void activityResumedLocked(IBinder token, boolean handleSplashScreenExit) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
ProtoLog.i(WM_DEBUG_STATES, "Resumed activity; dropping state of: %s", r);
if (r == null) {
@@ -5198,12 +5381,22 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
// been removed (e.g. destroy timeout), so the token could be null.
return;
}
+ r.setCustomizeSplashScreenExitAnimation(handleSplashScreenExit);
r.setSavedState(null /* savedState */);
r.mDisplayContent.handleActivitySizeCompatModeIfNeeded(r);
r.mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(r);
}
+ static void splashScreenAttachedLocked(IBinder token) {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r == null) {
+ Slog.w(TAG, "splashScreenTransferredLocked cannot find activity");
+ return;
+ }
+ r.onSplashScreenAttachComplete();
+ }
+
/**
* Once we know that we have asked an application to put an activity in the resumed state
* (either by launching it or explicitly telling it), this function updates the rest of our
@@ -5291,6 +5484,7 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
}
}
+ mDisplayContent.handleActivitySizeCompatModeIfNeeded(this);
mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
@@ -6042,7 +6236,13 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
pendingVoiceInteractionStart = false;
}
- void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) {
+ void showStartingWindow(boolean taskSwitch) {
+ showStartingWindow(null /* prev */, false /* newTask */, taskSwitch,
+ 0 /* splashScreenTheme */);
+ }
+
+ void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
+ int splashScreenTheme) {
if (mTaskOverlay) {
// We don't show starting window for overlay activities.
return;
@@ -6055,7 +6255,10 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
final CompatibilityInfo compatInfo =
mAtmService.compatibilityInfoForPackageLocked(info.applicationInfo);
- final boolean shown = addStartingWindow(packageName, theme,
+
+ final int resolvedTheme = evaluateStartingWindowTheme(packageName, theme,
+ splashScreenTheme);
+ final boolean shown = addStartingWindow(packageName, resolvedTheme,
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
allowTaskSnapshot(),
@@ -6648,7 +6851,7 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
// Allowing closing {@link ActivityRecord} to participate can lead to an Activity in another
// task being started in the wrong orientation during the transition.
if (!getDisplayContent().mClosingApps.contains(this)
- && (isVisible() || getDisplayContent().mOpeningApps.contains(this))) {
+ && (isVisibleRequested() || getDisplayContent().mOpeningApps.contains(this))) {
return mOrientation;
}
@@ -7125,7 +7328,32 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
return;
}
}
- super.onConfigurationChanged(newParentConfig);
+
+ final DisplayContent display = mDisplayContent;
+ if (inPinnedWindowingMode() && attachedToProcess() && display != null) {
+ // If the PIP activity is changing to fullscreen with display orientation change, the
+ // fixed rotation will take effect that requires to send fixed rotation adjustments
+ // before the process configuration (if the process is a configuration listener of the
+ // activity). So when performing process configuration on client side, it can apply
+ // the adjustments (see WindowToken#onFixedRotationStatePrepared).
+ try {
+ app.pauseConfigurationDispatch();
+ super.onConfigurationChanged(newParentConfig);
+ if (mVisibleRequested && !inMultiWindowMode()) {
+ final int rotation = display.rotationForActivityInDifferentOrientation(this);
+ if (rotation != ROTATION_UNDEFINED) {
+ app.resumeConfigurationDispatch();
+ display.setFixedRotationLaunchingApp(this, rotation);
+ }
+ }
+ } finally {
+ if (app.resumeConfigurationDispatch()) {
+ app.dispatchConfiguration(app.getConfiguration());
+ }
+ }
+ } else {
+ super.onConfigurationChanged(newParentConfig);
+ }
// Configuration's equality doesn't consider seq so if only seq number changes in resolved
// override configuration. Therefore ConfigurationContainer doesn't change merged override
@@ -7134,7 +7362,6 @@ public final class ActivityRecord extends WindowToken implements WindowManagerSe
onMergedOverrideConfigurationChanged();
}
- final DisplayContent display = mDisplayContent;
if (display == null) {
return;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 4cfb68e69543..026d31eb667f 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1999,6 +1999,13 @@ class ActivityStarter {
return START_SUCCESS;
}
+ // The reusedActivity could be finishing, for example of starting an activity with
+ // FLAG_ACTIVITY_CLEAR_TOP flag. In that case, use the top running activity in the
+ // task instead.
+ targetTaskTop = targetTaskTop.finishing
+ ? targetTask.getTopNonFinishingActivity()
+ : targetTaskTop;
+
// At this point we are certain we want the task moved to the front. If we need to dismiss
// any other always-on-top root tasks, now is the time to do it.
if (targetTaskTop.canTurnScreenOn() && mService.mInternal.isDreaming()) {
@@ -2007,8 +2014,7 @@ class ActivityStarter {
if (mMovedToFront) {
// We moved the task to front, use starting window to hide initial drawn delay.
- targetTaskTop.showStartingWindow(null /* prev */, false /* newTask */,
- true /* taskSwitch */);
+ targetTaskTop.showStartingWindow(true /* taskSwitch */);
} else if (mDoResume) {
// Make sure the root task and its belonging display are moved to topmost.
mTargetRootTask.moveToFront("intentActivityFound");
@@ -2016,11 +2022,8 @@ class ActivityStarter {
// We didn't do anything... but it was needed (a.k.a., client don't use that intent!)
// And for paranoia, make sure we have correctly resumed the top activity.
resumeTargetRootTaskIfNeeded();
- // The reusedActivity could be finishing, for example of starting an activity with
- // FLAG_ACTIVITY_CLEAR_TOP flag. In that case, return the top running activity in the
- // task instead.
- mLastStartActivityRecord =
- targetTaskTop.finishing ? targetTask.getTopNonFinishingActivity() : targetTaskTop;
+
+ mLastStartActivityRecord = targetTaskTop;
return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 7d2075cca84d..94379b1f230e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -574,4 +574,26 @@ public abstract class ActivityTaskManagerInternal {
* @return Whether the package is the base of any locked task
*/
public abstract boolean isBaseOfLockedTask(String packageName);
+
+ /**
+ * Create an interface to update configuration for an application.
+ */
+ public abstract PackageConfigurationUpdater createPackageConfigurationUpdater();
+
+ /**
+ * An interface to update configuration for an application, and will persist override
+ * configuration for this package.
+ */
+ public interface PackageConfigurationUpdater {
+ /**
+ * Sets the dark mode for the current application. This setting is persisted and will
+ * override the system configuration for this application.
+ */
+ PackageConfigurationUpdater setNightMode(int nightMode);
+
+ /**
+ * Commit changes.
+ */
+ void commit() throws RemoteException;
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index c7fbf7aed96a..f9b2aa30d97b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -130,6 +130,7 @@ import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.ActivityThread;
import android.app.AlertDialog;
+import android.app.AnrController;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.Dialog;
@@ -175,6 +176,7 @@ import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.power.Mode;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -222,6 +224,7 @@ import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.WindowManager;
import android.window.IWindowOrganizerController;
+import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.TaskSnapshot;
import android.window.WindowContainerTransaction;
@@ -315,6 +318,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public static final String DUMP_CONTAINERS_CMD = "containers";
public static final String DUMP_RECENTS_CMD = "recents";
public static final String DUMP_RECENTS_SHORT_CMD = "r";
+ public static final String DUMP_TOP_RESUMED_ACTIVITY = "top-resumed";
/** This activity is not being relaunched, or being relaunched for a non-resize reason. */
public static final int RELAUNCH_REASON_NONE = 0;
@@ -342,7 +346,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private StatusBarManagerInternal mStatusBarManagerInternal;
@VisibleForTesting
final ActivityTaskManagerInternal mInternal;
- PowerManagerInternal mPowerManagerInternal;
+ private PowerManagerInternal mPowerManagerInternal;
private UsageStatsManagerInternal mUsageStatsInternal;
PendingIntentController mPendingIntentController;
@@ -449,6 +453,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
/** The controller for all operations related to locktask. */
private LockTaskController mLockTaskController;
private ActivityStartController mActivityStartController;
+ PackageConfigPersister mPackageConfigPersister;
boolean mSuppressResizeConfigChanges;
@@ -494,6 +499,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
*/
private volatile long mLastStopAppSwitchesTime;
+ private final List<AnrController> mAnrController = new ArrayList<>();
IActivityController mController = null;
boolean mControllerIsAMonkey = false;
@@ -589,6 +595,22 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Retention(RetentionPolicy.SOURCE)
@IntDef({
+ POWER_MODE_REASON_START_ACTIVITY,
+ POWER_MODE_REASON_FREEZE_DISPLAY,
+ POWER_MODE_REASON_ALL,
+ })
+ @interface PowerModeReason {}
+
+ static final int POWER_MODE_REASON_START_ACTIVITY = 1 << 0;
+ static final int POWER_MODE_REASON_FREEZE_DISPLAY = 1 << 1;
+ /** This can only be used by {@link #endLaunchPowerMode(int)}.*/
+ static final int POWER_MODE_REASON_ALL = (1 << 2) - 1;
+
+ /** The reasons to use {@link Mode#LAUNCH} power mode. */
+ private @PowerModeReason int mLaunchPowerModeReasons;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
LAYOUT_REASON_CONFIG_CHANGED,
LAYOUT_REASON_VISIBILITY_CHANGED,
})
@@ -769,7 +791,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final boolean sizeCompatFreeform = Settings.Global.getInt(
resolver, DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, 0) != 0;
final boolean supportsNonResizableMultiWindow = Settings.Global.getInt(
- resolver, DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) != 0;
+ resolver, DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) != 0;
// Transfer any global setting for forcing RTL layout, into a System Property
DisplayProperties.debug_force_rtl(forceRtl);
@@ -842,11 +864,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mTaskChangeNotificationController =
new TaskChangeNotificationController(mGlobalLock, mTaskSupervisor, mH);
- mLockTaskController = new LockTaskController(mContext, mTaskSupervisor, mH);
+ mLockTaskController = new LockTaskController(mContext, mTaskSupervisor, mH,
+ mTaskChangeNotificationController);
mActivityStartController = new ActivityStartController(this);
setRecentTasks(new RecentTasks(this, mTaskSupervisor));
mVrController = new VrController(mGlobalLock);
mKeyguardController = mTaskSupervisor.getKeyguardController();
+ mPackageConfigPersister = new PackageConfigPersister(mTaskSupervisor.mPersisterQueue);
}
public void onActivityManagerInternalAdded() {
@@ -2008,8 +2032,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// We are reshowing a task, use a starting window to hide the initial draw delay
// so the transition can start earlier.
- topActivity.showStartingWindow(null /* prev */, false /* newTask */,
- true /* taskSwitch */);
+ topActivity.showStartingWindow(true /* taskSwitch */);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -2058,6 +2081,40 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return mAppSwitchesAllowed;
}
+ /** Register an {@link AnrController} to control the ANR dialog behavior */
+ public void registerAnrController(AnrController controller) {
+ synchronized (mGlobalLock) {
+ mAnrController.add(controller);
+ }
+ }
+
+ /** Unregister an {@link AnrController} */
+ public void unregisterAnrController(AnrController controller) {
+ synchronized (mGlobalLock) {
+ mAnrController.remove(controller);
+ }
+ }
+
+ /** @return the max ANR delay from all registered {@link AnrController} instances */
+ public long getMaxAnrDelayMillis(ApplicationInfo info) {
+ if (info == null || info.packageName == null) {
+ return 0;
+ }
+
+ final ArrayList<AnrController> controllers;
+ synchronized (mGlobalLock) {
+ controllers = new ArrayList<>(mAnrController);
+ }
+
+ final String packageName = info.packageName;
+ long maxDelayMs = 0;
+ for (AnrController controller : controllers) {
+ maxDelayMs = Math.max(maxDelayMs, controller.getAnrDelayMillis(packageName, info.uid));
+ }
+ maxDelayMs = Math.max(maxDelayMs, 0);
+ return maxDelayMs;
+ }
+
@Override
public void setActivityController(IActivityController controller, boolean imAMonkey) {
mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
@@ -3196,6 +3253,30 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
/**
+ * A splash screen view has copied, pass it to an activity.
+ *
+ * @param taskId Id of task to handle the material to reconstruct the view.
+ * @param parcelable Used to reconstruct the view, null means the surface is un-copyable.
+ * @hide
+ */
+ @Override
+ public void onSplashScreenViewCopyFinished(int taskId, SplashScreenViewParcelable parcelable)
+ throws RemoteException {
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_TASKS,
+ "copySplashScreenViewFinish()");
+ synchronized (mGlobalLock) {
+ final Task task = mRootWindowContainer.anyTaskForId(taskId,
+ MATCH_ATTACHED_TASK_ONLY);
+ if (task != null) {
+ final ActivityRecord r = task.getTopWaitSplashScreenActivity();
+ if (r != null) {
+ r.onCopySplashScreenFinish(parcelable);
+ }
+ }
+ }
+ }
+
+ /**
* Puts the given activity in picture in picture mode if possible.
*
* @return true if the activity is now in picture-in-picture mode, or false if it could not
@@ -3677,6 +3758,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
+ void dumpTopResumedActivityLocked(PrintWriter pw) {
+ pw.println("ACTIVITY MANAGER TOP-RESUMED (dumpsys activity top-resumed)");
+ ActivityRecord topRecord = mRootWindowContainer.getTopResumedActivity();
+ if (topRecord != null) {
+ topRecord.dump(pw, "", true);
+ }
+ }
+
void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage,
@@ -4059,6 +4148,20 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return changes;
}
+ void startLaunchPowerMode(@PowerModeReason int reason) {
+ if (mPowerManagerInternal == null) return;
+ mPowerManagerInternal.setPowerMode(Mode.LAUNCH, true);
+ mLaunchPowerModeReasons |= reason;
+ }
+
+ void endLaunchPowerMode(@PowerModeReason int reason) {
+ if (mPowerManagerInternal == null || mLaunchPowerModeReasons == 0) return;
+ mLaunchPowerModeReasons &= ~reason;
+ if (mLaunchPowerModeReasons == 0) {
+ mPowerManagerInternal.setPowerMode(Mode.LAUNCH, false);
+ }
+ }
+
/** @see WindowSurfacePlacer#deferLayout */
void deferWindowLayout() {
if (!mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
@@ -5366,6 +5469,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
mAppWarnings.onPackageUninstalled(name);
mCompatModePackages.handlePackageUninstalledLocked(name);
+ mPackageConfigPersister.onPackageUninstall(name);
}
}
@@ -5802,6 +5906,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (getRecentTasks() != null) {
getRecentTasks().dump(pw, dumpAll, dumpPackage);
}
+ } else if (DUMP_TOP_RESUMED_ACTIVITY.equals(cmd)) {
+ dumpTopResumedActivityLocked(pw);
}
}
}
@@ -6036,6 +6142,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public void removeUser(int userId) {
synchronized (mGlobalLock) {
mRootWindowContainer.removeUser(userId);
+ mPackageConfigPersister.removeUser(userId);
}
}
@@ -6133,6 +6240,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public void loadRecentTasksForUser(int userId) {
synchronized (mGlobalLock) {
mRecentTasks.loadUserRecentsLocked(userId);
+ // TODO renaming the methods(?)
+ mPackageConfigPersister.loadUserPackages(userId);
}
}
@@ -6241,5 +6350,54 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return getLockTaskController().isBaseOfLockedTask(packageName);
}
}
+
+ @Override
+ public PackageConfigurationUpdater createPackageConfigurationUpdater() {
+ synchronized (mGlobalLock) {
+ return new PackageConfigurationUpdaterImpl(Binder.getCallingPid());
+ }
+ }
+ }
+
+ final class PackageConfigurationUpdaterImpl implements
+ ActivityTaskManagerInternal.PackageConfigurationUpdater {
+ private int mPid;
+ private int mNightMode;
+
+ PackageConfigurationUpdaterImpl(int pid) {
+ mPid = pid;
+ }
+
+ @Override
+ public ActivityTaskManagerInternal.PackageConfigurationUpdater setNightMode(int nightMode) {
+ mNightMode = nightMode;
+ return this;
+ }
+
+ @Override
+ public void commit() throws RemoteException {
+ if (mPid == 0) {
+ throw new RemoteException("Invalid process");
+ }
+ synchronized (mGlobalLock) {
+ final WindowProcessController wpc = mProcessMap.getProcess(mPid);
+ if (wpc == null) {
+ Slog.w(TAG, "Override application configuration: cannot find application");
+ return;
+ }
+ if (wpc.getNightMode() == mNightMode) {
+ return;
+ }
+ if (!wpc.setOverrideNightMode(mNightMode)) {
+ return;
+ }
+ wpc.updateNightModeForAllActivities(mNightMode);
+ mPackageConfigPersister.updateFromImpl(wpc.mName, wpc.mUserId, this);
+ }
+ }
+
+ int getNightMode() {
+ return mNightMode;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index b5bfa0bf886a..e5d2680fe726 100755
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1851,7 +1851,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
// End power mode launch before going sleep
- mRootWindowContainer.endPowerModeLaunchIfNeeded();
+ mService.endLaunchPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_ALL);
removeSleepTimeouts();
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index d90e88576909..efcaaa42ec13 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -534,6 +534,28 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
return getActivityType() == ACTIVITY_TYPE_ASSISTANT;
}
+ /**
+ * Overrides the night mode applied to this ConfigurationContainer.
+ * @return true if the nightMode has been changed.
+ */
+ public boolean setOverrideNightMode(int nightMode) {
+ final int currentUiMode = mFullConfiguration.uiMode;
+ final int currentNightMode = getNightMode();
+ final int validNightMode = nightMode & Configuration.UI_MODE_NIGHT_MASK;
+ if (currentNightMode == validNightMode) {
+ return false;
+ }
+ mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
+ mRequestsTmpConfig.uiMode = validNightMode
+ | (currentUiMode & ~Configuration.UI_MODE_NIGHT_MASK);
+ onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
+ return true;
+ }
+
+ int getNightMode() {
+ return mFullConfiguration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ }
+
public boolean isActivityTypeDream() {
return getActivityType() == ACTIVITY_TYPE_DREAM;
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index f075d850c20f..759b7fe054bc 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -429,6 +429,10 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
void setOrganizer(IDisplayAreaOrganizer organizer, boolean skipDisplayAreaAppeared) {
if (mOrganizer == organizer) return;
+ if (mDisplayContent == null || !mDisplayContent.isTrusted()) {
+ throw new IllegalStateException(
+ "Don't organize or trigger events for unavailable or untrusted display.");
+ }
IDisplayAreaOrganizer lastOrganizer = mOrganizer;
// Update the new display area organizer before calling sendDisplayAreaVanished since it
// could result in a new SurfaceControl getting created that would notify the old organizer
@@ -500,6 +504,17 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
return false;
}
+ @Override
+ void removeImmediately() {
+ setOrganizer(null);
+ super.removeImmediately();
+ }
+
+ @Override
+ DisplayArea getDisplayArea() {
+ return this;
+ }
+
/**
* DisplayArea that contains WindowTokens, and orders them according to their type.
*/
@@ -580,11 +595,6 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
}
}
- @Override
- DisplayArea getDisplayArea() {
- return this;
- }
-
/**
* DisplayArea that can be dimmed.
*/
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index ed44876eb368..2beb3780633e 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -21,6 +21,7 @@ import static android.window.DisplayAreaOrganizer.FEATURE_RUNTIME_TASK_CONTAINER
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.DisplayArea.Type.ANY;
+import android.annotation.Nullable;
import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.os.IBinder;
@@ -77,6 +78,11 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
mService.enforceTaskPermission(func);
}
+ @Nullable
+ IDisplayAreaOrganizer getOrganizerByFeature(int featureId) {
+ return mOrganizersByFeatureIds.get(featureId);
+ }
+
@Override
public ParceledListSlice<DisplayAreaAppearedInfo> registerOrganizer(
IDisplayAreaOrganizer organizer, int feature) {
@@ -100,10 +106,18 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
}
final List<DisplayAreaAppearedInfo> displayAreaInfos = new ArrayList<>();
- mService.mRootWindowContainer.forAllDisplayAreas((da) -> {
- if (da.mFeatureId != feature) return;
- displayAreaInfos.add(organizeDisplayArea(organizer, da,
- "DisplayAreaOrganizerController.registerOrganizer"));
+ mService.mRootWindowContainer.forAllDisplays(dc -> {
+ if (!dc.isTrusted()) {
+ ProtoLog.w(WM_DEBUG_WINDOW_ORGANIZER,
+ "Don't organize or trigger events for untrusted displayId=%d",
+ dc.getDisplayId());
+ return;
+ }
+ dc.forAllDisplayAreas((da) -> {
+ if (da.mFeatureId != feature) return;
+ displayAreaInfos.add(organizeDisplayArea(organizer, da,
+ "DisplayAreaOrganizerController.registerOrganizer"));
+ });
});
mOrganizersByFeatureIds.put(feature, organizer);
@@ -148,6 +162,10 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
throw new IllegalArgumentException("createTaskDisplayArea unknown displayId="
+ displayId);
}
+ if (!display.isTrusted()) {
+ throw new IllegalArgumentException("createTaskDisplayArea untrusted displayId="
+ + displayId);
+ }
// The parentFeatureId can be either a RootDisplayArea or a TaskDisplayArea.
// Check if there is a RootDisplayArea with the given parentFeatureId.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d8720aba3f55..13e43a37eed2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -213,6 +213,7 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
+import android.window.IDisplayAreaOrganizer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
@@ -239,6 +240,7 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -662,12 +664,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private boolean mRemoved;
- /**
- * Non-null if the last size compatibility mode activity is using non-native screen
- * configuration. The activity is not able to put in multi-window mode, so it exists only one
- * per display.
- */
- private ActivityRecord mLastCompatModeActivity;
+ /** Set of activities in foreground size compat mode. */
+ private Set<ActivityRecord> mActiveSizeCompatActivities = new ArraySet<>();
// Used in updating the display size
private Point mTmpDisplaySize = new Point();
@@ -685,6 +683,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private boolean mInEnsureActivitiesVisible = false;
+ // Used to indicate that the movement of child tasks to top will not move the display to top as
+ // well and thus won't change the top resumed / focused record
+ boolean mDontMoveToTop;
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final ActivityRecord activity = w.mActivityRecord;
@@ -948,7 +950,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
final ActivityRecord activity = w.mActivityRecord;
- if (activity != null) {
+ if (activity != null && activity.isVisibleRequested()) {
activity.updateLetterboxSurface(w);
final boolean updateAllDrawn = activity.updateDrawnWindowStates(w);
if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(activity)) {
@@ -1000,7 +1002,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final InputChannel inputChannel = mWmService.mInputManager.monitorInput(
"PointerEventDispatcher" + mDisplayId, mDisplayId);
- mPointerEventDispatcher = new PointerEventDispatcher(inputChannel);
+ mPointerEventDispatcher = new PointerEventDispatcher(inputChannel, this);
// Tap Listeners are supported for:
// 1. All physical displays (multi-display).
@@ -1070,6 +1072,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// Sets the display content for the children.
onDisplayChanged(this);
+ updateDisplayAreaOrganizers();
mInputMonitor = new InputMonitor(mWmService, this);
mInsetsPolicy = new InsetsPolicy(mInsetsStateController, this);
@@ -1447,7 +1450,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
config = new Configuration();
computeScreenConfiguration(config);
- } else if (currentConfig != null) {
+ } else if (currentConfig != null
+ // If waiting for a remote rotation, don't prematurely update configuration.
+ && !mDisplayRotation.isWaitingForRemoteRotation()) {
// No obvious action we need to take, but if our current state mismatches the
// activity manager's, update it, disregarding font scale, which should remain set
// to the value of the previous configuration.
@@ -1597,7 +1602,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
&& mFixedRotationLaunchingApp != mFixedRotationTransitionListener.mAnimatingRecents;
}
- @VisibleForTesting
boolean isFixedRotationLaunchingApp(ActivityRecord r) {
return mFixedRotationLaunchingApp == r;
}
@@ -1826,8 +1830,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (w.mHasSurface && !rotateSeamlessly) {
ProtoLog.v(WM_DEBUG_ORIENTATION, "Set mOrientationChanging of %s", w);
w.setOrientationChanging(true);
- mWmService.mRoot.mOrientationChangeComplete = false;
- w.mLastFreezeDuration = 0;
}
w.mReportOrientationChanged = true;
}, true /* traverseTopToBottom */);
@@ -2709,6 +2711,30 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
/**
+ * Checks for all non-organized {@link DisplayArea}s for if there is any existing organizer for
+ * their features. If so, registers them with the matched organizer.
+ */
+ @VisibleForTesting
+ void updateDisplayAreaOrganizers() {
+ if (!isTrusted()) {
+ // No need to update for untrusted display.
+ return;
+ }
+ forAllDisplayAreas(displayArea -> {
+ if (displayArea.isOrganized()) {
+ return;
+ }
+ // Check if we have a registered organizer for the DA feature.
+ final IDisplayAreaOrganizer organizer =
+ mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController
+ .getOrganizerByFeature(displayArea.mFeatureId);
+ if (organizer != null) {
+ displayArea.setOrganizer(organizer);
+ }
+ });
+ }
+
+ /**
* Returns true if the input point is within an app window.
*/
boolean pointWithinAppWindow(int x, int y) {
@@ -5524,24 +5550,23 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** Checks whether the given activity is in size compatibility mode and notifies the change. */
void handleActivitySizeCompatModeIfNeeded(ActivityRecord r) {
final Task organizedTask = r.getOrganizedTask();
- if (!r.isState(RESUMED) || r.getWindowingMode() != WINDOWING_MODE_FULLSCREEN
- || organizedTask == null) {
- // The callback is only interested in the foreground changes of fullscreen activity.
+ if (organizedTask == null) {
+ mActiveSizeCompatActivities.remove(r);
return;
}
- // TODO(b/178327644) Update for per Task size compat
- if (!r.inSizeCompatMode()) {
- if (mLastCompatModeActivity != null) {
+
+ if (r.isState(RESUMED) && r.inSizeCompatMode()) {
+ if (mActiveSizeCompatActivities.add(r)) {
+ // Trigger task event for new size compat activity.
organizedTask.onSizeCompatActivityChanged();
}
- mLastCompatModeActivity = null;
return;
}
- if (mLastCompatModeActivity == r) {
- return;
+
+ if (mActiveSizeCompatActivities.remove(r)) {
+ // Trigger task event for activity no longer in foreground size compat.
+ organizedTask.onSizeCompatActivityChanged();
}
- mLastCompatModeActivity = r;
- organizedTask.onSizeCompatActivityChanged();
}
boolean isUidPresent(int uid) {
diff --git a/services/core/java/com/android/server/wm/ScreenshotHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java
index 03f4e2891d19..7e16c22ee15e 100644
--- a/services/core/java/com/android/server/wm/ScreenshotHashController.java
+++ b/services/core/java/com/android/server/wm/DisplayHashController.java
@@ -16,9 +16,8 @@
package com.android.server.wm;
-import static android.service.screenshot.ScreenshotHasherService.EXTRA_SCREENSHOT_HASH;
-import static android.service.screenshot.ScreenshotHasherService.EXTRA_VERIFICATION_STATUS;
-import static android.service.screenshot.ScreenshotHasherService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
+import static android.service.displayhash.DisplayHasherService.EXTRA_VERIFIED_DISPLAY_HASH;
+import static android.service.displayhash.DisplayHasherService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -46,11 +45,12 @@ import android.os.Message;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.service.screenshot.IScreenshotHasherService;
-import android.service.screenshot.ScreenshotHash;
-import android.service.screenshot.ScreenshotHasherService;
+import android.service.displayhash.DisplayHasherService;
+import android.service.displayhash.IDisplayHasherService;
import android.util.Slog;
import android.view.MagnificationSpec;
+import android.view.displayhash.DisplayHash;
+import android.view.displayhash.VerifiedDisplayHash;
import com.android.internal.annotations.GuardedBy;
@@ -61,29 +61,29 @@ import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
/**
- * Handles requests into {@link android.service.screenshot.ScreenshotHasherService}
+ * Handles requests into {@link android.service.displayhash.DisplayHasherService}
*
* Do not hold the {@link WindowManagerService#mGlobalLock} when calling methods since they are
* blocking calls into another service.
*/
-public class ScreenshotHashController {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenshotHashController" : TAG_WM;
+public class DisplayHashController {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayHashController" : TAG_WM;
private static final boolean DEBUG = false;
private final Object mServiceConnectionLock = new Object();
@GuardedBy("mServiceConnectionLock")
- private ScreenshotHasherServiceConnection mServiceConnection;
+ private DisplayHasherServiceConnection mServiceConnection;
private final Context mContext;
/**
- * Lock used for the cached {@link #mHashingAlgorithms} array
+ * Lock used for the cached {@link #mHashAlgorithms} array
*/
- private final Object mHashingAlgorithmsLock = new Object();
+ private final Object mHashAlgorithmsLock = new Object();
@GuardedBy("mHashingAlgorithmsLock")
- private String[] mHashingAlgorithms;
+ private String[] mHashAlgorithms;
private final Handler mHandler;
@@ -94,24 +94,24 @@ public class ScreenshotHashController {
private final RectF mTmpRectF = new RectF();
private interface Command {
- void run(IScreenshotHasherService service) throws RemoteException;
+ void run(IDisplayHasherService service) throws RemoteException;
}
- ScreenshotHashController(Context context) {
+ DisplayHashController(Context context) {
mContext = context;
mHandler = new Handler(Looper.getMainLooper());
mSalt = UUID.randomUUID().toString().getBytes();
}
- String[] getSupportedHashingAlgorithms() {
+ 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 (mHashingAlgorithmsLock) {
+ synchronized (mHashAlgorithmsLock) {
// Already have cached values
- if (mHashingAlgorithms != null) {
- return mHashingAlgorithms;
+ if (mHashAlgorithms != null) {
+ return mHashAlgorithms;
}
final ServiceInfo serviceInfo = getServiceInfo();
@@ -128,55 +128,48 @@ public class ScreenshotHashController {
final int resourceId = serviceInfo.metaData.getInt(
SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS);
- mHashingAlgorithms = res.getStringArray(resourceId);
+ mHashAlgorithms = res.getStringArray(resourceId);
- return mHashingAlgorithms;
+ return mHashAlgorithms;
}
}
- boolean verifyScreenshotHash(ScreenshotHash screenshotHash) {
+ @Nullable
+ VerifiedDisplayHash verifyDisplayHash(DisplayHash displayHash) {
final SyncCommand syncCommand = new SyncCommand();
Bundle results = syncCommand.run((service, remoteCallback) -> {
try {
- service.verifyScreenshotHash(mSalt, screenshotHash, remoteCallback);
+ service.verifyDisplayHash(mSalt, displayHash, remoteCallback);
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to invoke verifyScreenshotHash command");
+ Slog.e(TAG, "Failed to invoke verifyDisplayHash command");
}
});
- return results.getBoolean(EXTRA_VERIFICATION_STATUS);
+ return results.getParcelable(EXTRA_VERIFIED_DISPLAY_HASH);
}
- ScreenshotHash generateScreenshotHash(HardwareBuffer screenshot, Rect bounds,
- String hashAlgorithm) {
- final SyncCommand syncCommand = new SyncCommand();
- Bundle results = syncCommand.run((service, remoteCallback) -> {
- try {
- service.generateScreenshotHash(mSalt, screenshot, bounds, hashAlgorithm,
- remoteCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to invoke generateScreenshotHash command", e);
- }
- });
-
- return results.getParcelable(EXTRA_SCREENSHOT_HASH);
+ void generateDisplayHash(HardwareBuffer buffer, Rect bounds,
+ String hashAlgorithm, RemoteCallback callback) {
+ connectAndRun(
+ service -> service.generateDisplayHash(mSalt, buffer, bounds, hashAlgorithm,
+ callback));
}
/**
- * Calculate the bounds to take the screenshot when generating the ScreenshotHash. This
- * takes into account window transform, magnification, and display bounds.
+ * Calculate the bounds to generate the hash for. This takes into account window transform,
+ * magnification, and display bounds.
*
* Call while holding {@link WindowManagerService#mGlobalLock}
*
- * @param win Window that the ScreenshotHash is generated for.
- * @param boundsInWindow The bounds passed in about where in the window to take the screenshot.
- * @param outBounds The result of the calculated bounds
+ * @param win Window that the DisplayHash is generated for.
+ * @param boundsInWindow The bounds in the window where to generate the hash.
+ * @param outBounds The result of the calculated bounds
*/
- void calculateScreenshotHashBoundsLocked(WindowState win, Rect boundsInWindow,
+ void calculateDisplayHashBoundsLocked(WindowState win, Rect boundsInWindow,
Rect outBounds) {
if (DEBUG) {
Slog.d(TAG,
- "calculateScreenshotHashBoundsLocked: boundsInWindow=" + boundsInWindow);
+ "calculateDisplayHashBoundsLocked: boundsInWindow=" + boundsInWindow);
}
outBounds.set(boundsInWindow);
@@ -195,7 +188,7 @@ public class ScreenshotHashController {
if (DEBUG) {
Slog.d(TAG,
- "calculateScreenshotHashBoundsLocked: boundsIntersectWindow=" + outBounds);
+ "calculateDisplayHashBoundsLocked: boundsIntersectWindow=" + outBounds);
}
if (outBounds.isEmpty()) {
@@ -210,7 +203,7 @@ public class ScreenshotHashController {
outBounds.set((int) mTmpRectF.left, (int) mTmpRectF.top, (int) mTmpRectF.right,
(int) mTmpRectF.bottom);
if (DEBUG) {
- Slog.d(TAG, "calculateScreenshotHashBoundsLocked: boundsInDisplay=" + outBounds);
+ Slog.d(TAG, "calculateDisplayHashBoundsLocked: boundsInDisplay=" + outBounds);
}
// Apply the magnification spec values to the bounds since the content could be magnified
@@ -221,7 +214,7 @@ public class ScreenshotHashController {
}
if (DEBUG) {
- Slog.d(TAG, "calculateScreenshotHashBoundsLocked: boundsWithMagnification="
+ Slog.d(TAG, "calculateDisplayHashBoundsLocked: boundsWithMagnification="
+ outBounds);
}
@@ -229,12 +222,12 @@ public class ScreenshotHashController {
return;
}
- // Intersect with the display bounds since it shouldn't take a screenshot of content
- // outside the display since it's not visible to the user.
+ // Intersect with the display bounds since content outside the display are not visible to
+ // the user.
final Rect displayBounds = displayContent.getBounds();
outBounds.intersectUnchecked(displayBounds);
if (DEBUG) {
- Slog.d(TAG, "calculateScreenshotHashBoundsLocked: finalBounds=" + outBounds);
+ Slog.d(TAG, "calculateDisplayHashBoundsLocked: finalBounds=" + outBounds);
}
}
@@ -248,7 +241,7 @@ public class ScreenshotHashController {
if (DEBUG) Slog.v(TAG, "creating connection");
// Create the connection
- mServiceConnection = new ScreenshotHasherServiceConnection();
+ mServiceConnection = new DisplayHasherServiceConnection();
final ComponentName component = getServiceComponentName();
if (DEBUG) Slog.v(TAG, "binding to: " + component);
@@ -279,7 +272,7 @@ public class ScreenshotHashController {
return null;
}
- final Intent intent = new Intent(ScreenshotHasherService.SERVICE_INTERFACE);
+ final Intent intent = new Intent(DisplayHasherService.SERVICE_INTERFACE);
intent.setPackage(packageName);
final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
@@ -296,10 +289,10 @@ public class ScreenshotHashController {
if (serviceInfo == null) return null;
final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
- if (!Manifest.permission.BIND_SCREENSHOT_HASHER_SERVICE
+ if (!Manifest.permission.BIND_DISPLAY_HASHER_SERVICE
.equals(serviceInfo.permission)) {
Slog.w(TAG, name.flattenToShortString() + " requires permission "
- + Manifest.permission.BIND_SCREENSHOT_HASHER_SERVICE);
+ + Manifest.permission.BIND_DISPLAY_HASHER_SERVICE);
return null;
}
@@ -312,7 +305,7 @@ public class ScreenshotHashController {
private Bundle mResult;
private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
- public Bundle run(BiConsumer<IScreenshotHasherService, RemoteCallback> func) {
+ public Bundle run(BiConsumer<IDisplayHasherService, RemoteCallback> func) {
connectAndRun(service -> {
RemoteCallback callback = new RemoteCallback(result -> {
mResult = result;
@@ -331,9 +324,9 @@ public class ScreenshotHashController {
}
}
- private class ScreenshotHasherServiceConnection implements ServiceConnection {
+ private class DisplayHasherServiceConnection implements ServiceConnection {
@GuardedBy("mServiceConnectionLock")
- private IScreenshotHasherService mRemoteService;
+ private IDisplayHasherService mRemoteService;
@GuardedBy("mServiceConnectionLock")
private ArrayList<Command> mQueuedCommands;
@@ -342,7 +335,7 @@ public class ScreenshotHashController {
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) Slog.v(TAG, "onServiceConnected(): " + name);
synchronized (mServiceConnectionLock) {
- mRemoteService = IScreenshotHasherService.Stub.asInterface(service);
+ mRemoteService = IDisplayHasherService.Stub.asInterface(service);
if (mQueuedCommands != null) {
final int size = mQueuedCommands.size();
if (DEBUG) Slog.d(TAG, "running " + size + " queued commands");
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 658fd89b6374..ca3effb35dd5 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2232,6 +2232,7 @@ public class DisplayPolicy {
pi.getResDir(),
null /* splitResDirs */,
pi.getOverlayDirs(),
+ pi.getOverlayPaths(),
pi.getApplicationInfo().sharedLibraryFiles,
mDisplayContent.getDisplayId(),
null /* overrideConfig */,
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 41b0bcb8dedf..18042e9fbefd 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -542,12 +542,12 @@ public class DisplayRotation {
mRotation = rotation;
+ mDisplayContent.setLayoutNeeded();
+
mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);
- mDisplayContent.setLayoutNeeded();
-
if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
// The screen rotation animation uses a screenshot to freeze the screen while windows
// resize underneath. When we are rotating seamlessly, we allow the elements to
@@ -1563,6 +1563,21 @@ public class DisplayRotation {
}
@Override
+ public boolean canUseRotationResolver() {
+ if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
+
+ switch (mCurrentAppOrientation) {
+ case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
+ case ActivityInfo.SCREEN_ORIENTATION_USER:
+ case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
+ case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+ case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+ return true;
+ }
+ return false;
+ }
+
+ @Override
public void onProposedRotationChanged(int rotation) {
ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation);
Runnable r = mRunnableCache.get(rotation, null);
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index b82fdd237f2b..6d5abe1e2f31 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -16,11 +16,11 @@
package com.android.server.wm;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO;
import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED;
@@ -196,6 +196,14 @@ class DisplayWindowSettings {
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
+ void setDontMoveToTop(DisplayContent dc, boolean dontMoveToTop) {
+ DisplayInfo displayInfo = dc.getDisplayInfo();
+ SettingsProvider.SettingsEntry overrideSettings =
+ mSettingsProvider.getSettings(displayInfo);
+ overrideSettings.mDontMoveToTop = dontMoveToTop;
+ mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
+ }
+
boolean shouldShowSystemDecorsLocked(DisplayContent dc) {
if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
// Default display should show system decors.
@@ -274,6 +282,10 @@ class DisplayWindowSettings {
final int forcedScalingMode = settings.mForcedScalingMode != null
? settings.mForcedScalingMode : FORCE_SCALING_MODE_AUTO;
dc.mDisplayScalingDisabled = forcedScalingMode == FORCE_SCALING_MODE_DISABLED;
+
+ boolean dontMoveToTop = settings.mDontMoveToTop != null
+ ? settings.mDontMoveToTop : false;
+ dc.mDontMoveToTop = dontMoveToTop;
}
/**
@@ -358,6 +370,8 @@ class DisplayWindowSettings {
Boolean mIgnoreOrientationRequest;
@Nullable
Boolean mIgnoreDisplayCutout;
+ @Nullable
+ Boolean mDontMoveToTop;
SettingsEntry() {}
@@ -432,6 +446,10 @@ class DisplayWindowSettings {
mIgnoreDisplayCutout = other.mIgnoreDisplayCutout;
changed = true;
}
+ if (other.mDontMoveToTop != mDontMoveToTop) {
+ mDontMoveToTop = other.mDontMoveToTop;
+ changed = true;
+ }
return changed;
}
@@ -515,6 +533,11 @@ class DisplayWindowSettings {
mIgnoreDisplayCutout = delta.mIgnoreDisplayCutout;
changed = true;
}
+ if (delta.mDontMoveToTop != null
+ && delta.mDontMoveToTop != mDontMoveToTop) {
+ mDontMoveToTop = delta.mDontMoveToTop;
+ changed = true;
+ }
return changed;
}
@@ -531,7 +554,8 @@ class DisplayWindowSettings {
&& mImePolicy == null
&& mFixedToUserRotation == null
&& mIgnoreOrientationRequest == null
- && mIgnoreDisplayCutout == null;
+ && mIgnoreDisplayCutout == null
+ && mDontMoveToTop == null;
}
@Override
@@ -553,7 +577,8 @@ class DisplayWindowSettings {
&& Objects.equals(mImePolicy, that.mImePolicy)
&& Objects.equals(mFixedToUserRotation, that.mFixedToUserRotation)
&& Objects.equals(mIgnoreOrientationRequest, that.mIgnoreOrientationRequest)
- && Objects.equals(mIgnoreDisplayCutout, that.mIgnoreDisplayCutout);
+ && Objects.equals(mIgnoreDisplayCutout, that.mIgnoreDisplayCutout)
+ && Objects.equals(mDontMoveToTop, that.mDontMoveToTop);
}
@Override
@@ -561,7 +586,8 @@ class DisplayWindowSettings {
return Objects.hash(mWindowingMode, mUserRotationMode, mUserRotation, mForcedWidth,
mForcedHeight, mForcedDensity, mForcedScalingMode, mRemoveContentMode,
mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mImePolicy,
- mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout);
+ mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout,
+ mDontMoveToTop);
}
@Override
@@ -581,6 +607,7 @@ class DisplayWindowSettings {
+ ", mFixedToUserRotation=" + mFixedToUserRotation
+ ", mIgnoreOrientationRequest=" + mIgnoreOrientationRequest
+ ", mIgnoreDisplayCutout=" + mIgnoreDisplayCutout
+ + ", mDontMoveToTop=" + mDontMoveToTop
+ '}';
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index 5f3ab43eca7c..8fcdf2e96889 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
@@ -109,12 +109,13 @@ class DisplayWindowSettingsProvider implements SettingsProvider {
*/
void setBaseSettingsFilePath(@Nullable String path) {
AtomicFile settingsFile;
- if (path != null) {
- settingsFile = new AtomicFile(new File(path), WM_DISPLAY_COMMIT_TAG);
+ File file = path != null ? new File(path) : null;
+ if (file != null && file.exists()) {
+ settingsFile = new AtomicFile(file, WM_DISPLAY_COMMIT_TAG);
} else {
+ Slog.w(TAG, "display settings " + path + " does not exist, using vendor defaults");
settingsFile = getVendorSettingsFile();
}
-
setBaseSettingsStorage(new AtomicFileStorage(settingsFile));
}
@@ -405,6 +406,9 @@ class DisplayWindowSettingsProvider implements SettingsProvider {
"ignoreOrientationRequest", null /* defaultValue */);
settingsEntry.mIgnoreDisplayCutout = getBooleanAttribute(parser,
"ignoreDisplayCutout", null /* defaultValue */);
+ settingsEntry.mDontMoveToTop = getBooleanAttribute(parser,
+ "dontMoveToTop", null /* defaultValue */);
+
fileData.mSettings.put(name, settingsEntry);
}
XmlUtils.skipCurrentTag(parser);
@@ -496,6 +500,10 @@ class DisplayWindowSettingsProvider implements SettingsProvider {
out.attributeBoolean(null, "ignoreDisplayCutout",
settingsEntry.mIgnoreDisplayCutout);
}
+ if (settingsEntry.mDontMoveToTop != null) {
+ out.attributeBoolean(null, "dontMoveToTop",
+ settingsEntry.mDontMoveToTop);
+ }
out.endTag(null, "display");
}
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 4b3a43432fc5..e18516d7bc3a 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -141,6 +141,7 @@ public class LockTaskController {
private final IBinder mToken = new LockTaskToken();
private final ActivityTaskSupervisor mSupervisor;
private final Context mContext;
+ private final TaskChangeNotificationController mTaskChangeNotificationController;
// The following system services cannot be final, because they do not exist when this class
// is instantiated during device boot
@@ -204,10 +205,11 @@ public class LockTaskController {
private int mPendingDisableFromDismiss = UserHandle.USER_NULL;
LockTaskController(Context context, ActivityTaskSupervisor supervisor,
- Handler handler) {
+ Handler handler, TaskChangeNotificationController taskChangeNotificationController) {
mContext = context;
mSupervisor = supervisor;
mHandler = handler;
+ mTaskChangeNotificationController = taskChangeNotificationController;
}
/**
@@ -532,6 +534,7 @@ public class LockTaskController {
// thread, which makes it guarded by ATMS#mGlobalLock as ATMS#getLockTaskModeState.
final int oldLockTaskModeState = mLockTaskModeState;
mLockTaskModeState = LOCK_TASK_MODE_NONE;
+ mTaskChangeNotificationController.notifyLockTaskModeChanged(mLockTaskModeState);
// When lock task ends, we enable the status bars.
try {
setStatusBarState(mLockTaskModeState, userId);
@@ -661,6 +664,7 @@ public class LockTaskController {
}
mWindowManager.onLockTaskStateChanged(lockTaskModeState);
mLockTaskModeState = lockTaskModeState;
+ mTaskChangeNotificationController.notifyLockTaskModeChanged(mLockTaskModeState);
setStatusBarState(lockTaskModeState, userId);
setKeyguardState(lockTaskModeState, userId);
if (getDevicePolicyManager() != null) {
diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
new file mode 100644
index 000000000000..5d6d51377c3f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
@@ -0,0 +1,143 @@
+/*
+ * 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;
+
+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;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
+
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.util.proto.ProtoOutputStream;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.policy.WindowManagerPolicy;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+class NonAppWindowAnimationAdapter implements AnimationAdapter {
+
+ private final WindowState mWindow;
+ private RemoteAnimationTarget mTarget;
+ private SurfaceControl mCapturedLeash;
+
+ private long mDurationHint;
+ private long mStatusBarTransitionDelay;
+
+ @Override
+ public boolean getShowWallpaper() {
+ return false;
+ }
+
+ NonAppWindowAnimationAdapter(WindowState w,
+ long durationHint, long statusBarTransitionDelay) {
+ mWindow = w;
+ mDurationHint = durationHint;
+ mStatusBarTransitionDelay = statusBarTransitionDelay;
+ }
+
+ /**
+ * 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<>();
+
+ final WindowManagerPolicy policy = service.mPolicy;
+ service.mRoot.forAllWindows(nonAppWindow -> {
+ if (nonAppWindow.mActivityRecord == null && policy.canBeHiddenByKeyguardLw(nonAppWindow)
+ && nonAppWindow.wouldBeVisibleIfPolicyIgnored() && !nonAppWindow.isVisible()) {
+ final NonAppWindowAnimationAdapter nonAppAdapter = new NonAppWindowAnimationAdapter(
+ nonAppWindow, durationHint, statusBarTransitionDelay);
+ nonAppWindow.startAnimation(nonAppWindow.getPendingTransaction(),
+ nonAppAdapter, false /* hidden */, ANIMATION_TYPE_WINDOW_ANIMATION);
+ targets.add(nonAppAdapter.createRemoteAnimationTarget());
+ }
+ }, true /* traverseTopToBottom */);
+ return targets.toArray(new RemoteAnimationTarget[targets.size()]);
+ }
+
+ /**
+ * Create a remote animation target for this animation adapter.
+ */
+ 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);
+ return mTarget;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
+ int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
+ mCapturedLeash = animationLeash;
+ }
+
+ @Override
+ public long getDurationHint() {
+ return mDurationHint;
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return SystemClock.uptimeMillis() + mStatusBarTransitionDelay;
+ }
+
+ /**
+ * @return the leash for this animation (only valid after the non app window surface animation
+ * has started).
+ */
+ SurfaceControl getLeash() {
+ return mCapturedLeash;
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationCancelled");
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.print("token=");
+ pw.println(mWindow.mToken);
+ if (mTarget != null) {
+ pw.print(prefix);
+ pw.println("Target:");
+ mTarget.dump(pw, prefix + " ");
+ } else {
+ pw.print(prefix);
+ pw.println("Target: null");
+ }
+ }
+
+ @Override
+ public void dumpDebug(ProtoOutputStream proto) {
+ final long token = proto.start(REMOTE);
+ if (mTarget != null) {
+ mTarget.dumpDebug(proto, TARGET);
+ }
+ proto.end(token);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java
new file mode 100644
index 000000000000..1552a96d699a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -0,0 +1,380 @@
+/*
+ * 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;
+
+import static android.app.UiModeManager.MODE_NIGHT_AUTO;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
+
+import android.annotation.NonNull;
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+
+/**
+ * Persist configuration for each package, only persist the change if some on attributes are
+ * different from the global configuration. This class only applies to packages with Activities.
+ */
+public class PackageConfigPersister {
+ private static final String TAG = PackageConfigPersister.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private static final String TAG_CONFIG = "config";
+ private static final String ATTR_PACKAGE_NAME = "package_name";
+ private static final String ATTR_NIGHT_MODE = "night_mode";
+
+ private static final String PACKAGE_DIRNAME = "package_configs";
+ private static final String SUFFIX_FILE_NAME = "_config.xml";
+
+ private final PersisterQueue mPersisterQueue;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final SparseArray<HashMap<String, PackageConfigRecord>> mPendingWrite =
+ new SparseArray<>();
+ @GuardedBy("mLock")
+ private final SparseArray<HashMap<String, PackageConfigRecord>> mModified =
+ new SparseArray<>();
+
+ private static File getUserConfigsDir(int userId) {
+ return new File(Environment.getDataSystemCeDirectory(userId), PACKAGE_DIRNAME);
+ }
+
+ PackageConfigPersister(PersisterQueue queue) {
+ mPersisterQueue = queue;
+ }
+
+ @GuardedBy("mLock")
+ void loadUserPackages(int userId) {
+ synchronized (mLock) {
+ final File userConfigsDir = getUserConfigsDir(userId);
+ final File[] configFiles = userConfigsDir.listFiles();
+ if (configFiles == null) {
+ Slog.v(TAG, "loadPackages: empty list files from " + userConfigsDir);
+ return;
+ }
+
+ for (int fileIndex = 0; fileIndex < configFiles.length; ++fileIndex) {
+ final File configFile = configFiles[fileIndex];
+ if (DEBUG) {
+ Slog.d(TAG, "loadPackages: userId=" + userId
+ + ", configFile=" + configFile.getName());
+ }
+ if (!configFile.getName().endsWith(SUFFIX_FILE_NAME)) {
+ continue;
+ }
+
+ try (InputStream is = new FileInputStream(configFile)) {
+ final TypedXmlPullParser in = Xml.resolvePullParser(is);
+ int event;
+ String packageName = null;
+ int nightMode = MODE_NIGHT_AUTO;
+ while (((event = in.next()) != XmlPullParser.END_DOCUMENT)
+ && event != XmlPullParser.END_TAG) {
+ final String name = in.getName();
+ if (event == XmlPullParser.START_TAG) {
+ if (DEBUG) {
+ Slog.d(TAG, "loadPackages: START_TAG name=" + name);
+ }
+ if (TAG_CONFIG.equals(name)) {
+ for (int attIdx = in.getAttributeCount() - 1; attIdx >= 0;
+ --attIdx) {
+ final String attrName = in.getAttributeName(attIdx);
+ final String attrValue = in.getAttributeValue(attIdx);
+ switch (attrName) {
+ case ATTR_PACKAGE_NAME:
+ packageName = attrValue;
+ break;
+ case ATTR_NIGHT_MODE:
+ nightMode = Integer.parseInt(attrValue);
+ break;
+ }
+ }
+ }
+ }
+ XmlUtils.skipCurrentTag(in);
+ }
+ if (packageName != null) {
+ final PackageConfigRecord initRecord =
+ findRecordOrCreate(mModified, packageName, userId);
+ initRecord.mNightMode = nightMode;
+ if (DEBUG) {
+ Slog.d(TAG, "loadPackages: load one package " + initRecord);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (XmlPullParserException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ void updateConfigIfNeeded(@NonNull ConfigurationContainer container, int userId,
+ String packageName) {
+ synchronized (mLock) {
+ final PackageConfigRecord modifiedRecord = findRecord(mModified, packageName, userId);
+ if (DEBUG) {
+ Slog.d(TAG,
+ "updateConfigIfNeeded record " + container + " find? " + modifiedRecord);
+ }
+ if (modifiedRecord != null) {
+ container.setOverrideNightMode(modifiedRecord.mNightMode);
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ void updateFromImpl(String packageName, int userId,
+ ActivityTaskManagerService.PackageConfigurationUpdaterImpl impl) {
+ synchronized (mLock) {
+ PackageConfigRecord record = findRecordOrCreate(mModified, packageName, userId);
+ record.mNightMode = impl.getNightMode();
+
+ if (record.isResetNightMode()) {
+ removePackage(record.mName, record.mUserId);
+ } else {
+ final PackageConfigRecord pendingRecord =
+ findRecord(mPendingWrite, record.mName, record.mUserId);
+ final PackageConfigRecord writeRecord;
+ if (pendingRecord == null) {
+ writeRecord = findRecordOrCreate(mPendingWrite, record.mName,
+ record.mUserId);
+ } else {
+ writeRecord = pendingRecord;
+ }
+ if (writeRecord.mNightMode == record.mNightMode) {
+ return;
+ }
+ writeRecord.mNightMode = record.mNightMode;
+ if (DEBUG) {
+ Slog.d(TAG, "PackageConfigUpdater save config " + writeRecord);
+ }
+ mPersisterQueue.addItem(new WriteProcessItem(writeRecord), false /* flush */);
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ void removeUser(int userId) {
+ synchronized (mLock) {
+ final HashMap<String, PackageConfigRecord> modifyRecords = mModified.get(userId);
+ final HashMap<String, PackageConfigRecord> writeRecords = mPendingWrite.get(userId);
+ if ((modifyRecords == null || modifyRecords.size() == 0)
+ && (writeRecords == null || writeRecords.size() == 0)) {
+ return;
+ }
+ final HashMap<String, PackageConfigRecord> tempList = new HashMap<>(modifyRecords);
+ tempList.forEach((name, record) -> {
+ removePackage(record.mName, record.mUserId);
+ });
+ }
+ }
+
+ @GuardedBy("mLock")
+ void onPackageUninstall(String packageName) {
+ synchronized (mLock) {
+ for (int i = mModified.size() - 1; i > 0; i--) {
+ final int userId = mModified.keyAt(i);
+ removePackage(packageName, userId);
+ }
+ }
+ }
+
+ private void removePackage(String packageName, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "removePackage packageName :" + packageName + " userId " + userId);
+ }
+ final PackageConfigRecord record = findRecord(mPendingWrite, packageName, userId);
+ if (record != null) {
+ removeRecord(mPendingWrite, record);
+ mPersisterQueue.removeItems(item ->
+ item.mRecord.mName == record.mName
+ && item.mRecord.mUserId == record.mUserId,
+ WriteProcessItem.class);
+ }
+
+ final PackageConfigRecord modifyRecord = findRecord(mModified, packageName, userId);
+ if (modifyRecord != null) {
+ removeRecord(mModified, modifyRecord);
+ mPersisterQueue.addItem(new DeletePackageItem(userId, packageName),
+ false /* flush */);
+ }
+ }
+
+ // store a changed data so we don't need to get the process
+ static class PackageConfigRecord {
+ final String mName;
+ final int mUserId;
+ int mNightMode;
+
+ PackageConfigRecord(String name, int userId) {
+ mName = name;
+ mUserId = userId;
+ }
+
+ boolean isResetNightMode() {
+ return mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM;
+ }
+
+ @Override
+ public String toString() {
+ return "PackageConfigRecord package name: " + mName + " userId " + mUserId
+ + " nightMode " + mNightMode;
+ }
+ }
+
+ private PackageConfigRecord findRecordOrCreate(
+ SparseArray<HashMap<String, PackageConfigRecord>> list, String name, int userId) {
+ HashMap<String, PackageConfigRecord> records = list.get(userId);
+ if (records == null) {
+ records = new HashMap<>();
+ list.put(userId, records);
+ }
+ PackageConfigRecord record = records.get(name);
+ if (record != null) {
+ return record;
+ }
+ record = new PackageConfigRecord(name, userId);
+ records.put(name, record);
+ return record;
+ }
+
+ private PackageConfigRecord findRecord(SparseArray<HashMap<String, PackageConfigRecord>> list,
+ String name, int userId) {
+ HashMap<String, PackageConfigRecord> packages = list.get(userId);
+ if (packages == null) {
+ return null;
+ }
+ return packages.get(name);
+ }
+
+ private void removeRecord(SparseArray<HashMap<String, PackageConfigRecord>> list,
+ PackageConfigRecord record) {
+ final HashMap<String, PackageConfigRecord> processes = list.get(record.mUserId);
+ if (processes != null) {
+ processes.remove(record.mName);
+ }
+ }
+
+ private static class DeletePackageItem implements PersisterQueue.WriteQueueItem {
+ final int mUserId;
+ final String mPackageName;
+
+ DeletePackageItem(int userId, String packageName) {
+ mUserId = userId;
+ mPackageName = packageName;
+ }
+
+ @Override
+ public void process() {
+ File userConfigsDir = getUserConfigsDir(mUserId);
+ if (!userConfigsDir.isDirectory()) {
+ return;
+ }
+ final AtomicFile atomicFile = new AtomicFile(new File(userConfigsDir,
+ mPackageName + SUFFIX_FILE_NAME));
+ if (atomicFile.exists()) {
+ atomicFile.delete();
+ }
+ }
+ }
+
+ private class WriteProcessItem implements PersisterQueue.WriteQueueItem {
+ final PackageConfigRecord mRecord;
+
+ WriteProcessItem(PackageConfigRecord record) {
+ mRecord = record;
+ }
+
+ @Override
+ public void process() {
+ // Write out one user.
+ byte[] data = null;
+ synchronized (mLock) {
+ try {
+ data = saveToXml();
+ } catch (Exception e) {
+ }
+ removeRecord(mPendingWrite, mRecord);
+ }
+ if (data != null) {
+ // Write out xml file while not holding mService lock.
+ FileOutputStream file = null;
+ AtomicFile atomicFile = null;
+ try {
+ File userConfigsDir = getUserConfigsDir(mRecord.mUserId);
+ if (!userConfigsDir.isDirectory() && !userConfigsDir.mkdirs()) {
+ Slog.e(TAG, "Failure creating tasks directory for user " + mRecord.mUserId
+ + ": " + userConfigsDir);
+ return;
+ }
+ atomicFile = new AtomicFile(new File(userConfigsDir,
+ mRecord.mName + SUFFIX_FILE_NAME));
+ file = atomicFile.startWrite();
+ file.write(data);
+ atomicFile.finishWrite(file);
+ } catch (IOException e) {
+ if (file != null) {
+ atomicFile.failWrite(file);
+ }
+ Slog.e(TAG, "Unable to open " + atomicFile + " for persisting. " + e);
+ }
+ }
+ }
+
+ private byte[] saveToXml() throws IOException {
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ final TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(os);
+
+ xmlSerializer.startDocument(null, true);
+ if (DEBUG) {
+ Slog.d(TAG, "Writing package configuration=" + mRecord);
+ }
+ xmlSerializer.startTag(null, TAG_CONFIG);
+ xmlSerializer.attribute(null, ATTR_PACKAGE_NAME, mRecord.mName);
+ xmlSerializer.attributeInt(null, ATTR_NIGHT_MODE, mRecord.mNightMode);
+ xmlSerializer.endTag(null, TAG_CONFIG);
+ xmlSerializer.endDocument();
+ xmlSerializer.flush();
+
+ return os.toByteArray();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/PointerEventDispatcher.java b/services/core/java/com/android/server/wm/PointerEventDispatcher.java
index 6b8144c69079..08de9b052ced 100644
--- a/services/core/java/com/android/server/wm/PointerEventDispatcher.java
+++ b/services/core/java/com/android/server/wm/PointerEventDispatcher.java
@@ -16,11 +16,14 @@
package com.android.server.wm;
+import static com.android.server.input.InputManagerService.ENABLE_PER_WINDOW_INPUT_ROTATION;
+
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.MotionEvent;
+import android.view.Surface;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import com.android.server.UiThread;
@@ -31,8 +34,11 @@ public class PointerEventDispatcher extends InputEventReceiver {
private final ArrayList<PointerEventListener> mListeners = new ArrayList<>();
private PointerEventListener[] mListenersArray = new PointerEventListener[0];
- public PointerEventDispatcher(InputChannel inputChannel) {
+ private final DisplayContent mDisplayContent;
+
+ public PointerEventDispatcher(InputChannel inputChannel, DisplayContent dc) {
super(inputChannel, UiThread.getHandler().getLooper());
+ mDisplayContent = dc;
}
@Override
@@ -40,7 +46,16 @@ public class PointerEventDispatcher extends InputEventReceiver {
try {
if (event instanceof MotionEvent
&& (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- final MotionEvent motionEvent = (MotionEvent) event;
+ MotionEvent motionEvent = (MotionEvent) event;
+ if (ENABLE_PER_WINDOW_INPUT_ROTATION) {
+ int rotation = mDisplayContent.getRotation();
+ if (rotation != Surface.ROTATION_0) {
+ motionEvent = MotionEvent.obtain(motionEvent);
+ motionEvent.transform(MotionEvent.createRotateMatrix(rotation,
+ mDisplayContent.getDisplayMetrics().widthPixels,
+ mDisplayContent.getDisplayMetrics().heightPixels));
+ }
+ }
PointerEventListener[] listeners;
synchronized (mListeners) {
if (mListenersArray == null) {
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index b15701dafa3e..8547f0e69017 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -58,6 +58,8 @@ import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
@@ -1899,6 +1901,7 @@ class RecentTasks {
// Fill in some deprecated values.
rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID;
rti.persistentId = rti.taskId;
+ rti.lastSnapshotData.set(tr.mLastTaskSnapshotData);
// Fill in organized child task info for the task created by organizer.
if (tr.mCreatedByOrganizer) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 26f0f096278c..4f8ea1ac377a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -284,7 +284,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan
// Just to be sure end the launch hint in case the target activity was never launched.
// However, if we're keeping the activity and making it visible, we can leave it on.
if (reorderMode != REORDER_KEEP_IN_PLACE) {
- mService.mRootWindowContainer.endPowerModeLaunchIfNeeded();
+ mService.endLaunchPowerMode(
+ ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
}
// Once the target is shown, prevent spurious background app switches
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 392f27ead2bb..42cb96f65738 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -16,6 +16,9 @@
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;
@@ -126,7 +129,7 @@ class RemoteAnimationController implements DeathRecipient {
final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
// TODO(bc-unlock): Create the remote non app animation targets (if any)
- final RemoteAnimationTarget[] nonAppTargets = new RemoteAnimationTarget[0];
+ final RemoteAnimationTarget[] nonAppTargets = createNonAppWindowAnimations(transit);
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
try {
@@ -215,6 +218,17 @@ class RemoteAnimationController implements DeathRecipient {
}, mPendingWallpaperAnimations);
}
+ 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];
+ }
+
private void onAnimationFinished() {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationFinished(): mPendingAnimations=%d",
mPendingAnimations.size());
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6e9a2f17f6f6..55cf833d0ad9 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -89,7 +89,6 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE;
-import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
@@ -267,9 +266,6 @@ public class RootWindowContainer extends WindowContainer<DisplayContent>
*/
final SparseArray<SleepToken> mSleepTokens = new SparseArray<>();
- /** Set when a power mode launch has started, but not ended. */
- private boolean mPowerModeLaunchStarted;
-
// The default minimal size that will be used if the activity doesn't specify its minimal size.
// It will be calculated when the default display gets added.
int mDefaultMinSizeOfResizeableTaskDp = -1;
@@ -851,7 +847,6 @@ public class RootWindowContainer extends WindowContainer<DisplayContent>
mWmService.openSurfaceTransaction();
try {
applySurfaceChangesTransaction();
- mWmService.mSyncEngine.onSurfacePlacement();
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
@@ -865,6 +860,7 @@ public class RootWindowContainer extends WindowContainer<DisplayContent>
// Send any pending task-info changes that were queued-up during a layout deferment
mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
+ mWmService.mSyncEngine.onSurfacePlacement();
mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
checkAppTransitionReady(surfacePlacer);
@@ -1184,10 +1180,7 @@ public class RootWindowContainer extends WindowContainer<DisplayContent>
mUpdateRotation = true;
doRequest = true;
}
- if ((bulkUpdateParams & SET_ORIENTATION_CHANGE_COMPLETE) == 0) {
- mOrientationChangeComplete = false;
- } else {
- mOrientationChangeComplete = true;
+ if (mOrientationChangeComplete) {
mLastWindowFreezeSource = mWmService.mAnimator.mLastWindowFreezeSource;
if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
doRequest = true;
@@ -2663,7 +2656,7 @@ public class RootWindowContainer extends WindowContainer<DisplayContent>
void addStartingWindowsForVisibleActivities() {
forAllActivities((r) -> {
if (r.mVisibleRequested) {
- r.showStartingWindow(null /* prev */, false /* newTask */, true /*taskSwitch*/);
+ r.showStartingWindow(true /*taskSwitch*/);
}
});
}
@@ -3327,7 +3320,7 @@ public class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
// End power mode launch when idle.
- endPowerModeLaunchIfNeeded();
+ mService.endLaunchPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
return true;
}
@@ -3540,17 +3533,9 @@ public class RootWindowContainer extends WindowContainer<DisplayContent>
sendPowerModeLaunch = noResumedActivities[0] || allFocusedProcessesDiffer[0];
}
- if (sendPowerModeLaunch && mService.mPowerManagerInternal != null) {
- mService.mPowerManagerInternal.setPowerMode(Mode.LAUNCH, true);
- mPowerModeLaunchStarted = true;
- }
- }
-
- void endPowerModeLaunchIfNeeded() {
- // Trigger launch power mode off if activity is launched
- if (mPowerModeLaunchStarted && mService.mPowerManagerInternal != null) {
- mService.mPowerManagerInternal.setPowerMode(Mode.LAUNCH, false);
- mPowerModeLaunchStarted = false;
+ if (sendPowerModeLaunch) {
+ mService.startLaunchPowerMode(
+ ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
}
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 1f8daf6f361f..b82a30847c8d 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -54,10 +54,10 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Process;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
-import android.service.screenshot.ScreenshotHash;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.MergedConfiguration;
@@ -104,9 +104,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final boolean mCanAddInternalSystemWindow;
private final boolean mCanStartTasksFromRecents;
- // If non-system overlays from this process can be hidden by the user or app using
- // HIDE_NON_SYSTEM_OVERLAY_WINDOWS.
- final boolean mOverlaysCanBeHidden;
final boolean mCanCreateSystemApplicationOverlay;
final boolean mCanHideNonSystemOverlayWindows;
final boolean mCanAcquireSleepToken;
@@ -136,8 +133,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
== PERMISSION_GRANTED;
mCanStartTasksFromRecents = service.mContext.checkCallingOrSelfPermission(
START_TASKS_FROM_RECENTS) == PERMISSION_GRANTED;
- mOverlaysCanBeHidden = !mCanAddInternalSystemWindow
- && !mService.mAtmInternal.isCallerRecents(mUid);
mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER)
== PERMISSION_GRANTED;
mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
@@ -253,11 +248,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
- public void setTransparentRegion(IWindow window, Region region) {
- mService.setTransparentRegionWindow(this, window, region);
- }
-
- @Override
public void setInsets(IWindow window, int touchableInsets,
Rect contentInsets, Rect visibleInsets, Region touchableArea) {
mService.setInsetsWindow(this, window, touchableInsets, contentInsets,
@@ -682,7 +672,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
boolean changed;
- if (mOverlaysCanBeHidden && !mCanCreateSystemApplicationOverlay) {
+ if (!mCanAddInternalSystemWindow && !mCanCreateSystemApplicationOverlay) {
// We want to track non-system apps adding alert windows so we can post an
// on-going notification for the user to control their visibility.
if (visible) {
@@ -860,11 +850,11 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
- public ScreenshotHash generateScreenshotHash(IWindow window, Rect boundsInWindow,
- String hashAlgorithm) {
+ public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
+ RemoteCallback callback) {
final long origId = Binder.clearCallingIdentity();
try {
- return mService.generateScreenshotHash(this, window, boundsInWindow, hashAlgorithm);
+ mService.generateDisplayHash(this, window, boundsInWindow, hashAlgorithm, callback);
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index 94e14dd0a6b8..ef4a40f4837c 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -60,7 +60,7 @@ public class StartingSurfaceController {
final Task task = activity.getTask();
if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow(task,
- activity.token)) {
+ activity.token, theme)) {
return new ShellStartingSurface(task);
}
return null;
@@ -125,7 +125,8 @@ public class StartingSurfaceController {
return mService.mTaskSnapshotController
.createStartingSurface(activity, taskSnapshot);
}
- mService.mAtmService.mTaskOrganizerController.addStartingWindow(task, activity.token);
+ mService.mAtmService.mTaskOrganizerController.addStartingWindow(task, activity.token,
+ 0 /* launchTheme */);
return new ShellStartingSurface(task);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 25322bdcae39..9a791f22144e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -82,6 +82,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMA
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN;
+import static com.android.server.wm.ActivityRecord.TRANSFER_SPLASH_SCREEN_COPYING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -158,6 +159,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -293,6 +295,9 @@ class Task extends WindowContainer<WindowContainer> {
private static final String ATTR_MIN_HEIGHT = "min_height";
private static final String ATTR_PERSIST_TASK_VERSION = "persist_task_version";
private static final String ATTR_WINDOW_LAYOUT_AFFINITY = "window_layout_affinity";
+ private static final String ATTR_LAST_SNAPSHOT_TASK_SIZE = "last_snapshot_task_size";
+ private static final String ATTR_LAST_SNAPSHOT_CONTENT_INSETS = "last_snapshot_content_insets";
+ private static final String ATTR_LAST_SNAPSHOT_BUFFER_SIZE = "last_snapshot_buffer_size";
// Set to false to disable the preview that is shown while a new activity
// is being started.
@@ -540,6 +545,10 @@ class Task extends WindowContainer<WindowContainer> {
// NOTE: This value needs to be persisted with each task
private TaskDescription mTaskDescription;
+ // Information about the last snapshot that should be persisted with the task to allow SystemUI
+ // to layout without loading all the task snapshots
+ final PersistedTaskSnapshotData mLastTaskSnapshotData;
+
// If set to true, the task will report that it is not in the floating
// state regardless of it's root task affiliation. As the floating state drives
// production of content insets this can be used to preserve them across
@@ -613,8 +622,6 @@ class Task extends WindowContainer<WindowContainer> {
SurfaceControl.Transaction mMainWindowSizeChangeTransaction;
Task mMainWindowSizeChangeTask;
- Rect mPreAnimationBounds = new Rect();
-
private final AnimatingActivityRegistry mAnimatingActivityRegistry =
new AnimatingActivityRegistry();
@@ -813,6 +820,16 @@ class Task extends WindowContainer<WindowContainer> {
private boolean mDeferTaskAppear;
/**
+ * Forces this task to be unorganized. Currently it is used for deferring the control of
+ * organizer when windowing mode is changing from PiP to fullscreen with orientation change.
+ * It is true only during Task#setWindowingMode ~ DisplayRotation#continueRotation.
+ *
+ * TODO(b/179235349): Remove this field by making surface operations from task organizer sync
+ * with display rotation.
+ */
+ private boolean mForceNotOrganized;
+
+ /**
* This task was created by the task organizer which has the following implementations.
* <ul>
* <lis>The task won't be removed when it is empty. Removal has to be an explicit request
@@ -832,13 +849,13 @@ class Task extends WindowContainer<WindowContainer> {
ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId, int _effectiveUid,
String _lastDescription, long lastTimeMoved, boolean neverRelinquishIdentity,
- TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId,
- int nextTaskId, int callingUid, String callingPackage,
- @Nullable String callingFeatureId, int resizeMode, boolean supportsPictureInPicture,
- boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, int minHeight,
- ActivityInfo info, IVoiceInteractionSession _voiceSession,
- IVoiceInteractor _voiceInteractor, boolean _createdByOrganizer,
- IBinder _launchCookie, boolean _deferTaskAppear) {
+ TaskDescription _lastTaskDescription, PersistedTaskSnapshotData _lastSnapshotData,
+ int taskAffiliation, int prevTaskId, int nextTaskId, int callingUid,
+ String callingPackage, @Nullable String callingFeatureId, int resizeMode,
+ boolean supportsPictureInPicture, boolean _realActivitySuspended,
+ boolean userSetupComplete, int minWidth, int minHeight, ActivityInfo info,
+ IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
+ boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear) {
super(atmService.mWindowManager);
mAtmService = atmService;
@@ -848,7 +865,12 @@ class Task extends WindowContainer<WindowContainer> {
mUserId = _userId;
mResizeMode = resizeMode;
mSupportsPictureInPicture = supportsPictureInPicture;
- mTaskDescription = _lastTaskDescription;
+ mTaskDescription = _lastTaskDescription != null
+ ? _lastTaskDescription
+ : new TaskDescription();
+ mLastTaskSnapshotData = _lastSnapshotData != null
+ ? _lastSnapshotData
+ : new PersistedTaskSnapshotData();
// Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
setOrientation(SCREEN_ORIENTATION_UNSET);
mRemoteToken = new RemoteToken(this);
@@ -2249,6 +2271,21 @@ class Task extends WindowContainer<WindowContainer> {
if (pipChanging) {
mDisplayContent.getPinnedStackController().setPipWindowingModeChanging(true);
+ // If the top activity is using fixed rotation, it should be changing from PiP to
+ // fullscreen with display orientation change. Do not notify fullscreen task organizer
+ // because the restoration of task surface and the transformation of activity surface
+ // need to be done synchronously.
+ final ActivityRecord r = topRunningActivity();
+ if (r != null && mDisplayContent.isFixedRotationLaunchingApp(r)) {
+ mForceNotOrganized = true;
+ }
+ } else if (mForceNotOrganized) {
+ // If the display orientation change is done, let the corresponding task organizer take
+ // back the control of this task.
+ final ActivityRecord r = topRunningActivity();
+ if (r == null || !mDisplayContent.isFixedRotationLaunchingApp(r)) {
+ mForceNotOrganized = false;
+ }
}
try {
// We have 2 reasons why we need to report orientation change here.
@@ -2838,6 +2875,9 @@ class Task extends WindowContainer<WindowContainer> {
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
}
+ // Commit the resolved windowing mode so the canSpecifyOrientation won't get the old
+ // mode that may cause the bounds to be miscalculated, e.g. letterboxed.
+ getConfiguration().windowConfiguration.setWindowingMode(windowingMode);
Rect outOverrideBounds =
getResolvedOverrideConfiguration().windowConfiguration.getBounds();
@@ -3845,6 +3885,13 @@ class Task extends WindowContainer<WindowContainer> {
});
}
+ ActivityRecord getTopWaitSplashScreenActivity() {
+ return getActivity((r) -> {
+ return r.mHandleExitSplashScreen
+ && r.mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_COPYING;
+ });
+ }
+
boolean isTopActivityFocusable() {
final ActivityRecord r = topRunningActivity();
return r != null ? r.isFocusable()
@@ -3874,6 +3921,7 @@ class Task extends WindowContainer<WindowContainer> {
}
void onSnapshotChanged(TaskSnapshot snapshot) {
+ mLastTaskSnapshotData.set(snapshot);
mAtmService.getTaskChangeNotificationController().notifyTaskSnapshotChanged(
mTaskId, snapshot);
}
@@ -4132,7 +4180,6 @@ class Task extends WindowContainer<WindowContainer> {
void fillTaskInfo(TaskInfo info, boolean stripExtras) {
getNumRunningActivities(mReuseActivitiesReport);
info.userId = isLeafTask() ? mUserId : mCurrentUser;
- info.stackId = getRootTaskId();
info.taskId = mTaskId;
info.displayId = getDisplayId();
info.isRunning = getTopNonFinishingActivity() != null;
@@ -4231,6 +4278,9 @@ class Task extends WindowContainer<WindowContainer> {
if (mainWindow != null) {
info.mainWindowLayoutParams = mainWindow.getAttrs();
}
+ // If the developer has persist a different configuration, we need to override it to the
+ // starting window because persisted configuration does not effect to Task.
+ info.taskInfo.configuration.setTo(topActivity.getConfiguration());
}
final ActivityRecord topFullscreenActivity = getTopFullscreenActivity();
if (topFullscreenActivity != null) {
@@ -4536,6 +4586,9 @@ class Task extends WindowContainer<WindowContainer> {
pw.print(" mSupportsPictureInPicture="); pw.print(mSupportsPictureInPicture);
pw.print(" isResizeable="); pw.println(isResizeable());
pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
+ if (mForceNotOrganized) {
+ pw.print(prefix); pw.println("mForceNotOrganized=true");
+ }
pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
}
@@ -4679,6 +4732,19 @@ class Task extends WindowContainer<WindowContainer> {
out.attributeInt(null, ATTR_MIN_HEIGHT, mMinHeight);
out.attributeInt(null, ATTR_PERSIST_TASK_VERSION, PERSIST_TASK_VERSION);
+ if (mLastTaskSnapshotData.taskSize != null) {
+ out.attribute(null, ATTR_LAST_SNAPSHOT_TASK_SIZE,
+ mLastTaskSnapshotData.taskSize.flattenToString());
+ }
+ if (mLastTaskSnapshotData.contentInsets != null) {
+ out.attribute(null, ATTR_LAST_SNAPSHOT_CONTENT_INSETS,
+ mLastTaskSnapshotData.contentInsets.flattenToString());
+ }
+ if (mLastTaskSnapshotData.bufferSize != null) {
+ out.attribute(null, ATTR_LAST_SNAPSHOT_BUFFER_SIZE,
+ mLastTaskSnapshotData.bufferSize.flattenToString());
+ }
+
if (affinityIntent != null) {
out.startTag(null, TAG_AFFINITYINTENT);
affinityIntent.saveToXml(out);
@@ -4746,6 +4812,7 @@ class Task extends WindowContainer<WindowContainer> {
int taskId = INVALID_TASK_ID;
final int outerDepth = in.getDepth();
TaskDescription taskDescription = new TaskDescription();
+ PersistedTaskSnapshotData lastSnapshotData = new PersistedTaskSnapshotData();
int taskAffiliation = INVALID_TASK_ID;
int prevTaskId = INVALID_TASK_ID;
int nextTaskId = INVALID_TASK_ID;
@@ -4855,6 +4922,15 @@ class Task extends WindowContainer<WindowContainer> {
case ATTR_PERSIST_TASK_VERSION:
persistTaskVersion = Integer.parseInt(attrValue);
break;
+ case ATTR_LAST_SNAPSHOT_TASK_SIZE:
+ lastSnapshotData.taskSize = Point.unflattenFromString(attrValue);
+ break;
+ case ATTR_LAST_SNAPSHOT_CONTENT_INSETS:
+ lastSnapshotData.contentInsets = Rect.unflattenFromString(attrValue);
+ break;
+ case ATTR_LAST_SNAPSHOT_BUFFER_SIZE:
+ lastSnapshotData.bufferSize = Point.unflattenFromString(attrValue);
+ break;
default:
if (!attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
Slog.w(TAG, "Task: Unknown attribute=" + attrName);
@@ -4949,6 +5025,7 @@ class Task extends WindowContainer<WindowContainer> {
.setLastTimeMoved(lastTimeOnTop)
.setNeverRelinquishIdentity(neverRelinquishIdentity)
.setLastTaskDescription(taskDescription)
+ .setLastSnapshotData(lastSnapshotData)
.setTaskAffiliation(taskAffiliation)
.setPrevAffiliateTaskId(prevTaskId)
.setNextAffiliateTaskId(nextTaskId)
@@ -4980,6 +5057,9 @@ class Task extends WindowContainer<WindowContainer> {
}
private boolean canBeOrganized() {
+ if (mForceNotOrganized) {
+ return false;
+ }
// All root tasks can be organized
if (isRootTask()) {
return true;
@@ -5224,7 +5304,8 @@ class Task extends WindowContainer<WindowContainer> {
*/
void onWindowFocusChanged(boolean hasFocus) {
updateShadowsRadius(hasFocus, getSyncTransaction());
- dispatchTaskInfoChangedIfNeeded(false /* force */);
+ // TODO(b/180525887): Un-comment once there is resolution on the bug.
+ // dispatchTaskInfoChangedIfNeeded(false /* force */);
}
void onPictureInPictureParamsChanged() {
@@ -5782,12 +5863,8 @@ class Task extends WindowContainer<WindowContainer> {
mTaskSupervisor.acquireLaunchWakelock();
}
- if (didAutoPip) {
- // Already entered PIP mode, no need to keep pausing.
- return true;
- }
-
- if (mPausingActivity != null) {
+ // If already entered PIP mode, no need to keep pausing.
+ if (mPausingActivity != null && !didAutoPip) {
// Have the window manager pause its key dispatching until the new
// activity has started. If we're pausing the activity just because
// the screen is being turned off and the UI is sleeping, don't interrupt
@@ -5810,9 +5887,9 @@ class Task extends WindowContainer<WindowContainer> {
}
} else {
- // This activity failed to schedule the
- // pause, so just treat it as being paused now.
- ProtoLog.v(WM_DEBUG_STATES, "Activity not running, resuming next.");
+ // This activity either failed to schedule the pause or it entered PIP mode,
+ // so just treat it as being paused now.
+ ProtoLog.v(WM_DEBUG_STATES, "Activity not running or entered PiP, resuming next.");
if (resuming == null) {
mRootWindowContainer.resumeFocusedTasksTopActivities();
}
@@ -6501,10 +6578,9 @@ class Task extends WindowContainer<WindowContainer> {
Slog.i(TAG, "Restarting because process died: " + next);
if (!next.hasBeenLaunched) {
next.hasBeenLaunched = true;
- } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null
+ } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null
&& lastFocusedRootTask.isTopRootTaskInDisplayArea()) {
- next.showStartingWindow(null /* prev */, false /* newTask */,
- false /* taskSwitch */);
+ next.showStartingWindow(false /* taskSwitch */);
}
mTaskSupervisor.startSpecificActivity(next, true, false);
return true;
@@ -6527,8 +6603,7 @@ class Task extends WindowContainer<WindowContainer> {
next.hasBeenLaunched = true;
} else {
if (SHOW_APP_STARTING_PREVIEW) {
- next.showStartingWindow(null /* prev */, false /* newTask */,
- false /* taskSwich */);
+ next.showStartingWindow(false /* taskSwich */);
}
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
}
@@ -6694,7 +6769,10 @@ class Task extends WindowContainer<WindowContainer> {
prev = null;
}
}
- r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity));
+ final int splashScreenThemeResId = options != null
+ ? options.getSplashScreenThemeResId() : 0;
+ r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity),
+ splashScreenThemeResId);
}
} else {
// If this is the first activity, don't do any fancy animations,
@@ -7894,6 +7972,7 @@ class Task extends WindowContainer<WindowContainer> {
private long mLastTimeMoved;
private boolean mNeverRelinquishIdentity;
private TaskDescription mLastTaskDescription;
+ private PersistedTaskSnapshotData mLastSnapshotData;
private int mTaskAffiliation;
private int mPrevAffiliateTaskId = INVALID_TASK_ID;
private int mNextAffiliateTaskId = INVALID_TASK_ID;
@@ -8094,6 +8173,11 @@ class Task extends WindowContainer<WindowContainer> {
return this;
}
+ private Builder setLastSnapshotData(PersistedTaskSnapshotData lastSnapshotData) {
+ mLastSnapshotData = lastSnapshotData;
+ return this;
+ }
+
private Builder setOrigActivity(ComponentName origActivity) {
mOrigActivity = origActivity;
return this;
@@ -8207,9 +8291,6 @@ class Task extends WindowContainer<WindowContainer> {
mCallingPackage = mActivityInfo.packageName;
mResizeMode = mActivityInfo.resizeMode;
mSupportsPictureInPicture = mActivityInfo.supportsPictureInPicture();
- if (mLastTaskDescription == null) {
- mLastTaskDescription = new TaskDescription();
- }
final Task task = buildInner();
task.mHasBeenVisible = mHasBeenVisible;
@@ -8243,9 +8324,9 @@ class Task extends WindowContainer<WindowContainer> {
return new Task(mAtmService, mTaskId, mIntent, mAffinityIntent, mAffinity,
mRootAffinity, mRealActivity, mOrigActivity, mRootWasReset, mAutoRemoveRecents,
mAskedCompatMode, mUserId, mEffectiveUid, mLastDescription, mLastTimeMoved,
- mNeverRelinquishIdentity, mLastTaskDescription, mTaskAffiliation,
- mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid, mCallingPackage,
- mCallingFeatureId, mResizeMode, mSupportsPictureInPicture,
+ mNeverRelinquishIdentity, mLastTaskDescription, mLastSnapshotData,
+ mTaskAffiliation, mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid,
+ mCallingPackage, mCallingFeatureId, mResizeMode, mSupportsPictureInPicture,
mRealActivitySuspended, mUserSetupComplete, mMinWidth, mMinHeight,
mActivityInfo, mVoiceSession, mVoiceInteractor, mCreatedByOrganizer,
mLaunchCookie, mDeferTaskAppear);
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index e18219ef4f46..9b7257057836 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -59,6 +59,7 @@ class TaskChangeNotificationController {
private static final int NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG = 25;
private static final int NOTIFY_ACTIVITY_ROTATED_MSG = 26;
private static final int NOTIFY_TASK_MOVED_TO_BACK_LISTENERS_MSG = 27;
+ private static final int NOTIFY_LOCK_TASK_MODE_CHANGED_MSG = 28;
// Delay in notifying task stack change listeners (in millis)
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -177,6 +178,10 @@ class TaskChangeNotificationController {
l.onTaskMovedToBack((RunningTaskInfo) m.obj);
};
+ private final TaskStackConsumer mNotifyLockTaskModeChanged = (l, m) -> {
+ l.onLockTaskModeChanged(m.arg1);
+ };
+
@FunctionalInterface
public interface TaskStackConsumer {
void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -268,6 +273,9 @@ class TaskChangeNotificationController {
case NOTIFY_TASK_MOVED_TO_BACK_LISTENERS_MSG:
forAllRemoteListeners(mNotifyTaskMovedToBack, msg);
break;
+ case NOTIFY_LOCK_TASK_MODE_CHANGED_MSG:
+ forAllRemoteListeners(mNotifyLockTaskModeChanged, msg);
+ break;
}
if (msg.obj instanceof SomeArgs) {
((SomeArgs) msg.obj).recycle();
@@ -497,7 +505,7 @@ class TaskChangeNotificationController {
void notifyTaskDisplayChanged(int taskId, int newDisplayId) {
final Message msg = mHandler.obtainMessage(NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG,
taskId, newDisplayId);
- forAllLocalListeners(mNotifyTaskStackChanged, msg);
+ forAllLocalListeners(mNotifyTaskDisplayChanged, msg);
msg.sendToTarget();
}
@@ -550,4 +558,11 @@ class TaskChangeNotificationController {
forAllLocalListeners(mNotifyTaskMovedToBack, msg);
msg.sendToTarget();
}
+
+ void notifyLockTaskModeChanged(int lockTaskModeState) {
+ final Message msg = mHandler.obtainMessage(NOTIFY_LOCK_TASK_MODE_CHANGED_MSG,
+ lockTaskModeState, 0 /* unused */);
+ forAllLocalListeners(mNotifyLockTaskModeChanged, msg);
+ msg.sendToTarget();
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index f38cf6672c2b..151b58e8b562 100755
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -413,7 +413,11 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
}
// We don't allow untrusted display to top when root task moves to top,
// until user tapping this display to change display position as top intentionally.
- if (!mDisplayContent.isTrusted() && !getParent().isOnTop()) {
+ //
+ // Displays with {@code mDontMoveToTop} property set to {@code true} won't be
+ // allowed to top neither.
+ if ((!mDisplayContent.isTrusted() || mDisplayContent.mDontMoveToTop)
+ && !getParent().isOnTop()) {
includingParents = false;
}
final int targetPosition = findPositionForRootTask(position, child, false /* adding */);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 9fac3f003f70..c0bce6be8c54 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -117,8 +117,11 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return mTaskOrganizer.asBinder();
}
- void addStartingWindow(Task task, IBinder appToken) {
+ void addStartingWindow(Task task, IBinder appToken, int launchTheme) {
final StartingWindowInfo info = task.getStartingWindowInfo();
+ if (launchTheme != 0) {
+ info.splashScreenThemeResId = launchTheme;
+ }
mDeferTaskOrgCallbacksConsumer.accept(() -> {
try {
mTaskOrganizer.addStartingWindow(info, appToken);
@@ -138,6 +141,16 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
});
}
+ void copySplashScreenView(Task task) {
+ mDeferTaskOrgCallbacksConsumer.accept(() -> {
+ try {
+ mTaskOrganizer.copySplashScreenView(task.mTaskId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending copyStartingWindowView callback", e);
+ }
+ });
+ }
+
SurfaceControl prepareLeash(Task task, boolean visible, String reason) {
SurfaceControl outSurfaceControl = new SurfaceControl(task.getSurfaceControl(), reason);
if (!task.mCreatedByOrganizer && !visible) {
@@ -232,14 +245,18 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mUid = uid;
}
- void addStartingWindow(Task t, IBinder appToken) {
- mOrganizer.addStartingWindow(t, appToken);
+ void addStartingWindow(Task t, IBinder appToken, int launchTheme) {
+ mOrganizer.addStartingWindow(t, appToken, launchTheme);
}
void removeStartingWindow(Task t) {
mOrganizer.removeStartingWindow(t);
}
+ void copySplashScreenView(Task t) {
+ mOrganizer.copySplashScreenView(t);
+ }
+
/**
* Register this task with this state, but doesn't trigger the task appeared callback to
* the organizer.
@@ -465,14 +482,14 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return !ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, winMode);
}
- boolean addStartingWindow(Task task, IBinder appToken) {
+ boolean addStartingWindow(Task task, IBinder appToken, int launchTheme) {
final Task rootTask = task.getRootTask();
if (rootTask == null || rootTask.mTaskOrganizer == null) {
return false;
}
final TaskOrganizerState state =
mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
- state.addStartingWindow(task, appToken);
+ state.addStartingWindow(task, appToken, launchTheme);
return true;
}
@@ -486,6 +503,17 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
state.removeStartingWindow(task);
}
+ boolean copySplashScreenView(Task task) {
+ final Task rootTask = task.getRootTask();
+ if (rootTask == null || rootTask.mTaskOrganizer == null) {
+ return false;
+ }
+ final TaskOrganizerState state =
+ mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
+ state.copySplashScreenView(task);
+ return true;
+ }
+
void onTaskAppeared(ITaskOrganizer organizer, Task task) {
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
if (state != null && state.addTask(task)) {
@@ -646,7 +674,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
// Remove and add for re-ordering.
mPendingTaskEvents.remove(pending);
}
- pending.mForce = force;
+ pending.mForce |= force;
mPendingTaskEvents.add(pending);
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 91a6664e0b36..eb32486d6023 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -26,7 +26,6 @@ import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import android.content.Context;
import android.os.Trace;
@@ -134,8 +133,10 @@ public class WindowAnimator {
// Schedule next frame already such that back-pressure happens continuously.
scheduleAnimation();
+ final RootWindowContainer root = mService.mRoot;
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
- mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
+ mBulkUpdateParams = 0;
+ root.mOrientationChangeComplete = true;
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
@@ -144,14 +145,14 @@ public class WindowAnimator {
mService.openSurfaceTransaction();
try {
// Remove all deferred displays, tasks, and activities.
- mService.mRoot.handleCompleteDeferredRemoval();
+ root.handleCompleteDeferredRemoval();
final AccessibilityController accessibilityController =
mService.mAccessibilityController;
final int numDisplays = mDisplayContentsAnimators.size();
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+ final DisplayContent dc = root.getDisplayContent(displayId);
// Update animations of all applications, including those associated with
// exiting/removed apps.
dc.updateWindowsForAnimator();
@@ -160,7 +161,7 @@ public class WindowAnimator {
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+ final DisplayContent dc = root.getDisplayContent(displayId);
dc.checkAppWindowsReadyToShow();
if (accessibilityController != null) {
@@ -179,13 +180,14 @@ public class WindowAnimator {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
}
- final boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
- final boolean doRequest = mBulkUpdateParams != 0 && mService.mRoot.copyAnimToLayoutParams();
+ final boolean hasPendingLayoutChanges = root.hasPendingLayoutChanges(this);
+ final boolean doRequest = (mBulkUpdateParams != 0 || root.mOrientationChangeComplete)
+ && root.copyAnimToLayoutParams();
if (hasPendingLayoutChanges || doRequest) {
mService.mWindowPlacerLocked.requestTraversal();
}
- final boolean rootAnimating = mService.mRoot.isAnimating(TRANSITION | CHILDREN /* flags */,
+ final boolean rootAnimating = root.isAnimating(TRANSITION | CHILDREN /* flags */,
ANIMATION_TYPE_ALL /* typesToCheck */);
if (rootAnimating && !mLastRootAnimating) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
@@ -197,7 +199,7 @@ public class WindowAnimator {
mLastRootAnimating = rootAnimating;
final boolean runningExpensiveAnimations =
- mService.mRoot.isAnimating(TRANSITION | CHILDREN /* flags */,
+ root.isAnimating(TRANSITION | CHILDREN /* flags */,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_SCREEN_ROTATION
| ANIMATION_TYPE_RECENTS /* typesToCheck */);
if (runningExpensiveAnimations && !mRunningExpensiveAnimations) {
@@ -216,7 +218,7 @@ public class WindowAnimator {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
if (mRemoveReplacedWindows) {
- mService.mRoot.removeReplacedWindows();
+ root.removeReplacedWindows();
mRemoveReplacedWindows = false;
}
@@ -235,8 +237,8 @@ public class WindowAnimator {
if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
builder.append(" UPDATE_ROTATION");
}
- if ((bulkUpdateParams & WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE) != 0) {
- builder.append(" ORIENTATION_CHANGE_COMPLETE");
+ if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING) != 0) {
+ builder.append(" SET_WALLPAPER_ACTION_PENDING");
}
return builder.toString();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index bb04ace423c2..e183ea0d81ab 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -611,9 +611,9 @@ public abstract class WindowManagerInternal {
public abstract void removeNonHighRefreshRatePackage(@NonNull String packageName);
/**
- * Checks if this display is touchable.
+ * Checks if the device supports touch or faketouch.
*/
- public abstract boolean isTouchableDisplay(int displayId);
+ public abstract boolean isTouchOrFaketouchDevice();
/**
* Returns the info associated with the input token used to determine if a key should be
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4a9b023af371..dc087d81b7df 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -83,6 +83,10 @@ import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC;
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;
@@ -100,6 +104,7 @@ import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN;
import static com.android.server.LockGuard.INDEX_WINDOW;
import static com.android.server.LockGuard.installLock;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_FREEZE_DISPLAY;
import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
@@ -186,6 +191,7 @@ import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -198,7 +204,6 @@ import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
-import android.service.screenshot.ScreenshotHash;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.sysprop.SurfaceFlingerProperties;
@@ -261,6 +266,8 @@ import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.RemoveContentMode;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
+import android.view.displayhash.DisplayHash;
+import android.view.displayhash.VerifiedDisplayHash;
import android.window.ClientWindowFrames;
import android.window.TaskSnapshot;
@@ -754,6 +761,7 @@ public class WindowManagerService extends IWindowManager.Stub
final TaskSnapshotController mTaskSnapshotController;
boolean mIsTouchDevice;
+ boolean mIsFakeTouchDevice;
final H mH = new H();
@@ -770,7 +778,8 @@ public class WindowManagerService extends IWindowManager.Stub
final EmbeddedWindowController mEmbeddedWindowController;
final AnrController mAnrController;
- private final ScreenshotHashController mScreenshotHashController;
+ private final DisplayHashController mDisplayHashController;
+
@VisibleForTesting
final WindowContextListenerController mWindowContextListenerController =
new WindowContextListenerController();
@@ -979,7 +988,7 @@ public class WindowManagerService extends IWindowManager.Stub
void updateSupportsNonResizableMultiWindow() {
ContentResolver resolver = mContext.getContentResolver();
final boolean supportsNonResizableMultiWindow = Settings.Global.getInt(resolver,
- DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) != 0;
+ DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) != 0;
mAtmService.mSupportsNonResizableMultiWindow = supportsNonResizableMultiWindow;
}
@@ -1421,7 +1430,7 @@ public class WindowManagerService extends IWindowManager.Stub
mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
mContext.getResources());
- mScreenshotHashController = new ScreenshotHashController(mContext);
+ mDisplayHashController = new DisplayHashController(mContext);
setGlobalShadowSettings();
mAnrController = new AnrController(this);
mStartingSurfaceController = new StartingSurfaceController(this);
@@ -2152,23 +2161,6 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.i(tag, s, e);
}
- void setTransparentRegionWindow(Session session, IWindow client, Region region) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- WindowState w = windowForClientLocked(session, client, false);
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE transparentRegionHint=%s: %s",
- region, w);
-
- if ((w != null) && w.mHasSurface) {
- w.mWinAnimator.setTransparentRegionHintLocked(region);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets,
Rect visibleInsets, Region touchableRegion) {
int uid = Binder.getCallingUid();
@@ -4980,6 +4972,8 @@ public class WindowManagerService extends IWindowManager.Stub
mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked);
mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TOUCHSCREEN);
+ mIsFakeTouchDevice = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_FAKETOUCH);
}
try {
@@ -5746,8 +5740,6 @@ public class WindowManagerService extends IWindowManager.Stub
if (!w.mToken.okToDisplay() && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
ProtoLog.v(WM_DEBUG_ORIENTATION, "Changing surface while display frozen: %s", w);
w.setOrientationChanging(true);
- w.mLastFreezeDuration = 0;
- mRoot.mOrientationChangeComplete = false;
if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) {
mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
// XXX should probably keep timeout from
@@ -5858,6 +5850,9 @@ public class WindowManagerService extends IWindowManager.Stub
"startFreezingDisplayLocked: exitAnim=%d enterAnim=%d called by %s",
exitAnim, enterAnim, Debug.getCallers(8));
mScreenFrozenLock.acquire();
+ // Apply launch power mode to reduce screen frozen time because orientation change may
+ // relaunch activity and redraw windows. This may also help speed up user switching.
+ mAtmService.startLaunchPowerMode(POWER_MODE_REASON_FREEZE_DISPLAY);
mDisplayFrozen = true;
mDisplayFreezeTime = SystemClock.elapsedRealtime();
@@ -6007,6 +6002,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (configChanged) {
displayContent.sendNewConfiguration();
}
+ mAtmService.endLaunchPowerMode(POWER_MODE_REASON_FREEZE_DISPLAY);
mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN);
if (mPerf != null) {
mPerf.perfLockRelease();
@@ -7554,8 +7550,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (spec != null) {
result.setTo(spec);
}
- spec.scale *= windowState.mGlobalScale;
- return spec;
+ result.scale *= windowState.mGlobalScale;
+ return result;
}
}
@@ -7985,13 +7981,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public boolean isTouchableDisplay(int displayId) {
+ public boolean isTouchOrFaketouchDevice() {
synchronized (mGlobalLock) {
- final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
- final Configuration configuration =
- displayContent != null ? displayContent.getConfiguration() : null;
- return configuration != null
- && configuration.touchscreen == Configuration.TOUCHSCREEN_FINGER;
+ // All touchable devices are also faketouchable.
+ return mIsFakeTouchDevice;
}
}
@@ -8642,39 +8635,42 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public String[] getSupportedScreenshotHashingAlgorithms() {
- return mScreenshotHashController.getSupportedHashingAlgorithms();
+ public String[] getSupportedDisplayHashAlgorithms() {
+ return mDisplayHashController.getSupportedHashAlgorithms();
}
@Override
- public boolean verifyScreenshotHash(ScreenshotHash screenshotHash) {
- return mScreenshotHashController.verifyScreenshotHash(screenshotHash);
+ public VerifiedDisplayHash verifyDisplayHash(DisplayHash displayHash) {
+ return mDisplayHashController.verifyDisplayHash(displayHash);
}
- ScreenshotHash generateScreenshotHash(Session session, IWindow window,
- Rect boundsInWindow, String hashAlgorithm) {
+ void generateDisplayHash(Session session, IWindow window, Rect boundsInWindow,
+ String hashAlgorithm, RemoteCallback callback) {
final SurfaceControl displaySurfaceControl;
final Rect boundsInDisplay = new Rect(boundsInWindow);
synchronized (mGlobalLock) {
final WindowState win = windowForClientLocked(session, window, false);
if (win == null) {
- Slog.w(TAG, "Failed to generate ScreenshotHash. Invalid window");
- return null;
+ Slog.w(TAG, "Failed to generate DisplayHash. Invalid window");
+ sendDisplayHashError(callback, DISPLAY_HASH_ERROR_MISSING_WINDOW);
+ return;
}
DisplayContent displayContent = win.getDisplayContent();
if (displayContent == null) {
- Slog.w(TAG, "Failed to generate ScreenshotHash. Window is not on a display");
- return null;
+ Slog.w(TAG, "Failed to generate DisplayHash. Window is not on a display");
+ sendDisplayHashError(callback, DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
+ return;
}
displaySurfaceControl = displayContent.getSurfaceControl();
- mScreenshotHashController.calculateScreenshotHashBoundsLocked(win,
- boundsInWindow, boundsInDisplay);
+ mDisplayHashController.calculateDisplayHashBoundsLocked(win, boundsInWindow,
+ boundsInDisplay);
if (boundsInDisplay.isEmpty()) {
- Slog.w(TAG, "Failed to generate ScreenshotHash. Bounds are not on screen");
- return null;
+ Slog.w(TAG, "Failed to generate DisplayHash. Bounds are not on screen");
+ sendDisplayHashError(callback, DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
+ return;
}
}
@@ -8693,11 +8689,18 @@ public class WindowManagerService extends IWindowManager.Stub
SurfaceControl.captureLayers(args);
if (screenshotHardwareBuffer == null
|| screenshotHardwareBuffer.getHardwareBuffer() == null) {
- Slog.w(TAG, "Failed to generate ScreenshotHash. Failed to take screenshot");
- return null;
+ Slog.w(TAG, "Failed to generate DisplayHash. Couldn't capture content");
+ sendDisplayHashError(callback, DISPLAY_HASH_ERROR_UNKNOWN);
+ return;
}
- return mScreenshotHashController.generateScreenshotHash(
- screenshotHardwareBuffer.getHardwareBuffer(), boundsInWindow, hashAlgorithm);
+ mDisplayHashController.generateDisplayHash(screenshotHardwareBuffer.getHardwareBuffer(),
+ boundsInWindow, hashAlgorithm, callback);
+ }
+
+ 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/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index da31bb253831..5ef9420a10d8 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -16,25 +16,36 @@
package com.android.server.wm;
+import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER;
+
import static com.android.server.wm.WindowOrientationListenerProto.ENABLED;
import static com.android.server.wm.WindowOrientationListenerProto.ROTATION;
+import android.app.ActivityThread;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.rotationresolver.RotationResolverInternal;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Surface;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.LocalServices;
import java.io.PrintWriter;
import java.util.List;
+import java.util.Set;
/**
* A special helper class used by the WindowManager
@@ -55,6 +66,9 @@ public abstract class WindowOrientationListener {
private static final boolean USE_GRAVITY_SENSOR = false;
private static final int DEFAULT_BATCH_LATENCY = 100000;
+ private static final int DEFAULT_ROTATION_RESOLVER_ENABLED = 0; // disabled
+ private static final String KEY_ROTATION_RESOLVER_TIMEOUT = "rotation_resolver_timeout_millis";
+ private static final long DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS = 700L;
private Handler mHandler;
private SensorManager mSensorManager;
@@ -62,7 +76,13 @@ public abstract class WindowOrientationListener {
private int mRate;
private String mSensorType;
private Sensor mSensor;
- private OrientationJudge mOrientationJudge;
+
+ @VisibleForTesting
+ OrientationJudge mOrientationJudge;
+
+ @VisibleForTesting
+ RotationResolverInternal mRotationResolverService;
+
private int mCurrentRotation = -1;
private final Context mContext;
private final WindowManagerConstants mConstants;
@@ -256,6 +276,32 @@ public abstract class WindowOrientationListener {
}
/**
+ * Returns true if the current status of the phone is suitable for using rotation resolver
+ * service.
+ *
+ * To reduce the power consumption of rotation resolver service, rotation query should run less
+ * frequently than other low power orientation sensors. This method is used to check whether
+ * the current status of the phone is necessary to request a suggested screen rotation from the
+ * rotation resolver service. Note that it always returns {@code false} in the base class. It
+ * should be overridden in the derived classes.
+ */
+ public boolean canUseRotationResolver() {
+ return false;
+ }
+
+ /**
+ * Returns true if the rotation resolver feature is enabled by setting. It means {@link
+ * WindowOrientationListener} will then ask {@link RotationResolverInternal} for the appropriate
+ * screen rotation.
+ */
+ @VisibleForTesting
+ boolean isRotationResolverEnabled() {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.CAMERA_AUTOROTATE, DEFAULT_ROTATION_RESOLVER_ENABLED,
+ UserHandle.USER_CURRENT) == 1;
+ }
+
+ /**
* Called when the rotation view of the device has changed.
*
* This method is called whenever the orientation becomes certain of an orientation.
@@ -1045,6 +1091,30 @@ public abstract class WindowOrientationListener {
private int mProposedRotation = -1;
private int mDesiredRotation = -1;
private boolean mRotationEvaluationScheduled;
+ private long mRotationResolverTimeoutMillis;
+
+ OrientationSensorJudge() {
+ super();
+ setupRotationResolverParameters();
+ }
+
+ private void setupRotationResolverParameters() {
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_WINDOW_MANAGER,
+ ActivityThread.currentApplication().getMainExecutor(), (properties) -> {
+ final Set<String> keys = properties.getKeyset();
+ if (keys.contains(KEY_ROTATION_RESOLVER_TIMEOUT)) {
+ readRotationResolverParameters();
+ }
+ });
+ readRotationResolverParameters();
+ }
+
+ private void readRotationResolverParameters() {
+ mRotationResolverTimeoutMillis = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_ROTATION_RESOLVER_TIMEOUT,
+ DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS);
+ }
@Override
public int getProposedRotationLocked() {
@@ -1069,19 +1139,13 @@ public abstract class WindowOrientationListener {
@Override
public void onSensorChanged(SensorEvent event) {
- int newRotation;
-
int reportedRotation = (int) event.values[0];
if (reportedRotation < 0 || reportedRotation > 3) {
return;
}
- synchronized (mLock) {
- mDesiredRotation = reportedRotation;
- newRotation = evaluateRotationChangeLocked();
- }
- if (newRotation >= 0) {
- onProposedRotationChanged(newRotation);
+ // Log raw sensor rotation.
+ if (evaluateRotationChangeLocked() >= 0) {
if (mConstants.mRawSensorLoggingEnabled) {
FrameworkStatsLog.write(
FrameworkStatsLog.DEVICE_ROTATED,
@@ -1089,6 +1153,35 @@ public abstract class WindowOrientationListener {
rotationToLogEnum(reportedRotation));
}
}
+
+ if (isRotationResolverEnabled() && canUseRotationResolver()) {
+ if (mRotationResolverService == null) {
+ mRotationResolverService = LocalServices.getService(
+ RotationResolverInternal.class);
+ }
+
+ final CancellationSignal cancellationSignal = new CancellationSignal();
+ mRotationResolverService.resolveRotation(
+ new RotationResolverInternal.RotationResolverCallbackInternal() {
+ @Override
+ public void onSuccess(int result) {
+ finalizeRotation(result);
+ }
+
+ @Override
+ public void onFailure(int error) {
+ finalizeRotation(reportedRotation);
+ }
+ },
+ reportedRotation,
+ mCurrentRotation,
+ mRotationResolverTimeoutMillis,
+ cancellationSignal);
+ getHandler().postDelayed(cancellationSignal::cancel,
+ mRotationResolverTimeoutMillis);
+ } else {
+ finalizeRotation(reportedRotation);
+ }
}
@Override
@@ -1131,6 +1224,17 @@ public abstract class WindowOrientationListener {
return -1;
}
+ private void finalizeRotation(int reportedRotation) {
+ int newRotation;
+ synchronized (mLock) {
+ mDesiredRotation = reportedRotation;
+ newRotation = evaluateRotationChangeLocked();
+ }
+ if (newRotation >= 0) {
+ onProposedRotationChanged(newRotation);
+ }
+ }
+
private boolean isDesiredRotationAcceptableLocked(long now) {
if (mTouching) {
return false;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 8b4d415efcb5..c3620235d3df 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -256,6 +256,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
onConfigurationChanged(atm.getGlobalConfiguration());
+ mAtm.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, mName);
}
public void setPid(int pid) {
@@ -802,6 +803,13 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
return false;
}
+ void updateNightModeForAllActivities(int nightMode) {
+ for (int i = mActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord r = mActivities.get(i);
+ r.setOverrideNightMode(nightMode);
+ }
+ }
+
public void clearPackagePreferredForHomeActivities() {
synchronized (mAtm.mGlobalLock) {
for (int i = mActivities.size() - 1; i >= 0; --i) {
@@ -1329,6 +1337,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
mHasPendingConfigurationChange = true;
return;
}
+ dispatchConfiguration(config);
+ }
+
+ void dispatchConfiguration(Configuration config) {
mHasPendingConfigurationChange = false;
if (mThread == null) {
if (Build.IS_DEBUGGABLE && mHasImeService) {
@@ -1367,8 +1379,13 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
mPauseConfigurationDispatchCount++;
}
- void resumeConfigurationDispatch() {
+ /** Returns {@code true} if the configuration change is pending to dispatch. */
+ boolean resumeConfigurationDispatch() {
+ if (mPauseConfigurationDispatchCount == 0) {
+ return false;
+ }
mPauseConfigurationDispatchCount--;
+ return mHasPendingConfigurationChange;
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c1f2d02804ea..a94b0aa9b72f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1490,6 +1490,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void setOrientationChanging(boolean changing) {
mOrientationChanging = changing;
mOrientationChangeTimedOut = false;
+ if (changing) {
+ mLastFreezeDuration = 0;
+ mWmService.mRoot.mOrientationChangeComplete = false;
+ }
}
void orientationChangeTimedOut() {
@@ -1721,7 +1725,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
boolean isVisibleRequested() {
- return isVisible();
+ return isVisible() && (mActivityRecord == null || mActivityRecord.isVisibleRequested());
}
/**
@@ -2132,7 +2136,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
&& !mAnimatingExit
&& (mWindowFrames.mRelFrame.top != mWindowFrames.mLastRelFrame.top
|| mWindowFrames.mRelFrame.left != mWindowFrames.mLastRelFrame.left)
- && (!mIsChildWindow || !getParentWindow().hasMoved());
+ && (!mIsChildWindow || !getParentWindow().hasMoved())
+ && !mWmService.mAtmService.getTransitionController().isCollecting();
}
boolean isObscuringDisplay() {
@@ -3095,7 +3100,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
void setForceHideNonSystemOverlayWindowIfNeeded(boolean forceHide) {
- if (!mSession.mOverlaysCanBeHidden
+ if (mSession.mCanAddInternalSystemWindow
|| (!isSystemAlertWindowType(mAttrs.type) && mAttrs.type != TYPE_TOAST)) {
return;
}
@@ -3286,7 +3291,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
ProtoLog.v(WM_DEBUG_ORIENTATION,
"set mOrientationChanging of %s", this);
setOrientationChanging(true);
- mWmService.mRoot.mOrientationChangeComplete = false;
}
mLastFreezeDuration = 0;
setDisplayLayoutNeeded();
@@ -3635,6 +3639,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (mActivityRecord != null && mActivityRecord.isRelaunching()) {
return;
}
+ // If the activity is invisible or going invisible, don't report either since it is going
+ // away. This is likely during a transition so we want to preserve the original state.
+ if (mActivityRecord != null && !mActivityRecord.isVisibleRequested()) {
+ return;
+ }
if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
@@ -5304,7 +5313,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
updateSurfacePositionNonOrganized();
// Send information to SufaceFlinger about the priority of the current window.
updateFrameRateSelectionPriorityIfNeeded();
- updateGlobalScaleIfNeeded();
+ if (isVisibleRequested()) updateGlobalScaleIfNeeded();
mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
super.prepareSurfaces();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 2da3dda831e1..ece256e8c591 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -52,7 +52,6 @@ import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE;
import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
-import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import android.content.Context;
import android.graphics.Matrix;
@@ -664,7 +663,7 @@ class WindowStateAnimator {
if (w.getOrientationChanging()) {
if (!w.isDrawn()) {
- mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE;
+ w.mWmService.mRoot.mOrientationChangeComplete = false;
mAnimator.mLastWindowFreezeSource = w;
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Orientation continue waiting for draw in %s", w);
@@ -679,14 +678,6 @@ class WindowStateAnimator {
}
}
- void setTransparentRegionHintLocked(final Region region) {
- if (mSurfaceController == null) {
- Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true");
- return;
- }
- mSurfaceController.setTransparentRegionHint(region);
- }
-
boolean setWallpaperOffset(int dx, int dy, float scale) {
if (mXOffset == dx && mYOffset == dy && Float.compare(mWallpaperScale, scale) == 0) {
return false;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 82ba3c188b76..636f0bb6086f 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -194,22 +194,6 @@ class WindowSurfaceController {
return true;
}
- void setTransparentRegionHint(final Region region) {
- if (mSurfaceControl == null) {
- Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true");
- return;
- }
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setTransparentRegion");
- mService.openSurfaceTransaction();
- try {
- getGlobalTransaction().setTransparentRegionHint(mSurfaceControl, region);
- } finally {
- mService.closeSurfaceTransaction("setTransparentRegion");
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION setTransparentRegion");
- }
- }
-
void setOpaque(boolean isOpaque) {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isOpaque=%b: %s", isOpaque, title);
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 6b9fbcbf459f..2ee5fb01efb3 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -43,8 +43,7 @@ class WindowSurfacePlacer {
private int mLayoutRepeatCount;
static final int SET_UPDATE_ROTATION = 1 << 0;
- static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 2;
- static final int SET_WALLPAPER_ACTION_PENDING = 1 << 3;
+ static final int SET_WALLPAPER_ACTION_PENDING = 1 << 1;
private boolean mTraversalScheduled;
private int mDeferDepth = 0;
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index c3a4609c02a1..1c3fe029a56b 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -630,9 +630,14 @@ class WindowToken extends WindowContainer<WindowState> {
void updateSurfacePosition(SurfaceControl.Transaction t) {
super.updateSurfacePosition(t);
if (isFixedRotationTransforming()) {
- // The window is layouted in a simulated rotated display but the real display hasn't
- // rotated, so here transforms its surface to fit in the real display.
- mFixedRotationTransformState.transform(this);
+ final ActivityRecord r = asActivityRecord();
+ final Task rootTask = r != null ? r.getRootTask() : null;
+ // Don't transform the activity in PiP because the PiP task organizer will handle it.
+ if (rootTask == null || !rootTask.inPinnedWindowingMode()) {
+ // The window is laid out in a simulated rotated display but the real display hasn't
+ // rotated, so here transforms its surface to fit in the real display.
+ mFixedRotationTransformState.transform(this);
+ }
}
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 0689c6ceff4b..c51a3a2c16c7 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_static {
name: "libservices.core",
defaults: ["libservices.core-libs"],
@@ -30,6 +39,7 @@ cc_library_static {
"com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp",
"com_android_server_ConsumerIrService.cpp",
"com_android_server_devicepolicy_CryptoTestHelper.cpp",
+ "com_android_server_connectivity_Vpn.cpp",
"com_android_server_gpu_GpuService.cpp",
"com_android_server_HardwarePropertiesManagerService.cpp",
"com_android_server_input_InputManagerService.cpp",
@@ -54,13 +64,13 @@ cc_library_static {
"com_android_server_UsbMidiDevice.cpp",
"com_android_server_UsbHostManager.cpp",
"com_android_server_vibrator_VibratorController.cpp",
- "com_android_server_VibratorManagerService.cpp",
+ "com_android_server_vibrator_VibratorManagerService.cpp",
"com_android_server_PersistentDataBlockService.cpp",
- "com_android_server_am_CachedAppOptimizer.cpp",
"com_android_server_am_LowMemDetector.cpp",
"com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
"com_android_server_activityTriggerService.cpp",
"onload.cpp",
+ ":lib_cachedAppOptimizer_native",
":lib_networkStatsFactory_native",
],
@@ -150,7 +160,7 @@ cc_defaults {
"android.hardware.power@1.1",
"android.hardware.power-V1-cpp",
"android.hardware.power.stats@1.0",
- "android.hardware.power.stats-ndk_platform",
+ "android.hardware.power.stats-V1-ndk_platform",
"android.hardware.thermal@1.0",
"android.hardware.tv.input@1.0",
"android.hardware.vibrator-V2-cpp",
@@ -194,3 +204,10 @@ filegroup {
"com_android_server_net_NetworkStatsFactory.cpp",
],
}
+
+filegroup {
+ name: "lib_cachedAppOptimizer_native",
+ srcs: [
+ "com_android_server_am_CachedAppOptimizer.cpp",
+ ],
+}
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 9a8942b47428..d076434e7d90 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -1,10 +1,6 @@
# Display
per-file com_android_server_lights_LightsService.cpp = michaelwr@google.com, santoscordon@google.com
-# Haptics
-per-file com_android_server_vibrator_VibratorController.cpp = michaelwr@google.com
-per-file com_android_server_VibratorManagerService.cpp = michaelwr@google.com
-
# Input
per-file com_android_server_input_InputManagerService.cpp = michaelwr@google.com, svv@google.com
diff --git a/services/core/jni/com_android_server_VibratorManagerService.cpp b/services/core/jni/com_android_server_VibratorManagerService.cpp
deleted file mode 100644
index 71de9bda3c4f..000000000000
--- a/services/core/jni/com_android_server_VibratorManagerService.cpp
+++ /dev/null
@@ -1,103 +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.
- */
-
-#define LOG_TAG "VibratorManagerService"
-
-#include <nativehelper/JNIHelp.h>
-#include "android_runtime/AndroidRuntime.h"
-#include "core_jni_helpers.h"
-#include "jni.h"
-
-#include <utils/Log.h>
-#include <utils/misc.h>
-
-#include <vibratorservice/VibratorManagerHalWrapper.h>
-
-#include "com_android_server_VibratorManagerService.h"
-
-namespace android {
-
-static std::mutex gManagerMutex;
-static vibrator::ManagerHalWrapper* gManager GUARDED_BY(gManagerMutex) = nullptr;
-
-class NativeVibratorManagerService {
-public:
- NativeVibratorManagerService() : mHal(std::make_unique<vibrator::LegacyManagerHalWrapper>()) {}
- ~NativeVibratorManagerService() = default;
-
- vibrator::ManagerHalWrapper* hal() const { return mHal.get(); }
-
-private:
- const std::unique_ptr<vibrator::ManagerHalWrapper> mHal;
-};
-
-vibrator::ManagerHalWrapper* android_server_VibratorManagerService_getManager() {
- std::lock_guard<std::mutex> lock(gManagerMutex);
- return gManager;
-}
-
-static void destroyNativeService(void* ptr) {
- NativeVibratorManagerService* service = reinterpret_cast<NativeVibratorManagerService*>(ptr);
- if (service) {
- std::lock_guard<std::mutex> lock(gManagerMutex);
- gManager = nullptr;
- delete service;
- }
-}
-
-static jlong nativeInit(JNIEnv* /* env */, jclass /* clazz */) {
- std::unique_ptr<NativeVibratorManagerService> service =
- std::make_unique<NativeVibratorManagerService>();
- {
- std::lock_guard<std::mutex> lock(gManagerMutex);
- gManager = service->hal();
- }
- return reinterpret_cast<jlong>(service.release());
-}
-
-static jlong nativeGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
- return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeService));
-}
-
-static jintArray nativeGetVibratorIds(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
- NativeVibratorManagerService* service =
- reinterpret_cast<NativeVibratorManagerService*>(servicePtr);
- if (service == nullptr) {
- ALOGE("nativeGetVibratorIds failed because native service was not initialized");
- return nullptr;
- }
- auto result = service->hal()->getVibratorIds();
- if (!result.isOk()) {
- return nullptr;
- }
- std::vector<int32_t> vibratorIds = result.value();
- jintArray ids = env->NewIntArray(vibratorIds.size());
- env->SetIntArrayRegion(ids, 0, vibratorIds.size(), reinterpret_cast<jint*>(vibratorIds.data()));
- return ids;
-}
-
-static const JNINativeMethod method_table[] = {
- {"nativeInit", "()J", (void*)nativeInit},
- {"nativeGetFinalizer", "()J", (void*)nativeGetFinalizer},
- {"nativeGetVibratorIds", "(J)[I", (void*)nativeGetVibratorIds},
-};
-
-int register_android_server_VibratorManagerService(JNIEnv* env) {
- return jniRegisterNativeMethods(env, "com/android/server/VibratorManagerService", method_table,
- NELEM(method_table));
-}
-
-}; // namespace android
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 31cc295406a5..4551d49d9e58 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -278,6 +278,11 @@ static jint com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv
return retVal;
}
+static jstring com_android_server_am_CachedAppOptimizer_getFreezerCheckPath(JNIEnv* env,
+ jobject clazz) {
+ return env->NewStringUTF(CGROUP_FREEZE_PATH);
+}
+
static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
@@ -286,7 +291,9 @@ static const JNINativeMethod sMethods[] = {
(void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal},
{"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
{"getBinderFreezeInfo", "(I)I",
- (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}};
+ (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo},
+ {"getFreezerCheckPath", "()Ljava/lang/String;",
+ (void*)com_android_server_am_CachedAppOptimizer_getFreezerCheckPath}};
int register_android_server_am_CachedAppOptimizer(JNIEnv* env)
{
diff --git a/packages/Connectivity/service/jni/com_android_server_connectivity_Vpn.cpp b/services/core/jni/com_android_server_connectivity_Vpn.cpp
index ea5e7183c905..ea5e7183c905 100644
--- a/packages/Connectivity/service/jni/com_android_server_connectivity_Vpn.cpp
+++ b/services/core/jni/com_android_server_connectivity_Vpn.cpp
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 5b587e9859d8..643503d18bed 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -106,6 +106,7 @@ static struct {
jmethodID notifyFocusChanged;
jmethodID notifySensorEvent;
jmethodID notifySensorAccuracy;
+ jmethodID notifyVibratorState;
jmethodID notifyUntrustedTouch;
jmethodID filterInputEvent;
jmethodID interceptKeyBeforeQueueing;
@@ -305,6 +306,7 @@ public:
const std::vector<float>& values) override;
void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
InputDeviceSensorAccuracy accuracy) override;
+ void notifyVibratorState(int32_t deviceId, bool isOn) override;
void notifyUntrustedTouch(const std::string& obscuringPackage) override;
bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
@@ -918,6 +920,18 @@ void NativeInputManager::notifySensorAccuracy(int32_t deviceId, InputDeviceSenso
checkAndClearExceptionFromCallback(env, "notifySensorAccuracy");
}
+void NativeInputManager::notifyVibratorState(int32_t deviceId, bool isOn) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ ALOGD("notifyVibratorState isOn:%d", isOn);
+#endif
+ ATRACE_CALL();
+ JNIEnv* env = jniEnv();
+ ScopedLocalFrame localFrame(env);
+ env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyVibratorState,
+ static_cast<jint>(deviceId), static_cast<jboolean>(isOn));
+ checkAndClearExceptionFromCallback(env, "notifyVibratorState");
+}
+
void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
@@ -2248,6 +2262,8 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gServiceClassInfo.notifySensorAccuracy, clazz, "notifySensorAccuracy", "(III)V");
+ GET_METHOD_ID(gServiceClassInfo.notifyVibratorState, clazz, "notifyVibratorState", "(IZ)V");
+
GET_METHOD_ID(gServiceClassInfo.notifyUntrustedTouch, clazz, "notifyUntrustedTouch",
"(Ljava/lang/String;)V");
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 5a5b0a8d30aa..7b78b8d23b4b 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -44,7 +44,6 @@ namespace android {
namespace {
using android::base::borrowed_fd;
-using android::base::ReadFully;
using android::base::unique_fd;
using namespace std::literals;
@@ -173,7 +172,7 @@ static inline int32_t readLEInt32(borrowed_fd fd) {
static inline std::vector<char> readBytes(borrowed_fd fd) {
int32_t size = readLEInt32(fd);
std::vector<char> result(size);
- ReadFully(fd, result.data(), size);
+ android::base::ReadFully(fd, result.data(), size);
return result;
}
@@ -569,7 +568,7 @@ private:
// Awaiting adb handshake.
char okay_buf[OKAY.size()];
if (!android::base::ReadFully(inout, okay_buf, OKAY.size())) {
- ALOGE("Failed to receive OKAY. Abort.");
+ ALOGE("Failed to receive OKAY. Abort. Error %d", errno);
return false;
}
if (std::string_view(okay_buf, OKAY.size()) != OKAY) {
@@ -693,12 +692,12 @@ private:
continue;
}
if (res < 0) {
- ALOGE("Failed to poll. Abort.");
+ ALOGE("Failed to poll. Abort. Error %d", res);
mStatusListener->reportStatus(DATA_LOADER_UNRECOVERABLE);
break;
}
if (res == mEventFd) {
- ALOGE("Received stop signal. Sending EXIT to server.");
+ ALOGE("DataLoader requested to stop. Sending EXIT to server.");
sendRequest(inout, EXIT);
break;
}
@@ -712,7 +711,7 @@ private:
auto header = readHeader(remainingData);
if (header.fileIdx == -1 && header.blockType == 0 && header.compressionType == 0 &&
header.blockIdx == 0 && header.blockSize == 0) {
- ALOGI("Stop signal received. Sending exit command (remaining bytes: %d).",
+ ALOGI("Stop command received. Sending exit command (remaining bytes: %d).",
int(remainingData.size()));
sendRequest(inout, EXIT);
@@ -721,16 +720,15 @@ private:
}
if (header.fileIdx < 0 || header.blockSize <= 0 || header.blockType < 0 ||
header.compressionType < 0 || header.blockIdx < 0) {
- ALOGE("invalid header received. Abort.");
+ ALOGE("Invalid header received. Abort.");
mStopReceiving = true;
break;
}
+
const FileIdx fileIdx = header.fileIdx;
const android::dataloader::FileId fileId = convertFileIndexToFileId(mode, fileIdx);
if (!android::incfs::isValidFileId(fileId)) {
- ALOGE("Unknown data destination for file ID %d. "
- "Ignore.",
- header.fileIdx);
+ ALOGE("Unknown data destination for file ID %d. Ignore.", header.fileIdx);
continue;
}
@@ -738,7 +736,7 @@ private:
if (writeFd < 0) {
writeFd.reset(this->mIfs->openForSpecialOps(fileId).release());
if (writeFd < 0) {
- ALOGE("Failed to open file %d for writing (%d). Aborting.", header.fileIdx,
+ ALOGE("Failed to open file %d for writing (%d). Abort.", header.fileIdx,
-writeFd);
break;
}
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 7ed37481f8cb..89b931d35c40 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -29,7 +29,7 @@
#include <vibratorservice/VibratorHalController.h>
-#include "com_android_server_VibratorManagerService.h"
+#include "com_android_server_vibrator_VibratorManagerService.h"
namespace V1_0 = android::hardware::vibrator::V1_0;
namespace V1_1 = android::hardware::vibrator::V1_1;
@@ -73,11 +73,8 @@ static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));
static std::shared_ptr<vibrator::HalController> findVibrator(int32_t vibratorId) {
- // TODO(b/167946816): remove this once VibratorService is removed.
- if (vibratorId < 0) {
- return std::move(std::make_unique<vibrator::HalController>());
- }
- vibrator::ManagerHalWrapper* manager = android_server_VibratorManagerService_getManager();
+ vibrator::ManagerHalController* manager =
+ android_server_vibrator_VibratorManagerService_getManager();
if (manager == nullptr) {
return nullptr;
}
diff --git a/services/core/jni/com_android_server_vibrator_VibratorManagerService.cpp b/services/core/jni/com_android_server_vibrator_VibratorManagerService.cpp
new file mode 100644
index 000000000000..a47ab9d27c17
--- /dev/null
+++ b/services/core/jni/com_android_server_vibrator_VibratorManagerService.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "VibratorManagerService"
+
+#include <nativehelper/JNIHelp.h>
+#include "android_runtime/AndroidRuntime.h"
+#include "core_jni_helpers.h"
+#include "jni.h"
+
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+#include <vibratorservice/VibratorManagerHalController.h>
+
+#include "com_android_server_vibrator_VibratorManagerService.h"
+
+namespace android {
+
+static JavaVM* sJvm = nullptr;
+static jmethodID sMethodIdOnComplete;
+static std::mutex gManagerMutex;
+static vibrator::ManagerHalController* gManager GUARDED_BY(gManagerMutex) = nullptr;
+
+class NativeVibratorManagerService {
+public:
+ NativeVibratorManagerService(JNIEnv* env, jobject callbackListener)
+ : mHal(std::make_unique<vibrator::ManagerHalController>()),
+ mCallbackListener(env->NewGlobalRef(callbackListener)) {
+ LOG_ALWAYS_FATAL_IF(mHal == nullptr, "Unable to find reference to vibrator manager hal");
+ LOG_ALWAYS_FATAL_IF(mCallbackListener == nullptr,
+ "Unable to create global reference to vibration callback handler");
+ }
+
+ ~NativeVibratorManagerService() {
+ auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+ jniEnv->DeleteGlobalRef(mCallbackListener);
+ }
+
+ vibrator::ManagerHalController* hal() const { return mHal.get(); }
+
+ std::function<void()> createCallback(jlong vibrationId) {
+ return [vibrationId, this]() {
+ auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+ jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, vibrationId);
+ };
+ }
+
+private:
+ const std::unique_ptr<vibrator::ManagerHalController> mHal;
+ const jobject mCallbackListener;
+};
+
+vibrator::ManagerHalController* android_server_vibrator_VibratorManagerService_getManager() {
+ std::lock_guard<std::mutex> lock(gManagerMutex);
+ return gManager;
+}
+
+static void destroyNativeService(void* ptr) {
+ NativeVibratorManagerService* service = reinterpret_cast<NativeVibratorManagerService*>(ptr);
+ if (service) {
+ std::lock_guard<std::mutex> lock(gManagerMutex);
+ gManager = nullptr;
+ delete service;
+ }
+}
+
+static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject callbackListener) {
+ std::unique_ptr<NativeVibratorManagerService> service =
+ std::make_unique<NativeVibratorManagerService>(env, callbackListener);
+ {
+ std::lock_guard<std::mutex> lock(gManagerMutex);
+ gManager = service->hal();
+ }
+ return reinterpret_cast<jlong>(service.release());
+}
+
+static jlong nativeGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeService));
+}
+
+static jlong nativeGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
+ NativeVibratorManagerService* service =
+ reinterpret_cast<NativeVibratorManagerService*>(servicePtr);
+ if (service == nullptr) {
+ ALOGE("nativeGetCapabilities failed because native service was not initialized");
+ return 0;
+ }
+ auto result = service->hal()->getCapabilities();
+ return result.isOk() ? static_cast<jlong>(result.value()) : 0;
+}
+
+static jintArray nativeGetVibratorIds(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
+ NativeVibratorManagerService* service =
+ reinterpret_cast<NativeVibratorManagerService*>(servicePtr);
+ if (service == nullptr) {
+ ALOGE("nativeGetVibratorIds failed because native service was not initialized");
+ return nullptr;
+ }
+ auto result = service->hal()->getVibratorIds();
+ if (!result.isOk()) {
+ return nullptr;
+ }
+ std::vector<int32_t> vibratorIds = result.value();
+ jintArray ids = env->NewIntArray(vibratorIds.size());
+ env->SetIntArrayRegion(ids, 0, vibratorIds.size(), reinterpret_cast<jint*>(vibratorIds.data()));
+ return ids;
+}
+
+static jboolean nativePrepareSynced(JNIEnv* env, jclass /* clazz */, jlong servicePtr,
+ jintArray vibratorIds) {
+ NativeVibratorManagerService* service =
+ reinterpret_cast<NativeVibratorManagerService*>(servicePtr);
+ if (service == nullptr) {
+ ALOGE("nativePrepareSynced failed because native service was not initialized");
+ return JNI_FALSE;
+ }
+ jsize size = env->GetArrayLength(vibratorIds);
+ std::vector<int32_t> ids(size);
+ env->GetIntArrayRegion(vibratorIds, 0, size, reinterpret_cast<jint*>(ids.data()));
+ return service->hal()->prepareSynced(ids).isOk() ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean nativeTriggerSynced(JNIEnv* env, jclass /* clazz */, jlong servicePtr,
+ jlong vibrationId) {
+ NativeVibratorManagerService* service =
+ reinterpret_cast<NativeVibratorManagerService*>(servicePtr);
+ if (service == nullptr) {
+ ALOGE("nativeTriggerSynced failed because native service was not initialized");
+ return JNI_FALSE;
+ }
+ auto callback = service->createCallback(vibrationId);
+ return service->hal()->triggerSynced(callback).isOk() ? JNI_TRUE : JNI_FALSE;
+}
+
+static void nativeCancelSynced(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
+ NativeVibratorManagerService* service =
+ reinterpret_cast<NativeVibratorManagerService*>(servicePtr);
+ if (service == nullptr) {
+ ALOGE("nativeCancelSynced failed because native service was not initialized");
+ return;
+ }
+ service->hal()->cancelSynced();
+}
+
+inline static constexpr auto sNativeInitMethodSignature =
+ "(Lcom/android/server/vibrator/VibratorManagerService$OnSyncedVibrationCompleteListener;)J";
+
+static const JNINativeMethod method_table[] = {
+ {"nativeInit", sNativeInitMethodSignature, (void*)nativeInit},
+ {"nativeGetFinalizer", "()J", (void*)nativeGetFinalizer},
+ {"nativeGetCapabilities", "(J)J", (void*)nativeGetCapabilities},
+ {"nativeGetVibratorIds", "(J)[I", (void*)nativeGetVibratorIds},
+ {"nativePrepareSynced", "(J[I)Z", (void*)nativePrepareSynced},
+ {"nativeTriggerSynced", "(JJ)Z", (void*)nativeTriggerSynced},
+ {"nativeCancelSynced", "(J)V", (void*)nativeCancelSynced},
+};
+
+int register_android_server_vibrator_VibratorManagerService(JavaVM* jvm, JNIEnv* env) {
+ sJvm = jvm;
+ auto listenerClassName =
+ "com/android/server/vibrator/VibratorManagerService$OnSyncedVibrationCompleteListener";
+ jclass listenerClass = FindClassOrDie(env, listenerClassName);
+ sMethodIdOnComplete = GetMethodIDOrDie(env, listenerClass, "onComplete", "(J)V");
+
+ return jniRegisterNativeMethods(env, "com/android/server/vibrator/VibratorManagerService",
+ method_table, NELEM(method_table));
+}
+
+}; // namespace android
diff --git a/services/core/jni/com_android_server_VibratorManagerService.h b/services/core/jni/com_android_server_vibrator_VibratorManagerService.h
index 3f2a322b19ee..9924e24f14d0 100644
--- a/services/core/jni/com_android_server_VibratorManagerService.h
+++ b/services/core/jni/com_android_server_vibrator_VibratorManagerService.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 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.
@@ -17,11 +17,11 @@
#ifndef _ANDROID_SERVER_VIBRATOR_MANAGER_SERVICE_H
#define _ANDROID_SERVER_VIBRATOR_MANAGER_SERVICE_H
-#include <vibratorservice/VibratorManagerHalWrapper.h>
+#include <vibratorservice/VibratorManagerHalController.h>
namespace android {
-extern vibrator::ManagerHalWrapper* android_server_VibratorManagerService_getManager();
+extern vibrator::ManagerHalController* android_server_vibrator_VibratorManagerService_getManager();
} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 78971dc9c86f..085a285c4376 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -39,8 +39,9 @@ int register_android_server_UsbMidiDevice(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_vr_VrManagerService(JNIEnv* env);
int register_android_server_vibrator_VibratorController(JavaVM* vm, JNIEnv* env);
-int register_android_server_VibratorManagerService(JNIEnv* env);
+int register_android_server_vibrator_VibratorManagerService(JavaVM* vm, JNIEnv* env);
int register_android_server_location_GnssLocationProvider(JNIEnv* env);
+int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv*);
int register_android_server_tv_TvUinputBridge(JNIEnv* env);
int register_android_server_tv_TvInputHal(JNIEnv* env);
@@ -54,10 +55,8 @@ 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);
-int register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(
- JNIEnv* env);
+int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env);
+int register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(JNIEnv* env);
int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env);
int register_android_server_AdbDebuggingManager(JNIEnv* env);
int register_android_server_FaceService(JNIEnv* env);
@@ -91,9 +90,10 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_UsbHostManager(env);
register_android_server_vr_VrManagerService(env);
register_android_server_vibrator_VibratorController(vm, env);
- register_android_server_VibratorManagerService(env);
+ register_android_server_vibrator_VibratorManagerService(vm, env);
register_android_server_SystemServer(env);
register_android_server_location_GnssLocationProvider(env);
+ register_android_server_connectivity_Vpn(env);
register_android_server_devicepolicy_CryptoTestHelper(env);
register_android_server_ConsumerIrService(env);
register_android_server_BatteryStatsService(env);
@@ -110,10 +110,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
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);
- register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(
- env);
+ register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env);
+ register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(env);
register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env);
register_android_server_AdbDebuggingManager(env);
register_android_server_FaceService(env);
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index bdccf45627f4..c285ef519e44 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
xsd_config {
name: "default-permissions",
srcs: ["default-permissions.xsd"],
diff --git a/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd b/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd
index e27e1b8ca89d..1406dbb12e02 100644
--- a/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd
+++ b/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd
@@ -27,6 +27,13 @@
<xs:attribute type="xs:boolean" name="enabled" use="required" />
</xs:complexType>
+ <xs:complexType name="raw-override-value">
+ <xs:attribute type="xs:string" name="packageName" use="required" />
+ <xs:attribute type="xs:long" name="minVersionCode" />
+ <xs:attribute type="xs:long" name="maxVersionCode" />
+ <xs:attribute type="xs:boolean" name="enabled" use="required" />
+ </xs:complexType>
+
<xs:complexType name="change-overrides">
<xs:attribute type="xs:long" name="changeId" use="required"/>
<xs:element name="validated">
@@ -43,6 +50,13 @@
</xs:sequence>
</xs:complexType>
</xs:element>
+ <xs:element name="raw">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="raw-override-value" type="raw-override-value" maxOccurs="unbounded" minOccurs="0" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
</xs:complexType>
<xs:element name="overrides">
diff --git a/services/core/xsd/platform-compat/overrides/schema/current.txt b/services/core/xsd/platform-compat/overrides/schema/current.txt
index 08b82072747b..a5ccffcfbb2b 100644
--- a/services/core/xsd/platform-compat/overrides/schema/current.txt
+++ b/services/core/xsd/platform-compat/overrides/schema/current.txt
@@ -5,9 +5,11 @@ package com.android.server.compat.overrides {
ctor public ChangeOverrides();
method public long getChangeId();
method public com.android.server.compat.overrides.ChangeOverrides.Deferred getDeferred();
+ method public com.android.server.compat.overrides.ChangeOverrides.Raw getRaw();
method public com.android.server.compat.overrides.ChangeOverrides.Validated getValidated();
method public void setChangeId(long);
method public void setDeferred(com.android.server.compat.overrides.ChangeOverrides.Deferred);
+ method public void setRaw(com.android.server.compat.overrides.ChangeOverrides.Raw);
method public void setValidated(com.android.server.compat.overrides.ChangeOverrides.Validated);
}
@@ -16,6 +18,11 @@ package com.android.server.compat.overrides {
method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
}
+ public static class ChangeOverrides.Raw {
+ ctor public ChangeOverrides.Raw();
+ method public java.util.List<com.android.server.compat.overrides.RawOverrideValue> getRawOverrideValue();
+ }
+
public static class ChangeOverrides.Validated {
ctor public ChangeOverrides.Validated();
method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
@@ -34,6 +41,18 @@ package com.android.server.compat.overrides {
method public java.util.List<com.android.server.compat.overrides.ChangeOverrides> getChangeOverrides();
}
+ public class RawOverrideValue {
+ ctor public RawOverrideValue();
+ method public boolean getEnabled();
+ method public long getMaxVersionCode();
+ method public long getMinVersionCode();
+ method public String getPackageName();
+ method public void setEnabled(boolean);
+ method public void setMaxVersionCode(long);
+ method public void setMinVersionCode(long);
+ method public void setPackageName(String);
+ }
+
public class XmlParser {
ctor public XmlParser();
method public static com.android.server.compat.overrides.Overrides read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/xsd/vts/Android.bp b/services/core/xsd/vts/Android.bp
index a942108667a5..4d3c79eb28ae 100644
--- a/services/core/xsd/vts/Android.bp
+++ b/services/core/xsd/vts/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_test {
name: "vts_defaultPermissions_validate_test",
srcs: [
diff --git a/services/coverage/Android.bp b/services/coverage/Android.bp
index b3cee375f95c..82feafecdc56 100644
--- a/services/coverage/Android.bp
+++ b/services/coverage/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.coverage-sources",
srcs: ["java/**/*.java"],
diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp
index 5de48aefe28e..3a9853d66d10 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.devicepolicy-sources",
srcs: ["java/**/*.java"],
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 59b7367102c8..48ae8d672fb1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -138,9 +138,12 @@ class ActiveAdmin {
private static final String TAG_ENROLLMENT_SPECIFIC_ID = "enrollment-specific-id";
private static final String TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS =
"admin-can-grant-sensors-permissions";
+ private static final String TAG_NETWORK_SLICING_ENABLED = "network-slicing-enabled";
+ private static final String TAG_USB_DATA_SIGNALING = "usb-data-signaling";
private static final String ATTR_VALUE = "value";
private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
+ private static final boolean NETWORK_SLICING_ENABLED_DEFAULT = true;
DeviceAdminInfo info;
@@ -280,6 +283,10 @@ class ActiveAdmin {
public String mOrganizationId;
public String mEnrollmentSpecificId;
public boolean mAdminCanGrantSensorsPermissions;
+ public boolean mNetworkSlicingEnabled = NETWORK_SLICING_ENABLED_DEFAULT;
+
+ private static final boolean USB_DATA_SIGNALING_ENABLED_DEFAULT = true;
+ boolean mUsbDataSignalingEnabled = USB_DATA_SIGNALING_ENABLED_DEFAULT;
ActiveAdmin(DeviceAdminInfo info, boolean isParent) {
this.info = info;
@@ -548,6 +555,12 @@ class ActiveAdmin {
}
writeAttributeValueToXml(out, TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS,
mAdminCanGrantSensorsPermissions);
+ if (mNetworkSlicingEnabled != NETWORK_SLICING_ENABLED_DEFAULT) {
+ writeAttributeValueToXml(out, TAG_NETWORK_SLICING_ENABLED, mNetworkSlicingEnabled);
+ }
+ if (mUsbDataSignalingEnabled != USB_DATA_SIGNALING_ENABLED_DEFAULT) {
+ writeAttributeValueToXml(out, TAG_USB_DATA_SIGNALING, mUsbDataSignalingEnabled);
+ }
}
void writeTextToXml(TypedXmlSerializer out, String tag, String text) throws IOException {
@@ -777,6 +790,9 @@ class ActiveAdmin {
mAlwaysOnVpnPackage = parser.getAttributeValue(null, ATTR_VALUE);
} else if (TAG_ALWAYS_ON_VPN_LOCKDOWN.equals(tag)) {
mAlwaysOnVpnLockdown = parser.getAttributeBoolean(null, ATTR_VALUE, false);
+ } else if (TAG_NETWORK_SLICING_ENABLED.equals(tag)) {
+ mNetworkSlicingEnabled = parser.getAttributeBoolean(
+ null, ATTR_VALUE, NETWORK_SLICING_ENABLED_DEFAULT);
} else if (TAG_COMMON_CRITERIA_MODE.equals(tag)) {
mCommonCriteriaMode = parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_PASSWORD_COMPLEXITY.equals(tag)) {
@@ -800,6 +816,9 @@ class ActiveAdmin {
} else if (TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS.equals(tag)) {
mAdminCanGrantSensorsPermissions = parser.getAttributeBoolean(null, ATTR_VALUE,
false);
+ } else if (TAG_USB_DATA_SIGNALING.equals(tag)) {
+ mUsbDataSignalingEnabled = parser.getAttributeBoolean(null, ATTR_VALUE,
+ USB_DATA_SIGNALING_ENABLED_DEFAULT);
} else {
Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -1136,6 +1155,9 @@ class ActiveAdmin {
pw.print("mAlwaysOnVpnLockdown=");
pw.println(mAlwaysOnVpnLockdown);
+ pw.print("mNetworkSlicingEnabled=");
+ pw.println(mNetworkSlicingEnabled);
+
pw.print("mCommonCriteriaMode=");
pw.println(mCommonCriteriaMode);
@@ -1154,5 +1176,8 @@ class ActiveAdmin {
pw.print("mAdminCanGrantSensorsPermissions");
pw.println(mAdminCanGrantSensorsPermissions);
+
+ pw.print("mUsbDataSignaling=");
+ pw.println(mUsbDataSignalingEnabled);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 404b0cf8c7b8..30a1e2a10475 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -62,6 +62,7 @@ import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
import static android.app.admin.DevicePolicyManager.NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
+import static android.app.admin.DevicePolicyManager.OPERATION_SAFETY_REASON_NONE;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
@@ -92,7 +93,6 @@ import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_REMOVE_N
import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED;
import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED;
import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_STARTING_PROFILE_FAILED;
-import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
@@ -157,9 +157,9 @@ import android.app.admin.DevicePolicyCache;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
+import android.app.admin.DevicePolicyManager.OperationSafetyReason;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason;
-import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicySafetyChecker;
import android.app.admin.DeviceStateCache;
@@ -214,6 +214,7 @@ import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
+import android.hardware.usb.UsbManager;
import android.location.LocationManager;
import android.media.AudioManager;
import android.media.IAudioService;
@@ -221,6 +222,7 @@ import android.net.ConnectivityManager;
import android.net.IIpConnectivityMetrics;
import android.net.ProxyInfo;
import android.net.Uri;
+import android.net.VpnManager;
import android.net.metrics.IpConnectivityLog;
import android.net.wifi.WifiManager;
import android.os.Binder;
@@ -246,6 +248,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
+import android.permission.AdminPermissionControlParams;
import android.permission.IPermissionManager;
import android.permission.PermissionControllerManager;
import android.provider.CalendarContract;
@@ -421,10 +424,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
DELEGATION_CERT_SELECTION,
};
- // Subset of delegations that can only be delegated by Device Owner.
- private static final List<String> DEVICE_OWNER_DELEGATIONS = Arrays.asList(new String[] {
- DELEGATION_NETWORK_LOGGING,
- });
+ // Subset of delegations that can only be delegated by Device Owner or Profile Owner of a
+ // managed profile.
+ private static final List<String> DEVICE_OWNER_OR_MANAGED_PROFILE_OWNER_DELEGATIONS =
+ Arrays.asList(new String[]{
+ DELEGATION_NETWORK_LOGGING,
+ });
// Subset of delegations that only one single package within a given user can hold
private static final List<String> EXCLUSIVE_DELEGATIONS = Arrays.asList(new String[] {
@@ -530,8 +535,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
/**
* Strings logged with {@link
- * com.android.internal.logging.nano.MetricsProto.MetricsEvent#PROVISIONING_ENTRY_POINT_ADB}
- * and {@link DevicePolicyEnums#PROVISIONING_ENTRY_POINT_ADB}.
+ * com.android.internal.logging.nano.MetricsProto.MetricsEvent#PROVISIONING_ENTRY_POINT_ADB},
+ * {@link DevicePolicyEnums#PROVISIONING_ENTRY_POINT_ADB},
+ * {@link DevicePolicyEnums#SET_NETWORK_LOGGING_ENABLED} and
+ * {@link DevicePolicyEnums#RETRIEVE_NETWORK_LOGS}.
*/
private static final String LOG_TAG_PROFILE_OWNER = "profile-owner";
private static final String LOG_TAG_DEVICE_OWNER = "device-owner";
@@ -781,8 +788,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
* however it's too early in the boot process to register with IIpConnectivityMetrics
* to listen for events.
*/
- if (Intent.ACTION_USER_STARTED.equals(action)
- && userHandle == mOwners.getDeviceOwnerUserId()) {
+ if (Intent.ACTION_USER_STARTED.equals(action) && userHandle == UserHandle.USER_SYSTEM) {
synchronized (getLockObject()) {
if (isNetworkLoggingEnabledInternalLocked()) {
setNetworkLoggingActiveInternal(true);
@@ -1101,7 +1107,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
*/
private void checkCanExecuteOrThrowUnsafe(@DevicePolicyOperation int operation) {
int reason = getUnsafeOperationReason(operation);
- if (reason == UNSAFE_OPERATION_REASON_NONE) return;
+ if (reason == OPERATION_SAFETY_REASON_NONE) return;
if (mSafetyChecker == null) {
// Happens on CTS after it's set just once (by OneTimeSafetyChecker)
@@ -1114,23 +1120,33 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
/**
* Returns whether it's safe to execute the given {@code operation}, and why.
*/
- @UnsafeOperationReason
+ @OperationSafetyReason
int getUnsafeOperationReason(@DevicePolicyOperation int operation) {
- return mSafetyChecker == null ? UNSAFE_OPERATION_REASON_NONE
+ return mSafetyChecker == null ? OPERATION_SAFETY_REASON_NONE
: mSafetyChecker.getUnsafeOperationReason(operation);
}
@Override
public void setNextOperationSafety(@DevicePolicyOperation int operation,
- @UnsafeOperationReason int reason) {
+ @OperationSafetyReason int reason) {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %s)",
DevicePolicyManager.operationToString(operation),
- DevicePolicyManager.unsafeOperationReasonToString(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);
+ }
+ return mSafetyChecker == null ? true : mSafetyChecker.isSafeOperation(reason);
+ }
+
// Used by DevicePolicyManagerServiceShellCommand
List<OwnerDto> listAllOwners() {
Preconditions.checkCallAuthorization(
@@ -1257,6 +1273,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return mContext.getSystemService(ConnectivityManager.class);
}
+ VpnManager getVpnManager() {
+ return mContext.getSystemService(VpnManager.class);
+ }
+
LocationManager getLocationManager() {
return mContext.getSystemService(LocationManager.class);
}
@@ -1352,6 +1372,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return mContext.getSystemService(WifiManager.class);
}
+ UsbManager getUsbManager() {
+ return mContext.getSystemService(UsbManager.class);
+ }
+
@SuppressWarnings("AndroidFrameworkBinderIdentity")
long binderClearCallingIdentity() {
return Binder.clearCallingIdentity();
@@ -2923,6 +2947,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
revertTransferOwnershipIfNecessaryLocked();
}
+ updateUsbDataSignal();
}
private void revertTransferOwnershipIfNecessaryLocked() {
@@ -5874,10 +5899,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
// Retrieve the user ID of the calling process.
final int userId = caller.getUserId();
- final boolean hasDoDelegation = !Collections.disjoint(scopes, DEVICE_OWNER_DELEGATIONS);
// Ensure calling process is device/profile owner.
- if (hasDoDelegation) {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ if (!Collections.disjoint(scopes, DEVICE_OWNER_OR_MANAGED_PROFILE_OWNER_DELEGATIONS)) {
+ Preconditions.checkCallAuthorization(isDeviceOwner(caller)
+ || (isProfileOwner(caller) && isManagedProfile(caller.getUserId())));
} else {
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
}
@@ -6287,7 +6312,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
// If some package is uninstalled after the check above, it will be ignored by CM.
- if (!mInjector.getConnectivityManager().setAlwaysOnVpnPackageForUser(
+ if (!mInjector.getVpnManager().setAlwaysOnVpnPackageForUser(
userId, vpnPackage, lockdown, lockdownAllowlist)) {
throw new UnsupportedOperationException();
}
@@ -6319,8 +6344,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
return mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getConnectivityManager().getAlwaysOnVpnPackageForUser(
- caller.getUserId()));
+ () -> mInjector.getVpnManager().getAlwaysOnVpnPackageForUser(caller.getUserId()));
}
@Override
@@ -6346,7 +6370,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
return mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getConnectivityManager().isVpnLockdownEnabled(caller.getUserId()));
+ () -> mInjector.getVpnManager().isVpnLockdownEnabled(caller.getUserId()));
}
@Override
@@ -6367,8 +6391,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
return mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getConnectivityManager().getVpnLockdownWhitelist(
- caller.getUserId()));
+ () -> mInjector.getVpnManager().getVpnLockdownAllowlist(caller.getUserId()));
}
private void forceWipeDeviceNoLock(boolean wipeExtRequested, String reason, boolean wipeEuicc,
@@ -6438,7 +6461,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private void forceWipeUser(int userId, String wipeReasonForUser, boolean wipeSilently) {
boolean success = false;
try {
- if (getCurrentForegroundUser() == userId) {
+ if (getCurrentForegroundUserId() == userId) {
mInjector.getIActivityManager().switchUser(UserHandle.USER_SYSTEM);
}
@@ -7522,19 +7545,37 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
sendActiveAdminCommand(action, extras, deviceOwnerUserId, receiverComponent);
}
- private void sendProfileOwnerCommand(String action, Bundle extras, int userHandle) {
- sendActiveAdminCommand(action, extras, userHandle,
- mOwners.getProfileOwnerComponent(userHandle));
+ void sendDeviceOwnerOrProfileOwnerCommand(String action, Bundle extras, int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ }
+ ComponentName receiverComponent = null;
+ if (action.equals(DeviceAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE)) {
+ receiverComponent = resolveDelegateReceiver(DELEGATION_NETWORK_LOGGING, action, userId);
+ }
+ if (receiverComponent == null) {
+ receiverComponent = getOwnerComponent(userId);
+ }
+ sendActiveAdminCommand(action, extras, userId, receiverComponent);
+ }
+
+ private void sendProfileOwnerCommand(String action, Bundle extras, @UserIdInt int userId) {
+ sendActiveAdminCommand(action, extras, userId,
+ mOwners.getProfileOwnerComponent(userId));
}
private void sendActiveAdminCommand(String action, Bundle extras,
- int userHandle, ComponentName receiverComponent) {
+ @UserIdInt int userId, ComponentName receiverComponent) {
+ if (VERBOSE_LOG) {
+ Slog.v(LOG_TAG, "sending intent " + action + " to "
+ + receiverComponent.flattenToShortString() + " on user " + userId);
+ }
final Intent intent = new Intent(action);
intent.setComponent(receiverComponent);
if (extras != null) {
intent.putExtras(extras);
}
- mContext.sendBroadcastAsUser(intent, UserHandle.of(userHandle));
+ mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
private void sendOwnerChangedBroadcast(String broadcast, int userId) {
@@ -7861,6 +7902,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
updateDeviceOwnerLocked();
setDeviceOwnershipSystemPropertyLocked();
+ //TODO(b/180371154): when provisionFullyManagedDevice is used in tests, remove this
+ // hard-coded default value setting.
+ if (isAdb(caller)) {
+ activeAdmin.mAdminCanGrantSensorsPermissions = true;
+ mPolicyCache.setAdminCanGrantSensorsPermissions(userId, true);
+ saveSettingsLocked(userId);
+ }
+
mInjector.binderWithCleanCallingIdentity(() -> {
// Restrict adding a managed profile when a device owner is set on the device.
// That is to prevent the co-existence of a managed profile and a device owner
@@ -7880,7 +7929,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Slog.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId);
if (mInjector.userManagerIsHeadlessSystemUserMode()) {
- int currentForegroundUser = getCurrentForegroundUser();
+ int currentForegroundUser = getCurrentForegroundUserId();
Slog.i(LOG_TAG, "setDeviceOwner(): setting " + admin
+ " as profile owner on user " + currentForegroundUser);
// Sets profile owner on current foreground user since
@@ -8342,6 +8391,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
deleteTransferOwnershipBundleLocked(userId);
toggleBackupServiceActive(userId, true);
applyManagedProfileRestrictionIfDeviceOwnerLocked();
+ setNetworkLoggingActiveInternal(false);
}
@Override
@@ -9010,7 +9060,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return UserHandle.isSameApp(caller.getUid(), Process.SHELL_UID);
}
- private @UserIdInt int getCurrentForegroundUser() {
+ private @UserIdInt int getCurrentForegroundUserId() {
try {
return mInjector.getIActivityManager().getCurrentUser().id;
} catch (RemoteException e) {
@@ -9019,6 +9069,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return UserHandle.USER_NULL;
}
+ @Override
+ public List<UserHandle> listForegroundAffiliatedUsers() {
+ checkIsDeviceOwner(getCallerIdentity());
+
+ int userId = mInjector.binderWithCleanCallingIdentity(() -> getCurrentForegroundUserId());
+
+ boolean isAffiliated;
+ synchronized (getLockObject()) {
+ isAffiliated = isUserAffiliatedWithDeviceLocked(userId);
+ }
+
+ if (!isAffiliated) return Collections.emptyList();
+
+ List<UserHandle> users = new ArrayList<>(1);
+ users.add(UserHandle.of(userId));
+
+ return users;
+ }
+
protected int getProfileParentId(int userHandle) {
return mInjector.binderWithCleanCallingIdentity(() -> {
UserInfo parentUser = mUserManager.getProfileParent(userHandle);
@@ -9111,8 +9180,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
pw.printf("mIsWatch=%b\n", mIsWatch);
pw.printf("mIsAutomotive=%b\n", mIsAutomotive);
pw.printf("mHasTelephonyFeature=%b\n", mHasTelephonyFeature);
- String safetyChecker = mSafetyChecker == null ? "N/A" : mSafetyChecker.getClass().getName();
- pw.printf("mSafetyChecker=%b\n", safetyChecker);
+ pw.printf("mSafetyChecker=%s\n", mSafetyChecker);
pw.decreaseIndent();
}
@@ -10031,7 +10099,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int userId = user.id;
- // TODO(b/177547285): add CTS test
if (mInjector.userManagerIsHeadlessSystemUserMode()) {
ComponentName admin = mOwners.getDeviceOwnerComponent();
Slog.i(LOG_TAG, "Automatically setting profile owner (" + admin + ") on new user "
@@ -11103,6 +11170,50 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
+ public void setNetworkSlicingEnabled(boolean enabled) {
+ if (!mHasFeature) {
+ return;
+ }
+
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(isProfileOwner(caller),
+ "Caller is not profile owner; only profile owner may control the network slicing");
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(
+ caller.getUserId());
+ if (requiredAdmin != null && requiredAdmin.mNetworkSlicingEnabled != enabled) {
+ requiredAdmin.mNetworkSlicingEnabled = enabled;
+ saveSettingsLocked(caller.getUserId());
+ // TODO(b/178655595) notify CS the change.
+ // TODO(b/178655595) DevicePolicyEventLogger metrics
+ }
+ }
+ }
+
+ @Override
+ public boolean isNetworkSlicingEnabled(int userHandle) {
+ if (!mHasFeature) {
+ return false;
+ }
+
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
+ permission.READ_NETWORK_DEVICE_CONFIG) || isProfileOwner(caller),
+ "Caller is not profile owner and not granted"
+ + " READ_NETWORK_DEVICE_CONFIG permission");
+ synchronized (getLockObject()) {
+ final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(userHandle);
+ if (requiredAdmin != null) {
+ return requiredAdmin.mNetworkSlicingEnabled;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ @Override
public void setLockTaskPackages(ComponentName who, String[] packages)
throws SecurityException {
Objects.requireNonNull(who, "ComponentName is null");
@@ -12224,6 +12335,32 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
packageName, findInteractAcrossProfilesResetMode(packageName), userId);
}
+ @Override
+ public void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker, int reason,
+ boolean isSafe) {
+ // 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));
+ }
+
+ Preconditions.checkArgument(mSafetyChecker == checker,
+ "invalid checker: should be %s, was %s", mSafetyChecker, checker);
+
+ Bundle extras = new Bundle();
+ extras.putInt(DeviceAdminReceiver.EXTRA_OPERATION_SAFETY_REASON, reason);
+ extras.putBoolean(DeviceAdminReceiver.EXTRA_OPERATION_SAFETY_STATE, isSafe);
+
+ // TODO(b/178494483): add CTS test
+ sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED,
+ extras);
+ for (int profileOwnerId : mOwners.getProfileOwnerKeys()) {
+ sendProfileOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED,
+ extras, profileOwnerId);
+ }
+ }
+
private @Mode int findInteractAcrossProfilesResetMode(String packageName) {
return getDefaultCrossProfilePackages().contains(packageName)
? AppOpsManager.MODE_ALLOWED
@@ -12586,9 +12723,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED
|| grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
|| grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) {
+ AdminPermissionControlParams permissionParams =
+ new AdminPermissionControlParams(packageName, permission, grantState,
+ canAdminGrantSensorsPermissionsForUser(caller.getUserId()));
mInjector.getPermissionControllerManager(caller.getUserHandle())
.setRuntimePermissionGrantStateByDeviceAdmin(caller.getPackageName(),
- packageName, permission, grantState, mContext.getMainExecutor(),
+ permissionParams, mContext.getMainExecutor(),
(permissionWasSet) -> {
if (isPostQAdmin && !permissionWasSet) {
callback.sendResult(null);
@@ -12777,8 +12917,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (mOwners.hasProfileOwner(deviceOwnerUserId)) {
return CODE_USER_HAS_PROFILE_OWNER;
}
+
+ boolean isHeadlessSystemUserMode = mInjector.userManagerIsHeadlessSystemUserMode();
// System user is always running in headless system user mode.
- if (!mInjector.userManagerIsHeadlessSystemUserMode()
+ if (!isHeadlessSystemUserMode
&& !mUserManager.isUserRunning(new UserHandle(deviceOwnerUserId))) {
return CODE_USER_NOT_RUNNING;
}
@@ -12786,7 +12928,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return CODE_HAS_PAIRED;
}
- if (mInjector.userManagerIsHeadlessSystemUserMode()) {
+ if (isHeadlessSystemUserMode) {
if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
Slog.e(LOG_TAG, "In headless system user mode, "
+ "device owner can only be set on headless system user.");
@@ -12798,13 +12940,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// If shell command runs after user setup completed check device status. Otherwise, OK.
if (mIsWatch || hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
// In non-headless system user mode, DO can be setup only if
- // there's no non-system user
- if (!mInjector.userManagerIsHeadlessSystemUserMode()
- && mUserManager.getUserCount() > 1) {
+ // there's no non-system user.
+ // In headless system user mode, DO can be setup only if there are
+ // two users: the headless system user and the foreground user.
+ // If there could be multiple foreground users, this constraint should be modified.
+
+ int maxNumberOfExistingUsers = isHeadlessSystemUserMode ? 2 : 1;
+ if (mUserManager.getUserCount() > maxNumberOfExistingUsers) {
return CODE_NONSYSTEM_USER_EXISTS;
}
- int currentForegroundUser = getCurrentForegroundUser();
+ int currentForegroundUser = getCurrentForegroundUserId();
if (callingUserId != currentForegroundUser
&& mInjector.userManagerIsHeadlessSystemUserMode()
&& currentForegroundUser == UserHandle.USER_SYSTEM) {
@@ -12900,6 +13046,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return CODE_OK;
}
+ private void checkIsDeviceOwner(CallerIdentity caller) {
+ Preconditions.checkCallAuthorization(isDeviceOwner(caller), caller.getUid()
+ + " is not device owner");
+ }
+
private ComponentName getOwnerComponent(String packageName, int userId) {
if (isDeviceOwnerPackage(packageName, userId)) {
return mOwners.getDeviceOwnerComponent();
@@ -14096,7 +14247,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
final CallerIdentity caller = getCallerIdentity(admin, packageName);
- Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ final boolean isManagedProfileOwner = isProfileOwner(caller)
+ && isManagedProfile(caller.getUserId());
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isDeviceOwner(caller) || isManagedProfileOwner))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)));
synchronized (getLockObject()) {
@@ -14104,11 +14258,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// already in the requested state
return;
}
- ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- deviceOwner.isNetworkLoggingEnabled = enabled;
+ final ActiveAdmin activeAdmin = getDeviceOrProfileOwnerAdminLocked(caller.getUserId());
+ activeAdmin.isNetworkLoggingEnabled = enabled;
if (!enabled) {
- deviceOwner.numNetworkLoggingNotifications = 0;
- deviceOwner.lastNetworkLoggingNotificationTimeMs = 0;
+ activeAdmin.numNetworkLoggingNotifications = 0;
+ activeAdmin.lastNetworkLoggingNotificationTimeMs = 0;
}
saveSettingsLocked(caller.getUserId());
setNetworkLoggingActiveInternal(enabled);
@@ -14118,6 +14272,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.setAdmin(caller.getPackageName())
.setBoolean(/* isDelegate */ admin == null)
.setInt(enabled ? 1 : 0)
+ .setStrings(isManagedProfileOwner
+ ? LOG_TAG_PROFILE_OWNER : LOG_TAG_DEVICE_OWNER)
.write();
}
}
@@ -14126,7 +14282,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
mInjector.binderWithCleanCallingIdentity(() -> {
if (active) {
- mNetworkLogger = new NetworkLogger(this, mInjector.getPackageManagerInternal());
+ if (mNetworkLogger == null) {
+ final int affectedUserId = getNetworkLoggingAffectedUser();
+ mNetworkLogger = new NetworkLogger(this,
+ mInjector.getPackageManagerInternal(),
+ affectedUserId == UserHandle.USER_SYSTEM
+ ? UserHandle.USER_ALL : affectedUserId);
+ }
if (!mNetworkLogger.startNetworkLogging()) {
mNetworkLogger = null;
Slog.wtf(LOG_TAG, "Network logging could not be started due to the logging"
@@ -14146,6 +14308,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ private @UserIdInt int getNetworkLoggingAffectedUser() {
+ synchronized (getLockObject()) {
+ if (mOwners.hasDeviceOwner()) {
+ return mOwners.getDeviceOwnerUserId();
+ } else {
+ return mInjector.binderWithCleanCallingIdentity(
+ () -> getManagedUserId(UserHandle.USER_SYSTEM));
+ }
+ }
+ }
+
+ private ActiveAdmin getNetworkLoggingControllingAdminLocked() {
+ int affectedUserId = getNetworkLoggingAffectedUser();
+ if (affectedUserId < 0) {
+ return null;
+ }
+ return getDeviceOrProfileOwnerAdminLocked(affectedUserId);
+ }
+
@Override
public long forceNetworkLogs() {
Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()),
@@ -14166,10 +14347,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@GuardedBy("getLockObject()")
private void maybePauseDeviceWideLoggingLocked() {
if (!areAllUsersAffiliatedWithDeviceLocked()) {
- Slog.i(LOG_TAG, "There are unaffiliated users, network logging will be "
- + "paused if enabled.");
- if (mNetworkLogger != null) {
- mNetworkLogger.pause();
+ if (mOwners.hasDeviceOwner()) {
+ Slog.i(LOG_TAG, "There are unaffiliated users, network logging will be "
+ + "paused if enabled.");
+ if (mNetworkLogger != null) {
+ mNetworkLogger.pause();
+ }
}
if (!isOrganizationOwnedDeviceWithManagedProfile()) {
Slog.i(LOG_TAG, "Not org-owned managed profile device, security logging will be "
@@ -14188,7 +14371,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (allUsersAffiliated || orgOwnedProfileDevice) {
mSecurityLogMonitor.resume();
}
- if (allUsersAffiliated) {
+ // If there is no device owner, then per-user network logging may be enabled for the
+ // managed profile. In which case, all users do not need to be affiliated.
+ if (allUsersAffiliated || !mOwners.hasDeviceOwner()) {
if (mNetworkLogger != null) {
mNetworkLogger.resume();
}
@@ -14215,7 +14400,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
final CallerIdentity caller = getCallerIdentity(admin, packageName);
- Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isDeviceOwner(caller)
+ || (isProfileOwner(caller) && isManagedProfile(caller.getUserId()))))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING))
|| hasCallingOrSelfPermission(permission.MANAGE_USERS));
@@ -14225,8 +14412,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private boolean isNetworkLoggingEnabledInternalLocked() {
- ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- return (deviceOwner != null) && deviceOwner.isNetworkLoggingEnabled;
+ ActiveAdmin activeAdmin = getNetworkLoggingControllingAdminLocked();
+ return (activeAdmin != null) && activeAdmin.isNetworkLoggingEnabled;
}
/*
@@ -14243,9 +14430,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
final CallerIdentity caller = getCallerIdentity(admin, packageName);
- Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ final boolean isManagedProfileOwner = isProfileOwner(caller)
+ && isManagedProfile(caller.getUserId());
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isDeviceOwner(caller) || isManagedProfileOwner))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)));
- checkAllUsersAreAffiliatedWithDevice();
+ if (mOwners.hasDeviceOwner()) {
+ checkAllUsersAreAffiliatedWithDevice();
+ }
synchronized (getLockObject()) {
if (mNetworkLogger == null || !isNetworkLoggingEnabledInternalLocked()) {
@@ -14255,37 +14447,40 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.createEvent(DevicePolicyEnums.RETRIEVE_NETWORK_LOGS)
.setAdmin(caller.getPackageName())
.setBoolean(/* isDelegate */ admin == null)
+ .setStrings(isManagedProfileOwner
+ ? LOG_TAG_PROFILE_OWNER : LOG_TAG_DEVICE_OWNER)
.write();
final long currentTime = System.currentTimeMillis();
- DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
+ DevicePolicyData policyData = getUserData(caller.getUserId());
if (currentTime > policyData.mLastNetworkLogsRetrievalTime) {
policyData.mLastNetworkLogsRetrievalTime = currentTime;
- saveSettingsLocked(UserHandle.USER_SYSTEM);
+ saveSettingsLocked(caller.getUserId());
}
return mNetworkLogger.retrieveLogs(batchToken);
}
}
private void sendNetworkLoggingNotificationLocked() {
- final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- if (deviceOwner == null || !deviceOwner.isNetworkLoggingEnabled) {
+ ensureLocked();
+ final ActiveAdmin activeAdmin = getNetworkLoggingControllingAdminLocked();
+ if (activeAdmin == null || !activeAdmin.isNetworkLoggingEnabled) {
return;
}
- if (deviceOwner.numNetworkLoggingNotifications >=
- ActiveAdmin.DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN) {
+ if (activeAdmin.numNetworkLoggingNotifications
+ >= ActiveAdmin.DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN) {
return;
}
final long now = System.currentTimeMillis();
- if (now - deviceOwner.lastNetworkLoggingNotificationTimeMs < MS_PER_DAY) {
+ if (now - activeAdmin.lastNetworkLoggingNotificationTimeMs < MS_PER_DAY) {
return;
}
- deviceOwner.numNetworkLoggingNotifications++;
- if (deviceOwner.numNetworkLoggingNotifications
+ activeAdmin.numNetworkLoggingNotifications++;
+ if (activeAdmin.numNetworkLoggingNotifications
>= ActiveAdmin.DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN) {
- deviceOwner.lastNetworkLoggingNotificationTimeMs = 0;
+ activeAdmin.lastNetworkLoggingNotificationTimeMs = 0;
} else {
- deviceOwner.lastNetworkLoggingNotificationTimeMs = now;
+ activeAdmin.lastNetworkLoggingNotificationTimeMs = now;
}
final PackageManagerInternal pm = mInjector.getPackageManagerInternal();
final Intent intent = new Intent(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG);
@@ -14305,7 +14500,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(mOwners.getDeviceOwnerUserId());
+ saveSettingsLocked(activeAdmin.getUserHandle().getIdentifier());
}
/**
@@ -14369,8 +14564,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public long getLastNetworkLogRetrievalTime() {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller));
- return getUserData(UserHandle.USER_SYSTEM).mLastNetworkLogsRetrievalTime;
+
+ Preconditions.checkCallAuthorization(isDeviceOwner(caller)
+ || (isProfileOwner(caller) && isManagedProfile(caller.getUserId()))
+ || canManageUsers(caller));
+ final int affectedUserId = getNetworkLoggingAffectedUser();
+ return affectedUserId >= 0 ? getUserData(affectedUserId).mLastNetworkLogsRetrievalTime : -1;
}
@Override
@@ -15348,7 +15547,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private boolean isLockTaskFeatureEnabled(int lockTaskFeature) throws RemoteException {
//TODO(b/175285301): Explicitly get the user's identity to check.
int lockTaskFeatures =
- getUserData(getCurrentForegroundUser()).mLockTaskFeatures;
+ getUserData(getCurrentForegroundUserId()).mLockTaskFeatures;
return (lockTaskFeatures & lockTaskFeature) == lockTaskFeature;
}
@@ -15985,6 +16184,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public UserHandle createAndProvisionManagedProfile(
@NonNull ManagedProfileProvisioningParams provisioningParams,
@NonNull String callerPackage) {
+ Objects.requireNonNull(provisioningParams, "provisioningParams is null");
+ Objects.requireNonNull(callerPackage, "callerPackage is null");
+
final ComponentName admin = provisioningParams.getProfileAdminComponentName();
Objects.requireNonNull(admin, "admin is null");
@@ -15992,6 +16194,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ provisioningParams.logParams(callerPackage);
+
UserInfo userInfo = null;
final long identity = Binder.clearCallingIdentity();
try {
@@ -16291,9 +16495,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void provisionFullyManagedDevice(
- FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
- ComponentName deviceAdmin = provisioningParams.getDeviceAdminComponentName();
+ @NonNull FullyManagedDeviceProvisioningParams provisioningParams,
+ @NonNull String callerPackage) {
+ Objects.requireNonNull(provisioningParams, "provisioningParams is null.");
+ Objects.requireNonNull(callerPackage, "callerPackage is null.");
+ ComponentName deviceAdmin = provisioningParams.getDeviceAdminComponentName();
Objects.requireNonNull(deviceAdmin, "admin is null.");
Objects.requireNonNull(provisioningParams.getOwnerName(), "owner name is null.");
@@ -16301,6 +16508,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ provisioningParams.logParams(callerPackage);
+
final long identity = Binder.clearCallingIdentity();
try {
// TODO(b/178187130): This check fails silent provisioning, uncomment once silent
@@ -16318,8 +16527,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
setLocale(provisioningParams.getLocale());
+ final int deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
+ ? UserHandle.USER_SYSTEM : caller.getUserId();
if (!removeNonRequiredAppsForManagedDevice(
- caller.getUserId(),
+ deviceOwnerUserId,
provisioningParams.isLeaveAllSystemAppsEnabled(),
deviceAdmin)) {
throw new ServiceSpecificException(
@@ -16327,15 +16538,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
"PackageManager failed to remove non required apps.");
}
+
if (!setActiveAdminAndDeviceOwner(
- caller.getUserId(), deviceAdmin, provisioningParams.getOwnerName())) {
+ deviceOwnerUserId, deviceAdmin, provisioningParams.getOwnerName())) {
throw new ServiceSpecificException(
PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED,
"Failed to set device owner.");
}
disallowAddUser();
- setAdminCanGrantSensorsPermissionForUserUnchecked(caller.getUserId(),
+ setAdminCanGrantSensorsPermissionForUserUnchecked(deviceOwnerUserId,
provisioningParams.canDeviceOwnerGrantSensorsPermissions());
} catch (Exception e) {
DevicePolicyEventLogger
@@ -16378,30 +16590,42 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private boolean removeNonRequiredAppsForManagedDevice(
- int userId, boolean leaveAllSystemAppsEnabled, ComponentName admin) {
+ @UserIdInt int userId, boolean leaveAllSystemAppsEnabled, ComponentName admin) {
Set<String> packagesToDelete = leaveAllSystemAppsEnabled
? Collections.emptySet()
: mOverlayPackagesProvider.getNonRequiredApps(
admin, userId, ACTION_PROVISION_MANAGED_DEVICE);
+
+ removeNonInstalledPackages(packagesToDelete, userId);
if (packagesToDelete.isEmpty()) {
+ Slog.i(LOG_TAG, "No packages to delete on user " + userId);
return true;
}
+
NonRequiredPackageDeleteObserver packageDeleteObserver =
new NonRequiredPackageDeleteObserver(packagesToDelete.size());
for (String packageName : packagesToDelete) {
- if (isPackageInstalledForUser(packageName, userId)) {
- Slog.i(LOG_TAG, "Deleting package [" + packageName + "] as user " + userId);
- mContext.getPackageManager().deletePackageAsUser(
- packageName,
- packageDeleteObserver,
- PackageManager.DELETE_SYSTEM_APP,
- userId);
- }
+ Slog.i(LOG_TAG, "Deleting package [" + packageName + "] as user " + userId);
+ mContext.getPackageManager().deletePackageAsUser(
+ packageName,
+ packageDeleteObserver,
+ PackageManager.DELETE_SYSTEM_APP,
+ userId);
}
Slog.i(LOG_TAG, "Waiting for non required apps to be deleted");
return packageDeleteObserver.awaitPackagesDeletion();
}
+ private void removeNonInstalledPackages(Set<String> packages, @UserIdInt int userId) {
+ final Set<String> toBeRemoved = new HashSet<>();
+ for (String packageName : packages) {
+ if (!isPackageInstalledForUser(packageName, userId)) {
+ toBeRemoved.add(packageName);
+ }
+ }
+ packages.removeAll(toBeRemoved);
+ }
+
private void disallowAddUser() {
if (mInjector.userManagerIsHeadlessSystemUserMode()) {
Slog.i(LOG_TAG, "Not setting DISALLOW_ADD_USER on headless system user mode.");
@@ -16507,4 +16731,80 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return mPolicyCache.canAdminGrantSensorsPermissionsForUser(userId);
}
+
+ @Override
+ public void setUsbDataSignalingEnabled(String packageName, boolean enabled) {
+ Objects.requireNonNull(packageName, "Admin package name must be provided");
+ final CallerIdentity caller = getCallerIdentity(packageName);
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "USB data signaling can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
+ Preconditions.checkState(canUsbDataSignalingBeDisabled(),
+ "USB data signaling cannot be disabled.");
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+ if (admin.mUsbDataSignalingEnabled != enabled) {
+ admin.mUsbDataSignalingEnabled = enabled;
+ saveSettingsLocked(caller.getUserId());
+ updateUsbDataSignal();
+ }
+ }
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_USB_DATA_SIGNALING)
+ .setAdmin(packageName)
+ .setBoolean(enabled)
+ .write();
+ }
+
+ private void updateUsbDataSignal() {
+ if (!canUsbDataSignalingBeDisabled()) {
+ return;
+ }
+ final boolean usbEnabled;
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ usbEnabled = admin != null && admin.mUsbDataSignalingEnabled;
+ }
+ if (!mInjector.binderWithCleanCallingIdentity(
+ () -> mInjector.getUsbManager().enableUsbDataSignal(usbEnabled))) {
+ Slog.w(LOG_TAG, "Failed to set usb data signaling state");
+ }
+ }
+
+ @Override
+ public boolean isUsbDataSignalingEnabled(String packageName) {
+ final CallerIdentity caller = getCallerIdentity(packageName);
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "USB data signaling can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+ return admin.mUsbDataSignalingEnabled;
+ }
+ }
+
+ @Override
+ public boolean isUsbDataSignalingEnabledForUser(int userId) {
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(isSystemUid(caller));
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ return admin == null || admin.mUsbDataSignalingEnabled;
+ }
+ }
+
+ @Override
+ public boolean canUsbDataSignalingBeDisabled() {
+ return mInjector.binderWithCleanCallingIdentity(() ->
+ mInjector.getUsbManager() != null
+ && mInjector.getUsbManager().getUsbHalVersion() >= UsbManager.USB_HAL_V1_3
+ );
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 222c987d906f..5484a148b0b6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -27,6 +27,7 @@ import java.util.Objects;
final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
private static final String CMD_IS_SAFE_OPERATION = "is-operation-safe";
+ 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";
@@ -53,6 +54,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
switch (cmd) {
case CMD_IS_SAFE_OPERATION:
return runIsSafeOperation(pw);
+ case CMD_IS_SAFE_OPERATION_BY_REASON:
+ return runIsSafeOperationByReason(pw);
case CMD_SET_SAFE_OPERATION:
return runSetSafeOperation(pw);
case CMD_LIST_OWNERS:
@@ -73,12 +76,13 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
return -1;
}
-
private void showHelp(PrintWriter pw) {
pw.printf(" help\n");
pw.printf(" Prints this help text.\n\n");
pw.printf(" %s <OPERATION_ID>\n", CMD_IS_SAFE_OPERATION);
pw.printf(" Checks if the give operation is safe \n\n");
+ pw.printf(" %s <REASON_ID>\n", CMD_IS_SAFE_OPERATION_BY_REASON);
+ pw.printf(" Checks if the operations are safe for the given reason\n\n");
pw.printf(" %s <OPERATION_ID> <REASON_ID>\n", CMD_SET_SAFE_OPERATION);
pw.printf(" Emulates the result of the next call to check if the given operation is safe"
+ " \n\n");
@@ -89,10 +93,19 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
private int runIsSafeOperation(PrintWriter pw) {
int operation = Integer.parseInt(getNextArgRequired());
int reason = mService.getUnsafeOperationReason(operation);
- boolean safe = reason == DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
+ boolean safe = reason == DevicePolicyManager.OPERATION_SAFETY_REASON_NONE;
pw.printf("Operation %s is %b. Reason: %s\n",
DevicePolicyManager.operationToString(operation), safe,
- DevicePolicyManager.unsafeOperationReasonToString(reason));
+ DevicePolicyManager.operationSafetyReasonToString(reason));
+ return 0;
+ }
+
+ private int runIsSafeOperationByReason(PrintWriter pw) {
+ int reason = Integer.parseInt(getNextArgRequired());
+ boolean safe = mService.isSafeOperation(reason);
+ pw.printf("Operations affected by %s are %s\n",
+ DevicePolicyManager.operationSafetyReasonToString(reason),
+ (safe ? "SAFE" : "UNSAFE"));
return 0;
}
@@ -102,7 +115,7 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
mService.setNextOperationSafety(operation, reason);
pw.printf("Next call to check operation %s will return %s\n",
DevicePolicyManager.operationToString(operation),
- DevicePolicyManager.unsafeOperationReasonToString(reason));
+ DevicePolicyManager.operationSafetyReasonToString(reason));
return 0;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
index e9b2d7f56108..8843a5d5306e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
@@ -26,6 +26,7 @@ import android.os.Bundle;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
@@ -47,6 +48,10 @@ final class NetworkLogger {
private final PackageManagerInternal mPm;
private final AtomicBoolean mIsLoggingEnabled = new AtomicBoolean(false);
+ // The target userId to collect network events on. The target userId will be
+ // {@link android.os.UserHandle#USER_ALL} if network events should be collected for all users.
+ private final int mTargetUserId;
+
private IIpConnectivityMetrics mIpConnectivityMetrics;
private ServiceThread mHandlerThread;
private NetworkLoggingHandler mNetworkLoggingHandler;
@@ -58,6 +63,11 @@ final class NetworkLogger {
if (!mIsLoggingEnabled.get()) {
return;
}
+ // If the network logging was enabled by the profile owner, then do not
+ // include events in the personal profile.
+ if (!shouldLogNetworkEvent(uid)) {
+ return;
+ }
DnsEvent dnsEvent = new DnsEvent(hostname, ipAddresses, ipAddressesCount,
mPm.getNameForUid(uid), timestamp);
sendNetworkEvent(dnsEvent);
@@ -68,6 +78,11 @@ final class NetworkLogger {
if (!mIsLoggingEnabled.get()) {
return;
}
+ // If the network logging was enabled by the profile owner, then do not
+ // include events in the personal profile.
+ if (!shouldLogNetworkEvent(uid)) {
+ return;
+ }
ConnectEvent connectEvent = new ConnectEvent(ipAddr, port, mPm.getNameForUid(uid),
timestamp);
sendNetworkEvent(connectEvent);
@@ -81,11 +96,17 @@ final class NetworkLogger {
msg.setData(bundle);
mNetworkLoggingHandler.sendMessage(msg);
}
+
+ private boolean shouldLogNetworkEvent(int uid) {
+ return mTargetUserId == UserHandle.USER_ALL
+ || mTargetUserId == UserHandle.getUserId(uid);
+ }
};
- NetworkLogger(DevicePolicyManagerService dpm, PackageManagerInternal pm) {
+ NetworkLogger(DevicePolicyManagerService dpm, PackageManagerInternal pm, int targetUserId) {
mDpm = dpm;
mPm = pm;
+ mTargetUserId = targetUserId;
}
private boolean checkIpConnectivityMetricsService() {
@@ -114,7 +135,7 @@ final class NetworkLogger {
/* allowIo */ false);
mHandlerThread.start();
mNetworkLoggingHandler = new NetworkLoggingHandler(mHandlerThread.getLooper(),
- mDpm);
+ mDpm, mTargetUserId);
mNetworkLoggingHandler.scheduleBatchFinalization();
mIsLoggingEnabled.set(true);
return true;
@@ -153,7 +174,7 @@ final class NetworkLogger {
}
/**
- * If logs are being collected, keep collecting them but stop notifying the device owner that
+ * If logs are being collected, keep collecting them but stop notifying the admin that
* new logs are available (since they cannot be retrieved)
*/
void pause() {
@@ -163,11 +184,11 @@ final class NetworkLogger {
}
/**
- * If logs are being collected, start notifying the device owner when logs are ready to be
+ * If logs are being collected, start notifying the admin when logs are ready to be
* collected again (if it was paused).
* <p>If logging is enabled and there are logs ready to be retrieved, this method will attempt
- * to notify the device owner. Therefore calling identity should be cleared before calling it
- * (in case the method is called from a user other than the DO's user).
+ * to notify the admin. Therefore calling identity should be cleared before calling it
+ * (in case the method is called from a user other than the admin's user).
*/
void resume() {
if (mNetworkLoggingHandler != null) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
index 0a7070ffe4f6..84e89a08e1f4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
@@ -50,7 +50,7 @@ final class NetworkLoggingHandler extends Handler {
private static final int MAX_EVENTS_PER_BATCH = 1200;
/**
- * Maximum number of batches to store in memory. If more batches are generated and the DO
+ * Maximum number of batches to store in memory. If more batches are generated and the admin
* doesn't fetch them, we will discard the oldest one.
*/
private static final int MAX_BATCHES = 5;
@@ -74,6 +74,7 @@ final class NetworkLoggingHandler extends Handler {
private final AlarmManager mAlarmManager;
private long mId;
+ private int mTargetUserId;
private final OnAlarmListener mBatchTimeoutAlarmListener = new OnAlarmListener() {
@Override
@@ -82,10 +83,10 @@ final class NetworkLoggingHandler extends Handler {
+ mNetworkEvents.size() + " pending events.");
Bundle notificationExtras = null;
synchronized (NetworkLoggingHandler.this) {
- notificationExtras = finalizeBatchAndBuildDeviceOwnerMessageLocked();
+ notificationExtras = finalizeBatchAndBuildAdminMessageLocked();
}
if (notificationExtras != null) {
- notifyDeviceOwner(notificationExtras);
+ notifyDeviceOwnerOrProfileOwner(notificationExtras);
}
}
};
@@ -98,8 +99,8 @@ final class NetworkLoggingHandler extends Handler {
private ArrayList<NetworkEvent> mNetworkEvents = new ArrayList<>();
/**
- * Up to {@code MAX_BATCHES} finalized batches of logs ready to be retrieved by the DO. Already
- * retrieved batches are discarded after {@code RETRIEVED_BATCH_DISCARD_DELAY_MS}.
+ * Up to {@code MAX_BATCHES} finalized batches of logs ready to be retrieved by the admin.
+ * Already retrieved batches are discarded after {@code RETRIEVED_BATCH_DISCARD_DELAY_MS}.
*/
@GuardedBy("this")
private final LongSparseArray<ArrayList<NetworkEvent>> mBatches =
@@ -115,16 +116,18 @@ final class NetworkLoggingHandler extends Handler {
@GuardedBy("this")
private long mLastRetrievedBatchToken;
- NetworkLoggingHandler(Looper looper, DevicePolicyManagerService dpm) {
- this(looper, dpm, 0 /* event id */);
+ NetworkLoggingHandler(Looper looper, DevicePolicyManagerService dpm, int targetUserId) {
+ this(looper, dpm, 0 /* event id */, targetUserId);
}
@VisibleForTesting
- NetworkLoggingHandler(Looper looper, DevicePolicyManagerService dpm, long id) {
+ NetworkLoggingHandler(Looper looper, DevicePolicyManagerService dpm, long id,
+ int targetUserId) {
super(looper);
this.mDpm = dpm;
this.mAlarmManager = mDpm.mInjector.getAlarmManager();
this.mId = id;
+ this.mTargetUserId = targetUserId;
}
@Override
@@ -137,11 +140,11 @@ final class NetworkLoggingHandler extends Handler {
synchronized (NetworkLoggingHandler.this) {
mNetworkEvents.add(networkEvent);
if (mNetworkEvents.size() >= MAX_EVENTS_PER_BATCH) {
- notificationExtras = finalizeBatchAndBuildDeviceOwnerMessageLocked();
+ notificationExtras = finalizeBatchAndBuildAdminMessageLocked();
}
}
if (notificationExtras != null) {
- notifyDeviceOwner(notificationExtras);
+ notifyDeviceOwnerOrProfileOwner(notificationExtras);
}
}
break;
@@ -176,10 +179,10 @@ final class NetworkLoggingHandler extends Handler {
if (toWaitNanos > 0) {
return NANOSECONDS.toMillis(toWaitNanos) + 1; // Round up.
}
- notificationExtras = finalizeBatchAndBuildDeviceOwnerMessageLocked();
+ notificationExtras = finalizeBatchAndBuildAdminMessageLocked();
}
if (notificationExtras != null) {
- notifyDeviceOwner(notificationExtras);
+ notifyDeviceOwnerOrProfileOwner(notificationExtras);
}
return 0;
}
@@ -201,14 +204,15 @@ final class NetworkLoggingHandler extends Handler {
+ ", LastRetrievedBatch=" + mLastRetrievedBatchToken);
mPaused = false;
- // If there is a batch ready that the device owner hasn't been notified about, do it now.
+ // If there is a batch ready that the device owner or profile owner hasn't been
+ // notified about, do it now.
if (mBatches.size() > 0 && mLastRetrievedBatchToken != mCurrentBatchToken) {
scheduleBatchFinalization();
- notificationExtras = buildDeviceOwnerMessageLocked();
+ notificationExtras = buildAdminMessageLocked();
}
}
if (notificationExtras != null) {
- notifyDeviceOwner(notificationExtras);
+ notifyDeviceOwnerOrProfileOwner(notificationExtras);
}
}
@@ -219,8 +223,8 @@ final class NetworkLoggingHandler extends Handler {
}
@GuardedBy("this")
- /** @returns extras if a message should be sent to the device owner */
- private Bundle finalizeBatchAndBuildDeviceOwnerMessageLocked() {
+ /** @return extras if a message should be sent to the device owner or profile owner */
+ private Bundle finalizeBatchAndBuildAdminMessageLocked() {
mLastFinalizationNanos = System.nanoTime();
Bundle notificationExtras = null;
if (mNetworkEvents.size() > 0) {
@@ -243,10 +247,10 @@ final class NetworkLoggingHandler extends Handler {
mBatches.append(mCurrentBatchToken, mNetworkEvents);
mNetworkEvents = new ArrayList<>();
if (!mPaused) {
- notificationExtras = buildDeviceOwnerMessageLocked();
+ notificationExtras = buildAdminMessageLocked();
}
} else {
- // Don't notify the DO, since there are no events; DPC can still retrieve
+ // Don't notify the admin, since there are no events; DPC can still retrieve
// the last full batch if not paused.
Slog.d(TAG, "Was about to finalize the batch, but there were no events to send to"
+ " the DPC, the batchToken of last available batch: " + mCurrentBatchToken);
@@ -257,9 +261,9 @@ final class NetworkLoggingHandler extends Handler {
}
@GuardedBy("this")
- /** Build extras notification to the DO. Should only be called when there
+ /** Build extras notification to the admin. Should only be called when there
is a batch available. */
- private Bundle buildDeviceOwnerMessageLocked() {
+ private Bundle buildAdminMessageLocked() {
final Bundle extras = new Bundle();
final int lastBatchSize = mBatches.valueAt(mBatches.size() - 1).size();
extras.putLong(DeviceAdminReceiver.EXTRA_NETWORK_LOGS_TOKEN, mCurrentBatchToken);
@@ -267,16 +271,18 @@ final class NetworkLoggingHandler extends Handler {
return extras;
}
- /** Sends a notification to the DO. Should not hold locks as DevicePolicyManagerService may
- call into NetworkLoggingHandler. */
- private void notifyDeviceOwner(Bundle extras) {
- Slog.d(TAG, "Sending network logging batch broadcast to device owner, batchToken: "
- + extras.getLong(DeviceAdminReceiver.EXTRA_NETWORK_LOGS_TOKEN, -1));
+ /** Sends a notification to the device owner or profile owner. Should not hold locks as
+ DevicePolicyManagerService may call into NetworkLoggingHandler. */
+ private void notifyDeviceOwnerOrProfileOwner(Bundle extras) {
if (Thread.holdsLock(this)) {
Slog.wtfStack(TAG, "Shouldn't be called with NetworkLoggingHandler lock held");
return;
}
- mDpm.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE, extras);
+ Slog.d(TAG, "Sending network logging batch broadcast to device owner or profile owner, "
+ + "batchToken: "
+ + extras.getLong(DeviceAdminReceiver.EXTRA_NETWORK_LOGS_TOKEN, -1));
+ mDpm.sendDeviceOwnerOrProfileOwnerCommand(DeviceAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE,
+ extras, mTargetUserId);
}
synchronized List<NetworkEvent> retrieveFullLogBatch(final long batchToken) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
index 883f95d930a0..6ec6f915ea0f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
@@ -15,16 +15,18 @@
*/
package com.android.server.devicepolicy;
-import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
+import static android.app.admin.DevicePolicyManager.OPERATION_SAFETY_REASON_NONE;
+import static android.app.admin.DevicePolicyManager.operationSafetyReasonToString;
import static android.app.admin.DevicePolicyManager.operationToString;
-import static android.app.admin.DevicePolicyManager.unsafeOperationReasonToString;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
-import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
+import android.app.admin.DevicePolicyManager.OperationSafetyReason;
+import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicySafetyChecker;
import android.util.Slog;
import com.android.internal.os.IResultReceiver;
+import com.android.server.LocalServices;
import java.util.Objects;
@@ -43,10 +45,10 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker {
private final DevicePolicyManagerService mService;
private final DevicePolicySafetyChecker mRealSafetyChecker;
private final @DevicePolicyOperation int mOperation;
- private final @UnsafeOperationReason int mReason;
+ private final @OperationSafetyReason int mReason;
OneTimeSafetyChecker(DevicePolicyManagerService service,
- @DevicePolicyOperation int operation, @UnsafeOperationReason int reason) {
+ @DevicePolicyOperation int operation, @OperationSafetyReason int reason) {
mService = Objects.requireNonNull(service);
mOperation = operation;
mReason = reason;
@@ -55,25 +57,56 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker {
}
@Override
- @UnsafeOperationReason
+ @OperationSafetyReason
public int getUnsafeOperationReason(@DevicePolicyOperation int operation) {
String name = operationToString(operation);
- int reason = UNSAFE_OPERATION_REASON_NONE;
+ Slog.i(TAG, "getUnsafeOperationReason(" + name + ")");
+ int reason = OPERATION_SAFETY_REASON_NONE;
if (operation == mOperation) {
reason = mReason;
} else {
Slog.wtf(TAG, "invalid call to isDevicePolicyOperationSafe(): asked for " + name
+ ", should be " + operationToString(mOperation));
}
- Slog.i(TAG, "getDevicePolicyOperationSafety(" + name + "): returning "
- + unsafeOperationReasonToString(reason)
- + " and restoring DevicePolicySafetyChecker to " + mRealSafetyChecker);
- mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker);
+ String reasonName = operationSafetyReasonToString(reason);
+ DevicePolicyManagerInternal dpmi = LocalServices
+ .getService(DevicePolicyManagerInternal.class);
+
+ Slog.i(TAG, "notifying " + reasonName + " is active");
+ dpmi.notifyUnsafeOperationStateChanged(this, reason, true);
+
+ Slog.i(TAG, "notifying " + reasonName + " is inactive");
+ dpmi.notifyUnsafeOperationStateChanged(this, reason, false);
+
+ Slog.i(TAG, "returning " + reasonName);
+
+ disableSelf();
return reason;
}
@Override
+ public boolean isSafeOperation(@OperationSafetyReason int reason) {
+ boolean safe = mReason != reason;
+ Slog.i(TAG, "isSafeOperation(" + operationSafetyReasonToString(reason) + "): " + safe);
+
+ disableSelf();
+ return safe;
+ }
+
+ @Override
public void onFactoryReset(IResultReceiver callback) {
throw new UnsupportedOperationException();
}
+
+ private void disableSelf() {
+ Slog.i(TAG, "restoring DevicePolicySafetyChecker to " + mRealSafetyChecker);
+ mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker);
+ }
+
+ @Override
+ public String toString() {
+ return "OneTimeSafetyChecker[id=" + System.identityHashCode(this)
+ + ", reason=" + operationSafetyReasonToString(mReason)
+ + ", operation=" + operationToString(mOperation) + ']';
+ }
}
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 7534c7c40a3d..7e1e99fe0a23 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_defaults {
name: "service.incremental-proto-defaults",
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 56cb3d11d552..2fa927bcccfd 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -66,9 +66,19 @@ struct Constants {
static constexpr auto blockSize = 4096;
static constexpr auto systemPackage = "android"sv;
+ static constexpr auto userStatusDelay = 100ms;
+
static constexpr auto progressUpdateInterval = 1000ms;
static constexpr auto perUidTimeoutOffset = progressUpdateInterval * 2;
static constexpr auto minPerUidTimeout = progressUpdateInterval * 3;
+
+ // If DL was up and not crashing for 10mins, we consider it healthy and reset all delays.
+ static constexpr auto healthyDataLoaderUptime = 10min;
+ // 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;
};
static const Constants& constants() {
@@ -386,6 +396,28 @@ void IncrementalService::onDump(int fd) {
dprintf(fd, "}\n");
}
+bool IncrementalService::needStartDataLoaderLocked(IncFsMount& ifs) {
+ if (ifs.dataLoaderStub->params().packageName == Constants::systemPackage) {
+ return true;
+ }
+
+ // Check all permanent binds.
+ for (auto&& [_, bindPoint] : ifs.bindPoints) {
+ if (bindPoint.kind != BindKind::Permanent) {
+ continue;
+ }
+ const auto progress = getLoadingProgressFromPath(ifs, bindPoint.sourceDir,
+ /*stopOnFirstIncomplete=*/true);
+ if (!progress.isError() && !progress.fullyLoaded()) {
+ LOG(INFO) << "Non system mount: [" << bindPoint.sourceDir
+ << "], partial progress: " << progress.getProgress() * 100 << "%";
+ return true;
+ }
+ }
+
+ return false;
+}
+
void IncrementalService::onSystemReady() {
if (mSystemReady.exchange(true)) {
return;
@@ -396,8 +428,11 @@ void IncrementalService::onSystemReady() {
std::lock_guard l(mLock);
mounts.reserve(mMounts.size());
for (auto&& [id, ifs] : mMounts) {
- if (ifs->mountId == id &&
- ifs->dataLoaderStub->params().packageName == Constants::systemPackage) {
+ if (ifs->mountId != id) {
+ continue;
+ }
+
+ if (needStartDataLoaderLocked(*ifs)) {
mounts.push_back(ifs);
}
}
@@ -1539,6 +1574,11 @@ static long elapsedMcs(Duration start, Duration end) {
return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
}
+template <class Duration>
+static constexpr auto castToMs(Duration d) {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(d);
+}
+
// Extract lib files from zip, create new files in incfs and write data to them
// Lib files should be placed next to the APK file in the following matter:
// Example:
@@ -2134,9 +2174,43 @@ void IncrementalService::DataLoaderStub::setTargetStatusLocked(int status) {
<< status << " (current " << mCurrentStatus << ")";
}
+Milliseconds IncrementalService::DataLoaderStub::updateBindDelay() {
+ std::unique_lock lock(mMutex);
+ 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;
+ }
+
+ constexpr auto minBindDelayMs = castToMs(Constants::minBindDelay);
+ constexpr auto maxBindDelayMs = castToMs(Constants::maxBindDelay);
+
+ const auto bindDelayMs =
+ std::min(std::max(mPreviousBindDelay * Constants::bindDelayMultiplier, minBindDelayMs),
+ maxBindDelayMs)
+ .count();
+ 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();
+ if (bindDelay > 1s) {
+ LOG(INFO) << "Delaying bind to " << mParams.packageName << " by "
+ << bindDelay.count() / 1000 << "s";
+ }
+
bool result = false;
- auto status = mService.mDataLoaderManager->bindToDataLoader(id(), mParams, this, &result);
+ 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();
return false;
@@ -2234,13 +2308,24 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount
LOG(ERROR) << "Mount ID mismatch: expected " << id() << ", but got: " << mountId;
return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch.");
}
+ if (newStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
+ // User-provided status, let's postpone the handling to avoid possible deadlocks.
+ mService.addTimedJob(*mService.mTimedQueue, id(), Constants::userStatusDelay,
+ [this, newStatus]() { setCurrentStatus(newStatus); });
+ return binder::Status::ok();
+ }
+ setCurrentStatus(newStatus);
+ return binder::Status::ok();
+}
+
+void IncrementalService::DataLoaderStub::setCurrentStatus(int newStatus) {
int targetStatus, oldStatus;
DataLoaderStatusListener listener;
{
std::unique_lock lock(mMutex);
if (mCurrentStatus == newStatus) {
- return binder::Status::ok();
+ return;
}
oldStatus = mCurrentStatus;
@@ -2249,7 +2334,8 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount
listener = mStatusListener;
- if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE) {
+ if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE ||
+ mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
// For unavailable, unbind from DataLoader to ensure proper re-commit.
setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
}
@@ -2259,14 +2345,12 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount
<< newStatus << " (target " << targetStatus << ")";
if (listener) {
- listener->onStatusChanged(mountId, newStatus);
+ listener->onStatusChanged(id(), newStatus);
}
fsmStep();
mStatusCondition.notify_all();
-
- return binder::Status::ok();
}
binder::Status IncrementalService::DataLoaderStub::reportStreamHealth(MountId mountId,
@@ -2544,6 +2628,9 @@ void IncrementalService::DataLoaderStub::onDump(int fd) {
dprintf(fd, " blockIndex: %d\n", pendingRead.block);
dprintf(fd, " bootClockTsUs: %lld\n", (long long)pendingRead.bootClockTsUs);
}
+ dprintf(fd, " bind: %llds ago (delay: %llds)\n",
+ (long long)(elapsedMcs(mPreviousBindTs, Clock::now()) / 1000000),
+ (long long)(mPreviousBindDelay.count() / 1000));
dprintf(fd, " }\n");
const auto& params = mParams;
dprintf(fd, " dataLoaderParams: {\n");
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 5d53bac777b5..d8f2c91a971c 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -234,6 +234,8 @@ private:
binder::Status onStatusChanged(MountId mount, int newStatus) final;
binder::Status reportStreamHealth(MountId mount, int newStatus) final;
+ void setCurrentStatus(int newStatus);
+
sp<content::pm::IDataLoader> getDataLoader();
bool bind();
@@ -245,7 +247,6 @@ private:
void setTargetStatusLocked(int status);
bool fsmStep();
- bool fsmStep(int currentStatus, int targetStatus);
void onHealthStatus(StorageHealthListener healthListener, int healthStatus);
void updateHealthStatus(bool baseline = false);
@@ -259,6 +260,8 @@ private:
BootClockTsUs getOldestPendingReadTs();
+ Milliseconds updateBindDelay();
+
void registerForPendingReads();
void unregisterFromPendingReads();
@@ -276,6 +279,9 @@ private:
int mTargetStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
TimePoint mTargetStatusTs = {};
+ TimePoint mPreviousBindTs = {};
+ Milliseconds mPreviousBindDelay = {};
+
std::string mHealthPath;
incfs::UniqueControl mHealthControl;
struct {
@@ -370,6 +376,8 @@ private:
void addBindMountRecordLocked(IncFsMount& ifs, StorageId storage, std::string&& metadataName,
std::string&& source, std::string&& target, BindKind kind);
+ bool needStartDataLoaderLocked(IncFsMount& ifs);
+
DataLoaderStubPtr prepareDataLoader(IncFsMount& ifs,
content::pm::DataLoaderParamsParcel&& params,
const DataLoaderStatusListener* statusListener = nullptr,
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 25d3f77fa58b..659d6503e11c 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -70,9 +70,10 @@ public:
~RealDataLoaderManager() = default;
binder::Status bindToDataLoader(MountId mountId,
const content::pm::DataLoaderParamsParcel& params,
+ int bindDelayMs,
const sp<content::pm::IDataLoaderStatusListener>& listener,
bool* _aidl_return) const final {
- return mInterface->bindToDataLoader(mountId, params, listener, _aidl_return);
+ return mInterface->bindToDataLoader(mountId, params, bindDelayMs, listener, _aidl_return);
}
binder::Status getDataLoader(MountId mountId,
sp<content::pm::IDataLoader>* _aidl_return) const final {
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 71fd3acc6489..d60035a9274d 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -63,7 +63,7 @@ class DataLoaderManagerWrapper {
public:
virtual ~DataLoaderManagerWrapper() = default;
virtual binder::Status bindToDataLoader(
- MountId mountId, const content::pm::DataLoaderParamsParcel& params,
+ MountId mountId, const content::pm::DataLoaderParamsParcel& params, int bindDelayMs,
const sp<content::pm::IDataLoaderStatusListener>& listener, bool* result) const = 0;
virtual binder::Status getDataLoader(MountId mountId,
sp<content::pm::IDataLoader>* result) const = 0;
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 8713f9d3d821..ab491efe8583 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -208,8 +208,9 @@ public:
EXPECT_TRUE(mDataLoaderHolder != nullptr);
}
- MOCK_CONST_METHOD4(bindToDataLoader,
+ MOCK_CONST_METHOD5(bindToDataLoader,
binder::Status(int32_t mountId, const DataLoaderParamsParcel& params,
+ int bindDelayMs,
const sp<IDataLoaderStatusListener>& listener,
bool* _aidl_return));
MOCK_CONST_METHOD2(getDataLoader,
@@ -217,11 +218,11 @@ public:
MOCK_CONST_METHOD1(unbindFromDataLoader, binder::Status(int32_t mountId));
void bindToDataLoaderSuccess() {
- ON_CALL(*this, bindToDataLoader(_, _, _, _))
+ ON_CALL(*this, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Invoke(this, &MockDataLoaderManager::bindToDataLoaderOk));
}
void bindToDataLoaderFails() {
- ON_CALL(*this, bindToDataLoader(_, _, _, _))
+ ON_CALL(*this, bindToDataLoader(_, _, _, _, _))
.WillByDefault(Return(
(binder::Status::fromExceptionCode(1, String8("failed to prepare")))));
}
@@ -234,6 +235,7 @@ public:
.WillByDefault(Invoke(this, &MockDataLoaderManager::unbindFromDataLoaderOk));
}
binder::Status bindToDataLoaderOk(int32_t mountId, const DataLoaderParamsParcel& params,
+ int bindDelayMs,
const sp<IDataLoaderStatusListener>& listener,
bool* _aidl_return) {
mId = mountId;
@@ -245,6 +247,40 @@ public:
}
return binder::Status::ok();
}
+ binder::Status bindToDataLoaderOkWith10sDelay(int32_t mountId,
+ const DataLoaderParamsParcel& params,
+ int bindDelayMs,
+ const sp<IDataLoaderStatusListener>& listener,
+ bool* _aidl_return) {
+ CHECK(1000 * 9 <= bindDelayMs && bindDelayMs <= 1000 * 11) << bindDelayMs;
+ return bindToDataLoaderOk(mountId, params, bindDelayMs, listener, _aidl_return);
+ }
+ binder::Status bindToDataLoaderOkWith100sDelay(int32_t mountId,
+ const DataLoaderParamsParcel& params,
+ int bindDelayMs,
+ const sp<IDataLoaderStatusListener>& listener,
+ bool* _aidl_return) {
+ CHECK(1000 * 9 * 9 < bindDelayMs && bindDelayMs < 1000 * 11 * 11) << bindDelayMs;
+ return bindToDataLoaderOk(mountId, params, bindDelayMs, listener, _aidl_return);
+ }
+ binder::Status bindToDataLoaderOkWith1000sDelay(int32_t mountId,
+ const DataLoaderParamsParcel& params,
+ int bindDelayMs,
+ const sp<IDataLoaderStatusListener>& listener,
+ bool* _aidl_return) {
+ CHECK(1000 * 9 * 9 * 9 < bindDelayMs && bindDelayMs < 1000 * 11 * 11 * 11) << bindDelayMs;
+ return bindToDataLoaderOk(mountId, params, bindDelayMs, listener, _aidl_return);
+ }
+ binder::Status bindToDataLoaderOkWith10000sDelay(int32_t mountId,
+ const DataLoaderParamsParcel& params,
+ int bindDelayMs,
+ const sp<IDataLoaderStatusListener>& listener,
+ bool* _aidl_return) {
+ CHECK(1000 * 9 * 9 * 9 * 9 < bindDelayMs && bindDelayMs < 1000 * 11 * 11 * 11 * 11)
+ << bindDelayMs;
+ return bindToDataLoaderOk(mountId, params, bindDelayMs, listener, _aidl_return);
+ }
+
binder::Status getDataLoaderOk(int32_t mountId, sp<IDataLoader>* _aidl_return) {
*_aidl_return = mDataLoader;
return binder::Status::ok();
@@ -261,6 +297,9 @@ public:
void setDataLoaderStatusUnavailable() {
mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE);
}
+ void setDataLoaderStatusUnrecoverable() {
+ mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE);
+ }
binder::Status unbindFromDataLoaderOk(int32_t id) {
if (mDataLoader) {
if (auto status = mDataLoader->destroy(id); !status.isOk()) {
@@ -676,7 +715,7 @@ protected:
TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsFails) {
mVold->mountIncFsFails();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(0);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(0);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
@@ -686,7 +725,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsFails) {
TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsInvalidControlParcel) {
mVold->mountIncFsInvalidControlParcel();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(0);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(0);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
TemporaryDir tempDir;
int storageId =
@@ -698,7 +737,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsInvalidControlParcel)
TEST_F(IncrementalServiceTest, testCreateStorageMakeFileFails) {
mVold->mountIncFsSuccess();
mIncFs->makeFileFails();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(0);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(0);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
EXPECT_CALL(*mVold, unmountIncFs(_));
TemporaryDir tempDir;
@@ -712,7 +751,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageBindMountFails) {
mVold->mountIncFsSuccess();
mIncFs->makeFileSuccess();
mVold->bindMountFails();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(0);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(0);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
EXPECT_CALL(*mVold, unmountIncFs(_));
TemporaryDir tempDir;
@@ -727,7 +766,7 @@ TEST_F(IncrementalServiceTest, testCreateStoragePrepareDataLoaderFails) {
mIncFs->makeFileSuccess();
mVold->bindMountSuccess();
mDataLoaderManager->bindToDataLoaderFails();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(0);
EXPECT_CALL(*mDataLoader, start(_)).Times(0);
@@ -755,11 +794,11 @@ TEST_F(IncrementalServiceTest, testDeleteStorageSuccess) {
mIncrementalService->deleteStorage(storageId);
}
-TEST_F(IncrementalServiceTest, testDataLoaderDestroyed) {
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(2);
+TEST_F(IncrementalServiceTest, testDataLoaderDestroyedAndDelayed) {
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(6);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
- EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2);
- EXPECT_CALL(*mDataLoader, start(_)).Times(2);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(6);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(6);
EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
@@ -768,13 +807,38 @@ TEST_F(IncrementalServiceTest, testDataLoaderDestroyed) {
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {});
+
// 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);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoader, start(_)).Times(1);
@@ -793,7 +857,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderCreate) {
TEST_F(IncrementalServiceTest, testStartDataLoaderPendingStart) {
mDataLoader->initializeCreateOkNoStatus();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoader, start(_)).Times(1);
@@ -811,7 +875,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderPendingStart) {
TEST_F(IncrementalServiceTest, testStartDataLoaderCreateUnavailable) {
mDataLoader->initializeCreateOkNoStatus();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoader, start(_)).Times(0);
@@ -827,12 +891,30 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderCreateUnavailable) {
mDataLoaderManager->setDataLoaderStatusUnavailable();
}
+TEST_F(IncrementalServiceTest, testStartDataLoaderCreateUnrecoverable) {
+ mDataLoader->initializeCreateOkNoStatus();
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(0);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
+ mDataLoaderManager->setDataLoaderStatusUnrecoverable();
+}
+
TEST_F(IncrementalServiceTest, testStartDataLoaderRecreateOnPendingReads) {
mIncFs->waitForPendingReadsSuccess();
mIncFs->openMountSuccess();
mDataLoader->initializeCreateOkNoStatus();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(2);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(2);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(2);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2);
EXPECT_CALL(*mDataLoader, start(_)).Times(0);
@@ -856,7 +938,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderRecreateOnPendingReads) {
TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) {
mIncFs->openMountSuccess();
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoader, start(_)).Times(1);
@@ -1406,7 +1488,7 @@ static ErrorCode checkPerUidTimeoutsEmpty(
}
TEST_F(IncrementalServiceTest, testPerUidTimeoutsTooShort) {
- EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
EXPECT_CALL(*mDataLoader, start(_)).Times(1);
diff --git a/services/java/com/android/server/SystemConfigService.java b/services/java/com/android/server/SystemConfigService.java
index 1801f3bca30e..a2768c637d79 100644
--- a/services/java/com/android/server/SystemConfigService.java
+++ b/services/java/com/android/server/SystemConfigService.java
@@ -21,6 +21,10 @@ import static java.util.stream.Collectors.toMap;
import android.Manifest;
import android.content.Context;
import android.os.ISystemConfig;
+import android.util.ArraySet;
+import android.util.SparseArray;
+
+import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
import java.util.List;
@@ -64,6 +68,22 @@ public class SystemConfigService extends SystemService {
return SystemConfig.getInstance()
.getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
}
+
+ @Override
+ public int[] getSystemPermissionUids(String permissionName) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS,
+ "getSystemPermissionUids requires GET_RUNTIME_PERMISSIONS");
+ final List<Integer> uids = new ArrayList<>();
+ final SparseArray<ArraySet<String>> systemPermissions =
+ SystemConfig.getInstance().getSystemPermissions();
+ for (int i = 0; i < systemPermissions.size(); i++) {
+ final ArraySet<String> permissions = systemPermissions.valueAt(i);
+ if (permissions != null && permissions.contains(permissionName)) {
+ uids.add(systemPermissions.keyAt(i));
+ }
+ }
+ return ArrayUtils.convertToIntArray(uids);
+ }
};
public SystemConfigService(Context context) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ccff95b1251b..1271efdfa820 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -139,6 +139,7 @@ import com.android.server.integrity.AppIntegrityManagerService;
import com.android.server.lights.LightsService;
import com.android.server.location.LocationManagerService;
import com.android.server.media.MediaRouterService;
+import com.android.server.media.metrics.MediaMetricsManagerService;
import com.android.server.media.projection.MediaProjectionManagerService;
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.net.NetworkStatsService;
@@ -187,6 +188,7 @@ import com.android.server.telecom.TelecomLoaderService;
import com.android.server.testharness.TestHarnessModeService;
import com.android.server.textclassifier.TextClassificationManagerService;
import com.android.server.textservices.TextServicesManagerService;
+import com.android.server.tracing.TracingServiceProxy;
import com.android.server.trust.TrustManagerService;
import com.android.server.tv.TvInputManagerService;
import com.android.server.tv.TvRemoteService;
@@ -195,6 +197,7 @@ import com.android.server.twilight.TwilightService;
import com.android.server.uri.UriGrantsManagerService;
import com.android.server.usage.UsageStatsService;
import com.android.server.utils.TimingsTraceAndSlog;
+import com.android.server.vibrator.VibratorManagerService;
import com.android.server.vr.VrManagerService;
import com.android.server.webkit.WebViewUpdateService;
import com.android.server.wm.ActivityTaskManagerService;
@@ -255,6 +258,10 @@ public final class SystemServer implements Dumpable {
"com.android.server.companion.CompanionDeviceManagerService";
private static final String STATS_COMPANION_APEX_PATH =
"/apex/com.android.os.statsd/javalib/service-statsd.jar";
+ private static final String SCHEDULING_APEX_PATH =
+ "/apex/com.android.scheduling/javalib/service-scheduling.jar";
+ private static final String REBOOT_READINESS_LIFECYCLE_CLASS =
+ "com.android.server.scheduling.RebootReadinessManagerService$Lifecycle";
private static final String CONNECTIVITY_SERVICE_APEX_PATH =
"/apex/com.android.tethering/javalib/service-connectivity.jar";
private static final String STATS_COMPANION_LIFECYCLE_CLASS =
@@ -323,6 +330,8 @@ public final class SystemServer implements Dumpable {
"com.android.server.musicrecognition.MusicRecognitionManagerService";
private static final String SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS =
"com.android.server.systemcaptions.SystemCaptionsManagerService";
+ private static final String TEXT_TO_SPEECH_MANAGER_SERVICE_CLASS =
+ "com.android.server.texttospeech.TextToSpeechManagerService";
private static final String TIME_ZONE_RULES_MANAGER_SERVICE_CLASS =
"com.android.server.timezone.RulesManagerService$Lifecycle";
private static final String IOT_SERVICE_CLASS =
@@ -528,8 +537,7 @@ public final class SystemServer implements Dumpable {
String filename = "/data/system/heapdump/fdtrack-" + date + ".hprof";
Debug.dumpHprofData(filename);
} catch (IOException ex) {
- Slog.e("System", "Failed to dump fdtrack hprof");
- ex.printStackTrace();
+ Slog.e("System", "Failed to dump fdtrack hprof", ex);
}
}
@@ -1295,11 +1303,11 @@ public final class SystemServer implements Dumpable {
t.traceBegin("startOtherServices");
final Context context = mSystemContext;
- VibratorService vibrator = null;
DynamicSystemService dynamicSystem = null;
IStorageManager storageManager = null;
NetworkManagementService networkManagement = null;
IpSecService ipSecService = null;
+ VpnManagerService vpnManager = null;
VcnManagementService vcnManagement = null;
NetworkStatsService networkStats = null;
NetworkPolicyManagerService networkPolicy = null;
@@ -1424,11 +1432,6 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(VibratorManagerService.Lifecycle.class);
t.traceEnd();
- t.traceBegin("StartVibratorService");
- vibrator = new VibratorService(context);
- ServiceManager.addService("vibrator", vibrator);
- t.traceEnd();
-
t.traceBegin("StartDynamicSystemService");
dynamicSystem = new DynamicSystemService(context);
ServiceManager.addService("dynamic_system", dynamicSystem);
@@ -1721,6 +1724,7 @@ public final class SystemServer implements Dumpable {
startAttentionService(context, t);
startRotationResolverService(context, t);
startSystemCaptionsManagerService(context, t);
+ startTextToSpeechManagerService(context, t);
// System Speech Recognition Service
if (deviceHasConfigString(context,
@@ -1898,6 +1902,15 @@ public final class SystemServer implements Dumpable {
networkPolicy.bindConnectivityManager(connectivity);
t.traceEnd();
+ t.traceBegin("StartVpnManagerService");
+ try {
+ vpnManager = VpnManagerService.create(context);
+ ServiceManager.addService(Context.VPN_MANAGEMENT_SERVICE, vpnManager);
+ } catch (Throwable e) {
+ reportWtf("starting VPN Manager Service", e);
+ }
+ t.traceEnd();
+
t.traceBegin("StartVcnManagementService");
try {
vcnManagement = VcnManagementService.create(context);
@@ -2424,6 +2437,10 @@ public final class SystemServer implements Dumpable {
t.traceBegin("StartPeopleService");
mSystemServiceManager.startService(PeopleService.class);
t.traceEnd();
+
+ t.traceBegin("StartMediaMetricsManager");
+ mSystemServiceManager.startService(MediaMetricsManagerService.class);
+ t.traceEnd();
}
if (!isWatch) {
@@ -2479,6 +2496,12 @@ public final class SystemServer implements Dumpable {
STATS_COMPANION_LIFECYCLE_CLASS, STATS_COMPANION_APEX_PATH);
t.traceEnd();
+ // Reboot Readiness
+ t.traceBegin("StartRebootReadinessManagerService");
+ mSystemServiceManager.startServiceFromJar(
+ REBOOT_READINESS_LIFECYCLE_CLASS, SCHEDULING_APEX_PATH);
+ t.traceEnd();
+
// Statsd pulled atoms
t.traceBegin("StartStatsPullAtomService");
mSystemServiceManager.startService(STATS_PULL_ATOM_SERVICE_CLASS);
@@ -2520,16 +2543,13 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(AppBindingService.Lifecycle.class);
t.traceEnd();
- // It is now time to start up the app processes...
-
- t.traceBegin("MakeVibratorServiceReady");
- try {
- vibrator.systemReady();
- } catch (Throwable e) {
- reportWtf("making Vibrator Service ready", e);
- }
+ // Perfetto TracingServiceProxy
+ t.traceBegin("startTracingServiceProxy");
+ mSystemServiceManager.startService(TracingServiceProxy.class);
t.traceEnd();
+ // It is now time to start up the app processes...
+
t.traceBegin("MakeLockSettingsServiceReady");
if (lockSettings != null) {
try {
@@ -2676,6 +2696,7 @@ public final class SystemServer implements Dumpable {
final MediaRouterService mediaRouterF = mediaRouter;
final MmsServiceBroker mmsServiceF = mmsService;
final IpSecService ipSecServiceF = ipSecService;
+ final VpnManagerService vpnManagerF = vpnManager;
final VcnManagementService vcnManagementF = vcnManagement;
final WindowManagerService windowManagerF = wm;
final ConnectivityManager connectivityF = (ConnectivityManager)
@@ -2790,6 +2811,15 @@ public final class SystemServer implements Dumpable {
reportWtf("making Connectivity Service ready", e);
}
t.traceEnd();
+ t.traceBegin("MakeVpnManagerServiceReady");
+ try {
+ if (vpnManagerF != null) {
+ vpnManagerF.systemReady();
+ }
+ } catch (Throwable e) {
+ reportWtf("making VpnManagerService ready", e);
+ }
+ t.traceEnd();
t.traceBegin("MakeVcnManagementServiceReady");
try {
if (vcnManagementF != null) {
@@ -2947,6 +2977,13 @@ public final class SystemServer implements Dumpable {
t.traceEnd();
}
+ private void startTextToSpeechManagerService(@NonNull Context context,
+ @NonNull TimingsTraceAndSlog t) {
+ t.traceBegin("StartTextToSpeechManagerService");
+ mSystemServiceManager.startService(TEXT_TO_SPEECH_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
+ }
+
private void startContentCaptureService(@NonNull Context context,
@NonNull TimingsTraceAndSlog t) {
// First check if it was explicitly enabled by DeviceConfig
diff --git a/services/midi/Android.bp b/services/midi/Android.bp
index 013f23d67cd8..5adcfbaf432e 100644
--- a/services/midi/Android.bp
+++ b/services/midi/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.midi-sources",
srcs: ["java/**/*.java"],
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
index 0cb729d924b4..87b2c84a30f7 100644
--- a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
@@ -16,6 +16,7 @@
package com.android.server.musicrecognition;
+import static android.Manifest.permission.RECORD_AUDIO;
import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_AUDIO_UNAVAILABLE;
import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_SERVICE_KILLED;
import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_SERVICE_UNAVAILABLE;
@@ -25,6 +26,7 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
@@ -45,6 +47,7 @@ import com.android.server.infra.AbstractPerUserSystemService;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Objects;
/**
* Handles per-user requests received by
@@ -64,11 +67,20 @@ public final class MusicRecognitionManagerPerUserService extends
@Nullable
@GuardedBy("mLock")
private RemoteMusicRecognitionService mRemoteService;
+ private final AppOpsManager mAppOpsManager;
+
+ private String mAttributionTag;
+ private String mAttributionMessage;
+ private ServiceInfo mServiceInfo;
MusicRecognitionManagerPerUserService(
@NonNull MusicRecognitionManagerService primary,
@NonNull Object lock, int userId) {
super(primary, lock, userId);
+ mAppOpsManager = getContext().getSystemService(AppOpsManager.class);
+ mAttributionMessage = String.format("MusicRecognitionManager.invokedByUid.%s", userId);
+ mAttributionTag = null;
+ mServiceInfo = null;
}
@NonNull
@@ -114,6 +126,13 @@ public final class MusicRecognitionManagerPerUserService extends
new MusicRecognitionServiceCallback(clientCallback),
mMaster.isBindInstantServiceAllowed(),
mMaster.verbose);
+ try {
+ mServiceInfo =
+ getContext().getPackageManager().getServiceInfo(
+ mRemoteService.getComponentName(), 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Service was not found.", e);
+ }
}
return mRemoteService;
@@ -127,12 +146,8 @@ public final class MusicRecognitionManagerPerUserService extends
public void beginRecognitionLocked(
@NonNull RecognitionRequest recognitionRequest,
@NonNull IBinder callback) {
- int maxAudioLengthSeconds = Math.min(recognitionRequest.getMaxAudioLengthSeconds(),
- MAX_STREAMING_SECONDS);
IMusicRecognitionManagerCallback clientCallback =
IMusicRecognitionManagerCallback.Stub.asInterface(callback);
- AudioRecord audioRecord = createAudioRecord(recognitionRequest, maxAudioLengthSeconds);
-
mRemoteService = ensureRemoteServiceLocked(clientCallback);
if (mRemoteService == null) {
try {
@@ -158,52 +173,92 @@ public final class MusicRecognitionManagerPerUserService extends
ParcelFileDescriptor clientRead = clientPipe.first;
mMaster.mExecutorService.execute(() -> {
- try (OutputStream fos =
- new ParcelFileDescriptor.AutoCloseOutputStream(audioSink)) {
- int halfSecondBufferSize =
- audioRecord.getBufferSizeInFrames() / maxAudioLengthSeconds;
- byte[] byteBuffer = new byte[halfSecondBufferSize];
- int bytesRead = 0;
- int totalBytesRead = 0;
- int ignoreBytes =
- recognitionRequest.getIgnoreBeginningFrames() * BYTES_PER_SAMPLE;
- audioRecord.startRecording();
- while (bytesRead >= 0 && totalBytesRead
- < audioRecord.getBufferSizeInFrames() * BYTES_PER_SAMPLE
- && mRemoteService != null) {
- bytesRead = audioRecord.read(byteBuffer, 0, byteBuffer.length);
- if (bytesRead > 0) {
- totalBytesRead += bytesRead;
- // If we are ignoring the first x bytes, update that counter.
- if (ignoreBytes > 0) {
- ignoreBytes -= bytesRead;
- // If we've dipped negative, we've skipped through all ignored bytes
- // and then some. Write out the bytes we shouldn't have skipped.
- if (ignoreBytes < 0) {
- fos.write(byteBuffer, bytesRead + ignoreBytes, -ignoreBytes);
- }
- } else {
- fos.write(byteBuffer);
- }
- }
- }
- Slog.i(TAG, String.format("Streamed %s bytes from audio record", totalBytesRead));
- } catch (IOException e) {
- Slog.e(TAG, "Audio streaming stopped.", e);
- } finally {
- audioRecord.release();
- try {
- clientCallback.onAudioStreamClosed();
- } catch (RemoteException ignored) {
- // Ignored.
- }
- }
+ streamAudio(recognitionRequest, clientCallback, audioSink);
});
// Send the pipe down to the lookup service while we write to it asynchronously.
mRemoteService.writeAudioToPipe(clientRead, recognitionRequest.getAudioFormat());
}
/**
+ * Streams audio based on given request to the given audioSink. Notifies callback of errors.
+ *
+ * @param recognitionRequest the recognition request specifying audio parameters.
+ * @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) {
+ try {
+ startRecordAudioOp();
+ } 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.
+ Slog.e(TAG, "RECORD_AUDIO op not permitted on behalf of "
+ + mServiceInfo.getComponentName(), e);
+ try {
+ clientCallback.onRecognitionFailed(
+ RECOGNITION_FAILED_AUDIO_UNAVAILABLE);
+ } catch (RemoteException ignored) {
+ // Ignored.
+ }
+ return;
+ }
+
+ int maxAudioLengthSeconds = Math.min(recognitionRequest.getMaxAudioLengthSeconds(),
+ MAX_STREAMING_SECONDS);
+ AudioRecord audioRecord = createAudioRecord(recognitionRequest, maxAudioLengthSeconds);
+ try (OutputStream fos =
+ new ParcelFileDescriptor.AutoCloseOutputStream(audioSink)) {
+ streamAudio(recognitionRequest, maxAudioLengthSeconds, audioRecord, fos);
+ } catch (IOException e) {
+ Slog.e(TAG, "Audio streaming stopped.", e);
+ } finally {
+ audioRecord.release();
+ finishRecordAudioOp();
+ try {
+ clientCallback.onAudioStreamClosed();
+ } catch (RemoteException ignored) {
+ // Ignored.
+ }
+ }
+ }
+
+ /** Performs the actual streaming from audioRecord into outputStream. **/
+ private void streamAudio(@NonNull RecognitionRequest recognitionRequest,
+ int maxAudioLengthSeconds, AudioRecord audioRecord, OutputStream outputStream)
+ throws IOException {
+ int halfSecondBufferSize =
+ audioRecord.getBufferSizeInFrames() / maxAudioLengthSeconds;
+ byte[] byteBuffer = new byte[halfSecondBufferSize];
+ int bytesRead = 0;
+ int totalBytesRead = 0;
+ int ignoreBytes =
+ recognitionRequest.getIgnoreBeginningFrames() * BYTES_PER_SAMPLE;
+ audioRecord.startRecording();
+ while (bytesRead >= 0 && totalBytesRead
+ < audioRecord.getBufferSizeInFrames() * BYTES_PER_SAMPLE
+ && mRemoteService != null) {
+ bytesRead = audioRecord.read(byteBuffer, 0, byteBuffer.length);
+ if (bytesRead > 0) {
+ totalBytesRead += bytesRead;
+ // If we are ignoring the first x bytes, update that counter.
+ if (ignoreBytes > 0) {
+ ignoreBytes -= bytesRead;
+ // If we've dipped negative, we've skipped through all ignored bytes
+ // and then some. Write out the bytes we shouldn't have skipped.
+ if (ignoreBytes < 0) {
+ outputStream.write(byteBuffer, bytesRead + ignoreBytes, -ignoreBytes);
+ }
+ } else {
+ outputStream.write(byteBuffer);
+ }
+ }
+ }
+ Slog.i(TAG,
+ String.format("Streamed %s bytes from audio record", totalBytesRead));
+ }
+
+ /**
* Callback invoked by {@link android.service.musicrecognition.MusicRecognitionService} to pass
* back the music search result.
*/
@@ -264,6 +319,29 @@ 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(
+ Objects.requireNonNull(AppOpsManager.permissionToOp(RECORD_AUDIO)),
+ mServiceInfo.applicationInfo.uid,
+ mServiceInfo.packageName,
+ mAttributionTag,
+ mAttributionMessage);
+ }
+
+
+ /** Tracks that the RECORD_AUDIO operation finished. */
+ private void finishRecordAudioOp() {
+ mAppOpsManager.finishProxyOp(
+ Objects.requireNonNull(AppOpsManager.permissionToOp(RECORD_AUDIO)),
+ mServiceInfo.applicationInfo.uid,
+ mServiceInfo.packageName,
+ mAttributionTag);
+ }
+
/** Establishes an audio stream from the DSP audio source. */
private static AudioRecord createAudioRecord(
@NonNull RecognitionRequest recognitionRequest,
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java
index 38f43138aee0..e145d3301292 100644
--- a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java
@@ -45,7 +45,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
- * Service which allows a DSP audio event to be securely streamed to a designated {@link
+ * Service which allows audio to be securely streamed to a designated {@link
* MusicRecognitionService}.
*/
public class MusicRecognitionManagerService extends
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 7036ccfbd951..c68004a3591b 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.net-sources",
srcs: ["java/**/*.java"],
diff --git a/services/people/Android.bp b/services/people/Android.bp
index 9bdf488d6631..c0188eca1970 100644
--- a/services/people/Android.bp
+++ b/services/people/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library_static {
name: "services.people",
defaults: ["platform_service_defaults"],
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 5453de14a5f1..e7d01219f6c1 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -16,12 +16,14 @@
package com.android.server.people;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.people.ConversationChannel;
import android.app.people.ConversationStatus;
+import android.app.people.IConversationListener;
import android.app.people.IPeopleManager;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionSessionId;
@@ -29,12 +31,15 @@ import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.IPredictionCallback;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
+import android.content.pm.ShortcutInfo;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.Process;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -45,6 +50,7 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.people.data.DataManager;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@@ -56,7 +62,9 @@ public class PeopleService extends SystemService {
private static final String TAG = "PeopleService";
- private final DataManager mDataManager;
+ private DataManager mDataManager;
+ @VisibleForTesting
+ ConversationListenerHelper mConversationListenerHelper;
private PackageManagerInternal mPackageManagerInternal;
@@ -69,6 +77,8 @@ public class PeopleService extends SystemService {
super(context);
mDataManager = new DataManager(context);
+ mConversationListenerHelper = new ConversationListenerHelper();
+ mDataManager.addConversationsListener(mConversationListenerHelper);
}
@Override
@@ -146,12 +156,14 @@ public class PeopleService extends SystemService {
* @param message used as message if SecurityException is thrown
* @throws SecurityException if the caller is not system or root
*/
- private static void enforceSystemRootOrSystemUI(Context context, String message) {
+ @VisibleForTesting
+ protected void enforceSystemRootOrSystemUI(Context context, String message) {
if (isSystemOrRoot()) return;
context.enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
message);
}
+ @VisibleForTesting
final IBinder mService = new IPeopleManager.Stub() {
@Override
@@ -184,6 +196,20 @@ public class PeopleService extends SystemService {
}
@Override
+ public boolean isConversation(String packageName, int userId, String shortcutId) {
+ enforceHasReadPeopleDataPermission();
+ handleIncomingUser(userId);
+ return mDataManager.isConversation(packageName, userId, shortcutId);
+ }
+
+ private void enforceHasReadPeopleDataPermission() throws SecurityException {
+ if (getContext().checkCallingPermission(Manifest.permission.READ_PEOPLE_DATA)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Caller doesn't have READ_PEOPLE_DATA permission.");
+ }
+ }
+
+ @Override
public long getLastInteraction(String packageName, int userId, String shortcutId) {
enforceSystemRootOrSystemUI(getContext(), "get last interaction");
return mDataManager.getLastInteraction(packageName, userId, shortcutId);
@@ -225,8 +251,137 @@ public class PeopleService extends SystemService {
return new ParceledListSlice<>(
mDataManager.getStatuses(packageName, userId, conversationId));
}
+
+ @Override
+ public void registerConversationListener(
+ String packageName, int userId, String shortcutId, IConversationListener listener) {
+ enforceSystemRootOrSystemUI(getContext(), "register conversation listener");
+ mConversationListenerHelper.addConversationListener(
+ new ListenerKey(packageName, userId, shortcutId), listener);
+ }
+
+ @Override
+ public void unregisterConversationListener(IConversationListener listener) {
+ enforceSystemRootOrSystemUI(getContext(), "unregister conversation listener");
+ mConversationListenerHelper.removeConversationListener(listener);
+ }
};
+ /**
+ * Listeners for conversation changes.
+ *
+ * @hide
+ */
+ public interface ConversationsListener {
+ /**
+ * Triggers with the list of modified conversations from {@link DataManager} for dispatching
+ * relevant updates to clients.
+ *
+ * @param conversations The conversations with modified data
+ * @see IPeopleManager#registerConversationListener(String, int, String,
+ * android.app.people.ConversationListener)
+ */
+ default void onConversationsUpdate(@NonNull List<ConversationChannel> conversations) {
+ }
+ }
+
+ /**
+ * Implements {@code ConversationListenerHelper} to dispatch conversation updates to registered
+ * clients.
+ */
+ public static class ConversationListenerHelper implements ConversationsListener {
+
+ ConversationListenerHelper() {
+ }
+
+ @VisibleForTesting
+ final RemoteCallbackList<IConversationListener> mListeners =
+ new RemoteCallbackList<>();
+
+ /** Adds {@code listener} with {@code key} associated. */
+ public synchronized void addConversationListener(ListenerKey key,
+ IConversationListener listener) {
+ mListeners.unregister(listener);
+ mListeners.register(listener, key);
+ }
+
+ /** Removes {@code listener}. */
+ public synchronized void removeConversationListener(
+ IConversationListener listener) {
+ mListeners.unregister(listener);
+ }
+
+ @Override
+ /** Dispatches updates to {@code mListeners} with keys mapped to {@code conversations}. */
+ public void onConversationsUpdate(List<ConversationChannel> conversations) {
+ int count = mListeners.beginBroadcast();
+ // Early opt-out if no listeners are registered.
+ if (count == 0) {
+ return;
+ }
+ Map<ListenerKey, ConversationChannel> keyedConversations = new HashMap<>();
+ for (ConversationChannel conversation : conversations) {
+ keyedConversations.put(getListenerKey(conversation), conversation);
+ }
+ for (int i = 0; i < count; i++) {
+ final ListenerKey listenerKey = (ListenerKey) mListeners.getBroadcastCookie(i);
+ if (!keyedConversations.containsKey(listenerKey)) {
+ continue;
+ }
+ final IConversationListener listener = mListeners.getBroadcastItem(i);
+ try {
+ ConversationChannel channel = keyedConversations.get(listenerKey);
+ listener.onConversationUpdate(channel);
+ } catch (RemoteException e) {
+ // The RemoteCallbackList will take care of removing the dead object.
+ }
+ }
+ mListeners.finishBroadcast();
+ }
+
+ private ListenerKey getListenerKey(ConversationChannel conversation) {
+ ShortcutInfo info = conversation.getShortcutInfo();
+ return new ListenerKey(info.getPackage(), info.getUserId(),
+ info.getId());
+ }
+ }
+
+ private static class ListenerKey {
+ private final String mPackageName;
+ private final Integer mUserId;
+ private final String mShortcutId;
+
+ ListenerKey(String packageName, Integer userId, String shortcutId) {
+ this.mPackageName = packageName;
+ this.mUserId = userId;
+ this.mShortcutId = shortcutId;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public Integer getUserId() {
+ return mUserId;
+ }
+
+ public String getShortcutId() {
+ return mShortcutId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ ListenerKey key = (ListenerKey) o;
+ return key.getPackageName().equals(mPackageName) && key.getUserId() == mUserId
+ && key.getShortcutId().equals(mShortcutId);
+ }
+
+ @Override
+ public int hashCode() {
+ return mPackageName.hashCode() + mUserId.hashCode() + mShortcutId.hashCode();
+ }
+ }
+
@VisibleForTesting
final class LocalService extends PeopleServiceInternal {
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 444f9c685a1b..75614d60ab9e 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -48,6 +48,7 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.os.CancellationSignal;
import android.os.Handler;
+import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -58,6 +59,7 @@ import android.provider.Telephony.MmsSms;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
+import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -74,14 +76,17 @@ import com.android.internal.telephony.SmsApplication;
import com.android.server.LocalServices;
import com.android.server.notification.NotificationManagerInternal;
import com.android.server.notification.ShortcutHelper;
+import com.android.server.people.PeopleService;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -117,6 +122,11 @@ public class DataManager {
private final SparseArray<ScheduledFuture<?>> mUsageStatsQueryFutures = new SparseArray<>();
private final SparseArray<NotificationListener> mNotificationListeners = new SparseArray<>();
private final SparseArray<PackageMonitor> mPackageMonitors = new SparseArray<>();
+ @GuardedBy("mLock")
+ private final List<PeopleService.ConversationsListener> mConversationsListeners =
+ new ArrayList<>(1);
+ private final Handler mHandler;
+
private ContentObserver mCallLogContentObserver;
private ContentObserver mMmsSmsContentObserver;
@@ -127,14 +137,14 @@ public class DataManager {
private ConversationStatusExpirationBroadcastReceiver mStatusExpReceiver;
public DataManager(Context context) {
- this(context, new Injector());
+ this(context, new Injector(), BackgroundThread.get().getLooper());
}
- @VisibleForTesting
- DataManager(Context context, Injector injector) {
+ DataManager(Context context, Injector injector, Looper looper) {
mContext = context;
mInjector = injector;
mScheduledExecutor = mInjector.createScheduledExecutor();
+ mHandler = new Handler(looper);
}
/** Initialization. Called when the system services are up running. */
@@ -233,20 +243,19 @@ public class DataManager {
PackageData packageData = userData.getPackageData(packageName);
// App may have been uninstalled.
if (packageData != null) {
- return getConversationChannel(packageData, shortcutId);
+ ConversationInfo conversationInfo = packageData.getConversationInfo(shortcutId);
+ return getConversationChannel(packageName, userId, shortcutId, conversationInfo);
}
}
return null;
}
@Nullable
- private ConversationChannel getConversationChannel(PackageData packageData, String shortcutId) {
- ConversationInfo conversationInfo = packageData.getConversationInfo(shortcutId);
+ private ConversationChannel getConversationChannel(String packageName, int userId,
+ String shortcutId, ConversationInfo conversationInfo) {
if (conversationInfo == null) {
return null;
}
- int userId = packageData.getUserId();
- String packageName = packageData.getPackageName();
ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId);
if (shortcutInfo == null) {
return null;
@@ -277,7 +286,8 @@ public class DataManager {
return;
}
String shortcutId = conversationInfo.getShortcutId();
- ConversationChannel channel = getConversationChannel(packageData, shortcutId);
+ ConversationChannel channel = getConversationChannel(packageData.getPackageName(),
+ packageData.getUserId(), shortcutId, conversationInfo);
if (channel == null || channel.getParentNotificationChannel() == null) {
return;
}
@@ -354,6 +364,14 @@ public class DataManager {
});
}
+ /** Returns whether {@code shortcutId} is backed by Conversation. */
+ public boolean isConversation(String packageName, int userId, String shortcutId) {
+ ConversationChannel channel = getConversation(packageName, userId, shortcutId);
+ return channel != null
+ && channel.getShortcutInfo() != null
+ && !TextUtils.isEmpty(channel.getShortcutInfo().getLabel());
+ }
+
/**
* Returns the last notification interaction with the specified conversation. If the
* conversation can't be found or no interactions have been recorded, returns 0L.
@@ -375,7 +393,11 @@ public class DataManager {
ConversationInfo convToModify = getConversationInfoOrThrow(cs, conversationId);
ConversationInfo.Builder builder = new ConversationInfo.Builder(convToModify);
builder.addOrUpdateStatus(status);
- cs.addOrUpdate(builder.build());
+ ConversationInfo modifiedConv = builder.build();
+ cs.addOrUpdate(modifiedConv);
+ ConversationChannel conversation = getConversationChannel(packageName, userId,
+ conversationId, modifiedConv);
+ notifyConversationsListeners(Arrays.asList(conversation));
if (status.getEndTimeMillis() >= 0) {
mStatusExpReceiver.scheduleExpiration(
@@ -1226,6 +1248,32 @@ public class DataManager {
}
}
+ /** Adds {@code listener} to be notified on conversation changes. */
+ public void addConversationsListener(
+ @NonNull PeopleService.ConversationsListener listener) {
+ synchronized (mConversationsListeners) {
+ mConversationsListeners.add(Objects.requireNonNull(listener));
+ }
+ }
+
+ // TODO(b/178792356): Trigger ConversationsListener on all-related data changes.
+ @VisibleForTesting
+ void notifyConversationsListeners(
+ @Nullable final List<ConversationChannel> changedConversations) {
+ mHandler.post(() -> {
+ try {
+ final List<PeopleService.ConversationsListener> copy;
+ synchronized (mLock) {
+ copy = new ArrayList<>(mConversationsListeners);
+ }
+ for (PeopleService.ConversationsListener listener : copy) {
+ listener.onConversationsUpdate(changedConversations);
+ }
+ } catch (Exception e) {
+ }
+ });
+ }
+
/** A {@link BroadcastReceiver} that receives the intents for a specified user. */
private class PerUserBroadcastReceiver extends BroadcastReceiver {
diff --git a/services/print/Android.bp b/services/print/Android.bp
index be5f082eb2b3..5b4349a92692 100644
--- a/services/print/Android.bp
+++ b/services/print/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.print-sources",
srcs: ["java/**/*.java"],
diff --git a/services/profcollect/Android.bp b/services/profcollect/Android.bp
index 7f5f623879f7..2040bb6e544f 100644
--- a/services/profcollect/Android.bp
+++ b/services/profcollect/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.profcollect-javasources",
srcs: ["src/**/*.java"],
diff --git a/services/restrictions/Android.bp b/services/restrictions/Android.bp
index 60d161de1b2b..62316f14a099 100644
--- a/services/restrictions/Android.bp
+++ b/services/restrictions/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.restrictions-sources",
srcs: ["java/**/*.java"],
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index 1ae2aec90ba3..52eae21f9e66 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -16,6 +16,15 @@
// FrameworksServicesLib app just for Robolectric test target #
//##################################################################
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "FrameworksServicesLib",
platform_apis: true,
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index 32587accf160..506e1561e8d7 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -15,6 +15,15 @@
//##################################################################
// BackupFrameworksServicesLib app just for Robolectric test target #
//##################################################################
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "BackupFrameworksServicesLib",
platform_apis: true,
diff --git a/services/startop/Android.bp b/services/startop/Android.bp
index 157408f24c5a..c56c463d0168 100644
--- a/services/startop/Android.bp
+++ b/services/startop/Android.bp
@@ -14,6 +14,17 @@
* limitations under the License.
*/
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-MIT
+ // SPDX-license-identifier-Unicode-DFS
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library_static {
name: "services.startop",
defaults: ["platform_service_defaults"],
diff --git a/services/systemcaptions/Android.bp b/services/systemcaptions/Android.bp
index 54a5a794d086..4cb58cbb2284 100644
--- a/services/systemcaptions/Android.bp
+++ b/services/systemcaptions/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.systemcaptions-sources",
srcs: ["java/**/*.java"],
diff --git a/services/tests/PackageManagerComponentOverrideTests/Android.bp b/services/tests/PackageManagerComponentOverrideTests/Android.bp
index a2668a184fe0..19fdf6056453 100644
--- a/services/tests/PackageManagerComponentOverrideTests/Android.bp
+++ b/services/tests/PackageManagerComponentOverrideTests/Android.bp
@@ -17,6 +17,15 @@
// NOTE: This test is separate from service tests since it relies on same vs different calling UID,
// and this is more representative of a real caller. It also uses Mockito extended, and this
// prevents converting the entire services test module.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "PackageManagerComponentOverrideTests",
srcs: [
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index a941331fcec9..06313da58da1 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test_host {
name: "PackageManagerServiceHostTests",
srcs: ["src/**/*.kt"],
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp
index 4a3076e4736a..1cc7ccc01283 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "PackageManagerTestAppStub",
manifest: "AndroidManifestVersion1.xml",
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 d863194d6889..2d23fb4990bf 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
@@ -18,6 +18,7 @@ package com.android.server.pm.test.verify.domain
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
@@ -25,7 +26,6 @@ import android.content.pm.parsing.component.ParsedIntentInfo
import android.os.Build
import android.os.Process
import android.util.ArraySet
-import android.util.Singleton
import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.pm.PackageSetting
@@ -46,14 +46,11 @@ import org.mockito.Mockito.anyLong
import org.mockito.Mockito.anyString
import org.mockito.Mockito.eq
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.testng.Assert.assertThrows
import java.io.File
import java.util.UUID
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
-private typealias Enforcer = DomainVerificationEnforcer
-
@RunWith(Parameterized::class)
class DomainVerificationEnforcerTest {
@@ -64,64 +61,27 @@ class DomainVerificationEnforcerTest {
private const val VERIFIER_UID = Process.FIRST_APPLICATION_UID + 1
private const val NON_VERIFIER_UID = Process.FIRST_APPLICATION_UID + 2
- private const val TEST_PKG = "com.test"
+ private const val VISIBLE_PKG = "com.test.visible"
+ private val VISIBLE_UUID = UUID.fromString("8db01272-270d-4606-a3db-bb35228ff9a2")
+ private const val INVISIBLE_PKG = "com.test.invisible"
+ private val INVISIBLE_UUID = UUID.fromString("16dcb029-d96c-4a19-833a-4c9d72e2ebc3")
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun parameters(): Array<Any> {
- val makeEnforcer: (Context) -> DomainVerificationEnforcer = {
- DomainVerificationEnforcer(it)
- }
+ val visiblePkg = mockPkg(VISIBLE_PKG)
+ val visiblePkgSetting = mockPkgSetting(VISIBLE_PKG, VISIBLE_UUID)
+ val invisiblePkg = mockPkg(INVISIBLE_PKG)
+ val invisiblePkgSetting = mockPkgSetting(INVISIBLE_PKG, INVISIBLE_UUID)
- val mockPkg = mockThrowOnUnmocked<AndroidPackage> {
- whenever(packageName) { TEST_PKG }
- whenever(targetSdkVersion) { Build.VERSION_CODES.S }
- whenever(activities) {
- listOf(
- ParsedActivity().apply {
- addIntent(
- ParsedIntentInfo().apply {
- autoVerify = true
- addAction(Intent.ACTION_VIEW)
- addCategory(Intent.CATEGORY_BROWSABLE)
- addCategory(Intent.CATEGORY_DEFAULT)
- addDataScheme("https")
- addDataAuthority("example.com", null)
- }
- )
+ val makeEnforcer: (Context) -> DomainVerificationEnforcer = {
+ DomainVerificationEnforcer(it).apply {
+ setCallback(mockThrowOnUnmocked {
+ whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
+ whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
+ true
}
- )
- }
- }
-
- val uuid = UUID.randomUUID()
-
- // TODO: PackageSetting field encapsulation to move to whenever(name)
- val mockPkgSetting = spyThrowOnUnmocked(
- PackageSetting(
- TEST_PKG,
- TEST_PKG,
- File("/test"),
- null,
- null,
- null,
- null,
- 1,
- 0,
- 0,
- 0,
- null,
- null,
- null,
- uuid
- )
- ) {
- whenever(getPkg()) { mockPkg }
- whenever(domainSetId) { uuid }
- whenever(userState) {
- SparseArray<PackageUserState>().apply {
- this[0] = PackageUserState()
- }
+ })
}
}
@@ -129,142 +89,160 @@ class DomainVerificationEnforcerTest {
{
val callingUidInt = AtomicInteger(-1)
val callingUserIdInt = AtomicInteger(-1)
- Triple(
- callingUidInt, callingUserIdInt, DomainVerificationService(
- it,
- mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
- mockThrowOnUnmocked {
- whenever(
- isChangeEnabled(
- anyLong(),
- any()
- )
- ) { true }
- }).apply {
- setConnection(mockThrowOnUnmocked {
- whenever(callingUid) { callingUidInt.get() }
- whenever(callingUserId) { callingUserIdInt.get() }
- whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting }
- whenever(getPackageLocked(TEST_PKG)) { mockPkg }
- whenever(schedule(anyInt(), any()))
- whenever(scheduleWriteSettings())
- })
+
+ 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)
+ }
+
+ Triple(callingUidInt, callingUserIdInt, service)
}
fun enforcer(
type: Type,
name: String,
- block: DomainVerificationEnforcer.(
- callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy
- ) -> Unit
- ) = Params(
- type,
- makeEnforcer,
- name
- ) { enforcer, callingUid, callingUserId, userId, proxy ->
- enforcer.block(callingUid, callingUserId, userId, proxy)
+ block: DomainVerificationEnforcer.(Params.Input<DomainVerificationEnforcer>) -> Any?
+ ) = Params(type, makeEnforcer, name) {
+ it.target.block(it)
}
fun service(
type: Type,
name: String,
- block: DomainVerificationService.(
- callingUid: Int, callingUserId: Int, userId: Int
- ) -> Unit
- ) = Params(
- type,
- makeService,
- name
- ) { uidAndUserIdAndService, callingUid, callingUserId, userId, proxy ->
- val (callingUidInt, callingUserIdInt, service) = uidAndUserIdAndService
- callingUidInt.set(callingUid)
- callingUserIdInt.set(callingUserId)
- service.setProxy(proxy)
- service.addPackage(mockPkgSetting)
- service.block(callingUid, callingUserId, userId)
+ block: DomainVerificationService.(Params.Input<Triple<AtomicInteger, AtomicInteger, DomainVerificationService>>) -> Any?
+ ) = Params(type, makeService, name) {
+ val (callingUidInt, callingUserIdInt, service) = it.target
+ callingUidInt.set(it.callingUid)
+ callingUserIdInt.set(it.callingUserId)
+ service.proxy = it.proxy
+ service.addPackage(visiblePkgSetting)
+ service.addPackage(invisiblePkgSetting)
+ service.block(it)
}
return arrayOf(
- enforcer(Type.INTERNAL, "internal") { callingUid, _, _, _ ->
- assertInternal(callingUid)
+ enforcer(Type.INTERNAL, "internal") {
+ assertInternal(it.callingUid)
},
- enforcer(Type.QUERENT, "approvedQuerent") { callingUid, _, _, proxy ->
- assertApprovedQuerent(callingUid, proxy)
+ enforcer(Type.QUERENT, "approvedQuerent") {
+ assertApprovedQuerent(it.callingUid, it.proxy)
},
- enforcer(Type.VERIFIER, "approvedVerifier") { callingUid, _, _, proxy ->
- assertApprovedVerifier(callingUid, proxy)
+ enforcer(Type.VERIFIER, "approvedVerifier") {
+ assertApprovedVerifier(it.callingUid, it.proxy)
},
enforcer(
Type.SELECTOR,
"approvedUserSelector"
- ) { callingUid, callingUserId, userId, _ ->
- assertApprovedUserSelector(callingUid, callingUserId, userId)
+ ) {
+ assertApprovedUserSelector(
+ it.callingUid, it.callingUserId,
+ it.targetPackageName, it.userId
+ )
},
-
- service(Type.INTERNAL, "setStatusInternalPackageName") { _, _, _ ->
+ service(Type.INTERNAL, "setStatusInternalPackageName") {
setDomainVerificationStatusInternal(
- TEST_PKG,
+ it.targetPackageName,
DomainVerificationManager.STATE_SUCCESS,
ArraySet(setOf("example.com"))
)
},
- service(Type.INTERNAL, "setUserSelectionInternal") { _, _, userId ->
+ service(Type.INTERNAL, "setUserSelectionInternal") {
setDomainVerificationUserSelectionInternal(
- userId,
- TEST_PKG,
+ it.userId,
+ it.targetPackageName,
false,
ArraySet(setOf("example.com"))
)
},
- service(Type.INTERNAL, "verifyPackages") { _, _, _ ->
- verifyPackages(listOf(TEST_PKG), true)
+ service(Type.INTERNAL, "verifyPackages") {
+ verifyPackages(listOf(it.targetPackageName), true)
},
- service(Type.INTERNAL, "clearState") { _, _, _ ->
- clearDomainVerificationState(listOf(TEST_PKG))
+ service(Type.INTERNAL, "clearState") {
+ clearDomainVerificationState(listOf(it.targetPackageName))
},
- service(Type.INTERNAL, "clearUserSelections") { _, _, userId ->
- clearUserSelections(listOf(TEST_PKG), userId)
+ service(Type.INTERNAL, "clearUserSelections") {
+ clearUserSelections(listOf(it.targetPackageName), it.userId)
},
- service(Type.VERIFIER, "getPackageNames") { _, _, _ ->
+ service(Type.VERIFIER, "getPackageNames") {
validVerificationPackageNames
},
- service(Type.QUERENT, "getInfo") { _, _, _ ->
- getDomainVerificationInfo(TEST_PKG)
+ service(Type.QUERENT, "getInfo") {
+ getDomainVerificationInfo(it.targetPackageName)
},
- service(Type.VERIFIER, "setStatus") { _, _, _ ->
+ service(Type.VERIFIER, "setStatus") {
setDomainVerificationStatus(
- uuid,
+ it.targetDomainSetId,
setOf("example.com"),
DomainVerificationManager.STATE_SUCCESS
)
},
- service(Type.VERIFIER, "setStatusInternalUid") { callingUid, _, _ ->
+ service(Type.VERIFIER, "setStatusInternalUid") {
setDomainVerificationStatusInternal(
- callingUid,
- uuid,
+ it.callingUid,
+ it.targetDomainSetId,
setOf("example.com"),
DomainVerificationManager.STATE_SUCCESS
)
},
- service(Type.SELECTOR, "setLinkHandlingAllowed") { _, _, _ ->
- setDomainVerificationLinkHandlingAllowed(TEST_PKG, true)
+ service(Type.SELECTOR, "setLinkHandlingAllowed") {
+ setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true)
},
- service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") { _, _, userId ->
- setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, userId)
+ service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") {
+ setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true, it.userId)
},
- service(Type.SELECTOR, "getUserSelection") { _, _, _ ->
- getDomainVerificationUserSelection(TEST_PKG)
+ service(Type.SELECTOR, "getUserSelection") {
+ getDomainVerificationUserSelection(it.targetPackageName)
},
- service(Type.SELECTOR_USER, "getUserSelectionUserId") { _, _, userId ->
- getDomainVerificationUserSelection(TEST_PKG, userId)
+ service(Type.SELECTOR_USER, "getUserSelectionUserId") {
+ getDomainVerificationUserSelection(it.targetPackageName, it.userId)
},
- service(Type.SELECTOR, "setUserSelection") { _, _, _ ->
- setDomainVerificationUserSelection(uuid, setOf("example.com"), true)
+ service(Type.SELECTOR, "setUserSelection") {
+ setDomainVerificationUserSelection(
+ it.targetDomainSetId,
+ setOf("example.com"),
+ true
+ )
+ },
+ service(Type.SELECTOR_USER, "setUserSelectionUserId") {
+ setDomainVerificationUserSelection(
+ it.targetDomainSetId,
+ setOf("example.com"),
+ true,
+ it.userId
+ )
},
- service(Type.SELECTOR_USER, "setUserSelectionUserId") { _, _, userId ->
- setDomainVerificationUserSelection(uuid, setOf("example.com"), true, userId)
+ service(Type.LEGACY_SELECTOR, "setLegacyUserState") {
+ setLegacyUserState(
+ it.targetPackageName, it.userId,
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER
+ )
+ },
+ service(Type.LEGACY_QUERENT, "getLegacyUserState") {
+ getLegacyState(it.targetPackageName, it.userId)
},
)
}
@@ -273,9 +251,7 @@ class DomainVerificationEnforcerTest {
val type: Type,
val construct: (context: Context) -> T,
val name: String,
- private val method: (
- T, callingUid: Int, callingUserId: Int, userId: Int, proxy: DomainVerificationProxy
- ) -> Unit
+ private val method: (Input<T>) -> Any?
) {
override fun toString() = "${type}_$name"
@@ -284,11 +260,81 @@ class DomainVerificationEnforcerTest {
callingUid: Int,
callingUserId: Int,
userId: Int,
+ targetPackageName: String,
+ targetDomainSetId: UUID,
proxy: DomainVerificationProxy
- ) {
- @Suppress("UNCHECKED_CAST")
- method(target as T, callingUid, callingUserId, userId, proxy)
+ ): Any? = method(
+ Input(
+ @Suppress("UNCHECKED_CAST")
+ target as T,
+ callingUid,
+ callingUserId,
+ userId,
+ targetPackageName,
+ targetDomainSetId,
+ proxy
+ )
+ )
+
+ data class Input<T>(
+ val target: T,
+ val callingUid: Int,
+ val callingUserId: Int,
+ val userId: Int,
+ val targetPackageName: String,
+ val targetDomainSetId: UUID,
+ val proxy: DomainVerificationProxy
+ )
+ }
+
+ fun mockPkg(packageName: String) = mockThrowOnUnmocked<AndroidPackage> {
+ whenever(this.packageName) { packageName }
+ whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+ whenever(activities) {
+ listOf(
+ ParsedActivity().apply {
+ addIntent(
+ ParsedIntentInfo().apply {
+ autoVerify = true
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("https")
+ addDataAuthority("example.com", null)
+ }
+ )
+ }
+ )
+ }
+ }
+
+ fun mockPkgSetting(packageName: String, domainSetId: UUID) = spyThrowOnUnmocked(
+ PackageSetting(
+ packageName,
+ packageName,
+ File("/test"),
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ domainSetId
+ )
+ ) {
+ whenever(getPkg()) { mockPkg(packageName) }
+ whenever(this.domainSetId) { domainSetId }
+ whenever(userState) {
+ SparseArray<PackageUserState>().apply {
+ this[0] = PackageUserState()
+ }
}
+ whenever(getInstantApp(anyInt())) { false }
}
}
@@ -309,6 +355,8 @@ class DomainVerificationEnforcerTest {
Type.VERIFIER -> approvedVerifier()
Type.SELECTOR -> approvedUserSelector(verifyCrossUser = false)
Type.SELECTOR_USER -> approvedUserSelector(verifyCrossUser = true)
+ Type.LEGACY_QUERENT -> legacyQuerent()
+ Type.LEGACY_SELECTOR -> legacyUserSelector()
}.run { /*exhaust*/ }
}
@@ -316,24 +364,30 @@ class DomainVerificationEnforcerTest {
val context: Context = mockThrowOnUnmocked()
val target = params.construct(context)
- INTERNAL_UIDS.forEach { runMethod(target, it) }
- assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) }
- assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+ // Internal doesn't care about visibility
+ listOf(true, false).forEach { visible ->
+ INTERNAL_UIDS.forEach { runMethod(target, it, visible) }
+ assertFails { runMethod(target, VERIFIER_UID, visible) }
+ assertFails {
+ runMethod(target, NON_VERIFIER_UID, visible)
+ }
+ }
}
fun approvedQuerent() {
val allowUserSelection = AtomicBoolean(false)
+ val allowPreferredApps = AtomicBoolean(false)
+ val allowQueryAll = AtomicBoolean(false)
val context: Context = mockThrowOnUnmocked {
- whenever(
- enforcePermission(
- eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
- anyInt(), anyInt(), anyString()
- )
- ) {
- if (!allowUserSelection.get()) {
- throw SecurityException()
- }
- }
+ initPermission(
+ allowUserSelection,
+ android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
+ )
+ initPermission(
+ allowPreferredApps,
+ android.Manifest.permission.SET_PREFERRED_APPLICATIONS
+ )
+ initPermission(allowQueryAll, android.Manifest.permission.QUERY_ALL_PACKAGES)
}
val target = params.construct(context)
@@ -341,27 +395,41 @@ class DomainVerificationEnforcerTest {
verifyNoMoreInteractions(context)
+ assertFails { runMethod(target, VERIFIER_UID) }
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+ // Check that the verifier only needs QUERY_ALL to pass
+ allowQueryAll.set(true)
runMethod(target, VERIFIER_UID)
- assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+ allowQueryAll.set(false)
+
+ allowPreferredApps.set(true)
+
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
allowUserSelection.set(true)
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+ allowQueryAll.set(true)
+
runMethod(target, NON_VERIFIER_UID)
}
fun approvedVerifier() {
- val shouldThrow = AtomicBoolean(false)
+ val allowDomainVerificationAgent = AtomicBoolean(false)
+ val allowIntentVerificationAgent = AtomicBoolean(false)
+ val allowQueryAll = AtomicBoolean(false)
val context: Context = mockThrowOnUnmocked {
- whenever(
- enforcePermission(
- eq(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT),
- anyInt(), anyInt(), anyString()
- )
- ) {
- if (shouldThrow.get()) {
- throw SecurityException()
- }
- }
+ initPermission(
+ allowDomainVerificationAgent,
+ android.Manifest.permission.DOMAIN_VERIFICATION_AGENT
+ )
+ initPermission(
+ allowIntentVerificationAgent,
+ android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT
+ )
+ initPermission(allowQueryAll, android.Manifest.permission.QUERY_ALL_PACKAGES)
}
val target = params.construct(context)
@@ -369,94 +437,241 @@ class DomainVerificationEnforcerTest {
verifyNoMoreInteractions(context)
+ assertFails { runMethod(target, VERIFIER_UID) }
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+ allowDomainVerificationAgent.set(true)
+
+ assertFails { runMethod(target, VERIFIER_UID) }
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+ allowQueryAll.set(true)
+
runMethod(target, VERIFIER_UID)
- assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
- shouldThrow.set(true)
+ // Check that v1 verifiers are also allowed through
+ allowDomainVerificationAgent.set(false)
+ allowIntentVerificationAgent.set(true)
- assertThrows(SecurityException::class.java) { runMethod(target, VERIFIER_UID) }
- assertThrows(SecurityException::class.java) { runMethod(target, NON_VERIFIER_UID) }
+ runMethod(target, VERIFIER_UID)
+ assertFails { runMethod(target, NON_VERIFIER_UID) }
}
fun approvedUserSelector(verifyCrossUser: Boolean) {
- val allowUserSelection = AtomicBoolean(true)
- val allowInteractAcrossUsers = AtomicBoolean(true)
+ val allowUserSelection = AtomicBoolean(false)
+ val allowInteractAcrossUsers = AtomicBoolean(false)
val context: Context = mockThrowOnUnmocked {
- whenever(
- enforcePermission(
- eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
- anyInt(), anyInt(), anyString()
- )
- ) {
- if (!allowUserSelection.get()) {
- throw SecurityException()
+ initPermission(
+ allowUserSelection,
+ android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
+ )
+ initPermission(
+ allowInteractAcrossUsers,
+ android.Manifest.permission.INTERACT_ACROSS_USERS
+ )
+ }
+ val target = params.construct(context)
+
+ 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)
+ }
}
- }
- whenever(
- enforcePermission(
- eq(android.Manifest.permission.INTERACT_ACROSS_USERS),
- anyInt(), anyInt(), anyString()
- )
- ) {
- if (!allowInteractAcrossUsers.get()) {
- throw SecurityException()
+ } else {
+ allUids.forEach {
+ runMethod(target, it, visible = true, callingUserId, targetUserId)
}
}
- }
- val target = params.construct(context)
- fun runEachTestCaseWrapped(
- callingUserId: Int,
- targetUserId: Int,
- block: (testCase: () -> Unit) -> Unit = { it.invoke() }
- ) {
- block { runMethod(target, VERIFIER_UID, callingUserId, targetUserId) }
- block { runMethod(target, NON_VERIFIER_UID, 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)
+ }
+ }
}
val callingUserId = 0
val notCallingUserId = 1
- runEachTestCaseWrapped(callingUserId, callingUserId)
+ runTestCases(callingUserId, callingUserId, throws = true)
if (verifyCrossUser) {
- runEachTestCaseWrapped(callingUserId, notCallingUserId)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
}
- allowInteractAcrossUsers.set(false)
+ allowUserSelection.set(true)
+
+ runTestCases(callingUserId, callingUserId, throws = false)
+ if (verifyCrossUser) {
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+ }
- runEachTestCaseWrapped(callingUserId, callingUserId)
+ allowInteractAcrossUsers.set(true)
+ runTestCases(callingUserId, callingUserId, throws = false)
if (verifyCrossUser) {
- runEachTestCaseWrapped(callingUserId, notCallingUserId) {
- assertThrows(SecurityException::class.java, it)
+ runTestCases(callingUserId, notCallingUserId, throws = false)
+ }
+ }
+
+ private fun legacyUserSelector() {
+ val allowInteractAcrossUsers = AtomicBoolean(false)
+ val allowPreferredApps = AtomicBoolean(false)
+ val context: Context = mockThrowOnUnmocked {
+ initPermission(
+ allowInteractAcrossUsers,
+ android.Manifest.permission.INTERACT_ACROSS_USERS
+ )
+ initPermission(
+ allowPreferredApps,
+ android.Manifest.permission.SET_PREFERRED_APPLICATIONS
+ )
+ }
+ 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 doesn't use QUERY_ALL, so the invisible package should always fail
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = false, callingUserId, targetUserId)
+ }
}
}
- allowUserSelection.set(false)
+ val callingUserId = 0
+ val notCallingUserId = 1
+
+ runTestCases(callingUserId, callingUserId, throws = true)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+
+ allowPreferredApps.set(true)
- runEachTestCaseWrapped(callingUserId, callingUserId) {
- assertThrows(SecurityException::class.java, it)
+ runTestCases(callingUserId, callingUserId, throws = false)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+
+ allowInteractAcrossUsers.set(true)
+
+ runTestCases(callingUserId, callingUserId, throws = false)
+ runTestCases(callingUserId, notCallingUserId, throws = false)
+ }
+
+ private fun legacyQuerent() {
+ val allowInteractAcrossUsers = AtomicBoolean(false)
+ val allowInteractAcrossUsersFull = AtomicBoolean(false)
+ val context: Context = mockThrowOnUnmocked {
+ initPermission(
+ allowInteractAcrossUsers,
+ android.Manifest.permission.INTERACT_ACROSS_USERS
+ )
+ initPermission(
+ allowInteractAcrossUsersFull,
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
+ )
}
- if (verifyCrossUser) {
- runEachTestCaseWrapped(callingUserId, notCallingUserId) {
- assertThrows(SecurityException::class.java, it)
+ 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 doesn't use QUERY_ALL, so the invisible package should always fail
+ allUids.forEach {
+ assertFails {
+ runMethod(target, it, visible = false, callingUserId, targetUserId)
+ }
}
}
+ val callingUserId = 0
+ val notCallingUserId = 1
+
+ runTestCases(callingUserId, callingUserId, throws = false)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+
+ // Legacy requires the _FULL permission, so this should continue to fail
allowInteractAcrossUsers.set(true)
+ runTestCases(callingUserId, callingUserId, throws = false)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+
+ allowInteractAcrossUsersFull.set(true)
+ runTestCases(callingUserId, callingUserId, throws = false)
+ runTestCases(callingUserId, notCallingUserId, throws = false)
+ }
- runEachTestCaseWrapped(callingUserId, callingUserId) {
- assertThrows(SecurityException::class.java, it)
+ private fun Context.initPermission(boolean: AtomicBoolean, permission: String) {
+ whenever(enforcePermission(eq(permission), anyInt(), anyInt(), anyString())) {
+ if (!boolean.get()) {
+ throw SecurityException()
+ }
}
- if (verifyCrossUser) {
- runEachTestCaseWrapped(callingUserId, notCallingUserId) {
- assertThrows(SecurityException::class.java, it)
+ whenever(checkPermission(eq(permission), anyInt(), anyInt())) {
+ if (boolean.get()) {
+ PackageManager.PERMISSION_GRANTED
+ } else {
+ PackageManager.PERMISSION_DENIED
}
}
}
- private fun runMethod(target: Any, callingUid: Int, callingUserId: Int = 0, userId: Int = 0) {
- params.runMethod(target, callingUid, callingUserId, userId, proxy)
+ private fun runMethod(
+ target: Any,
+ callingUid: Int,
+ visible: Boolean = true,
+ callingUserId: Int = 0,
+ userId: Int = 0
+ ): Any? {
+ val packageName = if (visible) VISIBLE_PKG else INVISIBLE_PKG
+ val uuid = if (visible) VISIBLE_UUID else INVISIBLE_UUID
+ return params.runMethod(target, callingUid, callingUserId, userId, packageName, uuid, proxy)
+ }
+
+ 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")
+ }
+ }
+ } catch (e: SecurityException) {
+ } catch (e: PackageManager.NameNotFoundException) {
+ } catch (e: DomainVerificationManager.InvalidDomainSetException) {
+ // Any of these 3 exceptions are considered failures, which is expected
+ }
}
enum class Type {
@@ -473,6 +688,12 @@ class DomainVerificationEnforcerTest {
SELECTOR,
// Holding the user setting permission, but targeting cross user
- SELECTOR_USER
+ SELECTOR_USER,
+
+ // Legacy required no permissions except when cross-user
+ LEGACY_QUERENT,
+
+ // Holding the legacy preferred apps permission
+ LEGACY_SELECTOR
}
}
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 a76152c9df7d..a92ab9e35ddc 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
@@ -104,14 +104,14 @@ class DomainVerificationPersistenceTest {
userSelectionStates[1] = DomainVerificationUserState(1).apply {
addHosts(setOf("example-user1.com", "example-user1.org"))
- isDisallowLinkHandling = false
+ isLinkHandlingAllowed = true
}
}
val stateOne = mockEmptyPkgState(1).apply {
// It's valid to have a user selection without any autoVerify domains
userSelectionStates[1] = DomainVerificationUserState(1).apply {
addHosts(setOf("example-user1.com", "example-user1.org"))
- isDisallowLinkHandling = true
+ isLinkHandlingAllowed = false
}
}
@@ -137,13 +137,15 @@ class DomainVerificationPersistenceTest {
hasAutoVerifyDomains="true"
>
<state>
- <domain name="example.com" state="${DomainVerificationManager.STATE_SUCCESS}"/>
- <domain name="example.org" state="${DomainVerificationManager.STATE_FIRST_VERIFIER_DEFINED}"/>
+ <domain name="example.com" state="${
+ DomainVerificationManager.STATE_SUCCESS}"/>
+ <domain name="example.org" state="${
+ DomainVerificationManager.STATE_FIRST_VERIFIER_DEFINED}"/>
<not-domain name="not-domain.com" state="1"/>
<domain name="missing-state.com"/>
</state>
<user-states>
- <user-state userId="1" disallowLinkHandling="false">
+ <user-state userId="1" allowLinkHandling="true">
<enabled-hosts>
<host name="example-user1.com"/>
<not-host name="not-host.com"/>
@@ -171,7 +173,7 @@ class DomainVerificationPersistenceTest {
>
<state/>
<user-states>
- <user-state userId="1" disallowLinkHandling="true">
+ <user-state userId="1" allowLinkHandling="false">
<enabled-hosts>
<host name="example-user1.com"/>
<host name="example-user1.org"/>
@@ -214,9 +216,9 @@ class DomainVerificationPersistenceTest {
stateMap["$packageName.com"] = id
userSelectionStates[id] = DomainVerificationUserState(id).apply {
addHosts(setOf("$packageName-user.com"))
- isDisallowLinkHandling = true
+ isLinkHandlingAllowed = true
}
}
- private fun pkgName(id: Int) = "${PKG_PREFIX}.pkg$id"
+ private fun pkgName(id: Int) = "$PKG_PREFIX.pkg$id"
}
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
new file mode 100644
index 000000000000..48518f4693dd
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -0,0 +1,275 @@
+/*
+ * 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.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.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.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.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
+import java.io.File
+import java.util.UUID
+
+@RunWith(Parameterized::class)
+class DomainVerificationSettingsMutationTest {
+
+ companion object {
+ private const val TEST_PKG = "com.test"
+
+ // Pretend to be the system. This class doesn't verify any enforcement behavior.
+ private const val TEST_UID = Process.SYSTEM_UID
+ private const val TEST_USER_ID = 10
+ private val TEST_UUID = UUID.fromString("5168e42e-327e-432b-b562-cfb553518a70")
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun parameters(): Array<Any> {
+ val context: Context = mockThrowOnUnmocked {
+ whenever(
+ enforcePermission(
+ eq(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT),
+ anyInt(), anyInt(), anyString()
+ )
+ )
+ whenever(
+ enforcePermission(
+ eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
+ anyInt(), anyInt(), anyString()
+ )
+ )
+ whenever(
+ enforcePermission(
+ eq(android.Manifest.permission.INTERACT_ACROSS_USERS),
+ anyInt(), anyInt(), anyString()
+ )
+ )
+ whenever(
+ enforcePermission(
+ eq(android.Manifest.permission.SET_PREFERRED_APPLICATIONS),
+ anyInt(), anyInt(), anyString()
+ )
+ )
+ }
+ val proxy: DomainVerificationProxy = mockThrowOnUnmocked {
+ whenever(isCallerVerifier(anyInt())) { true }
+ whenever(sendBroadcastForPackages(any()))
+ }
+
+ val makeService: (DomainVerificationManagerInternal.Connection) -> DomainVerificationService =
+ { connection ->
+ DomainVerificationService(
+ context,
+ mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
+ mockThrowOnUnmocked {
+ whenever(isChangeEnabled(anyLong(),any())) { true }
+ }).apply {
+ setConnection(connection)
+ }
+ }
+
+ fun service(name: String, block: DomainVerificationService.() -> Unit) =
+ Params(makeService, name) { service ->
+ service.proxy = proxy
+ service.addPackage(mockPkgSetting())
+ service.block()
+ }
+
+ return arrayOf(
+ service("clearPackage") {
+ clearPackage(TEST_PKG)
+ },
+ service("clearUser") {
+ clearUser(TEST_USER_ID)
+ },
+ service("clearState") {
+ clearDomainVerificationState(listOf(TEST_PKG))
+ },
+ service("clearUserSelections") {
+ clearUserSelections(listOf(TEST_PKG), TEST_USER_ID)
+ },
+ service("setStatus") {
+ setDomainVerificationStatus(
+ TEST_UUID,
+ setOf("example.com"),
+ DomainVerificationManager.STATE_SUCCESS
+ )
+ },
+ service("setStatusInternalPackageName") {
+ setDomainVerificationStatusInternal(
+ TEST_PKG,
+ DomainVerificationManager.STATE_SUCCESS,
+ ArraySet(setOf("example.com"))
+ )
+ },
+ service("setStatusInternalUid") {
+ setDomainVerificationStatusInternal(
+ TEST_UID,
+ TEST_UUID,
+ setOf("example.com"),
+ DomainVerificationManager.STATE_SUCCESS
+ )
+ },
+ service("setLinkHandlingAllowed") {
+ setDomainVerificationLinkHandlingAllowed(TEST_PKG, true)
+ },
+ service("setLinkHandlingAllowedUserId") {
+ setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, TEST_USER_ID)
+ },
+ service("setLinkHandlingAllowedInternal") {
+ setDomainVerificationLinkHandlingAllowedInternal(TEST_PKG, true, TEST_USER_ID)
+ },
+ service("setUserSelection") {
+ setDomainVerificationUserSelection(TEST_UUID, setOf("example.com"), true)
+ },
+ service("setUserSelectionUserId") {
+ setDomainVerificationUserSelection(
+ TEST_UUID,
+ setOf("example.com"),
+ true,
+ TEST_USER_ID
+ )
+ },
+ service("setUserSelectionInternal") {
+ setDomainVerificationUserSelectionInternal(
+ TEST_USER_ID,
+ TEST_PKG,
+ true,
+ ArraySet(setOf("example.com")),
+ )
+ },
+ service("setLegacyUserState") {
+ setLegacyUserState(
+ TEST_PKG,
+ TEST_USER_ID,
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER
+ )
+ },
+ )
+ }
+
+ data class Params(
+ val construct: (
+ DomainVerificationManagerInternal.Connection
+ ) -> DomainVerificationService,
+ val name: String,
+ val method: (DomainVerificationService) -> Unit
+ ) {
+ override fun toString() = name
+ }
+
+
+ fun mockPkg() = mockThrowOnUnmocked<AndroidPackage> {
+ whenever(packageName) { TEST_PKG }
+ whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+ whenever(activities) {
+ listOf(
+ ParsedActivity().apply {
+ addIntent(
+ ParsedIntentInfo().apply {
+ autoVerify = true
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("https")
+ addDataAuthority("example.com", null)
+ }
+ )
+ }
+ )
+ }
+ }
+
+ // TODO: PackageSetting field encapsulation to move to whenever(name)
+ fun mockPkgSetting() = spyThrowOnUnmocked(
+ PackageSetting(
+ TEST_PKG,
+ TEST_PKG,
+ File("/test"),
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ TEST_UUID
+ )
+ ) {
+ whenever(getPkg()) { mockPkg() }
+ whenever(domainSetId) { TEST_UUID }
+ whenever(userState) {
+ SparseArray<PackageUserState>().apply {
+ this[0] = PackageUserState()
+ }
+ }
+ whenever(getInstantApp(anyInt())) { false }
+ }
+ }
+
+ @Parameterized.Parameter(0)
+ lateinit var params: Params
+
+ @Test
+ fun writeScheduled() {
+ val connection = mockConnection()
+ val service = params.construct(connection)
+ params.method(service)
+
+ verify(connection).scheduleWriteSettings()
+ }
+
+ private fun mockConnection(): DomainVerificationManagerInternal.Connection =
+ mockThrowOnUnmocked {
+ whenever(callingUid) { TEST_UID }
+ whenever(callingUserId) { TEST_USER_ID }
+ whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting() }
+ whenever(getPackageLocked(TEST_PKG)) { mockPkg() }
+ whenever(schedule(anyInt(), any()))
+ whenever(scheduleWriteSettings())
+
+ // This doesn't check for visibility; that's done in the enforcer test
+ whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+ }
+}
diff --git a/services/tests/inprocesstests/AndroidTest.xml b/services/tests/inprocesstests/AndroidTest.xml
index 89abe3c0891c..b541512fdc9e 100644
--- a/services/tests/inprocesstests/AndroidTest.xml
+++ b/services/tests/inprocesstests/AndroidTest.xml
@@ -24,8 +24,8 @@
</target_preparer>
<!-- Restart to clear test code from system server -->
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="teardown-command" value="am restart" />
+ <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
+ <option name="cleanup-action" value="REBOOT" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 7c935d532f22..64b9a63991b4 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -11,9 +11,25 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+java_defaults {
+ name: "FrameworkMockingServicesTests-jni-defaults",
+ jni_libs: [
+ "libactivitymanagermockingservicestestjni",
+ ],
+}
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
android_test {
name: "FrameworksMockingServicesTests",
+ defaults: ["FrameworkMockingServicesTests-jni-defaults"],
srcs: ["src/**/*.java", "src/**/*.kt"],
@@ -72,4 +88,4 @@ java_library {
libs: [
"android.test.runner",
],
-} \ No newline at end of file
+}
diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp
new file mode 100644
index 000000000000..928065a7ebd9
--- /dev/null
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -0,0 +1,33 @@
+cc_library_shared {
+ name: "libactivitymanagermockingservicestestjni",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wthread-safety",
+ ],
+
+ srcs: [
+ ":lib_cachedAppOptimizer_native",
+ "onload.cpp",
+ ],
+
+ include_dirs: [
+ "frameworks/base/libs",
+ "frameworks/native/services",
+ "system/memory/libmeminfo/include",
+ ],
+
+ shared_libs: [
+ "libandroid",
+ "libandroid_runtime",
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libmeminfo",
+ "libnativehelper",
+ "libprocessgroup",
+ "libutils",
+ ],
+}
diff --git a/services/tests/mockingservicestests/jni/onload.cpp b/services/tests/mockingservicestests/jni/onload.cpp
new file mode 100644
index 000000000000..147cc479be18
--- /dev/null
+++ b/services/tests/mockingservicestests/jni/onload.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/*
+ * this is a mini native libaray for cached app optimizer tests to run properly. It
+ * loads all the native methods necessary.
+ */
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+
+namespace android {
+int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
+};
+
+using namespace android;
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
+{
+ JNIEnv* env = NULL;
+ jint result = -1;
+
+ if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+ ALOGE("GetEnv failed!");
+ return result;
+ }
+ ALOG_ASSERT(env, "Could not retrieve the env!");
+ register_android_server_am_CachedAppOptimizer(env);
+ return JNI_VERSION_1_4;
+}
+
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 1254df95a1c0..91098813380e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -18,6 +18,7 @@ package com.android.server.alarm;
import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
import static android.app.AlarmManager.FLAG_STANDALONE;
@@ -25,11 +26,14 @@ import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
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.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
@@ -44,7 +48,7 @@ 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_FOR_CANCELED;
-import static com.android.server.alarm.AlarmManagerService.Constants.ALLOW_WHILE_IDLE_WINDOW;
+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_QUOTA;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LAZY_BATCHING;
@@ -61,6 +65,7 @@ import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
import static com.android.server.alarm.Constants.TEST_CALLING_UID;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -75,20 +80,29 @@ import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
+import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IAlarmCompleteListener;
import android.app.IAlarmListener;
+import android.app.IAlarmManager;
import android.app.PendingIntent;
+import android.app.compat.CompatChanges;
import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.content.Intent;
+import android.content.PermissionChecker;
import android.os.BatteryManager;
+import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -102,9 +116,12 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.dx.mockito.inline.extended.MockedVoidMethod;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsService;
import com.android.server.AlarmManagerInternal;
import com.android.server.AppStateTracker;
import com.android.server.AppStateTrackerImpl;
+import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.usage.AppStandbyInternal;
@@ -125,6 +142,7 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.LongConsumer;
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -134,14 +152,21 @@ public class AlarmManagerServiceTest {
private static final int TEST_CALLING_USER = UserHandle.getUserId(TEST_CALLING_UID);
private long mAppStandbyWindow;
+ private long mAllowWhileIdleWindow;
private AlarmManagerService mService;
private AppStandbyInternal.AppIdleStateChangeListener mAppStandbyListener;
private AlarmManagerService.ChargingReceiver mChargingReceiver;
+ private IAppOpsCallback mIAppOpsCallback;
+ private IAlarmManager mBinder;
@Mock
private Context mMockContext;
@Mock
private IActivityManager mIActivityManager;
@Mock
+ private IAppOpsService mIAppOpsService;
+ @Mock
+ private DeviceIdleInternal mDeviceIdleInternal;
+ @Mock
private UsageStatsManagerInternal mUsageStatsManagerInternal;
@Mock
private AppStandbyInternal mAppStandbyInternal;
@@ -280,6 +305,11 @@ public class AlarmManagerServiceTest {
// "IllegalStateException: Querying activity state off main thread is not allowed."
// when AlarmManager calls DeviceConfig.addOnPropertiesChangedListener().
}
+
+ @Override
+ IAppOpsService getAppOpsService() {
+ return mIAppOpsService;
+ }
}
@Before
@@ -287,15 +317,20 @@ public class AlarmManagerServiceTest {
mMockingSession = mockitoSession()
.initMocks(this)
.spyStatic(ActivityManager.class)
+ .mockStatic(CompatChanges.class)
.spyStatic(DeviceConfig.class)
.mockStatic(LocalServices.class)
.spyStatic(Looper.class)
+ .mockStatic(PermissionChecker.class)
.mockStatic(Settings.Global.class)
.mockStatic(ServiceManager.class)
.spyStatic(UserHandle.class)
.strictness(Strictness.WARN)
.startMocking();
+
doReturn(mIActivityManager).when(ActivityManager::getService);
+ doReturn(mDeviceIdleInternal).when(
+ () -> LocalServices.getService(DeviceIdleInternal.class));
doReturn(mActivityManagerInternal).when(
() -> LocalServices.getService(ActivityManagerInternal.class));
doReturn(mAppStateTracker).when(() -> LocalServices.getService(AppStateTracker.class));
@@ -330,6 +365,9 @@ public class AlarmManagerServiceTest {
() -> DeviceConfig.getProperties(
eq(DeviceConfig.NAMESPACE_ALARM_MANAGER), ArgumentMatchers.<String>any()));
+ when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(
+ mock(AppOpsManager.class));
+
mInjector = new Injector(mMockContext);
mService = new AlarmManagerService(mMockContext, mInjector);
spyOn(mService);
@@ -346,6 +384,7 @@ public class AlarmManagerServiceTest {
// Other boot phases don't matter
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
mAppStandbyWindow = mService.mConstants.APP_STANDBY_WINDOW;
+ mAllowWhileIdleWindow = mService.mConstants.ALLOW_WHILE_IDLE_WINDOW;
ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> captor =
ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class);
verify(mAppStandbyInternal).addListener(captor.capture());
@@ -358,6 +397,20 @@ public class AlarmManagerServiceTest {
&& filter.hasAction(BatteryManager.ACTION_DISCHARGING)));
mChargingReceiver = chargingReceiverCaptor.getValue();
+ ArgumentCaptor<IBinder> binderCaptor = ArgumentCaptor.forClass(IBinder.class);
+ verify(() -> ServiceManager.addService(eq(Context.ALARM_SERVICE), binderCaptor.capture(),
+ anyBoolean(), anyInt()));
+ mBinder = IAlarmManager.Stub.asInterface(binderCaptor.getValue());
+
+ ArgumentCaptor<IAppOpsCallback> appOpsCallbackCaptor = ArgumentCaptor.forClass(
+ IAppOpsCallback.class);
+ try {
+ verify(mIAppOpsService).startWatchingMode(eq(AppOpsManager.OP_SCHEDULE_EXACT_ALARM),
+ isNull(), appOpsCallbackCaptor.capture());
+ } catch (RemoteException e) {
+ // Not expected on a mock.
+ }
+ mIAppOpsCallback = appOpsCallbackCaptor.getValue();
setTestableQuotas();
}
@@ -389,13 +442,18 @@ 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);
+ }
+
+ 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,
- null, null, callingUid, TEST_CALLING_PACKAGE);
+ null, null, callingUid, TEST_CALLING_PACKAGE, idleOptions);
}
private void setTestAlarmWithListener(int type, long triggerTime, IAlarmListener listener) {
mService.setImpl(type, triggerTime, WINDOW_EXACT, 0, null, listener, "test",
- FLAG_STANDALONE, null, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+ FLAG_STANDALONE, null, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE, null);
}
@@ -506,14 +564,27 @@ public class AlarmManagerServiceTest {
setDeviceConfigLong(KEY_MIN_INTERVAL, 10);
setDeviceConfigLong(KEY_MAX_INTERVAL, 15);
setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, 20);
+ setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, 25);
setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, 30);
setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 35);
assertEquals(5, mService.mConstants.MIN_FUTURITY);
assertEquals(10, mService.mConstants.MIN_INTERVAL);
assertEquals(15, mService.mConstants.MAX_INTERVAL);
assertEquals(20, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA);
+ assertEquals(25, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA);
assertEquals(30, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION);
assertEquals(35, mService.mConstants.LISTENER_TIMEOUT);
+
+ // Test safeguards.
+ setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, -3);
+ assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA);
+ setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, 0);
+ assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA);
+
+ setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, -8);
+ assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA);
+ setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, 0);
+ assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA);
}
@Test
@@ -559,56 +630,45 @@ public class AlarmManagerServiceTest {
assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed());
}
- private void testQuotasDeferralOnSet(int standbyBucket) throws Exception {
- final int quota = mService.getQuotaForBucketLocked(standbyBucket);
- when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
- anyLong())).thenReturn(standbyBucket);
+ private void testQuotasDeferralOnSet(LongConsumer alarmSetter, int quota, long window)
+ throws Exception {
final long firstTrigger = mNowElapsedTest + 10;
for (int i = 0; i < quota; i++) {
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
- getNewMockPendingIntent());
+ alarmSetter.accept(firstTrigger + i);
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
}
// This one should get deferred on set
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
- getNewMockPendingIntent());
- final long expectedNextTrigger = firstTrigger + mAppStandbyWindow;
+ alarmSetter.accept(firstTrigger + quota);
+ final long expectedNextTrigger = firstTrigger + window;
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
}
- private void testQuotasDeferralOnExpiration(int standbyBucket) throws Exception {
- final int quota = mService.getQuotaForBucketLocked(standbyBucket);
- when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
- anyLong())).thenReturn(standbyBucket);
+ private void testQuotasDeferralOnExpiration(LongConsumer alarmSetter, int quota, long window)
+ throws Exception {
final long firstTrigger = mNowElapsedTest + 10;
for (int i = 0; i < quota; i++) {
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
- getNewMockPendingIntent());
+ alarmSetter.accept(firstTrigger + i);
}
- // This one should get deferred after the latest alarm expires
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
- getNewMockPendingIntent());
+ // This one should get deferred after the latest alarm expires.
+ alarmSetter.accept(firstTrigger + quota);
for (int i = 0; i < quota; i++) {
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
}
- final long expectedNextTrigger = firstTrigger + mAppStandbyWindow;
+ final long expectedNextTrigger = firstTrigger + window;
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
}
- private void testQuotasNoDeferral(int standbyBucket) throws Exception {
- final int quota = mService.getQuotaForBucketLocked(standbyBucket);
- when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
- anyLong())).thenReturn(standbyBucket);
+ private void testQuotasNoDeferral(LongConsumer alarmSetter, int quota, long window)
+ throws Exception {
final long firstTrigger = mNowElapsedTest + 10;
for (int i = 0; i < quota; i++) {
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10 + i,
- getNewMockPendingIntent());
+ alarmSetter.accept(firstTrigger + i);
}
// This delivery time maintains the quota invariant. Should not be deferred.
- final long expectedNextTrigger = firstTrigger + mAppStandbyWindow + 5;
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, expectedNextTrigger, getNewMockPendingIntent());
+ final long expectedNextTrigger = firstTrigger + window + 5;
+ alarmSetter.accept(expectedNextTrigger);
for (int i = 0; i < quota; i++) {
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
@@ -616,64 +676,88 @@ public class AlarmManagerServiceTest {
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
}
+ private void testStandbyQuotasDeferralOnSet(int standbyBucket) throws Exception {
+ final int quota = mService.getQuotaForBucketLocked(standbyBucket);
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(standbyBucket);
+ testQuotasDeferralOnSet(trigger -> setTestAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
+ getNewMockPendingIntent()), quota, mAppStandbyWindow);
+ }
+
+ private void testStandbyQuotasDeferralOnExpiration(int standbyBucket) throws Exception {
+ final int quota = mService.getQuotaForBucketLocked(standbyBucket);
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(standbyBucket);
+ testQuotasDeferralOnExpiration(trigger -> setTestAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
+ getNewMockPendingIntent()), quota, mAppStandbyWindow);
+ }
+
+ private void testStandbyQuotasNoDeferral(int standbyBucket) throws Exception {
+ final int quota = mService.getQuotaForBucketLocked(standbyBucket);
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(standbyBucket);
+ testQuotasNoDeferral(trigger -> setTestAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
+ getNewMockPendingIntent()), quota, mAppStandbyWindow);
+ }
+
@Test
public void testActiveQuota_deferredOnSet() throws Exception {
- testQuotasDeferralOnSet(STANDBY_BUCKET_ACTIVE);
+ testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_ACTIVE);
}
@Test
public void testActiveQuota_deferredOnExpiration() throws Exception {
- testQuotasDeferralOnExpiration(STANDBY_BUCKET_ACTIVE);
+ testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_ACTIVE);
}
@Test
public void testActiveQuota_notDeferred() throws Exception {
- testQuotasNoDeferral(STANDBY_BUCKET_ACTIVE);
+ testStandbyQuotasNoDeferral(STANDBY_BUCKET_ACTIVE);
}
@Test
public void testWorkingQuota_deferredOnSet() throws Exception {
- testQuotasDeferralOnSet(STANDBY_BUCKET_WORKING_SET);
+ testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_WORKING_SET);
}
@Test
public void testWorkingQuota_deferredOnExpiration() throws Exception {
- testQuotasDeferralOnExpiration(STANDBY_BUCKET_WORKING_SET);
+ testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_WORKING_SET);
}
@Test
public void testWorkingQuota_notDeferred() throws Exception {
- testQuotasNoDeferral(STANDBY_BUCKET_WORKING_SET);
+ testStandbyQuotasNoDeferral(STANDBY_BUCKET_WORKING_SET);
}
@Test
public void testFrequentQuota_deferredOnSet() throws Exception {
- testQuotasDeferralOnSet(STANDBY_BUCKET_FREQUENT);
+ testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_FREQUENT);
}
@Test
public void testFrequentQuota_deferredOnExpiration() throws Exception {
- testQuotasDeferralOnExpiration(STANDBY_BUCKET_FREQUENT);
+ testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_FREQUENT);
}
@Test
public void testFrequentQuota_notDeferred() throws Exception {
- testQuotasNoDeferral(STANDBY_BUCKET_FREQUENT);
+ testStandbyQuotasNoDeferral(STANDBY_BUCKET_FREQUENT);
}
@Test
public void testRareQuota_deferredOnSet() throws Exception {
- testQuotasDeferralOnSet(STANDBY_BUCKET_RARE);
+ testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_RARE);
}
@Test
public void testRareQuota_deferredOnExpiration() throws Exception {
- testQuotasDeferralOnExpiration(STANDBY_BUCKET_RARE);
+ testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_RARE);
}
@Test
public void testRareQuota_notDeferred() throws Exception {
- testQuotasNoDeferral(STANDBY_BUCKET_RARE);
+ testStandbyQuotasNoDeferral(STANDBY_BUCKET_RARE);
}
@Test
@@ -731,7 +815,7 @@ public class AlarmManagerServiceTest {
}
@Test
- public void testQuotaDowngrade() throws Exception {
+ public void testStandbyQuotaDowngrade() throws Exception {
final int workingQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_WORKING_SET);
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET);
@@ -758,7 +842,7 @@ public class AlarmManagerServiceTest {
}
@Test
- public void testQuotaUpgrade() throws Exception {
+ public void testStandbyQuotaUpgrade() throws Exception {
final int frequentQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_FREQUENT);
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT);
@@ -1308,7 +1392,7 @@ public class AlarmManagerServiceTest {
public void allowWhileIdleAlarmsWhileDeviceIdle() throws Exception {
doReturn(0).when(mService).fuzzForDuration(anyLong());
- setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + ALLOW_WHILE_IDLE_WINDOW + 1000,
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + mAllowWhileIdleWindow + 1000,
getNewMockPendingIntent());
assertNotNull(mService.mPendingIdleUntil);
@@ -1323,7 +1407,7 @@ public class AlarmManagerServiceTest {
// This one should get deferred on set.
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
getNewMockPendingIntent(), false);
- final long expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW;
+ final long expectedNextTrigger = firstTrigger + mAllowWhileIdleWindow;
assertEquals("Incorrect trigger when no quota left", expectedNextTrigger,
mTestTimer.getElapsed());
@@ -1449,61 +1533,24 @@ public class AlarmManagerServiceTest {
when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
TEST_CALLING_PACKAGE)).thenReturn(true);
when(mAppStateTracker.isForceAllAppsStandbyEnabled()).thenReturn(true);
-
final int quota = mService.mConstants.ALLOW_WHILE_IDLE_QUOTA;
- long firstTrigger = mNowElapsedTest + 10;
- for (int i = 0; i < quota; i++) {
- setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
- getNewMockPendingIntent(), false);
- mNowElapsedTest = mTestTimer.getElapsed();
- mTestTimer.expire();
- }
- // This one should get deferred on set.
- setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
- getNewMockPendingIntent(), false);
- long expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW;
- assertEquals("Incorrect trigger when no quota available", expectedNextTrigger,
- mTestTimer.getElapsed());
+
+ testQuotasDeferralOnSet(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
+ getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
// Refresh the state
mService.removeLocked(TEST_CALLING_UID);
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
- firstTrigger = mNowElapsedTest + 10;
- for (int i = 0; i < quota; i++) {
- setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
- getNewMockPendingIntent(), false);
- }
- // This one should get deferred after the latest alarm expires.
- setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
- getNewMockPendingIntent(), false);
- for (int i = 0; i < quota; i++) {
- mNowElapsedTest = mTestTimer.getElapsed();
- mTestTimer.expire();
- }
- expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW;
- assertEquals("Incorrect trigger when no quota available", expectedNextTrigger,
- mTestTimer.getElapsed());
+ testQuotasDeferralOnExpiration(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP,
+ trigger, getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
// Refresh the state
mService.removeLocked(TEST_CALLING_UID);
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
- firstTrigger = mNowElapsedTest + 10;
- for (int i = 0; i < quota; i++) {
- setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
- getNewMockPendingIntent(), false);
- }
- // This delivery time maintains the quota invariant. Should not be deferred.
- expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW + 5;
- setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, expectedNextTrigger,
- getNewMockPendingIntent(), false);
- for (int i = 0; i < quota; i++) {
- mNowElapsedTest = mTestTimer.getElapsed();
- mTestTimer.expire();
- }
- assertEquals("Incorrect trigger when no quota available", expectedNextTrigger,
- mTestTimer.getElapsed());
+ testQuotasNoDeferral(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
+ getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
}
@Test
@@ -1545,6 +1592,259 @@ public class AlarmManagerServiceTest {
}
@Test
+ public void canScheduleExactAlarms() throws RemoteException {
+ doReturn(PermissionChecker.PERMISSION_GRANTED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ assertTrue(mBinder.canScheduleExactAlarms());
+
+ doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ assertFalse(mBinder.canScheduleExactAlarms());
+
+ doReturn(PermissionChecker.PERMISSION_SOFT_DENIED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ assertFalse(mBinder.canScheduleExactAlarms());
+ }
+
+ @Test
+ public void noPermissionCheckWhenChangeDisabled() throws RemoteException {
+ doReturn(false).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ // alarm clock
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
+ getNewMockPendingIntent(), null, null, null,
+ mock(AlarmManager.AlarmClockInfo.class));
+
+ // 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);
+
+ // inexact, allow-while-idle
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_HEURISTIC, 0,
+ FLAG_ALLOW_WHILE_IDLE, getNewMockPendingIntent(), null, null, null, null);
+
+ verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM), never());
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
+ }
+
+ @Test
+ public void exactAllowWhileIdleBinderCallWhenChangeDisabled() 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,
+ 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_COMPAT | FLAG_STANDALONE), isNull(), isNull(),
+ eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
+
+ final Bundle idleOptions = bundleCaptor.getValue();
+ final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
+ public void inexactAllowWhileIdleBinderCallWhenChangeDisabled() 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_HEURISTIC, 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), anyLong(), eq(0L),
+ eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(),
+ isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
+
+ final Bundle idleOptions = bundleCaptor.getValue();
+ final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
+ public void alarmClockBinderCallWhenChangeDisabled() throws Exception {
+ doReturn(false).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class);
+ mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
+ alarmPi, null, null, null, alarmClock);
+
+ verify(mService).setImpl(eq(RTC_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE | FLAG_WAKE_FROM_IDLE),
+ isNull(), eq(alarmClock), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), isNull());
+ }
+
+ @Test
+ public void alarmClockBinderCall() throws RemoteException {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ doReturn(PermissionChecker.PERMISSION_GRANTED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class);
+ mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
+ alarmPi, null, null, null, alarmClock);
+
+ // Correct permission checks are invoked.
+ 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(RTC_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE | FLAG_WAKE_FROM_IDLE),
+ isNull(), eq(alarmClock), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE),
+ bundleCaptor.capture());
+
+ final Bundle idleOptions = bundleCaptor.getValue();
+ final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @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 Bundle idleOptions = bundleCaptor.getValue();
+ final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
+ 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, 1234, WINDOW_EXACT, 0,
+ FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
+
+ verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ 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(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 Bundle idleOptions = bundleCaptor.getValue();
+ final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
+ public void inexactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
+ 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()));
+
+ 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());
+
+ final Bundle idleOptions = bundleCaptor.getValue();
+ final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
+ public void inexactAllowWhileIdleBinderCallWithoutAllowlist() throws RemoteException {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false);
+ 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()));
+
+ 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());
+
+ final Bundle idleOptions = bundleCaptor.getValue();
+ final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED, type);
+ }
+
+ @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,
+ idleOptions);
+
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+
+ verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class),
+ any(), any(Handler.class), isNull(), eq(idleOptions));
+ }
+
+ @Test
public void alarmStoreMigration() {
setDeviceConfigBoolean(KEY_LAZY_BATCHING, false);
final int numAlarms = 10;
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
index 42fa3d480046..12894ae4f75e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
@@ -69,12 +69,12 @@ public class AlarmStoreTest {
mock(PendingIntent.class));
return new Alarm(ELAPSED_REALTIME_WAKEUP, whenElapsed, whenElapsed, 0, 0,
mock(PendingIntent.class), null, null, null, 0, info, TEST_CALLING_UID,
- TEST_CALLING_PACKAGE);
+ TEST_CALLING_PACKAGE, null);
}
private static Alarm createAlarm(int type, long whenElapsed, long windowLength, int flags) {
return new Alarm(type, whenElapsed, whenElapsed, windowLength, 0, mock(PendingIntent.class),
- null, null, null, flags, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+ null, null, null, flags, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE, null);
}
private void addAlarmsToStore(Alarm... alarms) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java
index e80f0655e641..b64528c2f4d4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java
@@ -17,10 +17,17 @@
package com.android.server.alarm;
import static android.app.AlarmManager.ELAPSED_REALTIME;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
+import static android.app.AlarmManager.FLAG_STANDALONE;
+import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
+import static android.app.AlarmManager.RTC_WAKEUP;
import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
import static com.android.server.alarm.Alarm.NUM_POLICIES;
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
+import static com.android.server.alarm.AlarmManagerService.isExemptFromAppStandby;
import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
import static com.android.server.alarm.Constants.TEST_CALLING_UID;
@@ -28,7 +35,9 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import android.app.AlarmManager;
import android.app.PendingIntent;
import android.platform.test.annotations.Presubmit;
@@ -43,15 +52,29 @@ import java.util.Random;
@RunWith(AndroidJUnit4.class)
public class AlarmTest {
- private Alarm createDefaultAlarm(long requestedElapsed, long windowLength) {
+ private Alarm createDefaultAlarm(long requestedElapsed, long windowLength, int flags) {
return new Alarm(ELAPSED_REALTIME, 0, requestedElapsed, windowLength, 0,
- mock(PendingIntent.class), null, null, null, 0, null, TEST_CALLING_UID,
- TEST_CALLING_PACKAGE);
+ createAlarmSender(), null, null, null, flags, null, TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE, null);
+ }
+
+ private Alarm createAlarmClock(long requestedRtc) {
+ final AlarmManager.AlarmClockInfo info = mock(AlarmManager.AlarmClockInfo.class);
+ return new Alarm(RTC_WAKEUP, requestedRtc, requestedRtc, 0, 0, createAlarmSender(),
+ null, null, null, FLAG_WAKE_FROM_IDLE | FLAG_STANDALONE, info, TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE, null);
+ }
+
+ private PendingIntent createAlarmSender() {
+ final PendingIntent alarmPi = mock(PendingIntent.class);
+ when(alarmPi.getCreatorPackage()).thenReturn(TEST_CALLING_PACKAGE);
+ when(alarmPi.getCreatorUid()).thenReturn(TEST_CALLING_UID);
+ return alarmPi;
}
@Test
public void initSetsOnlyRequesterPolicy() {
- final Alarm a = createDefaultAlarm(4567, 2);
+ final Alarm a = createDefaultAlarm(4567, 2, 0);
for (int i = 0; i < NUM_POLICIES; i++) {
if (i == REQUESTER_POLICY_INDEX) {
@@ -86,7 +109,7 @@ public class AlarmTest {
@Test
public void whenElapsed() {
- final Alarm a = createDefaultAlarm(0, 0);
+ final Alarm a = createDefaultAlarm(0, 0, 0);
final long[][] uniqueData = generatePolicyTestMatrix(NUM_POLICIES);
for (int i = 0; i < NUM_POLICIES; i++) {
@@ -104,7 +127,7 @@ public class AlarmTest {
@Test
public void maxWhenElapsed() {
- final Alarm a = createDefaultAlarm(10, 12);
+ final Alarm a = createDefaultAlarm(10, 12, 0);
assertEquals(22, a.getMaxWhenElapsed());
a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 15);
@@ -128,7 +151,7 @@ public class AlarmTest {
@Test
public void setPolicyElapsedExact() {
- final Alarm exactAlarm = createDefaultAlarm(10, 0);
+ final Alarm exactAlarm = createDefaultAlarm(10, 0, 0);
assertTrue(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4));
assertTrue(exactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10));
@@ -143,7 +166,7 @@ public class AlarmTest {
@Test
public void setPolicyElapsedInexact() {
- final Alarm inexactAlarm = createDefaultAlarm(10, 5);
+ final Alarm inexactAlarm = createDefaultAlarm(10, 5, 0);
assertTrue(inexactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4));
assertTrue(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10));
@@ -154,4 +177,20 @@ public class AlarmTest {
assertFalse(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 8));
}
+
+ @Test
+ public void isExemptFromStandby() {
+ final long anything = 35412; // Arbitrary number, doesn't matter for this test.
+
+ assertFalse("Basic alarm exempt", isExemptFromAppStandby(
+ createDefaultAlarm(anything, anything, 0)));
+ assertFalse("FLAG_ALLOW_WHILE_IDLE_COMPAT exempt", isExemptFromAppStandby(
+ createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE_COMPAT)));
+
+ assertTrue("ALLOW_WHILE_IDLE not exempt", isExemptFromAppStandby(
+ createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE)));
+ assertTrue("ALLOW_WHILE_IDLE_UNRESTRICTED not exempt", isExemptFromAppStandby(
+ createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)));
+ assertTrue("Alarm clock not exempt", isExemptFromAppStandby(createAlarmClock(anything)));
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java
index 5bb6a42b2604..0e795a938d48 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java
@@ -45,7 +45,7 @@ public class BackgroundRestrictedAlarmsTest {
}
uidAlarms.add(new Alarm(
removeIt ? RTC : RTC_WAKEUP,
- 0, 0, 0, 0, null, null, null, null, 0, null, uid, name));
+ 0, 0, 0, 0, null, null, null, null, 0, null, uid, name, null));
return all;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index d86032622e43..56d30ccdf59f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -97,6 +97,7 @@ public final class CachedAppOptimizerTest {
@Before
public void setUp() {
+ System.loadLibrary("activitymanagermockingservicestestjni");
mHandlerThread = new HandlerThread("");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index af4130d92cfe..ca534927bd66 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -200,12 +200,12 @@ public class LocalDisplayAdapterTest {
}
private static class DisplayModeWrapper {
- public SurfaceControl.DisplayConfig config;
+ public SurfaceControl.DisplayMode mode;
public float[] expectedAlternativeRefreshRates;
- DisplayModeWrapper(SurfaceControl.DisplayConfig config,
+ DisplayModeWrapper(SurfaceControl.DisplayMode mode,
float[] expectedAlternativeRefreshRates) {
- this.config = config;
+ this.mode = mode;
this.expectedAlternativeRefreshRates = expectedAlternativeRefreshRates;
}
}
@@ -215,14 +215,15 @@ public class LocalDisplayAdapterTest {
* <code>expectedAlternativeRefreshRates</code> are present for each of the
* <code>modes</code>.
*/
- private void testAlternativeRefreshRatesCommon(FakeDisplay display, DisplayModeWrapper[] modes)
+ private void testAlternativeRefreshRatesCommon(FakeDisplay display,
+ DisplayModeWrapper[] wrappedModes)
throws InterruptedException {
// Update the display.
- SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[modes.length];
- for (int i = 0; i < modes.length; i++) {
- configs[i] = modes[i].config;
+ SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[wrappedModes.length];
+ for (int i = 0; i < wrappedModes.length; i++) {
+ modes[i] = wrappedModes[i].mode;
}
- display.configs = configs;
+ display.modes = modes;
setUpDisplay(display);
mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
@@ -233,11 +234,11 @@ public class LocalDisplayAdapterTest {
mListener.changedDisplays.get(mListener.changedDisplays.size() - 1);
displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
Display.Mode[] supportedModes = displayDevice.getDisplayDeviceInfoLocked().supportedModes;
- assertThat(supportedModes.length).isEqualTo(configs.length);
+ assertThat(supportedModes.length).isEqualTo(modes.length);
- for (int i = 0; i < modes.length; i++) {
- assertModeIsSupported(supportedModes, configs[i],
- modes[i].expectedAlternativeRefreshRates);
+ for (int i = 0; i < wrappedModes.length; i++) {
+ assertModeIsSupported(supportedModes, modes[i],
+ wrappedModes[i].expectedAlternativeRefreshRates);
}
}
@@ -251,56 +252,56 @@ public class LocalDisplayAdapterTest {
testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 60f, 0), new float[]{24f, 50f}),
+ createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{24f, 50f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 50f, 0), new float[]{24f, 60f}),
+ createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{24f, 60f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 24f, 0), new float[]{50f, 60f}),
+ createFakeDisplayMode(1920, 1080, 24f, 0), new float[]{50f, 60f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 60f, 0), new float[]{24f, 50f}),
+ createFakeDisplayMode(3840, 2160, 60f, 0), new float[]{24f, 50f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 50f, 0), new float[]{24f, 60f}),
+ createFakeDisplayMode(3840, 2160, 50f, 0), new float[]{24f, 60f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 24f, 0), new float[]{50f, 60f}),
+ createFakeDisplayMode(3840, 2160, 24f, 0), new float[]{50f, 60f}),
});
testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 60f, 0), new float[]{50f}),
+ createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{50f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 50f, 0), new float[]{60f}),
+ createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{60f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 24f, 1), new float[0]),
+ createFakeDisplayMode(1920, 1080, 24f, 1), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 60f, 2), new float[0]),
+ createFakeDisplayMode(3840, 2160, 60f, 2), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 50f, 3), new float[]{24f}),
+ createFakeDisplayMode(3840, 2160, 50f, 3), new float[]{24f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 24f, 3), new float[]{50f}),
+ createFakeDisplayMode(3840, 2160, 24f, 3), new float[]{50f}),
});
testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 60f, 0), new float[0]),
+ createFakeDisplayMode(1920, 1080, 60f, 0), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 50f, 1), new float[0]),
+ createFakeDisplayMode(1920, 1080, 50f, 1), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 24f, 2), new float[0]),
+ createFakeDisplayMode(1920, 1080, 24f, 2), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 60f, 3), new float[0]),
+ createFakeDisplayMode(3840, 2160, 60f, 3), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 50f, 4), new float[0]),
+ createFakeDisplayMode(3840, 2160, 50f, 4), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 24f, 5), new float[0]),
+ createFakeDisplayMode(3840, 2160, 24f, 5), new float[0]),
});
}
@Test
public void testAfterDisplayChange_DisplayModesAreUpdated() throws Exception {
- SurfaceControl.DisplayConfig displayConfig = createFakeDisplayConfig(1920, 1080, 60f);
- SurfaceControl.DisplayConfig[] configs =
- new SurfaceControl.DisplayConfig[]{displayConfig};
- FakeDisplay display = new FakeDisplay(PORT_A, configs, 0);
+ SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(1920, 1080, 60f);
+ SurfaceControl.DisplayMode[] modes =
+ new SurfaceControl.DisplayMode[]{displayMode};
+ FakeDisplay display = new FakeDisplay(PORT_A, modes, 0);
setUpDisplay(display);
updateAvailableDisplays();
mAdapter.registerLocked();
@@ -312,29 +313,29 @@ public class LocalDisplayAdapterTest {
DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(
0).getDisplayDeviceInfoLocked();
- assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length);
- assertModeIsSupported(displayDeviceInfo.supportedModes, displayConfig);
+ assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length);
+ assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode);
Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
- assertThat(defaultMode.matches(displayConfig.width, displayConfig.height,
- displayConfig.refreshRate)).isTrue();
+ assertThat(defaultMode.matches(displayMode.width, displayMode.height,
+ displayMode.refreshRate)).isTrue();
Display.Mode activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
- assertThat(activeMode.matches(displayConfig.width, displayConfig.height,
- displayConfig.refreshRate)).isTrue();
+ assertThat(activeMode.matches(displayMode.width, displayMode.height,
+ displayMode.refreshRate)).isTrue();
// Change the display
- SurfaceControl.DisplayConfig addedDisplayInfo = createFakeDisplayConfig(3840, 2160,
+ SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(3840, 2160,
60f);
- configs = new SurfaceControl.DisplayConfig[]{displayConfig, addedDisplayInfo};
- display.configs = configs;
- display.activeConfig = 1;
+ modes = new SurfaceControl.DisplayMode[]{displayMode, addedDisplayInfo};
+ display.modes = modes;
+ display.activeMode = 1;
setUpDisplay(display);
mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
- assertThat(SurfaceControl.getActiveConfig(display.token)).isEqualTo(1);
- assertThat(SurfaceControl.getDisplayConfigs(display.token).length).isEqualTo(2);
+ assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1);
+ assertThat(SurfaceControl.getDisplayModes(display.token).length).isEqualTo(2);
assertThat(mListener.addedDisplays.size()).isEqualTo(1);
assertThat(mListener.changedDisplays.size()).isEqualTo(1);
@@ -343,8 +344,8 @@ public class LocalDisplayAdapterTest {
displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
- assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length);
- assertModeIsSupported(displayDeviceInfo.supportedModes, displayConfig);
+ assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length);
+ assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode);
assertModeIsSupported(displayDeviceInfo.supportedModes, addedDisplayInfo);
activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
@@ -358,11 +359,11 @@ public class LocalDisplayAdapterTest {
@Test
public void testAfterDisplayChange_ActiveModeIsUpdated() throws Exception {
- SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[]{
- createFakeDisplayConfig(1920, 1080, 60f),
- createFakeDisplayConfig(1920, 1080, 50f)
+ SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{
+ createFakeDisplayMode(1920, 1080, 60f),
+ createFakeDisplayMode(1920, 1080, 50f)
};
- FakeDisplay display = new FakeDisplay(PORT_A, configs, /* activeConfig */ 0);
+ FakeDisplay display = new FakeDisplay(PORT_A, modes, /* activeMode */ 0);
setUpDisplay(display);
updateAvailableDisplays();
mAdapter.registerLocked();
@@ -378,12 +379,12 @@ public class LocalDisplayAdapterTest {
assertThat(activeMode.matches(1920, 1080, 60f)).isTrue();
// Change the display
- display.activeConfig = 1;
+ display.activeMode = 1;
setUpDisplay(display);
mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
- assertThat(SurfaceControl.getActiveConfig(display.token)).isEqualTo(1);
+ assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1);
assertThat(mListener.addedDisplays.size()).isEqualTo(1);
assertThat(mListener.changedDisplays.size()).isEqualTo(1);
@@ -471,14 +472,14 @@ public class LocalDisplayAdapterTest {
}
@Test
- public void testDisplayChange_withStaleDesiredDisplayConfigSpecs() throws Exception {
- SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[]{
- createFakeDisplayConfig(1920, 1080, 60f),
- createFakeDisplayConfig(1920, 1080, 50f)
+ public void testDisplayChange_withStaleDesiredDisplayModeSpecs() throws Exception {
+ SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{
+ createFakeDisplayMode(1920, 1080, 60f),
+ createFakeDisplayMode(1920, 1080, 50f)
};
- final int activeConfig = 0;
- FakeDisplay display = new FakeDisplay(PORT_A, configs, activeConfig);
- display.desiredDisplayConfigSpecs.defaultConfig = 1;
+ final int activeMode = 0;
+ FakeDisplay display = new FakeDisplay(PORT_A, modes, activeMode);
+ display.desiredDisplayModeSpecs.defaultMode = 1;
setUpDisplay(display);
updateAvailableDisplays();
@@ -486,12 +487,12 @@ public class LocalDisplayAdapterTest {
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
// Change the display
- display.configs = new SurfaceControl.DisplayConfig[]{
- createFakeDisplayConfig(1920, 1080, 60f)
+ display.modes = new SurfaceControl.DisplayMode[]{
+ createFakeDisplayMode(1920, 1080, 60f)
};
- // SurfaceFlinger can return a stale defaultConfig. Make sure this doesn't
+ // SurfaceFlinger can return a stale defaultMode. Make sure this doesn't
// trigger ArrayOutOfBoundsException.
- display.desiredDisplayConfigSpecs.defaultConfig = 1;
+ display.desiredDisplayModeSpecs.defaultMode = 1;
setUpDisplay(display);
updateAvailableDisplays();
@@ -562,13 +563,13 @@ public class LocalDisplayAdapterTest {
}
private void assertModeIsSupported(Display.Mode[] supportedModes,
- SurfaceControl.DisplayConfig mode) {
+ SurfaceControl.DisplayMode mode) {
assertThat(Arrays.stream(supportedModes).anyMatch(
x -> x.matches(mode.width, mode.height, mode.refreshRate))).isTrue();
}
private void assertModeIsSupported(Display.Mode[] supportedModes,
- SurfaceControl.DisplayConfig mode, float[] alternativeRefreshRates) {
+ SurfaceControl.DisplayMode mode, float[] alternativeRefreshRates) {
float[] sortedAlternativeRates =
Arrays.copyOf(alternativeRefreshRates, alternativeRefreshRates.length);
Arrays.sort(sortedAlternativeRates);
@@ -588,13 +589,13 @@ public class LocalDisplayAdapterTest {
public final DisplayAddress.Physical address;
public final IBinder token = new Binder();
public final SurfaceControl.DisplayInfo info;
- public SurfaceControl.DisplayConfig[] configs;
- public int activeConfig;
+ public SurfaceControl.DisplayMode[] modes;
+ public int activeMode;
public int[] colorModes = new int[]{ Display.COLOR_MODE_DEFAULT };
public Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(new int[0],
1000, 1000, 0);
- public SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs =
- new SurfaceControl.DesiredDisplayConfigSpecs(/* defaultConfig */ 0,
+ public SurfaceControl.DesiredDisplayModeSpecs desiredDisplayModeSpecs =
+ new SurfaceControl.DesiredDisplayModeSpecs(/* defaultMode */ 0,
/* allowGroupSwitching */ false,
/* primaryRefreshRateMin */ 60.f,
/* primaryRefreshRateMax */ 60.f,
@@ -604,17 +605,17 @@ public class LocalDisplayAdapterTest {
private FakeDisplay(int port) {
this.address = createDisplayAddress(port);
this.info = createFakeDisplayInfo();
- this.configs = new SurfaceControl.DisplayConfig[]{
- createFakeDisplayConfig(800, 600, 60f)
+ this.modes = new SurfaceControl.DisplayMode[]{
+ createFakeDisplayMode(800, 600, 60f)
};
- this.activeConfig = 0;
+ this.activeMode = 0;
}
- private FakeDisplay(int port, SurfaceControl.DisplayConfig[] configs, int activeConfig) {
+ private FakeDisplay(int port, SurfaceControl.DisplayMode[] modes, int activeMode) {
this.address = createDisplayAddress(port);
this.info = createFakeDisplayInfo();
- this.configs = configs;
- this.activeConfig = activeConfig;
+ this.modes = modes;
+ this.activeMode = activeMode;
}
}
@@ -623,16 +624,16 @@ public class LocalDisplayAdapterTest {
doReturn(display.token).when(() ->
SurfaceControl.getPhysicalDisplayToken(display.address.getPhysicalDisplayId()));
doReturn(display.info).when(() -> SurfaceControl.getDisplayInfo(display.token));
- doReturn(display.configs).when(
- () -> SurfaceControl.getDisplayConfigs(display.token));
- doReturn(display.activeConfig).when(() -> SurfaceControl.getActiveConfig(display.token));
+ doReturn(display.modes).when(
+ () -> SurfaceControl.getDisplayModes(display.token));
+ doReturn(display.activeMode).when(() -> SurfaceControl.getActiveDisplayMode(display.token));
doReturn(0).when(() -> SurfaceControl.getActiveColorMode(display.token));
doReturn(display.colorModes).when(
() -> SurfaceControl.getDisplayColorModes(display.token));
doReturn(display.hdrCapabilities).when(
() -> SurfaceControl.getHdrCapabilities(display.token));
- doReturn(display.desiredDisplayConfigSpecs)
- .when(() -> SurfaceControl.getDesiredDisplayConfigSpecs(display.token));
+ doReturn(display.desiredDisplayModeSpecs)
+ .when(() -> SurfaceControl.getDesiredDisplayModeSpecs(display.token));
}
private void updateAvailableDisplays() {
@@ -655,21 +656,21 @@ public class LocalDisplayAdapterTest {
return info;
}
- private static SurfaceControl.DisplayConfig createFakeDisplayConfig(int width, int height,
+ private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height,
float refreshRate) {
- return createFakeDisplayConfig(width, height, refreshRate, 0);
- }
-
- private static SurfaceControl.DisplayConfig createFakeDisplayConfig(int width, int height,
- float refreshRate, int configGroup) {
- final SurfaceControl.DisplayConfig config = new SurfaceControl.DisplayConfig();
- config.width = width;
- config.height = height;
- config.refreshRate = refreshRate;
- config.xDpi = 100;
- config.yDpi = 100;
- config.configGroup = configGroup;
- return config;
+ return createFakeDisplayMode(width, height, refreshRate, 0);
+ }
+
+ private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height,
+ float refreshRate, int group) {
+ final SurfaceControl.DisplayMode mode = new SurfaceControl.DisplayMode();
+ mode.width = width;
+ mode.height = height;
+ mode.refreshRate = refreshRate;
+ mode.xDpi = 100;
+ mode.yDpi = 100;
+ mode.group = group;
+ return mode;
}
private static void waitForHandlerToComplete(Handler handler, long waitTimeMs)
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
new file mode 100644
index 000000000000..35ac8979d46a
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.job;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManagerInternal;
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.os.UserHandle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+import com.android.server.LocalServices;
+import com.android.server.job.JobConcurrencyManager.GracePeriodObserver;
+import com.android.server.job.controllers.JobStatus;
+import com.android.server.pm.UserManagerInternal;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class JobConcurrencyManagerTest {
+ private static final int UNAVAILABLE_USER = 0;
+ private JobConcurrencyManager mJobConcurrencyManager;
+ private UserManagerInternal mUserManagerInternal;
+ private ActivityManagerInternal mActivityManagerInternal;
+ private int mNextUserId;
+ private GracePeriodObserver mGracePeriodObserver;
+ private Context mContext;
+ private Resources mResources;
+
+ @BeforeClass
+ public static void setUpOnce() {
+ LocalServices.addService(UserManagerInternal.class, mock(UserManagerInternal.class));
+ LocalServices.addService(
+ ActivityManagerInternal.class, mock(ActivityManagerInternal.class));
+ }
+
+ @AfterClass
+ public static void tearDownOnce() {
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ }
+
+ @Before
+ public void setUp() {
+ final JobSchedulerService jobSchedulerService = mock(JobSchedulerService.class);
+ mContext = mock(Context.class);
+ mResources = mock(Resources.class);
+ doReturn(true).when(mResources).getBoolean(
+ R.bool.config_jobSchedulerRestrictBackgroundUser);
+ when(mContext.getResources()).thenReturn(mResources);
+ doReturn(mContext).when(jobSchedulerService).getTestableContext();
+ mJobConcurrencyManager = new JobConcurrencyManager(jobSchedulerService);
+ mGracePeriodObserver = mock(GracePeriodObserver.class);
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mNextUserId = 10;
+ mJobConcurrencyManager.mGracePeriodObserver = mGracePeriodObserver;
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_currentUser() {
+ assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createCurrentUser(false))));
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_currentProfile() {
+ assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createCurrentUser(true))));
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_primaryUser() {
+ assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createPrimaryUser(false))));
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_primaryProfile() {
+ assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createPrimaryUser(true))));
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_UnexpiredUser() {
+ assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createUnexpiredUser(false))));
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_UnexpiredProfile() {
+ assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createUnexpiredUser(true))));
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_restrictedUser() {
+ assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createRestrictedUser(false))));
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_restrictedProfile() {
+ assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createRestrictedUser(true))));
+ }
+
+ private UserInfo createCurrentUser(boolean isProfile) {
+ final UserInfo ui = createNewUser();
+ doReturn(ui.id).when(mActivityManagerInternal).getCurrentUserId();
+ return isProfile ? createNewProfile(ui) : ui;
+ }
+
+ private UserInfo createPrimaryUser(boolean isProfile) {
+ final UserInfo ui = createNewUser();
+ doReturn(true).when(ui).isPrimary();
+ return isProfile ? createNewProfile(ui) : ui;
+ }
+
+ private UserInfo createUnexpiredUser(boolean isProfile) {
+ final UserInfo ui = createNewUser();
+ doReturn(true).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id);
+ return isProfile ? createNewProfile(ui) : ui;
+ }
+
+ private UserInfo createRestrictedUser(boolean isProfile) {
+ final UserInfo ui = createNewUser();
+ doReturn(UNAVAILABLE_USER).when(mActivityManagerInternal).getCurrentUserId();
+ doReturn(false).when(ui).isPrimary();
+ doReturn(false).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id);
+ return isProfile ? createNewProfile(ui) : ui;
+ }
+
+ private UserInfo createNewProfile(UserInfo parent) {
+ final UserInfo ui = createNewUser();
+ parent.profileGroupId = parent.id;
+ ui.profileGroupId = parent.id;
+ doReturn(true).when(ui).isProfile();
+ return ui;
+ }
+
+ private UserInfo createNewUser() {
+ final UserInfo ui = mock(UserInfo.class);
+ ui.id = mNextUserId++;
+ doReturn(ui).when(mUserManagerInternal).getUserInfo(ui.id);
+ ui.profileGroupId = UserInfo.NO_PROFILE_GROUP_ID;
+ return ui;
+ }
+
+ private static JobStatus createJob(UserInfo userInfo) {
+ JobStatus jobStatus = JobStatus.createFromJobInfo(
+ new JobInfo.Builder(1, new ComponentName("foo", "bar")).build(),
+ userInfo.id * UserHandle.PER_USER_RANGE,
+ null, userInfo.id, "JobConcurrencyManagerTest");
+ return jobStatus;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 4effa4d445bb..f2bb47bfb8ad 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -62,6 +62,7 @@ import com.android.server.LocalServices;
import com.android.server.PowerAllowlistInternal;
import com.android.server.SystemServiceManager;
import com.android.server.job.controllers.JobStatus;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.usage.AppStandbyInternal;
import org.junit.After;
@@ -128,6 +129,9 @@ public class JobSchedulerServiceTest {
// Called in DeviceIdleJobsController constructor.
doReturn(mock(DeviceIdleInternal.class))
.when(() -> LocalServices.getService(DeviceIdleInternal.class));
+ // Used in JobConcurrencyManager.
+ doReturn(mock(UserManagerInternal.class))
+ .when(() -> LocalServices.getService(UserManagerInternal.class));
// Used in JobStatus.
doReturn(mock(PackageManagerInternal.class))
.when(() -> LocalServices.getService(PackageManagerInternal.class));
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 8099eda9a0af..775276bd03f6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -63,7 +63,6 @@ import android.util.DataUnit;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobSchedulerService.Constants;
-import com.android.server.job.JobServiceContext;
import com.android.server.net.NetworkPolicyManagerInternal;
import org.junit.Before;
@@ -144,8 +143,7 @@ public class ConnectivityControllerTest {
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
final ConnectivityController controller = new ConnectivityController(mService);
- when(mService.getMaxJobExecutionTimeMs(any()))
- .thenReturn(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS);
+ when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(10 * 60_000L);
// Slow network is too slow
assertFalse(controller.isSatisfied(createJobStatus(job), net,
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 c4c9173536ad..b72121f096ba 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
@@ -82,8 +82,6 @@ import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.PowerAllowlistInternal;
import com.android.server.job.JobSchedulerService;
-import com.android.server.job.JobSchedulerService.Constants;
-import com.android.server.job.JobServiceContext;
import com.android.server.job.JobStore;
import com.android.server.job.controllers.QuotaController.ExecutionStats;
import com.android.server.job.controllers.QuotaController.QcConstants;
@@ -123,6 +121,7 @@ public class QuotaControllerTest {
private BroadcastReceiver mChargingReceiver;
private QuotaController mQuotaController;
private QuotaController.QcConstants mQcConstants;
+ private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants();
private int mSourceUid;
private PowerAllowlistInternal.TempAllowlistChangeListener mTempAllowlistListener;
private IUidObserver mUidObserver;
@@ -158,7 +157,7 @@ public class QuotaControllerTest {
// Called in StateController constructor.
when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
- when(mJobSchedulerService.getConstants()).thenReturn(mock(Constants.class));
+ when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
// Called in QuotaController constructor.
IActivityManager activityManager = ActivityManager.getService();
spyOn(activityManager);
@@ -1282,23 +1281,23 @@ public class QuotaControllerTest {
}
@Test
- public void testGetMaxJobExecutionTimeLocked() {
+ public void testGetMaxJobExecutionTimeLocked_Regular() {
mQuotaController.saveTimingSession(0, SOURCE_PACKAGE,
createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS),
3 * MINUTE_IN_MILLIS, 5), false);
JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked", 0);
- job.setStandbyBucket(RARE_INDEX);
+ setStandbyBucket(RARE_INDEX, job);
setCharging();
synchronized (mQuotaController.mLock) {
- assertEquals(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS,
+ assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
}
setDischarging();
setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
synchronized (mQuotaController.mLock) {
- assertEquals(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS,
+ assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
}
@@ -1310,7 +1309,7 @@ public class QuotaControllerTest {
}
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
synchronized (mQuotaController.mLock) {
- assertEquals(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS,
+ assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
mQuotaController.maybeStopTrackingJobLocked(job, null, false);
}
@@ -1322,6 +1321,81 @@ public class QuotaControllerTest {
}
}
+ @Test
+ public void testGetMaxJobExecutionTimeLocked_EJ() {
+ final long timeUsedMs = 3 * MINUTE_IN_MILLIS;
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS),
+ timeUsedMs, 5), true);
+ JobStatus job = createExpeditedJobStatus("testGetMaxJobExecutionTimeLocked_EJ", 0);
+ setStandbyBucket(RARE_INDEX, job);
+ mQuotaController.maybeStartTrackingJobLocked(job, null);
+
+ setCharging();
+ synchronized (mQuotaController.mLock) {
+ assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ }
+
+ setDischarging();
+ setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(mQcConstants.EJ_LIMIT_WORKING_MS / 2,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ }
+
+ // Top-started job
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(job);
+ }
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+ }
+
+ setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ }
+
+ // Test used quota rolling out of window.
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.clearAppStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE);
+ }
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(sElapsedRealtimeClock.millis() - mQcConstants.EJ_WINDOW_SIZE_MS,
+ timeUsedMs, 5), true);
+
+ setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(mQcConstants.EJ_LIMIT_WORKING_MS / 2,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ }
+
+ // Top-started job
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job, null);
+ mQuotaController.prepareForExecutionLocked(job);
+ }
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+ }
+
+ setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(mQcConstants.EJ_LIMIT_RARE_MS,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ }
+ }
+
/**
* Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
* window.
@@ -2508,7 +2582,7 @@ public class QuotaControllerTest {
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[WORKING_INDEX]);
assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]);
assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RARE_INDEX]);
- assertEquals(0, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]);
+ assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]);
assertEquals(0, mQuotaController.getEjLimitSpecialAdditionMs());
assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJLimitWindowSizeMs());
assertEquals(1, mQuotaController.getEJTopAppTimeChunkSizeMs());
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeDeviceIdleHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeDeviceIdleHelper.java
new file mode 100644
index 000000000000..d2ab6462d96b
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeDeviceIdleHelper.java
@@ -0,0 +1,38 @@
+/*
+ * 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.location.injector;
+
+public class FakeDeviceIdleHelper extends DeviceIdleHelper {
+
+ private boolean mDeviceIdle = false;
+
+ public void setIdle(boolean deviceIdle) {
+ mDeviceIdle = deviceIdle;
+ notifyDeviceIdleChanged();
+ }
+
+ @Override
+ protected void registerInternal() {}
+
+ @Override
+ protected void unregisterInternal() {}
+
+ @Override
+ public boolean isDeviceIdle() {
+ return mDeviceIdle;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeDeviceStationaryHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeDeviceStationaryHelper.java
new file mode 100644
index 000000000000..b1d921aa9db1
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeDeviceStationaryHelper.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.injector;
+
+import com.android.server.DeviceIdleInternal;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public class FakeDeviceStationaryHelper extends DeviceStationaryHelper {
+
+ private final CopyOnWriteArrayList<DeviceIdleInternal.StationaryListener> mListeners =
+ new CopyOnWriteArrayList<>();
+
+ private boolean mStationary = false;
+
+ @Override
+ public void addListener(DeviceIdleInternal.StationaryListener listener) {
+ synchronized (mListeners) {
+ mListeners.add(listener);
+ listener.onDeviceStationaryChanged(mStationary);
+ }
+ }
+
+ @Override
+ public void removeListener(DeviceIdleInternal.StationaryListener listener) {
+ mListeners.remove(listener);
+ }
+
+ public void setStationary(boolean stationary) {
+ synchronized (mListeners) {
+ mStationary = stationary;
+ }
+
+ for (DeviceIdleInternal.StationaryListener listener : mListeners) {
+ listener.onDeviceStationaryChanged(stationary);
+ }
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java
index 0311920477a1..059744388197 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java
@@ -19,6 +19,8 @@ package com.android.server.location.injector;
import android.os.IPowerManager;
import android.os.PowerManager.LocationPowerSaveMode;
+import com.android.server.location.eventlog.LocationEventLog;
+
/**
* Version of LocationPowerSaveModeHelper for testing. Power save mode is initialized as "no
* change".
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java
index c39a77eac23f..6156ba9359cd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java
@@ -43,6 +43,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.LocalServices;
+import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.injector.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener;
import org.junit.After;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
index 8e5b16e1ee3a..1f102ac32a8e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
@@ -16,9 +16,10 @@
package com.android.server.location.injector;
+import com.android.server.location.eventlog.LocationEventLog;
+
public class TestInjector implements Injector {
- private final LocationEventLog mLocationEventLog;
private final FakeUserInfoHelper mUserInfoHelper;
private final FakeAlarmHelper mAlarmHelper;
private final FakeAppOpsHelper mAppOpsHelper;
@@ -27,20 +28,27 @@ public class TestInjector implements Injector {
private final FakeAppForegroundHelper mAppForegroundHelper;
private final FakeLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
private final FakeScreenInteractiveHelper mScreenInteractiveHelper;
+ private final FakeDeviceStationaryHelper mDeviceStationaryHelper;
+ private final FakeDeviceIdleHelper mDeviceIdleHelper;
private final LocationAttributionHelper mLocationAttributionHelper;
private final FakeEmergencyHelper mEmergencyHelper;
private final LocationUsageLogger mLocationUsageLogger;
public TestInjector() {
- mLocationEventLog = new LocationEventLog();
+ this(new LocationEventLog());
+ }
+
+ public TestInjector(LocationEventLog eventLog) {
mUserInfoHelper = new FakeUserInfoHelper();
mAlarmHelper = new FakeAlarmHelper();
mAppOpsHelper = new FakeAppOpsHelper();
mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
mSettingsHelper = new FakeSettingsHelper();
mAppForegroundHelper = new FakeAppForegroundHelper();
- mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper(mLocationEventLog);
+ mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper(eventLog);
mScreenInteractiveHelper = new FakeScreenInteractiveHelper();
+ mDeviceStationaryHelper = new FakeDeviceStationaryHelper();
+ mDeviceIdleHelper = new FakeDeviceIdleHelper();
mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
mEmergencyHelper = new FakeEmergencyHelper();
mLocationUsageLogger = new LocationUsageLogger();
@@ -87,6 +95,16 @@ public class TestInjector implements Injector {
}
@Override
+ public FakeDeviceStationaryHelper getDeviceStationaryHelper() {
+ return mDeviceStationaryHelper;
+ }
+
+ @Override
+ public FakeDeviceIdleHelper getDeviceIdleHelper() {
+ return mDeviceIdleHelper;
+ }
+
+ @Override
public LocationAttributionHelper getLocationAttributionHelper() {
return mLocationAttributionHelper;
}
@@ -100,9 +118,4 @@ public class TestInjector implements Injector {
public LocationUsageLogger getLocationUsageLogger() {
return mLocationUsageLogger;
}
-
- @Override
- public LocationEventLog getLocationEventLog() {
- return mLocationEventLog;
- }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 3b5cc887c798..66b037d70a40 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -64,6 +64,7 @@ import android.location.LocationManagerInternal;
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
+import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
@@ -82,6 +83,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.server.FgThread;
import com.android.server.LocalServices;
+import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.injector.FakeUserInfoHelper;
import com.android.server.location.injector.TestInjector;
@@ -158,17 +160,19 @@ public class LocationProviderManagerTest {
doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
- mInjector = new TestInjector();
+ LocationEventLog eventLog = new LocationEventLog();
+
+ mInjector = new TestInjector(eventLog);
mInjector.getUserInfoHelper().startUser(OTHER_USER);
- mPassive = new PassiveLocationProviderManager(mContext, mInjector);
+ mPassive = new PassiveLocationProviderManager(mContext, mInjector, eventLog);
mPassive.startManager();
mPassive.setRealProvider(new PassiveLocationProvider(mContext));
mProvider = new TestProvider(PROPERTIES, IDENTITY);
mProvider.setProviderAllowed(true);
- mManager = new LocationProviderManager(mContext, mInjector, NAME, mPassive);
+ mManager = new LocationProviderManager(mContext, mInjector, eventLog, NAME, mPassive);
mManager.startManager();
mManager.setRealProvider(mProvider);
}
@@ -662,6 +666,23 @@ public class LocationProviderManagerTest {
}
@Test
+ public void testProviderRequestListener() throws Exception {
+ IProviderRequestListener requestListener = mock(IProviderRequestListener.class);
+ mManager.addProviderRequestListener(requestListener);
+
+ ILocationListener locationListener = createMockLocationListener();
+ LocationRequest request = new LocationRequest.Builder(1).setWorkSource(
+ WORK_SOURCE).build();
+ mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, locationListener);
+
+ verify(requestListener, timeout(TIMEOUT_MS).times(1)).onProviderRequestChanged(anyString(),
+ any(ProviderRequest.class));
+
+ mManager.unregisterLocationRequest(locationListener);
+ mManager.removeProviderRequestListener(requestListener);
+ }
+
+ @Test
public void testGetCurrentLocation() throws Exception {
ILocationCallback listener = createMockGetCurrentLocationListener();
LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
new file mode 100644
index 000000000000..c3cca64154d6
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.provider;
+
+import static com.android.server.location.LocationUtils.createLocation;
+import static com.android.server.location.LocationUtils.createLocationResult;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.location.Location;
+import android.location.LocationResult;
+import android.location.provider.ProviderRequest;
+import android.platform.test.annotations.Presubmit;
+import android.util.Log;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.location.eventlog.LocationEventLog;
+import com.android.server.location.injector.TestInjector;
+import com.android.server.location.test.FakeProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.Random;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class StationaryThrottlingLocationProviderTest {
+
+ private static final String TAG = "StationaryThrottlingLocationProviderTest";
+
+ private Random mRandom;
+ private TestInjector mInjector;
+ private FakeProvider mDelegateProvider;
+
+ private @Mock AbstractLocationProvider.Listener mListener;
+ private @Mock FakeProvider.FakeProviderInterface mDelegate;
+
+ private StationaryThrottlingLocationProvider mProvider;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+
+ long seed = System.currentTimeMillis();
+ Log.i(TAG, "location random seed: " + seed);
+
+ mRandom = new Random(seed);
+
+ mInjector = new TestInjector();
+ mDelegateProvider = new FakeProvider(mDelegate);
+
+ mProvider = new StationaryThrottlingLocationProvider("test_provider", mInjector,
+ mDelegateProvider, new LocationEventLog());
+ mProvider.getController().setListener(mListener);
+ mProvider.getController().start();
+ }
+
+ @After
+ public void tearDown() {
+ mProvider.getController().setRequest(ProviderRequest.EMPTY_REQUEST);
+ mProvider.getController().stop();
+ }
+
+ @Test
+ public void testThrottle() {
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
+
+ mProvider.getController().setRequest(request);
+ verify(mDelegate).onSetRequest(request);
+
+ mDelegateProvider.reportLocation(createLocationResult("test_provider", mRandom));
+ verify(mListener, times(1)).onReportLocation(any(LocationResult.class));
+
+ mInjector.getDeviceStationaryHelper().setStationary(true);
+ verify(mDelegate, never()).onSetRequest(ProviderRequest.EMPTY_REQUEST);
+
+ mInjector.getDeviceIdleHelper().setIdle(true);
+ verify(mDelegate).onSetRequest(ProviderRequest.EMPTY_REQUEST);
+ verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
+ verify(mListener, timeout(75).times(3)).onReportLocation(any(LocationResult.class));
+
+ mInjector.getDeviceStationaryHelper().setStationary(false);
+ verify(mDelegate, times(2)).onSetRequest(request);
+ verify(mListener, after(75).times(3)).onReportLocation(any(LocationResult.class));
+ }
+
+ @Test
+ public void testThrottle_NoInitialLocation() {
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
+
+ mProvider.getController().setRequest(request);
+ verify(mDelegate).onSetRequest(request);
+
+ mInjector.getDeviceStationaryHelper().setStationary(true);
+ mInjector.getDeviceIdleHelper().setIdle(true);
+ verify(mDelegate, never()).onSetRequest(ProviderRequest.EMPTY_REQUEST);
+
+ mDelegateProvider.reportLocation(createLocationResult("test_provider", mRandom));
+ verify(mListener, times(1)).onReportLocation(any(LocationResult.class));
+ verify(mDelegate, times(1)).onSetRequest(ProviderRequest.EMPTY_REQUEST);
+ verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
+
+ mInjector.getDeviceStationaryHelper().setStationary(false);
+ verify(mDelegate, times(2)).onSetRequest(request);
+ verify(mListener, after(75).times(2)).onReportLocation(any(LocationResult.class));
+ }
+
+ @Test
+ public void testNoThrottle_noLocation() {
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
+
+ mProvider.getController().setRequest(request);
+ verify(mDelegate).onSetRequest(request);
+ verify(mListener, never()).onReportLocation(any(LocationResult.class));
+
+ mInjector.getDeviceStationaryHelper().setStationary(true);
+ mInjector.getDeviceIdleHelper().setIdle(true);
+ verify(mDelegate, never()).onSetRequest(ProviderRequest.EMPTY_REQUEST);
+ verify(mListener, after(75).times(0)).onReportLocation(any(LocationResult.class));
+ }
+
+ @Test
+ public void testNoThrottle_oldLocation() {
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
+
+ mProvider.getController().setRequest(request);
+ verify(mDelegate).onSetRequest(request);
+
+ Location l = createLocation("test_provider", mRandom);
+ l.setElapsedRealtimeNanos(0);
+
+ LocationResult loc = LocationResult.wrap(l);
+ mDelegateProvider.reportLocation(loc);
+ verify(mListener, times(1)).onReportLocation(loc);
+
+ mInjector.getDeviceStationaryHelper().setStationary(true);
+ mInjector.getDeviceIdleHelper().setIdle(true);
+ verify(mDelegate, never()).onSetRequest(ProviderRequest.EMPTY_REQUEST);
+ verify(mListener, after(75).times(1)).onReportLocation(any(LocationResult.class));
+ }
+
+ @Test
+ public void testNoThrottle_locationSettingsIgnored() {
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(
+ 50).setLocationSettingsIgnored(true).build();
+
+ mProvider.getController().setRequest(request);
+ verify(mDelegate).onSetRequest(request);
+
+ LocationResult loc = createLocationResult("test_provider", mRandom);
+ mDelegateProvider.reportLocation(loc);
+ verify(mListener, times(1)).onReportLocation(loc);
+
+ mInjector.getDeviceStationaryHelper().setStationary(true);
+ mInjector.getDeviceIdleHelper().setIdle(true);
+ verify(mDelegate, never()).onSetRequest(ProviderRequest.EMPTY_REQUEST);
+ verify(mListener, after(75).times(1)).onReportLocation(any(LocationResult.class));
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS
index d825dfd7cf00..46b797b83f1d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS
@@ -1 +1,3 @@
include /services/core/java/com/android/server/pm/OWNERS
+
+per-file StagingManagerTest.java = dariofreni@google.com, ioffe@google.com, olilan@google.com
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
new file mode 100644
index 000000000000..195cc010c9a7
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -0,0 +1,725 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.apex.ApexSessionInfo;
+import android.content.Context;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
+import android.os.SystemProperties;
+import android.os.storage.IStorageManager;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.content.PackageHelper;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+
+import static org.mockito.ArgumentMatchers.any;
+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.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+@Presubmit
+@RunWith(JUnit4.class)
+public class StagingManagerTest {
+ @Rule
+ public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ @Mock private Context mContext;
+ @Mock private IStorageManager mStorageManager;
+ @Mock private ApexManager mApexManager;
+
+ private File mTmpDir;
+ private StagingManager mStagingManager;
+
+ private MockitoSession mMockitoSession;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(eq(Context.POWER_SERVICE))).thenReturn(null);
+
+ mMockitoSession = ExtendedMockito.mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .mockStatic(SystemProperties.class)
+ .mockStatic(PackageHelper.class)
+ .startMocking();
+
+ when(mStorageManager.supportsCheckpoint()).thenReturn(true);
+ when(mStorageManager.needsCheckpoint()).thenReturn(true);
+ when(PackageHelper.getStorageManager()).thenReturn(mStorageManager);
+
+ when(SystemProperties.get(eq("ro.apex.updatable"))).thenReturn("true");
+ when(SystemProperties.get(eq("ro.apex.updatable"), anyString())).thenReturn("true");
+
+ mTmpDir = mTemporaryFolder.newFolder("StagingManagerTest");
+ mStagingManager = new StagingManager(mContext, null, mApexManager);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mMockitoSession != null) {
+ mMockitoSession.finishMocking();
+ }
+ }
+
+ /**
+ * Tests that sessions committed later shouldn't cause earlier ones to fail the overlapping
+ * check.
+ */
+ @Test
+ public void checkNonOverlappingWithStagedSessions_laterSessionShouldNotFailEarlierOnes()
+ throws Exception {
+ // Create 2 sessions with overlapping packages
+ StagingManager.StagedSession session1 = createSession(111, "com.foo", 1);
+ StagingManager.StagedSession session2 = createSession(222, "com.foo", 2);
+
+ mStagingManager.createSession(session1);
+ mStagingManager.createSession(session2);
+ // Session1 should not fail in spite of the overlapping packages
+ mStagingManager.checkNonOverlappingWithStagedSessions(session1);
+ // Session2 should fail due to overlapping packages
+ assertThrows(PackageManagerException.class,
+ () -> mStagingManager.checkNonOverlappingWithStagedSessions(session2));
+ }
+
+ @Test
+ public void restoreSessions_nonParentSession_throwsIAE() throws Exception {
+ FakeStagedSession session = new FakeStagedSession(239);
+ session.setParentSessionId(1543);
+
+ assertThrows(IllegalArgumentException.class,
+ () -> mStagingManager.restoreSessions(Arrays.asList(session), false));
+ }
+
+ @Test
+ public void restoreSessions_nonCommittedSession_throwsIAE() throws Exception {
+ FakeStagedSession session = new FakeStagedSession(239);
+
+ assertThrows(IllegalArgumentException.class,
+ () -> mStagingManager.restoreSessions(Arrays.asList(session), false));
+ }
+
+ @Test
+ public void restoreSessions_terminalSession_throwsIAE() throws Exception {
+ FakeStagedSession session = new FakeStagedSession(239);
+ session.setCommitted(true);
+ session.setSessionApplied();
+
+ assertThrows(IllegalArgumentException.class,
+ () -> mStagingManager.restoreSessions(Arrays.asList(session), false));
+ }
+
+ @Test
+ public void restoreSessions_deviceUpgrading_failsAllSessions() throws Exception {
+ FakeStagedSession session1 = new FakeStagedSession(37);
+ session1.setCommitted(true);
+ FakeStagedSession session2 = new FakeStagedSession(57);
+ session2.setCommitted(true);
+
+ mStagingManager.restoreSessions(Arrays.asList(session1, session2), true);
+
+ assertThat(session1.getErrorCode()).isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+ assertThat(session1.getErrorMessage()).isEqualTo("Build fingerprint has changed");
+
+ assertThat(session2.getErrorCode()).isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+ assertThat(session2.getErrorMessage()).isEqualTo("Build fingerprint has changed");
+ }
+
+ @Test
+ public void restoreSessions_multipleSessions_deviceWithoutFsCheckpointSupport_throwISE()
+ throws Exception {
+ FakeStagedSession session1 = new FakeStagedSession(37);
+ session1.setCommitted(true);
+ FakeStagedSession session2 = new FakeStagedSession(57);
+ session2.setCommitted(true);
+
+ when(mStorageManager.supportsCheckpoint()).thenReturn(false);
+
+ assertThrows(IllegalStateException.class,
+ () -> mStagingManager.restoreSessions(Arrays.asList(session1, session2), false));
+ }
+
+ @Test
+ public void restoreSessions_handlesDestroyedAndNotReadySessions() throws Exception {
+ FakeStagedSession destroyedApkSession = new FakeStagedSession(23);
+ destroyedApkSession.setCommitted(true);
+ destroyedApkSession.setDestroyed(true);
+
+ FakeStagedSession destroyedApexSession = new FakeStagedSession(37);
+ destroyedApexSession.setCommitted(true);
+ destroyedApexSession.setDestroyed(true);
+ destroyedApexSession.setIsApex(true);
+
+ FakeStagedSession nonReadyApkSession = new FakeStagedSession(57);
+ nonReadyApkSession.setCommitted(true);
+
+ FakeStagedSession nonReadyApexSession = new FakeStagedSession(73);
+ nonReadyApexSession.setCommitted(true);
+ nonReadyApexSession.setIsApex(true);
+
+ FakeStagedSession destroyedNonReadySession = new FakeStagedSession(101);
+ destroyedNonReadySession.setCommitted(true);
+ destroyedNonReadySession.setDestroyed(true);
+
+ FakeStagedSession regularApkSession = new FakeStagedSession(239);
+ regularApkSession.setCommitted(true);
+ regularApkSession.setSessionReady();
+
+ List<StagingManager.StagedSession> sessions = new ArrayList<>();
+ sessions.add(destroyedApkSession);
+ sessions.add(destroyedApexSession);
+ sessions.add(nonReadyApkSession);
+ sessions.add(nonReadyApexSession);
+ sessions.add(destroyedNonReadySession);
+ sessions.add(regularApkSession);
+
+ mStagingManager.restoreSessions(sessions, false);
+
+ assertThat(sessions).containsExactly(regularApkSession);
+ assertThat(destroyedApkSession.isDestroyed()).isTrue();
+ assertThat(destroyedApexSession.isDestroyed()).isTrue();
+ assertThat(destroyedNonReadySession.isDestroyed()).isTrue();
+
+ mStagingManager.onBootCompletedBroadcastReceived();
+ assertThat(nonReadyApkSession.hasPreRebootVerificationStarted()).isTrue();
+ assertThat(nonReadyApexSession.hasPreRebootVerificationStarted()).isTrue();
+ }
+
+ @Test
+ public void restoreSessions_unknownApexSession_failsAllSessions() throws Exception {
+ FakeStagedSession apkSession = new FakeStagedSession(239);
+ apkSession.setCommitted(true);
+ apkSession.setSessionReady();
+
+ FakeStagedSession apexSession = new FakeStagedSession(1543);
+ apexSession.setCommitted(true);
+ apexSession.setIsApex(true);
+ apexSession.setSessionReady();
+
+ List<StagingManager.StagedSession> sessions = new ArrayList<>();
+ sessions.add(apkSession);
+ sessions.add(apexSession);
+
+ when(mApexManager.getSessions()).thenReturn(new SparseArray<>());
+ mStagingManager.restoreSessions(sessions, false);
+
+ // Validate checkpoint wasn't aborted.
+ verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
+
+ assertThat(apexSession.getErrorCode())
+ .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+ assertThat(apexSession.getErrorMessage()).isEqualTo("apexd did not know anything about a "
+ + "staged session supposed to be activated");
+
+ assertThat(apkSession.getErrorCode())
+ .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+ assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
+ }
+
+ @Test
+ public void restoreSessions_failedApexSessions_failsAllSessions() throws Exception {
+ FakeStagedSession apkSession = new FakeStagedSession(239);
+ apkSession.setCommitted(true);
+ apkSession.setSessionReady();
+
+ FakeStagedSession apexSession1 = new FakeStagedSession(1543);
+ apexSession1.setCommitted(true);
+ apexSession1.setIsApex(true);
+ apexSession1.setSessionReady();
+
+ FakeStagedSession apexSession2 = new FakeStagedSession(101);
+ apexSession2.setCommitted(true);
+ apexSession2.setIsApex(true);
+ apexSession2.setSessionReady();
+
+ FakeStagedSession apexSession3 = new FakeStagedSession(57);
+ apexSession3.setCommitted(true);
+ apexSession3.setIsApex(true);
+ apexSession3.setSessionReady();
+
+ ApexSessionInfo activationFailed = new ApexSessionInfo();
+ activationFailed.sessionId = 1543;
+ activationFailed.isActivationFailed = true;
+
+ ApexSessionInfo staged = new ApexSessionInfo();
+ staged.sessionId = 101;
+ staged.isStaged = true;
+
+ SparseArray<ApexSessionInfo> apexdSessions = new SparseArray<>();
+ apexdSessions.put(1543, activationFailed);
+ apexdSessions.put(101, staged);
+ when(mApexManager.getSessions()).thenReturn(apexdSessions);
+
+ List<StagingManager.StagedSession> sessions = new ArrayList<>();
+ sessions.add(apkSession);
+ sessions.add(apexSession1);
+ sessions.add(apexSession2);
+ sessions.add(apexSession3);
+
+ mStagingManager.restoreSessions(sessions, false);
+
+ // Validate checkpoint wasn't aborted.
+ verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
+
+ assertThat(apexSession1.getErrorCode())
+ .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+ assertThat(apexSession1.getErrorMessage()).isEqualTo("APEX activation failed. Check logcat "
+ + "messages from apexd for more information.");
+
+ assertThat(apexSession2.getErrorCode())
+ .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+ assertThat(apexSession2.getErrorMessage()).isEqualTo("Staged session 101 at boot didn't "
+ + "activate nor fail. Marking it as failed anyway.");
+
+ assertThat(apexSession3.getErrorCode())
+ .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+ assertThat(apexSession3.getErrorMessage()).isEqualTo("apexd did not know anything about a "
+ + "staged session supposed to be activated");
+
+ assertThat(apkSession.getErrorCode())
+ .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+ assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
+ }
+
+ @Test
+ public void restoreSessions_stagedApexSession_failsAllSessions() throws Exception {
+ FakeStagedSession apkSession = new FakeStagedSession(239);
+ apkSession.setCommitted(true);
+ apkSession.setSessionReady();
+
+ FakeStagedSession apexSession = new FakeStagedSession(1543);
+ apexSession.setCommitted(true);
+ apexSession.setIsApex(true);
+ apexSession.setSessionReady();
+
+ ApexSessionInfo staged = new ApexSessionInfo();
+ staged.sessionId = 1543;
+ staged.isStaged = true;
+
+ SparseArray<ApexSessionInfo> apexdSessions = new SparseArray<>();
+ apexdSessions.put(1543, staged);
+ when(mApexManager.getSessions()).thenReturn(apexdSessions);
+
+ List<StagingManager.StagedSession> sessions = new ArrayList<>();
+ sessions.add(apkSession);
+ sessions.add(apexSession);
+
+ mStagingManager.restoreSessions(sessions, false);
+
+ // Validate checkpoint wasn't aborted.
+ verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
+
+ assertThat(apexSession.getErrorCode())
+ .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+ assertThat(apexSession.getErrorMessage()).isEqualTo("Staged session 1543 at boot didn't "
+ + "activate nor fail. Marking it as failed anyway.");
+
+ assertThat(apkSession.getErrorCode())
+ .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+ assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
+ }
+
+ @Test
+ public void restoreSessions_failedAndActivatedApexSessions_abortsCheckpoint() throws Exception {
+ FakeStagedSession apkSession = new FakeStagedSession(239);
+ apkSession.setCommitted(true);
+ apkSession.setSessionReady();
+
+ FakeStagedSession apexSession1 = new FakeStagedSession(1543);
+ apexSession1.setCommitted(true);
+ apexSession1.setIsApex(true);
+ apexSession1.setSessionReady();
+
+ FakeStagedSession apexSession2 = new FakeStagedSession(101);
+ apexSession2.setCommitted(true);
+ apexSession2.setIsApex(true);
+ apexSession2.setSessionReady();
+
+ FakeStagedSession apexSession3 = new FakeStagedSession(57);
+ apexSession3.setCommitted(true);
+ apexSession3.setIsApex(true);
+ apexSession3.setSessionReady();
+
+ FakeStagedSession apexSession4 = new FakeStagedSession(37);
+ apexSession4.setCommitted(true);
+ apexSession4.setIsApex(true);
+ apexSession4.setSessionReady();
+
+ ApexSessionInfo activationFailed = new ApexSessionInfo();
+ activationFailed.sessionId = 1543;
+ activationFailed.isActivationFailed = true;
+
+ ApexSessionInfo activated = new ApexSessionInfo();
+ activated.sessionId = 101;
+ activated.isActivated = true;
+
+ ApexSessionInfo staged = new ApexSessionInfo();
+ staged.sessionId = 57;
+ staged.isActivationFailed = true;
+
+ SparseArray<ApexSessionInfo> apexdSessions = new SparseArray<>();
+ apexdSessions.put(1543, activationFailed);
+ apexdSessions.put(101, activated);
+ apexdSessions.put(57, staged);
+ when(mApexManager.getSessions()).thenReturn(apexdSessions);
+
+ List<StagingManager.StagedSession> sessions = new ArrayList<>();
+ sessions.add(apkSession);
+ sessions.add(apexSession1);
+ sessions.add(apexSession2);
+ sessions.add(apexSession3);
+ sessions.add(apexSession4);
+
+ mStagingManager.restoreSessions(sessions, false);
+
+ // Validate checkpoint was aborted.
+ verify(mStorageManager, times(1)).abortChanges(eq("abort-staged-install"), eq(false));
+ }
+
+ @Test
+ public void restoreSessions_apexSessionInImpossibleState_failsAllSessions() throws Exception {
+ FakeStagedSession apkSession = new FakeStagedSession(239);
+ apkSession.setCommitted(true);
+ apkSession.setSessionReady();
+
+ FakeStagedSession apexSession = new FakeStagedSession(1543);
+ apexSession.setCommitted(true);
+ apexSession.setIsApex(true);
+ apexSession.setSessionReady();
+
+ ApexSessionInfo impossible = new ApexSessionInfo();
+ impossible.sessionId = 1543;
+
+ SparseArray<ApexSessionInfo> apexdSessions = new SparseArray<>();
+ apexdSessions.put(1543, impossible);
+ when(mApexManager.getSessions()).thenReturn(apexdSessions);
+
+ List<StagingManager.StagedSession> sessions = new ArrayList<>();
+ sessions.add(apkSession);
+ sessions.add(apexSession);
+
+ mStagingManager.restoreSessions(sessions, false);
+
+ // Validate checkpoint wasn't aborted.
+ verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false));
+
+ assertThat(apexSession.getErrorCode())
+ .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+ assertThat(apexSession.getErrorMessage()).isEqualTo("Impossible state");
+
+ assertThat(apkSession.getErrorCode())
+ .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
+ assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
+ }
+
+ private StagingManager.StagedSession createSession(int sessionId, String packageName,
+ long committedMillis) {
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ params.isStaged = true;
+
+ InstallSource installSource = InstallSource.create("testInstallInitiator",
+ "testInstallOriginator", "testInstaller", "testAttributionTag");
+
+ PackageInstallerSession session = new PackageInstallerSession(
+ /* callback */ null,
+ /* context */ null,
+ /* pm */ null,
+ /* sessionProvider */ null,
+ /* looper */ BackgroundThread.getHandler().getLooper(),
+ /* stagingManager */ null,
+ /* sessionId */ sessionId,
+ /* userId */ 456,
+ /* installerUid */ -1,
+ /* installSource */ installSource,
+ /* sessionParams */ params,
+ /* createdMillis */ 0L,
+ /* committedMillis */ committedMillis,
+ /* stageDir */ mTmpDir,
+ /* stageCid */ null,
+ /* files */ null,
+ /* checksums */ null,
+ /* prepared */ true,
+ /* committed */ true,
+ /* destroyed */ false,
+ /* sealed */ false, // Setting to true would trigger some PM logic.
+ /* childSessionIds */ null,
+ /* parentSessionId */ -1,
+ /* isReady */ false,
+ /* isFailed */ false,
+ /* isApplied */false,
+ /* stagedSessionErrorCode */ PackageInstaller.SessionInfo.STAGED_SESSION_NO_ERROR,
+ /* stagedSessionErrorMessage */ "no error");
+
+ StagingManager.StagedSession stagedSession = spy(session.mStagedSession);
+ doReturn(packageName).when(stagedSession).getPackageName();
+ doAnswer(invocation -> {
+ Predicate<StagingManager.StagedSession> filter = invocation.getArgument(0);
+ return filter.test(stagedSession);
+ }).when(stagedSession).sessionContains(any());
+ return stagedSession;
+ }
+
+ private static final class FakeStagedSession implements StagingManager.StagedSession {
+ private final int mSessionId;
+ private boolean mIsApex = false;
+ private boolean mIsCommitted = false;
+ private boolean mIsReady = false;
+ private boolean mIsApplied = false;
+ private boolean mIsFailed = false;
+ private @StagedSessionErrorCode int mErrorCode = -1;
+ private String mErrorMessage;
+ private boolean mIsDestroyed = false;
+ private int mParentSessionId = -1;
+ private String mPackageName;
+ private boolean mIsAbandonded = false;
+ private boolean mPreRebootVerificationStarted = false;
+ private final List<StagingManager.StagedSession> mChildSessions = new ArrayList<>();
+
+ private FakeStagedSession(int sessionId) {
+ mSessionId = sessionId;
+ }
+
+ private void setParentSessionId(int parentSessionId) {
+ mParentSessionId = parentSessionId;
+ }
+
+ private void setCommitted(boolean isCommitted) {
+ mIsCommitted = isCommitted;
+ }
+
+ private void setIsApex(boolean isApex) {
+ mIsApex = isApex;
+ }
+
+ private void setDestroyed(boolean isDestroyed) {
+ mIsDestroyed = isDestroyed;
+ }
+
+ private void setPackageName(String packageName) {
+ mPackageName = packageName;
+ }
+
+ private boolean isAbandonded() {
+ return mIsAbandonded;
+ }
+
+ private boolean hasPreRebootVerificationStarted() {
+ return mPreRebootVerificationStarted;
+ }
+
+ private FakeStagedSession addChildSession(FakeStagedSession session) {
+ mChildSessions.add(session);
+ session.setParentSessionId(sessionId());
+ return this;
+ }
+
+ private @StagedSessionErrorCode int getErrorCode() {
+ return mErrorCode;
+ }
+
+ private String getErrorMessage() {
+ return mErrorMessage;
+ }
+
+ @Override
+ public boolean isMultiPackage() {
+ return !mChildSessions.isEmpty();
+ }
+
+ @Override
+ public boolean isApexSession() {
+ return mIsApex;
+ }
+
+ @Override
+ public boolean isCommitted() {
+ return mIsCommitted;
+ }
+
+ @Override
+ public boolean isInTerminalState() {
+ return isSessionApplied() || isSessionFailed();
+ }
+
+ @Override
+ public boolean isDestroyed() {
+ return mIsDestroyed;
+ }
+
+ @Override
+ public boolean isSessionReady() {
+ return mIsReady;
+ }
+
+ @Override
+ public boolean isSessionApplied() {
+ return mIsApplied;
+ }
+
+ @Override
+ public boolean isSessionFailed() {
+ return mIsFailed;
+ }
+
+ @Override
+ public List<StagingManager.StagedSession> getChildSessions() {
+ return mChildSessions;
+ }
+
+ @Override
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ @Override
+ public int getParentSessionId() {
+ return mParentSessionId;
+ }
+
+ @Override
+ public int sessionId() {
+ return mSessionId;
+ }
+
+ @Override
+ public PackageInstaller.SessionParams sessionParams() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean sessionContains(Predicate<StagingManager.StagedSession> filter) {
+ return filter.test(this);
+ }
+
+ @Override
+ public boolean containsApkSession() {
+ Preconditions.checkState(!hasParentSessionId(), "Child session");
+ if (!isMultiPackage()) {
+ return !isApexSession();
+ }
+ for (StagingManager.StagedSession session : mChildSessions) {
+ if (!session.isApexSession()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean containsApexSession() {
+ Preconditions.checkState(!hasParentSessionId(), "Child session");
+ if (!isMultiPackage()) {
+ return isApexSession();
+ }
+ for (StagingManager.StagedSession session : mChildSessions) {
+ if (session.isApexSession()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void setSessionReady() {
+ mIsReady = true;
+ }
+
+ @Override
+ public void setSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) {
+ Preconditions.checkState(!mIsApplied, "Already marked as applied");
+ mIsFailed = true;
+ mErrorCode = errorCode;
+ mErrorMessage = errorMessage;
+ }
+
+ @Override
+ public void setSessionApplied() {
+ Preconditions.checkState(!mIsFailed, "Already marked as failed");
+ mIsApplied = true;
+ }
+
+ @Override
+ public void installSession(IntentSender statusReceiver) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasParentSessionId() {
+ return mParentSessionId != -1;
+ }
+
+ @Override
+ public long getCommittedMillis() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void abandon() {
+ mIsAbandonded = true;
+ }
+
+ @Override
+ public boolean notifyStartPreRebootVerification() {
+ mPreRebootVerificationStarted = true;
+ // TODO(ioffe): change to true when tests for pre-reboot verification are added.
+ return false;
+ }
+
+ @Override
+ public void notifyEndPreRebootVerification() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void verifySession() {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/services/tests/rescueparty/Android.bp b/services/tests/rescueparty/Android.bp
index 6733af4eb26a..ed7de967f069 100644
--- a/services/tests/rescueparty/Android.bp
+++ b/services/tests/rescueparty/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_test {
name: "log_rescueparty_reset_event_reported",
srcs: ["log_rescueparty_reset_event_reported.cpp"],
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 7a0cb8e5dead..bea4ae3af1fd 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -2,6 +2,15 @@
// Build FrameworksServicesTests package
//########################################################################
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksServicesTests",
@@ -61,7 +70,7 @@ android_test {
libs: [
"android.hardware.power-V1-java",
"android.hardware.tv.cec-V1.0-java",
- "android.hardware.vibrator-V1-java",
+ "android.hardware.vibrator-V2-java",
"android.hidl.manager-V1.0-java",
"android.test.mock",
"android.test.base",
diff --git a/services/tests/servicestests/aidl/Android.bp b/services/tests/servicestests/aidl/Android.bp
index d4e53dddf4a7..678053192e82 100644
--- a/services/tests/servicestests/aidl/Android.bp
+++ b/services/tests/servicestests/aidl/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "servicestests-aidl",
sdk_version: "current",
diff --git a/services/tests/servicestests/apks/Android.bp b/services/tests/servicestests/apks/Android.bp
index 3e11604005a0..6c918064dbff 100644
--- a/services/tests/servicestests/apks/Android.bp
+++ b/services/tests/servicestests/apks/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_defaults {
name: "FrameworksServicesTests_apks_defaults",
sdk_version: "current",
diff --git a/services/tests/servicestests/apks/install-split-base/Android.bp b/services/tests/servicestests/apks/install-split-base/Android.bp
index 1b62aa2a8e96..39992f600b96 100644
--- a/services/tests/servicestests/apks/install-split-base/Android.bp
+++ b/services/tests/servicestests/apks/install-split-base/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksServicesTests_install_split_base",
defaults: ["FrameworksServicesTests_apks_defaults"],
diff --git a/services/tests/servicestests/apks/install-split-feature-a/Android.bp b/services/tests/servicestests/apks/install-split-feature-a/Android.bp
index 45d891720166..ca7295e48fbc 100644
--- a/services/tests/servicestests/apks/install-split-feature-a/Android.bp
+++ b/services/tests/servicestests/apks/install-split-feature-a/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksServicesTests_install_split_feature_a",
defaults: ["FrameworksServicesTests_apks_defaults"],
diff --git a/services/tests/servicestests/apks/install_intent_filters/Android.bp b/services/tests/servicestests/apks/install_intent_filters/Android.bp
index 59c8524e01c2..643824de785c 100644
--- a/services/tests/servicestests/apks/install_intent_filters/Android.bp
+++ b/services/tests/servicestests/apks/install_intent_filters/Android.bp
@@ -1,7 +1,15 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksServicesTests_install_intent_filters",
defaults: ["FrameworksServicesTests_apks_defaults"],
srcs: ["**/*.java"],
}
-
diff --git a/services/tests/servicestests/apks/install_uses_sdk/Android.bp b/services/tests/servicestests/apks/install_uses_sdk/Android.bp
index c24aa2bd8bba..feb152c49eb3 100644
--- a/services/tests/servicestests/apks/install_uses_sdk/Android.bp
+++ b/services/tests/servicestests/apks/install_uses_sdk/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FrameworksServicesTests_install_uses_sdk_r0",
defaults: ["FrameworksServicesTests_apks_defaults"],
diff --git a/services/tests/servicestests/res/layout/widget_preview.xml b/services/tests/servicestests/res/layout/widget_preview.xml
new file mode 100644
index 000000000000..137ff46663d5
--- /dev/null
+++ b/services/tests/servicestests/res/layout/widget_preview.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.
+ -->
+<TextView android:id="@+id/widget_preview"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="Widget preview" /> \ No newline at end of file
diff --git a/services/tests/servicestests/res/values/strings.xml b/services/tests/servicestests/res/values/values.xml
index 1f07ad50cdbc..0404ebcb60d0 100644
--- a/services/tests/servicestests/res/values/strings.xml
+++ b/services/tests/servicestests/res/values/values.xml
@@ -36,4 +36,12 @@
<string name="module_2_name" translatable="false">module_2_name</string>
<string name="widget_description">widget description string</string>
+ <dimen name="widget_min_width">80dp</dimen>
+ <dimen name="widget_min_height">40dp</dimen>
+ <dimen name="widget_min_resize_width">40dp</dimen>
+ <dimen name="widget_min_resize_height">20dp</dimen>
+ <dimen name="widget_max_resize_width">160dp</dimen>
+ <dimen name="widget_max_resize_height">80dp</dimen>
+ <integer name="widget_target_cell_width">2</integer>
+ <integer name="widget_target_cell_height">1</integer>
</resources>
diff --git a/services/tests/servicestests/res/xml/dummy_appwidget_info.xml b/services/tests/servicestests/res/xml/dummy_appwidget_info.xml
index 65462168c5d8..de06191ec281 100644
--- a/services/tests/servicestests/res/xml/dummy_appwidget_info.xml
+++ b/services/tests/servicestests/res/xml/dummy_appwidget_info.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2017 The Android Open Source Project
~
@@ -15,11 +16,18 @@
-->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
- android:minWidth="40dp"
- android:minHeight="40dp"
+ android:minWidth="@dimen/widget_min_width"
+ android:minHeight="@dimen/widget_min_height"
+ android:minResizeWidth="@dimen/widget_min_resize_width"
+ android:minResizeHeight="@dimen/widget_min_resize_height"
+ android:maxResizeWidth="@dimen/widget_max_resize_width"
+ android:maxResizeHeight="@dimen/widget_max_resize_height"
+ android:targetCellWidth="@integer/widget_target_cell_width"
+ android:targetCellHeight="@integer/widget_target_cell_height"
android:updatePeriodMillis="86400000"
android:previewImage="@drawable/icon1"
+ android:previewLayout="@layout/widget_preview"
android:resizeMode="horizontal|vertical"
android:description="@string/widget_description"
android:widgetCategory="home_screen">
-</appwidget-provider> \ No newline at end of file
+</appwidget-provider>
diff --git a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
index 50e7a0395a2a..58d6dae1637a 100644
--- a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
+++ b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
@@ -34,7 +34,7 @@ public class EntropyMixerTest extends AndroidTestCase {
assertEquals(0, FileUtils.readTextFile(file, 0, null).length());
// The constructor has the side effect of writing to file
- new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath(), "/dev/null");
+ new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath());
assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0);
}
diff --git a/services/tests/servicestests/src/com/android/server/OWNERS b/services/tests/servicestests/src/com/android/server/OWNERS
index 6561778cb47d..f1402ead3866 100644
--- a/services/tests/servicestests/src/com/android/server/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/OWNERS
@@ -3,5 +3,4 @@ per-file *AppOp* = file:/core/java/android/permission/OWNERS
per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
-per-file *Vibrator* = file:/services/core/java/com/android/server/vibrator/OWNERS
per-file GestureLauncherServiceTest.java = file:platform/packages/apps/EmergencyInfo:/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
deleted file mode 100644
index 2a7905a451b9..000000000000
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ /dev/null
@@ -1,774 +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.server;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.AppOpsManager;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManagerInternal;
-import android.hardware.input.IInputManager;
-import android.hardware.input.InputManager;
-import android.hardware.vibrator.IVibrator;
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IVibratorStateListener;
-import android.os.Looper;
-import android.os.PowerManagerInternal;
-import android.os.PowerSaveState;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.VibrationAttributes;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
-import android.os.VibratorInfo;
-import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
-import android.view.InputDevice;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.internal.util.test.FakeSettingsProviderRule;
-import com.android.server.vibrator.FakeVibrator;
-import com.android.server.vibrator.FakeVibratorControllerProvider;
-import com.android.server.vibrator.VibratorController;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-/**
- * Tests for {@link VibratorService}.
- *
- * Build/Install/Run:
- * atest FrameworksServicesTests:VibratorServiceTest
- */
-@Presubmit
-public class VibratorServiceTest {
-
- private static final int UID = Process.ROOT_UID;
- private static final int VIBRATOR_ID = 1;
- private static final String PACKAGE_NAME = "package";
- private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
- private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
- .setBatterySaverEnabled(true).build();
- private static final VibrationAttributes ALARM_ATTRS =
- new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build();
- private static final VibrationAttributes HAPTIC_FEEDBACK_ATTRS =
- new VibrationAttributes.Builder().setUsage(
- VibrationAttributes.USAGE_TOUCH).build();
- private static final VibrationAttributes NOTIFICATION_ATTRS =
- new VibrationAttributes.Builder().setUsage(
- VibrationAttributes.USAGE_NOTIFICATION).build();
- private static final VibrationAttributes RINGTONE_ATTRS =
- new VibrationAttributes.Builder().setUsage(
- VibrationAttributes.USAGE_RINGTONE).build();
-
- @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
- @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
-
- @Mock private PackageManagerInternal mPackageManagerInternalMock;
- @Mock private PowerManagerInternal mPowerManagerInternalMock;
- @Mock private AppOpsManager mAppOpsManagerMock;
- @Mock private IVibratorStateListener mVibratorStateListenerMock;
- @Mock private IInputManager mIInputManagerMock;
- @Mock private IBinder mVibratorStateListenerBinderMock;
-
- private TestLooper mTestLooper;
- private ContextWrapper mContextSpy;
- private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
- private FakeVibrator mFakeVibrator;
- private FakeVibratorControllerProvider mVibratorProvider;
-
- @Before
- public void setUp() throws Exception {
- mTestLooper = new TestLooper();
- mFakeVibrator = new FakeVibrator();
- mVibratorProvider = new FakeVibratorControllerProvider(mTestLooper.getLooper());
- mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
- InputManager inputManager = InputManager.resetInstance(mIInputManagerMock);
-
- ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
- when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
- when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mFakeVibrator);
- when(mContextSpy.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager);
- when(mContextSpy.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManagerMock);
- when(mVibratorStateListenerMock.asBinder()).thenReturn(mVibratorStateListenerBinderMock);
- when(mPackageManagerInternalMock.getSystemUiServiceComponent())
- .thenReturn(new ComponentName("", ""));
- doAnswer(invocation -> {
- mRegisteredPowerModeListener = invocation.getArgument(0);
- return null;
- }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
- when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]);
-
- setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_MEDIUM);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_MEDIUM);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_MEDIUM);
-
- addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternalMock);
- addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock);
- }
-
- @After
- public void tearDown() throws Exception {
- InputManager.clearInstance();
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
- LocalServices.removeServiceForTest(PowerManagerInternal.class);
- }
-
- private VibratorService createService() {
- VibratorService service = new VibratorService(mContextSpy,
- new VibratorService.Injector() {
- @Override
- VibratorController createVibratorController(
- VibratorController.OnVibrationCompleteListener listener) {
- return mVibratorProvider.newVibratorController(VIBRATOR_ID, listener);
- }
-
- @Override
- Handler createHandler(Looper looper) {
- return new Handler(mTestLooper.getLooper());
- }
-
- @Override
- void addService(String name, IBinder service) {
- // ignore
- }
- });
- service.systemReady();
- return service;
- }
-
- @Test
- public void createService_initializesNativeService() {
- createService();
- assertTrue(mVibratorProvider.isInitialized());
- }
-
- @Test
- public void hasVibrator_withVibratorHalPresent_returnsTrue() {
- assertTrue(createService().hasVibrator());
- }
-
- @Test
- public void hasVibrator_withNoVibratorHalPresent_returnsFalse() {
- mVibratorProvider.disableVibrators();
- assertFalse(createService().hasVibrator());
- }
-
- @Test
- public void hasAmplitudeControl_withAmplitudeControlSupport_returnsTrue() {
- mVibratorProvider.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- assertTrue(createService().hasAmplitudeControl());
- }
-
- @Test
- public void hasAmplitudeControl_withNoAmplitudeControlSupport_returnsFalse() {
- assertFalse(createService().hasAmplitudeControl());
- }
-
- @Test
- public void hasAmplitudeControl_withInputDevices_returnsTrue() throws Exception {
- when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
- when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
- mVibratorProvider.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
- assertTrue(createService().hasAmplitudeControl());
- }
-
- @Test
- public void getVibratorInfo_returnsSameInfoFromNative() {
- mVibratorProvider.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS,
- IVibrator.CAP_AMPLITUDE_CONTROL);
- mVibratorProvider.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- mVibratorProvider.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK);
-
- VibratorInfo info = createService().getVibratorInfo();
- assertTrue(info.hasAmplitudeControl());
- assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES,
- info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
- assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
- info.isEffectSupported(VibrationEffect.EFFECT_TICK));
- assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
- assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
- }
-
- @Test
- public void vibrate_withRingtone_usesRingtoneSettings() throws Exception {
- setRingerMode(AudioManager.RINGER_MODE_NORMAL);
- setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
- vibrate(createService(), VibrationEffect.createOneShot(1, 1), RINGTONE_ATTRS);
-
- setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1);
- vibrateAndWait(createService(), VibrationEffect.createOneShot(10, 10), RINGTONE_ATTRS);
-
- setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
- setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
- vibrateAndWait(createService(), VibrationEffect.createOneShot(100, 100), RINGTONE_ATTRS);
-
- List<VibrationEffect> effects = mVibratorProvider.getEffects();
- assertEquals(2, effects.size());
- assertEquals(10, effects.get(0).getDuration());
- assertEquals(100, effects.get(1).getDuration());
- }
-
- @Test
- public void vibrate_withPowerModeChange_usesLowPowerModeState() throws Exception {
- VibratorService service = createService();
- mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
- vibrate(service, VibrationEffect.createOneShot(1, 1), HAPTIC_FEEDBACK_ATTRS);
- vibrateAndWait(service, VibrationEffect.createOneShot(2, 2), RINGTONE_ATTRS);
-
- mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
- vibrateAndWait(service, VibrationEffect.createOneShot(3, 3), /* attributes= */ null);
- vibrateAndWait(service, VibrationEffect.createOneShot(4, 4), NOTIFICATION_ATTRS);
-
- List<VibrationEffect> effects = mVibratorProvider.getEffects();
- assertEquals(3, effects.size());
- assertEquals(2, effects.get(0).getDuration());
- assertEquals(3, effects.get(1).getDuration());
- assertEquals(4, effects.get(2).getDuration());
- }
-
- @Test
- public void vibrate_withAudioAttributes_usesOriginalAudioUsageInAppOpsManager() {
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
- AudioAttributes audioAttributes = new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY).build();
- VibrationAttributes vibrationAttributes = new VibrationAttributes.Builder(
- audioAttributes, effect).build();
-
- vibrate(service, effect, vibrationAttributes);
-
- verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
- eq(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY), anyInt(), anyString());
- }
-
- @Test
- public void vibrate_withVibrationAttributes_usesCorrespondingAudioUsageInAppOpsManager() {
- VibratorService service = createService();
-
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), NOTIFICATION_ATTRS);
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), HAPTIC_FEEDBACK_ATTRS);
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
- new VibrationAttributes.Builder().setUsage(
- VibrationAttributes.USAGE_COMMUNICATION_REQUEST).build());
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK),
- new VibrationAttributes.Builder().setUsage(
- VibrationAttributes.USAGE_UNKNOWN).build());
-
- InOrder inOrderVerifier = inOrder(mAppOpsManagerMock);
- inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
- eq(AudioAttributes.USAGE_ALARM), anyInt(), anyString());
- inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
- eq(AudioAttributes.USAGE_NOTIFICATION), anyInt(), anyString());
- inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
- eq(AudioAttributes.USAGE_NOTIFICATION_RINGTONE), anyInt(), anyString());
- inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
- eq(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION), anyInt(), anyString());
- inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
- eq(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST),
- anyInt(), anyString());
- inOrderVerifier.verify(mAppOpsManagerMock).checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
- eq(AudioAttributes.USAGE_UNKNOWN), anyInt(), anyString());
- }
-
- @Test
- public void vibrate_withOneShotAndInputDevices_vibratesInputDevices() throws Exception {
- when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
- when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
- setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.createOneShot(100, 128);
- vibrate(service, effect, ALARM_ATTRS);
- verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
-
- // VibrationThread will start this vibration async, so wait before checking it never played.
- Thread.sleep(10);
- assertTrue(mVibratorProvider.getEffects().isEmpty());
- }
-
- @Test
- public void vibrate_withOneShotAndAmplitudeControl_turnsVibratorOnAndSetsAmplitude()
- throws Exception {
- mVibratorProvider.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- VibratorService service = createService();
-
- vibrateAndWait(service, VibrationEffect.createOneShot(100, 128), ALARM_ATTRS);
-
- List<VibrationEffect> effects = mVibratorProvider.getEffects();
- assertEquals(1, effects.size());
- assertEquals(100, effects.get(0).getDuration());
- assertEquals(Arrays.asList(128), mVibratorProvider.getAmplitudes());
- }
-
- @Test
- public void vibrate_withOneShotAndNoAmplitudeControl_turnsVibratorOnAndIgnoresAmplitude()
- throws Exception {
- VibratorService service = createService();
- clearInvocations();
-
- vibrateAndWait(service, VibrationEffect.createOneShot(100, 128), ALARM_ATTRS);
-
- List<VibrationEffect> effects = mVibratorProvider.getEffects();
- assertEquals(1, effects.size());
- assertEquals(100, effects.get(0).getDuration());
- assertTrue(mVibratorProvider.getAmplitudes().isEmpty());
- }
-
- @Test
- public void vibrate_withPrebaked_performsEffect() throws Exception {
- mVibratorProvider.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
- vibrateAndWait(service, effect, ALARM_ATTRS);
-
- VibrationEffect.Prebaked expectedEffect = new VibrationEffect.Prebaked(
- VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG);
- assertEquals(Arrays.asList(expectedEffect), mVibratorProvider.getEffects());
- }
-
- @Test
- public void vibrate_withPrebakedAndInputDevices_vibratesFallbackWaveformOnInputDevices()
- throws Exception {
- when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
- when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
- setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
- VibratorService service = createService();
-
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
- verify(mIInputManagerMock).vibrate(eq(1), any(), any());
-
- // VibrationThread will start this vibration async, so wait before checking it never played.
- Thread.sleep(10);
- assertTrue(mVibratorProvider.getEffects().isEmpty());
- }
-
- @Test
- public void vibrate_enteringLowPowerMode_cancelVibration() throws Exception {
- VibratorService service = createService();
-
- mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
- vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
-
- // VibrationThread will start this vibration async, so wait before triggering callbacks.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
-
- mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
-
- // Wait for callback to cancel vibration.
- Thread.sleep(10);
- assertFalse(service.isVibrating());
- }
-
- @Test
- public void vibrate_enteringLowPowerModeAndRingtone_doNotCancelVibration() throws Exception {
- VibratorService service = createService();
-
- mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
- vibrate(service, VibrationEffect.createOneShot(1000, 100), RINGTONE_ATTRS);
-
- // VibrationThread will start this vibration async, so wait before triggering callbacks.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
-
- mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
-
- // Wait for callback to cancel vibration.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
- }
-
- @Test
- public void vibrate_withSettingsChanged_doNotCancelVibration() throws Exception {
- VibratorService service = createService();
- vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
-
- // VibrationThread will start this vibration async, so wait before triggering callbacks.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
-
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_MEDIUM);
-
- // FakeSettingsProvider don't support testing triggering ContentObserver yet.
- service.updateVibrators();
-
- // Wait for callback to cancel vibration.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
- }
-
- @Test
- public void vibrate_withComposed_performsEffect() throws Exception {
- mVibratorProvider.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
- .compose();
- vibrateAndWait(service, effect, ALARM_ATTRS);
- assertEquals(Arrays.asList(effect), mVibratorProvider.getEffects());
- }
-
- @Test
- public void vibrate_withComposedAndInputDevices_vibratesInputDevices() throws Exception {
- when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2});
- when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
- when(mIInputManagerMock.getInputDevice(2)).thenReturn(createInputDeviceWithVibrator(2));
- setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
- .compose();
- vibrate(service, effect, ALARM_ATTRS);
- InOrder inOrderVerifier = inOrder(mIInputManagerMock);
- inOrderVerifier.verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
- inOrderVerifier.verify(mIInputManagerMock).vibrate(eq(2), eq(effect), any());
-
- // VibrationThread will start this vibration async, so wait before checking it never played.
- Thread.sleep(10);
- assertTrue(mVibratorProvider.getEffects().isEmpty());
- }
-
- @Test
- public void vibrate_withWaveform_controlsVibratorAmplitudeDuringTotalVibrationTime()
- throws Exception {
- mVibratorProvider.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.createWaveform(
- new long[]{10, 10, 10}, new int[]{100, 200, 50}, -1);
- vibrateAndWait(service, effect, ALARM_ATTRS);
-
- assertEquals(Arrays.asList(100, 200, 50), mVibratorProvider.getAmplitudes());
- assertEquals(
- Arrays.asList(VibrationEffect.createOneShot(30, VibrationEffect.DEFAULT_AMPLITUDE)),
- mVibratorProvider.getEffects());
- }
-
- @Test
- public void vibrate_withWaveformAndInputDevices_vibratesInputDevices() throws Exception {
- when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
- when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
- setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.createWaveform(
- new long[]{10, 10, 10}, new int[]{100, 200, 50}, -1);
- vibrate(service, effect, ALARM_ATTRS);
- verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
-
- // VibrationThread will start this vibration async, so wait before checking it never played.
- Thread.sleep(10);
- assertTrue(mVibratorProvider.getEffects().isEmpty());
- }
-
- @Test
- public void vibrate_withNativeCallbackTriggered_finishesVibration() throws Exception {
- mVibratorProvider.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- VibratorService service = createService();
-
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
-
- // VibrationThread will start this vibration async, so wait before triggering callbacks.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
-
- // Trigger callbacks from controller.
- mTestLooper.moveTimeForward(50);
- mTestLooper.dispatchAll();
-
- // VibrationThread needs some time to react to native callbacks and stop the vibrator.
- Thread.sleep(10);
- assertFalse(service.isVibrating());
- }
-
- @Test
- public void cancelVibrate_withDeviceVibrating_callsOff() throws Exception {
- VibratorService service = createService();
-
- vibrate(service, VibrationEffect.createOneShot(100, 100), ALARM_ATTRS);
-
- // VibrationThread will start this vibration async, so wait before checking.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
-
- service.cancelVibrate(service);
-
- // VibrationThread will stop this vibration async, so wait before checking.
- Thread.sleep(10);
- assertFalse(service.isVibrating());
- }
-
- @Test
- public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
- VibratorService service = createService();
- service.registerVibratorStateListener(mVibratorStateListenerMock);
-
- vibrateAndWait(service, VibrationEffect.createOneShot(100, 100), ALARM_ATTRS);
-
- InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock);
- // First notification done when listener is registered.
- inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(false));
- inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(true));
- inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(false));
- inOrderVerifier.verifyNoMoreInteractions();
- }
-
- @Test
- public void unregisterVibratorStateListener_callbackNotTriggeredAfter() throws Exception {
- VibratorService service = createService();
-
- service.registerVibratorStateListener(mVibratorStateListenerMock);
-
- vibrate(service, VibrationEffect.createOneShot(30, 100), ALARM_ATTRS);
-
- // VibrationThread will start this vibration async, so wait before triggering callbacks.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
-
- service.unregisterVibratorStateListener(mVibratorStateListenerMock);
- // Trigger callbacks from controller.
- mTestLooper.moveTimeForward(50);
- mTestLooper.dispatchAll();
- Thread.sleep(20);
- assertFalse(service.isVibrating());
-
- InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock);
- // First notification done when listener is registered.
- inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(false));
- inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(true));
- inOrderVerifier.verify(mVibratorStateListenerMock, atLeastOnce()).asBinder(); // unregister
- inOrderVerifier.verifyNoMoreInteractions();
- }
-
- @Test
- public void scale_withPrebaked_userIntensitySettingAsEffectStrength() throws Exception {
- // Alarm vibration is always VIBRATION_INTENSITY_HIGH.
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_MEDIUM);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_LOW);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
- mVibratorProvider.setSupportedEffects(
- VibrationEffect.EFFECT_CLICK,
- VibrationEffect.EFFECT_TICK,
- VibrationEffect.EFFECT_DOUBLE_CLICK,
- VibrationEffect.EFFECT_HEAVY_CLICK);
- VibratorService service = createService();
-
- vibrateAndWait(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
- vibrateAndWait(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK),
- NOTIFICATION_ATTRS);
- vibrateAndWait(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK),
- HAPTIC_FEEDBACK_ATTRS);
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS);
-
- List<Integer> playedStrengths = mVibratorProvider.getEffects().stream()
- .map(VibrationEffect.Prebaked.class::cast)
- .map(VibrationEffect.Prebaked::getEffectStrength)
- .collect(Collectors.toList());
- assertEquals(Arrays.asList(
- VibrationEffect.EFFECT_STRENGTH_STRONG,
- VibrationEffect.EFFECT_STRENGTH_MEDIUM,
- VibrationEffect.EFFECT_STRENGTH_LIGHT),
- playedStrengths);
- }
-
- @Test
- public void scale_withOneShotAndWaveform_usesScaleLevelOnAmplitude() throws Exception {
- mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_HIGH);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_LOW);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
-
- mVibratorProvider.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- VibratorService service = createService();
-
- vibrateAndWait(service, VibrationEffect.createOneShot(20, 100), ALARM_ATTRS);
- vibrateAndWait(service, VibrationEffect.createOneShot(20, 100), NOTIFICATION_ATTRS);
- vibrateAndWait(service,
- VibrationEffect.createWaveform(new long[]{10}, new int[]{100}, -1),
- HAPTIC_FEEDBACK_ATTRS);
- vibrate(service, VibrationEffect.createOneShot(20, 255), RINGTONE_ATTRS);
-
- List<Integer> amplitudes = mVibratorProvider.getAmplitudes();
- assertEquals(3, amplitudes.size());
- // Alarm vibration is never scaled.
- assertEquals(100, amplitudes.get(0).intValue());
- // Notification vibrations will be scaled with SCALE_VERY_HIGH.
- assertTrue(amplitudes.get(1) > 150);
- // Haptic feedback vibrations will be scaled with SCALE_LOW.
- assertTrue(amplitudes.get(2) < 100 && amplitudes.get(2) > 50);
- }
-
- @Test
- public void scale_withComposed_usesScaleLevelOnPrimitiveScaleValues() throws Exception {
- mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_HIGH);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_LOW);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
-
- mVibratorProvider.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- VibratorService service = createService();
-
- VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
- .compose();
-
- vibrateAndWait(service, effect, ALARM_ATTRS);
- vibrateAndWait(service, effect, NOTIFICATION_ATTRS);
- vibrateAndWait(service, effect, HAPTIC_FEEDBACK_ATTRS);
- vibrate(service, effect, RINGTONE_ATTRS);
-
- List<VibrationEffect.Composition.PrimitiveEffect> primitives =
- mVibratorProvider.getEffects().stream()
- .map(VibrationEffect.Composed.class::cast)
- .map(VibrationEffect.Composed::getPrimitiveEffects)
- .flatMap(List::stream)
- .collect(Collectors.toList());
-
- // Ringtone vibration is off, so only the other 3 are propagated to native.
- assertEquals(6, primitives.size());
-
- // Alarm vibration is never scaled.
- assertEquals(1f, primitives.get(0).scale, /* delta= */ 1e-2);
- assertEquals(0.5f, primitives.get(1).scale, /* delta= */ 1e-2);
-
- // Notification vibrations will be scaled with SCALE_VERY_HIGH.
- assertEquals(1f, primitives.get(2).scale, /* delta= */ 1e-2);
- assertTrue(0.7 < primitives.get(3).scale);
-
- // Haptic feedback vibrations will be scaled with SCALE_LOW.
- assertTrue(0.5 < primitives.get(4).scale);
- assertTrue(0.5 > primitives.get(5).scale);
- }
-
- private void vibrate(VibratorService service, VibrationEffect effect,
- VibrationAttributes attrs) {
- service.vibrate(UID, PACKAGE_NAME, effect, attrs, "some reason", service);
- }
-
- private void vibrateAndWait(VibratorService service, VibrationEffect effect,
- VibrationAttributes attrs) throws Exception {
- CountDownLatch startedCount = new CountDownLatch(1);
- CountDownLatch finishedCount = new CountDownLatch(1);
- service.registerVibratorStateListener(new IVibratorStateListener() {
- @Override
- public void onVibrating(boolean vibrating) {
- if (vibrating) {
- startedCount.countDown();
- } else if (startedCount.getCount() == 0) {
- finishedCount.countDown();
- }
- }
-
- @Override
- public IBinder asBinder() {
- return mock(IBinder.class);
- }
- });
-
- mTestLooper.startAutoDispatch();
- service.vibrate(UID, PACKAGE_NAME, effect, attrs, "some reason", service);
- assertTrue(startedCount.await(1, TimeUnit.SECONDS));
- assertTrue(finishedCount.await(1, TimeUnit.SECONDS));
- mTestLooper.stopAutoDispatchAndIgnoreExceptions();
- }
-
- private InputDevice createInputDeviceWithVibrator(int id) {
- return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
- null, /* hasVibrator= */ true, false, false, false /* hasSensor */,
- false /* hasBattery */);
- }
-
- private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
- LocalServices.removeServiceForTest(clazz);
- LocalServices.addService(clazz, mock);
- }
-
- private void setRingerMode(int ringerMode) {
- AudioManager audioManager = mContextSpy.getSystemService(AudioManager.class);
- audioManager.setRingerModeInternal(ringerMode);
- assertEquals(ringerMode, audioManager.getRingerModeInternal());
- }
-
- private void setUserSetting(String settingName, int value) {
- Settings.System.putIntForUser(
- mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
- }
-
- private void setGlobalSetting(String settingName, int value) {
- Settings.Global.putInt(mContextSpy.getContentResolver(), settingName, value);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 27edfd4ef1d2..6963a1ab1538 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -110,8 +110,6 @@ public class AccessibilityServiceConnectionTest {
mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class);
when(mMockIBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient);
- when(mMockWindowManagerInternal.isTouchableDisplay(Display.DEFAULT_DISPLAY)).thenReturn(
- true);
mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
@@ -197,8 +195,9 @@ public class AccessibilityServiceConnectionTest {
}
@Test
- public void sendGesture_touchableDisplay_injectEvents()
+ public void sendGesture_touchableDevice_injectEvents()
throws RemoteException {
+ when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(true);
setServiceBinding(COMPONENT_NAME);
mConnection.bindLocked();
mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
@@ -213,10 +212,9 @@ public class AccessibilityServiceConnectionTest {
}
@Test
- public void sendGesture_untouchableDisplay_performGestureResultFailed()
+ public void sendGesture_untouchableDevice_performGestureResultFailed()
throws RemoteException {
- when(mMockWindowManagerInternal.isTouchableDisplay(Display.DEFAULT_DISPLAY)).thenReturn(
- false);
+ when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(false);
setServiceBinding(COMPONENT_NAME);
mConnection.bindLocked();
mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
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 fdf509504837..8d54ead75761 100644
--- a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
@@ -16,8 +16,12 @@
package com.android.server.am;
+import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL;
+import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU;
+import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_DISPLAY;
+
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import android.content.Context;
@@ -29,17 +33,17 @@ import android.hardware.power.stats.EnergyMeasurement;
import android.hardware.power.stats.PowerEntity;
import android.hardware.power.stats.StateResidencyResult;
import android.power.PowerStatsInternal;
+import android.util.IntArray;
import android.util.SparseArray;
-import android.util.SparseLongArray;
import androidx.test.InstrumentationRegistry;
import com.android.internal.os.BatteryStatsImpl;
-import com.android.internal.power.MeasuredEnergyArray;
import org.junit.Before;
import org.junit.Test;
+import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
/**
@@ -64,41 +68,66 @@ public class BatteryExternalStatsWorkerTest {
}
@Test
- public void getEnergyConsumptionData() {
- SparseLongArray expectSubsystems = new SparseLongArray();
+ public void testTargetedEnergyConsumerQuerying() {
+ final int numCpuClusters = 4;
+ final int numOther = 3;
+
+ final IntArray tempAllIds = new IntArray();
// Add some energy consumers used by BatteryExternalStatsWorker.
final int displayId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.DISPLAY, 0,
"display");
+ tempAllIds.add(displayId);
mPowerStatsInternal.incrementEnergyConsumption(displayId, 12345);
- expectSubsystems.put(MeasuredEnergyArray.SUBSYSTEM_DISPLAY, 12345);
- // Add an arbitrary energy consumer unused by BatteryExternalStatsWorker.
- // Must be changed if '154' ever becomes an EnergyConsumerType used by BESW.
- final int someId = mPowerStatsInternal.addEnergyConsumer((byte) 154, 0, "some_consumer");
- mPowerStatsInternal.incrementEnergyConsumption(someId, 34567);
+ final int[] cpuClusterIds = new int[numCpuClusters];
+ for (int i = 0; i < numCpuClusters; i++) {
+ cpuClusterIds[i] = mPowerStatsInternal.addEnergyConsumer(
+ EnergyConsumerType.CPU_CLUSTER, i, "cpu_cluster" + i);
+ tempAllIds.add(cpuClusterIds[i]);
+ mPowerStatsInternal.incrementEnergyConsumption(cpuClusterIds[i], 1111 + i);
+ }
+ Arrays.sort(cpuClusterIds);
+
+ final int[] otherIds = new int[numOther];
+ for (int i = 0; i < numOther; i++) {
+ otherIds[i] = mPowerStatsInternal.addEnergyConsumer(
+ EnergyConsumerType.OTHER, i, "other" + i);
+ tempAllIds.add(otherIds[i]);
+ mPowerStatsInternal.incrementEnergyConsumption(otherIds[i], 3000 + i);
+ }
+ Arrays.sort(otherIds);
+
+ final int[] allIds = tempAllIds.toArray();
+ Arrays.sort(allIds);
// Inform BESW that PowerStatsInternal is ready to query
mBatteryExternalStatsWorker.systemServicesReady();
- MeasuredEnergyArray energies = mBatteryExternalStatsWorker.getEnergyConsumptionData();
-
- assertEquals(expectSubsystems.size(), energies.size());
- final int size = expectSubsystems.size();
-
- for (int i = 0; i < size; i++) {
- int subsystem = expectSubsystems.keyAt(i);
- // find the subsystem in the returned MeasuredEnergyArray
- int subsystemIndex = -1;
- for (int j = 0; j < size; j++) {
- if (subsystem == energies.getSubsystem(i)) {
- subsystemIndex = i;
- break;
- }
- }
- assertNotEquals("Subsystem " + subsystem + " not found in MeasuredEnergyArray", -1,
- subsystemIndex);
- assertEquals(expectSubsystems.valueAt(i), energies.getEnergy(subsystemIndex));
+ final EnergyConsumerResult[] displayResults =
+ mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_DISPLAY).getNow(null);
+ // Results should only have the display energy consumer
+ assertEquals(1, displayResults.length);
+ assertEquals(displayId, displayResults[0].id);
+
+ final EnergyConsumerResult[] cpuResults =
+ mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_CPU).getNow(null);
+ // Results should only have the cpu cluster energy consumers
+ final int[] receivedCpuIds = new int[cpuResults.length];
+ for (int i = 0; i < cpuResults.length; i++) {
+ receivedCpuIds[i] = cpuResults[i].id;
+ }
+ Arrays.sort(receivedCpuIds);
+ assertArrayEquals(cpuClusterIds, receivedCpuIds);
+
+ final EnergyConsumerResult[] allResults =
+ mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_ALL).getNow(null);
+ // All energy consumer results should be available
+ final int[] receivedAllIds = new int[allResults.length];
+ for (int i = 0; i < allResults.length; i++) {
+ receivedAllIds[i] = allResults[i].id;
}
+ Arrays.sort(receivedAllIds);
+ assertArrayEquals(allIds, receivedAllIds);
}
public class TestInjector extends BatteryExternalStatsWorker.Injector {
@@ -122,9 +151,8 @@ public class BatteryExternalStatsWorkerTest {
}
public class TestPowerStatsInternal extends PowerStatsInternal {
- private final SparseArray<EnergyConsumer> mEnergyConsumers = new SparseArray<>();
- private final SparseArray<EnergyConsumerResult> mEnergyConsumerResults =
- new SparseArray<>();
+ private final SparseArray<EnergyConsumer> mEnergyConsumers = new SparseArray();
+ private final SparseArray<EnergyConsumerResult> mEnergyConsumerResults = new SparseArray();
private final int mTimeSinceBoot = 0;
@Override
@@ -141,10 +169,19 @@ public class BatteryExternalStatsWorkerTest {
public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync(
int[] energyConsumerIds) {
final CompletableFuture<EnergyConsumerResult[]> future = new CompletableFuture();
- final int size = mEnergyConsumerResults.size();
- final EnergyConsumerResult[] results = new EnergyConsumerResult[size];
- for (int i = 0; i < size; i++) {
- results[i] = mEnergyConsumerResults.valueAt(i);
+ final EnergyConsumerResult[] results;
+ final int length = energyConsumerIds.length;
+ if (length == 0) {
+ final int size = mEnergyConsumerResults.size();
+ results = new EnergyConsumerResult[size];
+ for (int i = 0; i < size; i++) {
+ results[i] = mEnergyConsumerResults.valueAt(i);
+ }
+ } else {
+ results = new EnergyConsumerResult[length];
+ for (int i = 0; i < length; i++) {
+ results[i] = mEnergyConsumerResults.get(energyConsumerIds[i]);
+ }
}
future.complete(results);
return future;
diff --git a/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java b/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java
index 67d379a47420..1efce39e00fa 100644
--- a/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java
@@ -16,18 +16,23 @@
package com.android.server.am;
+import static com.android.server.am.MeasuredEnergySnapshot.UNAVAILABLE;
+
import static org.junit.Assert.assertEquals;
-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 android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerAttribution;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.util.SparseArray;
import android.util.SparseLongArray;
import androidx.test.filters.SmallTest;
-import com.android.internal.power.MeasuredEnergyArray;
+import com.android.server.am.MeasuredEnergySnapshot.MeasuredEnergyDeltaData;
-import org.junit.Before;
import org.junit.Test;
/**
@@ -38,134 +43,198 @@ import org.junit.Test;
*/
@SmallTest
public final class MeasuredEnergySnapshotTest {
- private static final int NUMBER_SUBSYSTEMS = 3;
- private static final int SUBSYSTEM_DISPLAY = 0;
- private static final int SUBSYSTEM_NEVER_USED = 1;
- private static final int SUBSYSTEM_CATAPULT = 2;
-
- private MeasuredEnergySnapshot mSnapshot;
-
- // Basic MeasuredEnergyArray that supports all the subsystems. Out of order on purpose.
- private final int[] mAllSubsystems =
- {SUBSYSTEM_DISPLAY, SUBSYSTEM_CATAPULT, SUBSYSTEM_NEVER_USED};
- // E.g. mAllSubsystems[mSubsystemIndices[SUBSYSTEM_CATAPULT]]=SUBSYSTEM_CATAPULT
- private final int[] mSubsystemIndices = {0, 2, 1};
- private final long[] mCurrentSubsystemEnergyUJ = {111, 0, 0};
- private final MeasuredEnergyArray mOmniEnergyArray = new MeasuredEnergyArray() {
- @Override
- public int getSubsystem(int index) {
- return mAllSubsystems[index];
- }
-
- @Override
- public long getEnergy(int index) {
- return mCurrentSubsystemEnergyUJ[index];
- }
-
- @Override
- public int size() {
- return mAllSubsystems.length;
- }
+ private static final EnergyConsumer CONSUMER_DISPLAY = createEnergyConsumer(
+ 0, 0, EnergyConsumerType.DISPLAY, "Display");
+ private static final EnergyConsumer CONSUMER_OTHER_0 = createEnergyConsumer(
+ 47, 0, EnergyConsumerType.OTHER, "GPU");
+ private static final EnergyConsumer CONSUMER_OTHER_1 = createEnergyConsumer(
+ 1, 1, EnergyConsumerType.OTHER, "HPU");
+ private static final EnergyConsumer CONSUMER_OTHER_2 = createEnergyConsumer(
+ 436, 2, EnergyConsumerType.OTHER, "IPU");
+
+ private static final SparseArray<EnergyConsumer> ALL_ID_CONSUMER_MAP = createIdToConsumerMap(
+ CONSUMER_DISPLAY, CONSUMER_OTHER_0, CONSUMER_OTHER_1, CONSUMER_OTHER_2);
+ private static final SparseArray<EnergyConsumer> SOME_ID_CONSUMER_MAP = createIdToConsumerMap(
+ CONSUMER_DISPLAY);
+
+ // Elements in each results are purposefully out of order.
+ private static final EnergyConsumerResult[] RESULTS_0 = new EnergyConsumerResult[] {
+ createEnergyConsumerResult(CONSUMER_OTHER_0.id, 90, new int[] {47, 3}, new long[] {14, 13}),
+ createEnergyConsumerResult(CONSUMER_DISPLAY.id, 14, null, null),
+ createEnergyConsumerResult(CONSUMER_OTHER_1.id, 0, null, null),
+ // No CONSUMER_OTHER_2
};
- private final MeasuredEnergyArray mJustDisplayEnergyArray = new MeasuredEnergyArray() {
- @Override
- public int getSubsystem(int index) {
- return mAllSubsystems[0];
- }
-
- @Override
- public long getEnergy(int index) {
- return mCurrentSubsystemEnergyUJ[0];
- }
-
- @Override
- public int size() {
- return 1;
- }
+ private static final EnergyConsumerResult[] RESULTS_1 = new EnergyConsumerResult[] {
+ createEnergyConsumerResult(CONSUMER_DISPLAY.id, 24, null, null),
+ createEnergyConsumerResult(CONSUMER_OTHER_0.id, 90, new int[] {47, 3}, new long[] {14, 13}),
+ createEnergyConsumerResult(CONSUMER_OTHER_2.id, 12, new int[] {6}, new long[] {10}),
+ createEnergyConsumerResult(CONSUMER_OTHER_1.id, 12_000, null, null),
+ };
+ private static final EnergyConsumerResult[] RESULTS_2 = new EnergyConsumerResult[] {
+ createEnergyConsumerResult(CONSUMER_DISPLAY.id, 36, null, null),
+ // No CONSUMER_OTHER_0
+ // No CONSUMER_OTHER_1
+ // No CONSUMER_OTHER_2
+ };
+ private static final EnergyConsumerResult[] RESULTS_3 = new EnergyConsumerResult[] {
+ // No CONSUMER_DISPLAY
+ createEnergyConsumerResult(CONSUMER_OTHER_2.id, 13, new int[] {6}, new long[] {10}),
+ createEnergyConsumerResult(
+ CONSUMER_OTHER_0.id, 190, new int[] {2, 3, 47, 7}, new long[] {9, 18, 14, 6}),
+ createEnergyConsumerResult(CONSUMER_OTHER_1.id, 12_000, null, null),
+ };
+ private static final EnergyConsumerResult[] RESULTS_4 = new EnergyConsumerResult[] {
+ createEnergyConsumerResult(CONSUMER_DISPLAY.id, 43, null, null),
+ createEnergyConsumerResult(
+ CONSUMER_OTHER_0.id, 290, new int[] {7, 47, 3, 2}, new long[] {6, 14, 18, 11}),
+ // No CONSUMER_OTHER_1
+ createEnergyConsumerResult(CONSUMER_OTHER_2.id, 165, new int[] {6, 47}, new long[] {10, 8}),
};
- @Before
- public void setUp() {
- mSnapshot = new MeasuredEnergySnapshot(NUMBER_SUBSYSTEMS, mOmniEnergyArray);
+ @Test
+ public void testUpdateAndGetDelta_empty() {
+ final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(ALL_ID_CONSUMER_MAP);
+ assertNull(snapshot.updateAndGetDelta(null));
+ assertNull(snapshot.updateAndGetDelta(new EnergyConsumerResult[0]));
}
@Test
public void testUpdateAndGetDelta() {
- SparseLongArray result;
-
- // Increment DISPLAY by 15
- incrementEnergyOfSubsystem(SUBSYSTEM_DISPLAY, 15);
- result = mSnapshot.updateAndGetDelta(mOmniEnergyArray);
- assertEquals(1, result.size());
- assertEquals(15, result.get(SUBSYSTEM_DISPLAY));
-
- // Increment DISPLAY by 7
- // Increment CATAPULT by 5. But do NOT include (pull) it in the passed in energy array.
- incrementEnergyOfSubsystem(SUBSYSTEM_DISPLAY, 7);
- incrementEnergyOfSubsystem(SUBSYSTEM_CATAPULT, 5);
- result = mSnapshot.updateAndGetDelta(mJustDisplayEnergyArray); // Just pull display.
- assertEquals(1, result.size());
- assertEquals(7, result.get(SUBSYSTEM_DISPLAY));
-
- // Increment CATAPULT by 64 (in addition to the previous increase of 5)
- incrementEnergyOfSubsystem(SUBSYSTEM_CATAPULT, 64);
- result = mSnapshot.updateAndGetDelta(mOmniEnergyArray);
- assertEquals(1, result.size());
- assertEquals(5 + 64, result.get(SUBSYSTEM_CATAPULT));
-
- // Do nothing
- result = mSnapshot.updateAndGetDelta(mOmniEnergyArray);
- assertEquals("0 results should not appear at all", 0, result.size());
-
- // Increment DISPLAY by 42
- incrementEnergyOfSubsystem(SUBSYSTEM_DISPLAY, 42);
- result = mSnapshot.updateAndGetDelta(mOmniEnergyArray);
- assertEquals(1, result.size());
- assertEquals(42, result.get(SUBSYSTEM_DISPLAY));
-
- // Increment DISPLAY by 106 and CATAPULT by 13
- incrementEnergyOfSubsystem(SUBSYSTEM_DISPLAY, 106);
- incrementEnergyOfSubsystem(SUBSYSTEM_CATAPULT, 13);
- result = mSnapshot.updateAndGetDelta(mOmniEnergyArray);
- assertEquals(2, result.size());
- assertEquals(106, result.get(SUBSYSTEM_DISPLAY));
- assertEquals(13, result.get(SUBSYSTEM_CATAPULT));
+ final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(ALL_ID_CONSUMER_MAP);
+
+ // results0
+ MeasuredEnergyDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0);
+ if (delta != null) { // null is fine here. If non-null, it better be uninteresting though.
+ assertEquals(UNAVAILABLE, delta.displayEnergyUJ);
+ assertNull(delta.otherTotalEnergyUJ);
+ assertNull(delta.otherUidEnergiesUJ);
+ }
+
+ // results1
+ delta = snapshot.updateAndGetDelta(RESULTS_1);
+ assertNotNull(delta);
+ assertEquals(24 - 14, delta.displayEnergyUJ);
+
+ assertNotNull(delta.otherTotalEnergyUJ);
+ assertEquals(90 - 90, delta.otherTotalEnergyUJ[0]);
+ assertEquals(12_000 - 0, delta.otherTotalEnergyUJ[1]);
+ assertEquals(0, delta.otherTotalEnergyUJ[2]); // First good pull. Treat delta as 0.
+
+ assertNotNull(delta.otherUidEnergiesUJ);
+ assertNullOrEmpty(delta.otherUidEnergiesUJ[0]); // No change in uid energies
+ assertNullOrEmpty(delta.otherUidEnergiesUJ[1]);
+ assertNullOrEmpty(delta.otherUidEnergiesUJ[2]);
+
+ // results2
+ delta = snapshot.updateAndGetDelta(RESULTS_2);
+ assertNotNull(delta);
+ assertEquals(36 - 24, delta.displayEnergyUJ);
+ assertNull(delta.otherUidEnergiesUJ);
+ assertNull(delta.otherTotalEnergyUJ);
+
+ // results3
+ delta = snapshot.updateAndGetDelta(RESULTS_3);
+ assertNotNull(delta);
+ assertEquals(UNAVAILABLE, delta.displayEnergyUJ);
+
+ assertNotNull(delta.otherTotalEnergyUJ);
+ assertEquals(190 - 90, delta.otherTotalEnergyUJ[0]);
+ assertEquals(12_000 - 12_000, delta.otherTotalEnergyUJ[1]);
+ assertEquals(13 - 12, delta.otherTotalEnergyUJ[2]);
+
+ assertNotNull(delta.otherUidEnergiesUJ);
+ assertEquals(3, delta.otherUidEnergiesUJ[0].size());
+ assertEquals(9 - 0, delta.otherUidEnergiesUJ[0].get(2));
+ assertEquals(18 - 13, delta.otherUidEnergiesUJ[0].get(3));
+ assertEquals(6 - 0, delta.otherUidEnergiesUJ[0].get(7));
+ assertNullOrEmpty(delta.otherUidEnergiesUJ[1]);
+ assertNullOrEmpty(delta.otherUidEnergiesUJ[2]);
+
+ // results4
+ delta = snapshot.updateAndGetDelta(RESULTS_4);
+ assertNotNull(delta);
+ assertEquals(43 - 36, delta.displayEnergyUJ);
+
+ assertNotNull(delta.otherTotalEnergyUJ);
+ assertEquals(290 - 190, delta.otherTotalEnergyUJ[0]);
+ assertEquals(0, delta.otherTotalEnergyUJ[1]); // Not present (e.g. missing data)
+ assertEquals(165 - 13, delta.otherTotalEnergyUJ[2]);
+
+ assertNotNull(delta.otherUidEnergiesUJ);
+ assertEquals(1, delta.otherUidEnergiesUJ[0].size());
+ assertEquals(11 - 9, delta.otherUidEnergiesUJ[0].get(2));
+ assertNullOrEmpty(delta.otherUidEnergiesUJ[1]); // Not present
+ assertEquals(1, delta.otherUidEnergiesUJ[2].size());
+ assertEquals(8, delta.otherUidEnergiesUJ[2].get(47));
}
- private void incrementEnergyOfSubsystem(int subsystem, long energy) {
- mCurrentSubsystemEnergyUJ[mSubsystemIndices[subsystem]] += energy;
+ /** Test updateAndGetDelta() when the results have consumers absent from idToConsumerMap. */
+ @Test
+ public void testUpdateAndGetDelta_some() {
+ final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(SOME_ID_CONSUMER_MAP);
+
+ // results0
+ MeasuredEnergyDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0);
+ if (delta != null) { // null is fine here. If non-null, it better be uninteresting though.
+ assertEquals(UNAVAILABLE, delta.displayEnergyUJ);
+ assertNull(delta.otherTotalEnergyUJ);
+ assertNull(delta.otherUidEnergiesUJ);
+ }
+
+ // results1
+ delta = snapshot.updateAndGetDelta(RESULTS_1);
+ assertNotNull(delta);
+ assertEquals(24 - 14, delta.displayEnergyUJ);
+ assertNull(delta.otherTotalEnergyUJ); // Although in the results, they're not in the idMap
+ assertNull(delta.otherUidEnergiesUJ);
}
@Test
- public void testUpdateAndGetDelta_null() {
- assertNull(mSnapshot.updateAndGetDelta(null));
+ public void testGetNumOtherOrdinals() {
+ final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(ALL_ID_CONSUMER_MAP);
+ assertEquals(3, snapshot.getNumOtherOrdinals());
}
@Test
- public void testHasSubsystem() {
- // Setup MeasuredEnergySnapshot which reported some of the subsystems.
- final int[] subsystems = {SUBSYSTEM_DISPLAY, SUBSYSTEM_CATAPULT};
- MeasuredEnergyArray measuredEnergyArray = new MeasuredEnergyArray() {
- @Override
- public int getSubsystem(int index) {
- return subsystems[index];
- }
+ public void testGetNumOtherOrdinals_none() {
+ final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(SOME_ID_CONSUMER_MAP);
+ assertEquals(0, snapshot.getNumOtherOrdinals());
+ }
- @Override
- public long getEnergy(int index) {
- return 0; // Irrelevant for this test.
- }
+ private static EnergyConsumer createEnergyConsumer(int id, int ord, byte type, String name) {
+ final EnergyConsumer ec = new EnergyConsumer();
+ ec.id = id;
+ ec.ordinal = ord;
+ ec.type = type;
+ ec.name = name;
+ return ec;
+ }
- @Override
- public int size() {
- return subsystems.length;
+ private static SparseArray<EnergyConsumer> createIdToConsumerMap(EnergyConsumer ... ecs) {
+ final SparseArray<EnergyConsumer> map = new SparseArray<>();
+ for (EnergyConsumer ec : ecs) {
+ map.put(ec.id, ec);
+ }
+ return map;
+ }
+
+ private static EnergyConsumerResult createEnergyConsumerResult(
+ int id, long energyUWs, int[] uids, long[] uidEnergies) {
+ final EnergyConsumerResult ecr = new EnergyConsumerResult();
+ ecr.id = id;
+ ecr.energyUWs = energyUWs;
+ if (uids != null) {
+ ecr.attribution = new EnergyConsumerAttribution[uids.length];
+ for (int i = 0; i < uids.length; i++) {
+ ecr.attribution[i] = new EnergyConsumerAttribution();
+ ecr.attribution[i].uid = uids[i];
+ ecr.attribution[i].energyUWs = uidEnergies[i];
}
- };
- final MeasuredEnergySnapshot snapshot =
- new MeasuredEnergySnapshot(NUMBER_SUBSYSTEMS, measuredEnergyArray);
+ }
+ return ecr;
+ }
- assertTrue(snapshot.hasSubsystem(SUBSYSTEM_DISPLAY));
- assertTrue(snapshot.hasSubsystem(SUBSYSTEM_CATAPULT));
- assertFalse(snapshot.hasSubsystem(SUBSYSTEM_NEVER_USED));
+ private void assertNullOrEmpty(SparseLongArray a) {
+ if (a != null) assertEquals("Array should be null or empty", 0, a.size());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
index 738f008f9f3f..6d4189eb4168 100644
--- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
@@ -90,7 +90,7 @@ public class GameManagerServiceSettingsTests {
writeGameServiceXml();
}
- private void verifyGameServiceSettingsData(Settings settings) {
+ private void verifyGameServiceSettingsData(GameManagerSettings settings) {
assertThat(settings.getGameModeLocked(PACKAGE_NAME_1), is(1));
assertThat(settings.getGameModeLocked(PACKAGE_NAME_2), is(2));
assertThat(settings.getGameModeLocked(PACKAGE_NAME_3), is(3));
@@ -107,7 +107,7 @@ public class GameManagerServiceSettingsTests {
/* write out files and read */
writeOldFiles();
final Context context = InstrumentationRegistry.getContext();
- Settings settings = new Settings(context.getFilesDir());
+ GameManagerSettings settings = new GameManagerSettings(context.getFilesDir());
assertThat(settings.readPersistentDataLocked(), is(true));
verifyGameServiceSettingsData(settings);
}
@@ -118,7 +118,7 @@ public class GameManagerServiceSettingsTests {
// write out files and read
writeOldFiles();
final Context context = InstrumentationRegistry.getContext();
- Settings settings = new Settings(context.getFilesDir());
+ GameManagerSettings settings = new GameManagerSettings(context.getFilesDir());
assertThat(settings.readPersistentDataLocked(), is(true));
// write out, read back in and verify the same
diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
index caa46da306fa..d039a9d6bfe1 100644
--- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -18,14 +18,23 @@ package com.android.server.app;
import static org.junit.Assert.assertEquals;
+import android.Manifest;
import android.app.GameManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
import androidx.test.InstrumentationRegistry;
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.Mock;
+
+import java.util.HashMap;
+import java.util.function.Supplier;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -37,15 +46,88 @@ public class GameManagerServiceTests {
private static final int USER_ID_1 = 1001;
private static final int USER_ID_2 = 1002;
+ // Stolen from ConnectivityServiceTest.MockContext
+ class MockContext extends ContextWrapper {
+ private static final String TAG = "MockContext";
+
+ // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
+ private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();
+
+ MockContext(Context base) {
+ super(base);
+ }
+
+ /**
+ * Mock checks for the specified permission, and have them behave as per {@code granted}.
+ *
+ * <p>Passing null reverts to default behavior, which does a real permission check on the
+ * test package.
+ * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or
+ * {@link PackageManager#PERMISSION_DENIED}.
+ */
+ public void setPermission(String permission, Integer granted) {
+ mMockedPermissions.put(permission, granted);
+ }
+
+ private int checkMockedPermission(String permission, Supplier<Integer> ifAbsent) {
+ final Integer granted = mMockedPermissions.get(permission);
+ return granted != null ? granted : ifAbsent.get();
+ }
+
+ @Override
+ public int checkPermission(String permission, int pid, int uid) {
+ return checkMockedPermission(
+ permission, () -> super.checkPermission(permission, pid, uid));
+ }
+
+ @Override
+ public int checkCallingOrSelfPermission(String permission) {
+ return checkMockedPermission(
+ permission, () -> super.checkCallingOrSelfPermission(permission));
+ }
+
+ @Override
+ public void enforceCallingOrSelfPermission(String permission, String message) {
+ final Integer granted = mMockedPermissions.get(permission);
+ if (granted == null) {
+ super.enforceCallingOrSelfPermission(permission, message);
+ return;
+ }
+
+ if (!granted.equals(PackageManager.PERMISSION_GRANTED)) {
+ throw new SecurityException("[Test] permission denied: " + permission);
+ }
+ }
+ }
+
+ @Mock
+ private MockContext mMockContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockContext = new MockContext(InstrumentationRegistry.getContext());
+ }
+
+ private void mockModifyGameModeGranted() {
+ mMockContext.setPermission(Manifest.permission.MANAGE_GAME_MODE,
+ PackageManager.PERMISSION_GRANTED);
+ }
+
+ private void mockModifyGameModeDenied() {
+ mMockContext.setPermission(Manifest.permission.MANAGE_GAME_MODE,
+ PackageManager.PERMISSION_DENIED);
+ }
+
/**
* By default game mode is not supported.
*/
@Test
public void testGameModeDefaultValue() {
- GameManagerService gameManagerService =
- new GameManagerService(InstrumentationRegistry.getContext());
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
gameManagerService.onUserStarting(USER_ID_1);
+ mockModifyGameModeGranted();
+
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
gameManagerService.getGameMode(PACKAGE_NAME_0, USER_ID_1));
}
@@ -55,10 +137,11 @@ public class GameManagerServiceTests {
*/
@Test
public void testDefaultValueForNonexistentUser() {
- GameManagerService gameManagerService =
- new GameManagerService(InstrumentationRegistry.getContext());
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
gameManagerService.onUserStarting(USER_ID_1);
+ mockModifyGameModeGranted();
+
gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_2);
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_2));
@@ -69,10 +152,11 @@ public class GameManagerServiceTests {
*/
@Test
public void testGameMode() {
- GameManagerService gameManagerService =
- new GameManagerService(InstrumentationRegistry.getContext());
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
gameManagerService.onUserStarting(USER_ID_1);
+ mockModifyGameModeGranted();
+
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_1);
@@ -83,4 +167,48 @@ public class GameManagerServiceTests {
assertEquals(GameManager.GAME_MODE_PERFORMANCE,
gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
}
+
+ /**
+ * Test permission.MANAGE_GAME_MODE is checked
+ */
+ @Test
+ public void testGetGameModePermissionDenied() {
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+
+ // Update the game mode so we can read back something valid.
+ mockModifyGameModeGranted();
+ gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
+
+ // Deny permission.MANAGE_GAME_MODE and verify we get back GameManager.GAME_MODE_UNSUPPORTED
+ mockModifyGameModeDenied();
+ assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
+ gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
+ }
+
+ /**
+ * Test permission.MANAGE_GAME_MODE is checked
+ */
+ @Test
+ public void testSetGameModePermissionDenied() {
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+
+ // Update the game mode so we can read back something valid.
+ mockModifyGameModeGranted();
+ gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_STANDARD, USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
+
+ // Deny permission.MANAGE_GAME_MODE and verify the game mode is not updated.
+ mockModifyGameModeDenied();
+ gameManagerService.setGameMode(PACKAGE_NAME_1, GameManager.GAME_MODE_PERFORMANCE,
+ USER_ID_1);
+
+ mockModifyGameModeGranted();
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ gameManagerService.getGameMode(PACKAGE_NAME_1, USER_ID_1));
+ }
}
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 9b6c7234981c..73b0105210c4 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
@@ -16,8 +16,6 @@
package com.android.server.appsearch.external.localstorage;
-import static android.app.appsearch.AppSearchResult.RESULT_OK;
-
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.expectThrows;
@@ -27,7 +25,7 @@ import android.app.appsearch.GenericDocument;
import android.app.appsearch.SearchResult;
import android.app.appsearch.SearchResultPage;
import android.app.appsearch.SearchSpec;
-import android.app.appsearch.SetSchemaResult;
+import android.app.appsearch.SetSchemaResponse;
import android.app.appsearch.exceptions.AppSearchException;
import android.content.Context;
import android.util.ArraySet;
@@ -66,14 +64,14 @@ public class AppSearchImplTest {
@Before
public void setUp() throws Exception {
Context context = ApplicationProvider.getApplicationContext();
+
// Give ourselves global query permissions
mAppSearchImpl =
AppSearchImpl.create(
mTemporaryFolder.newFolder(),
context,
VisibilityStore.NO_OP_USER_ID,
- /*globalQuerierPackage
- =*/ context.getPackageName());
+ /*globalQuerierPackage=*/ context.getPackageName());
}
// TODO(b/175430168) add test to verify reset is working properly.
@@ -425,18 +423,24 @@ public class AppSearchImplTest {
GetOptimizeInfoResultProto optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked();
assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(0);
- // delete 999 documents , we will reach the threshold to trigger optimize() in next
+ // delete 999 documents, we will reach the threshold to trigger optimize() in next
// deletion.
for (int i = 0; i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - 1; i++) {
mAppSearchImpl.remove("package", "database", "namespace", "uri" + i);
}
- // optimize() still not be triggered since we are in the interval to call getOptimizeInfo()
+ // Updates the check for optimize counter, checkForOptimize() will be triggered since
+ // CHECK_OPTIMIZE_INTERVAL is reached but optimize() won't since
+ // OPTIMIZE_THRESHOLD_DOC_COUNT is not.
+ mAppSearchImpl.checkForOptimize(
+ /*mutateBatchSize=*/ AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - 1);
+
+ // Verify optimize() still not be triggered.
optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked();
assertThat(optimizeInfo.getOptimizableDocs())
.isEqualTo(AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - 1);
- // Keep delete docs, will reach the interval this time and trigger optimize().
+ // Keep delete docs
for (int i = AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT;
i
< AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT
@@ -444,6 +448,9 @@ public class AppSearchImplTest {
i++) {
mAppSearchImpl.remove("package", "database", "namespace", "uri" + i);
}
+ // updates the check for optimize counter, will reach both CHECK_OPTIMIZE_INTERVAL and
+ // OPTIMIZE_THRESHOLD_DOC_COUNT this time and trigger a optimize().
+ mAppSearchImpl.checkForOptimize(/*mutateBatchSize*/ AppSearchImpl.CHECK_OPTIMIZE_INTERVAL);
// Verify optimize() is triggered
optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked();
@@ -783,7 +790,7 @@ public class AppSearchImplTest {
Collections.singletonList(new AppSearchSchema.Builder("Email").build());
// set email incompatible and delete text
- SetSchemaResult setSchemaResult =
+ SetSchemaResponse setSchemaResponse =
mAppSearchImpl.setSchema(
"package",
"database1",
@@ -791,9 +798,8 @@ public class AppSearchImplTest {
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ true);
- assertThat(setSchemaResult.getDeletedSchemaTypes()).containsExactly("Text");
- assertThat(setSchemaResult.getIncompatibleSchemaTypes()).containsExactly("Email");
- assertThat(setSchemaResult.getResultCode()).isEqualTo(RESULT_OK);
+ assertThat(setSchemaResponse.getDeletedTypes()).containsExactly("Text");
+ assertThat(setSchemaResponse.getIncompatibleTypes()).containsExactly("Email");
}
@Test
@@ -836,7 +842,7 @@ public class AppSearchImplTest {
final List<AppSearchSchema> finalSchemas =
Collections.singletonList(new AppSearchSchema.Builder("Email").build());
- SetSchemaResult setSchemaResult =
+ SetSchemaResponse setSchemaResponse =
mAppSearchImpl.setSchema(
"package",
"database1",
@@ -846,7 +852,7 @@ public class AppSearchImplTest {
/*forceOverride=*/ false);
// Check the Document type has been deleted.
- assertThat(setSchemaResult.getDeletedSchemaTypes()).containsExactly("Document");
+ assertThat(setSchemaResponse.getDeletedTypes()).containsExactly("Document");
// ForceOverride to delete.
mAppSearchImpl.setSchema(
@@ -1046,4 +1052,136 @@ public class AppSearchImplTest {
strippedDocumentProto.build()));
}
}
+
+ @Test
+ public void testThrowsExceptionIfClosed() throws Exception {
+ Context context = ApplicationProvider.getApplicationContext();
+ AppSearchImpl appSearchImpl =
+ AppSearchImpl.create(
+ mTemporaryFolder.newFolder(),
+ context,
+ VisibilityStore.NO_OP_USER_ID,
+ /*globalQuerierPackage=*/ "");
+
+ // Initial check that we could do something at first.
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ appSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false);
+
+ appSearchImpl.close();
+
+ // Check all our public APIs
+ expectThrows(
+ IllegalStateException.class,
+ () -> {
+ appSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false);
+ });
+
+ expectThrows(
+ IllegalStateException.class,
+ () -> {
+ appSearchImpl.getSchema("package", "database");
+ });
+
+ expectThrows(
+ IllegalStateException.class,
+ () -> {
+ appSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("uri", "type")
+ .setNamespace("namespace")
+ .build());
+ });
+
+ expectThrows(
+ IllegalStateException.class,
+ () -> {
+ appSearchImpl.getDocument(
+ "package", "database", "namespace", "uri", Collections.emptyMap());
+ });
+
+ expectThrows(
+ IllegalStateException.class,
+ () -> {
+ appSearchImpl.query(
+ "package",
+ "database",
+ "query",
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .build());
+ });
+
+ expectThrows(
+ IllegalStateException.class,
+ () -> {
+ appSearchImpl.globalQuery(
+ "query",
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .build(),
+ "package",
+ /*callerUid=*/ 1);
+ });
+
+ expectThrows(
+ IllegalStateException.class,
+ () -> {
+ appSearchImpl.getNextPage(/*nextPageToken=*/ 1L);
+ });
+
+ expectThrows(
+ IllegalStateException.class,
+ () -> {
+ appSearchImpl.invalidateNextPageToken(/*nextPageToken=*/ 1L);
+ });
+
+ expectThrows(
+ IllegalStateException.class,
+ () -> {
+ appSearchImpl.reportUsage(
+ "package",
+ "database",
+ "namespace",
+ "uri",
+ /*usageTimestampMillis=*/ 1000L);
+ });
+
+ expectThrows(
+ IllegalStateException.class,
+ () -> {
+ appSearchImpl.remove("package", "database", "namespace", "uri");
+ });
+
+ expectThrows(
+ IllegalStateException.class,
+ () -> {
+ appSearchImpl.removeByQuery(
+ "package",
+ "database",
+ "query",
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .build());
+ });
+
+ expectThrows(
+ IllegalStateException.class,
+ () -> {
+ appSearchImpl.persistToDisk();
+ });
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
index 96f434405199..ff8fedce9368 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
@@ -16,6 +16,8 @@
package com.android.server.appwidget;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -44,6 +46,7 @@ import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.widget.RemoteViews;
+import com.android.frameworks.servicestests.R;
import com.android.internal.appwidget.IAppWidgetHost;
import com.android.server.LocalServices;
@@ -100,6 +103,26 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase {
assertEquals(info.loadDescription(mTestContext), "widget description string");
}
+ public void testParseSizeConfiguration() {
+ AppWidgetProviderInfo info =
+ mManager.getInstalledProvidersForPackage(mPkgName, null).get(0);
+
+ assertThat(info.minWidth).isEqualTo(getDimensionResource(R.dimen.widget_min_width));
+ assertThat(info.minHeight).isEqualTo(getDimensionResource(R.dimen.widget_min_height));
+ assertThat(info.minResizeWidth)
+ .isEqualTo(getDimensionResource(R.dimen.widget_min_resize_width));
+ assertThat(info.minResizeHeight)
+ .isEqualTo(getDimensionResource(R.dimen.widget_min_resize_height));
+ assertThat(info.maxResizeWidth)
+ .isEqualTo(getDimensionResource(R.dimen.widget_max_resize_width));
+ assertThat(info.maxResizeHeight)
+ .isEqualTo(getDimensionResource(R.dimen.widget_max_resize_height));
+ assertThat(info.targetCellWidth)
+ .isEqualTo(getIntegerResource(R.integer.widget_target_cell_width));
+ assertThat(info.targetCellHeight)
+ .isEqualTo(getIntegerResource(R.integer.widget_target_cell_height));
+ }
+
public void testRequestPinAppWidget_otherProvider() {
ComponentName otherProvider = null;
for (AppWidgetProviderInfo provider : mManager.getInstalledProviders()) {
@@ -293,6 +316,13 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase {
}
}
+ public void testGetPreviewLayout() {
+ AppWidgetProviderInfo info =
+ mManager.getInstalledProvidersForPackage(mPkgName, null).get(0);
+
+ assertThat(info.previewLayout).isEqualTo(R.layout.widget_preview);
+ }
+
private int setupHostAndWidget() {
List<PendingHostUpdate> updates = mService.startListening(
mMockHost, mPkgName, HOST_ID, new int[0]).getList();
@@ -315,6 +345,14 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase {
latch.await();
}
+ private int getDimensionResource(int resId) {
+ return mTestContext.getResources().getDimensionPixelSize(resId);
+ }
+
+ private int getIntegerResource(int resId) {
+ return mTestContext.getResources().getInteger(resId);
+ }
+
private class TestContext extends ContextWrapper {
public TestContext() {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 8d890b9cd606..7a4b901bdc5e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -103,7 +103,8 @@ public class AuthSessionTest {
@Test
public void testNewAuthSession_eligibleSensorsSetToStateUnknown() throws RemoteException {
setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_REAR);
- setupFace(1 /* id */, false /* confirmationAlwaysRequired */);
+ setupFace(1 /* id */, false /* confirmationAlwaysRequired */,
+ mock(IBiometricAuthenticator.class));
final AuthSession session = createAuthSession(mSensors,
false /* checkDevicePolicyManager */,
@@ -118,7 +119,8 @@ public class AuthSessionTest {
@Test
public void testStartNewAuthSession() throws RemoteException {
- setupFace(0 /* id */, false /* confirmationAlwaysRequired */);
+ setupFace(0 /* id */, false /* confirmationAlwaysRequired */,
+ mock(IBiometricAuthenticator.class));
setupFingerprint(1 /* id */, FingerprintSensorProperties.TYPE_REAR);
final boolean requireConfirmation = true;
@@ -181,9 +183,6 @@ public class AuthSessionTest {
final long operationId = 123;
final int userId = 10;
- final int callingUid = 100;
- final int callingPid = 1000;
- final int callingUserId = 10000;
final AuthSession session = createAuthSession(mSensors,
false /* checkDevicePolicyManager */,
@@ -220,7 +219,25 @@ public class AuthSessionTest {
assertEquals(STATE_AUTH_STARTED_UI_SHOWING, session.getState());
assertEquals(BiometricSensor.STATE_AUTHENTICATING,
session.mPreAuthInfo.eligibleSensors.get(0).getSensorState());
+ }
+
+ @Test
+ public void testCancelAuthentication_whenStateAuthCalled_invokesCancel()
+ throws RemoteException {
+ final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
+
+ setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator);
+ final AuthSession session = createAuthSession(mSensors,
+ false /* checkDevicePolicyManager */,
+ Authenticators.BIOMETRIC_STRONG,
+ 0 /* operationId */,
+ 0 /* userId */);
+
+ session.goToInitialState();
+ assertEquals(STATE_AUTH_CALLED, session.getState());
+ session.onCancelAuthSession(false /* force */);
+ verify(faceAuthenticator).cancelAuthenticationFromService(eq(mToken), eq(TEST_PACKAGE));
}
private PreAuthInfo createPreAuthInfo(List<BiometricSensor> sensors, int userId,
@@ -282,14 +299,14 @@ public class AuthSessionTest {
false /* resetLockoutRequiresHardwareAuthToken */));
}
- private void setupFace(int id, boolean confirmationAlwaysRequired) throws RemoteException {
- IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
- when(faceAuthenticator.isHardwareDetected(any())).thenReturn(true);
- when(faceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
+ private void setupFace(int id, boolean confirmationAlwaysRequired,
+ IBiometricAuthenticator authenticator) throws RemoteException {
+ when(authenticator.isHardwareDetected(any())).thenReturn(true);
+ when(authenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
mSensors.add(new BiometricSensor(id,
TYPE_FACE /* modality */,
Authenticators.BIOMETRIC_STRONG /* strength */,
- faceAuthenticator) {
+ authenticator) {
@Override
boolean confirmationAlwaysRequired(int userId) {
return confirmationAlwaysRequired;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index 2d457260b8fe..7dd073499c73 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -27,6 +27,8 @@ 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 static org.mockito.Mockito.withSettings;
import android.content.Context;
import android.hardware.biometrics.BiometricConstants;
@@ -159,8 +161,8 @@ public class BiometricSchedulerTest {
// Client 1 cleans up properly
verify(listener1).onError(eq(TEST_SENSOR_ID), anyInt(),
- eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED), eq(0));
- verify(callback1).onClientFinished(eq(client1), eq(true) /* success */);
+ eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(0));
+ verify(callback1).onClientFinished(eq(client1), eq(false) /* success */);
verify(callback1, never()).onClientStarted(any());
// Client 2 was able to start
@@ -310,6 +312,37 @@ public class BiometricSchedulerTest {
assertNull(mScheduler.getCurrentClient());
}
+ @Test
+ public void testInterruptPrecedingClients_whenExpected() {
+ final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class,
+ withSettings().extraInterfaces(Interruptable.class));
+
+ final BaseClientMonitor interrupter = mock(BaseClientMonitor.class);
+ when(interrupter.interruptsPrecedingClients()).thenReturn(true);
+
+ mScheduler.scheduleClientMonitor(interruptableMonitor);
+ mScheduler.scheduleClientMonitor(interrupter);
+ waitForIdle();
+
+ verify((Interruptable) interruptableMonitor).cancel();
+ mScheduler.getInternalCallback().onClientFinished(interruptableMonitor, true /* success */);
+ }
+
+ @Test
+ public void testDoesNotInterruptPrecedingClients_whenNotExpected() {
+ final BaseClientMonitor interruptableMonitor = mock(BaseClientMonitor.class,
+ withSettings().extraInterfaces(Interruptable.class));
+
+ final BaseClientMonitor interrupter = mock(BaseClientMonitor.class);
+ when(interrupter.interruptsPrecedingClients()).thenReturn(false);
+
+ mScheduler.scheduleClientMonitor(interruptableMonitor);
+ mScheduler.scheduleClientMonitor(interrupter);
+ waitForIdle();
+
+ verify((Interruptable) interruptableMonitor, never()).cancel();
+ }
+
private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception {
return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer));
}
diff --git a/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java b/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java
index d0767ccb6f87..c165c661a625 100644
--- a/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java
@@ -22,6 +22,7 @@ class ApplicationInfoBuilder {
private boolean mIsDebuggable;
private int mTargetSdk;
private String mPackageName;
+ private long mVersionCode;
private ApplicationInfoBuilder() {
mTargetSdk = -1;
@@ -46,6 +47,11 @@ class ApplicationInfoBuilder {
return this;
}
+ ApplicationInfoBuilder withVersionCode(Long versionCode) {
+ mVersionCode = versionCode;
+ return this;
+ }
+
ApplicationInfo build() {
final ApplicationInfo applicationInfo = new ApplicationInfo();
if (mIsDebuggable) {
@@ -53,6 +59,7 @@ class ApplicationInfoBuilder {
}
applicationInfo.packageName = mPackageName;
applicationInfo.targetSdkVersion = mTargetSdk;
+ applicationInfo.longVersionCode = mVersionCode;
return applicationInfo;
}
}
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 a53ff9bc7fdc..8b0e948579fb 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -18,6 +18,7 @@ package com.android.server.compat;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -25,6 +26,7 @@ import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
import android.app.compat.ChangeIdStateCache;
+import android.app.compat.PackageOverride;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -33,6 +35,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.compat.AndroidBuildClassifier;
+import com.android.internal.compat.CompatibilityOverrideConfig;
import org.junit.Before;
import org.junit.Test;
@@ -46,6 +49,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
+import java.util.Collections;
import java.util.UUID;
@RunWith(AndroidJUnit4.class)
@@ -83,6 +87,8 @@ public class CompatConfigTest {
when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
when(mBuildClassifier.isFinalBuild()).thenReturn(false);
ChangeIdStateCache.disable();
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt()))
+ .thenThrow(new NameNotFoundException());
}
@Test
@@ -163,6 +169,10 @@ public class CompatConfigTest {
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
.addDisabledChangeWithId(1234L)
.build();
+ ApplicationInfo info = ApplicationInfoBuilder.create()
+ .withPackageName("com.some.package").build();
+ when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+ .thenReturn(info);
compatConfig.addOverride(1234L, "com.some.package", true);
@@ -177,6 +187,10 @@ public class CompatConfigTest {
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
.addEnabledChangeWithId(1234L)
.build();
+ ApplicationInfo info = ApplicationInfoBuilder.create()
+ .withPackageName("com.some.package").build();
+ when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+ .thenReturn(info);
compatConfig.addOverride(1234L, "com.some.package", false);
@@ -191,6 +205,10 @@ public class CompatConfigTest {
CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
compatConfig.forceNonDebuggableFinalForTest(false);
+ ApplicationInfo info = ApplicationInfoBuilder.create()
+ .withPackageName("com.some.package").build();
+ when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+ .thenReturn(info);
compatConfig.addOverride(1234L, "com.some.package", false);
@@ -265,6 +283,71 @@ public class CompatConfigTest {
}
@Test
+ public void testOverrideWithAppVersion() throws Exception {
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("com.installed.foo")
+ .withVersionCode(100L)
+ .debuggable().build();
+ when(mPackageManager.getApplicationInfo(eq("com.installed.foo"), anyInt()))
+ .thenReturn(applicationInfo);
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1234L).build();
+ when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+ when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+ // Add override that doesn't include the installed app version
+ CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(
+ Collections.singletonMap(1234L,
+ new PackageOverride.Builder()
+ .setMaxVersionCode(99L)
+ .setEnabled(true)
+ .build()));
+ compatConfig.addOverrides(config, "com.installed.foo");
+ assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
+
+ // Add override that does include the installed app version
+ config = new CompatibilityOverrideConfig(
+ Collections.singletonMap(1234L,
+ new PackageOverride.Builder()
+ .setMinVersionCode(100L)
+ .setMaxVersionCode(100L)
+ .setEnabled(true)
+ .build()));
+ compatConfig.addOverrides(config, "com.installed.foo");
+ assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
+ }
+
+ @Test
+ public void testApplyDeferredOverridesAfterInstallingAppVersion() throws Exception {
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("com.notinstalled.foo")
+ .withVersionCode(100L)
+ .debuggable().build();
+ when(mPackageManager.getApplicationInfo(eq("com.notinstalled.foo"), anyInt()))
+ .thenThrow(new NameNotFoundException());
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1234L).build();
+ when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+ when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+ // Add override before the app is available.
+ CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(
+ Collections.singletonMap(1234L, new PackageOverride.Builder()
+ .setMaxVersionCode(99L)
+ .setEnabled(true)
+ .build()));
+ compatConfig.addOverrides(config, "com.notinstalled.foo");
+ assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
+
+ // Pretend the app is now installed.
+ when(mPackageManager.getApplicationInfo(eq("com.notinstalled.foo"), anyInt()))
+ .thenReturn(applicationInfo);
+
+ compatConfig.recheckOverrides("com.notinstalled.foo");
+ assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
+ }
+
+ @Test
public void testApplyDeferredOverrideClearsOverrideAfterUninstall() throws Exception {
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
.withPackageName("com.installedapp.foo")
@@ -384,6 +467,8 @@ public class CompatConfigTest {
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
.withPackageName("com.some.package")
.build();
+ when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+ .thenReturn(applicationInfo);
assertThat(compatConfig.addOverride(1234L, "com.some.package", false)).isTrue();
assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
@@ -404,6 +489,8 @@ public class CompatConfigTest {
.withPackageName("foo.bar")
.withTargetSdk(2)
.build();
+ when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
+ .thenReturn(applicationInfo);
assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isFalse();
assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse();
@@ -425,7 +512,8 @@ public class CompatConfigTest {
.withPackageName("foo.bar")
.withTargetSdk(2)
.build();
-
+ when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
+ .thenReturn(applicationInfo);
assertThat(compatConfig.enableTargetSdkChangesForPackage("foo.bar", 3)).isEqualTo(1);
assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isTrue();
assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse();
@@ -533,22 +621,114 @@ public class CompatConfigTest {
+ " <override-value packageName=\"foo.bar\" enabled=\"true\">\n"
+ " </override-value>\n"
+ " </validated>\n"
- + " <deferred>\n"
- + " </deferred>\n"
+ + " <raw>\n"
+ + " <raw-override-value packageName=\"foo.bar\" "
+ + "minVersionCode=\"-9223372036854775808\" "
+ + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ + " </raw-override-value>\n"
+ + " </raw>\n"
+ " </change-overrides>\n"
+ " <change-overrides changeId=\"2\">\n"
+ " <validated>\n"
+ " </validated>\n"
- + " <deferred>\n"
- + " <override-value packageName=\"bar.baz\" enabled=\"false\">\n"
- + " </override-value>\n"
- + " </deferred>\n"
+ + " <raw>\n"
+ + " <raw-override-value packageName=\"bar.baz\" "
+ + "minVersionCode=\"-9223372036854775808\" "
+ + "maxVersionCode=\"9223372036854775807\" enabled=\"false\">\n"
+ + " </raw-override-value>\n"
+ + " </raw>\n"
+ + " </change-overrides>\n"
+ + "</overrides>\n");
+ }
+
+ @Test
+ public void testSaveOverridesWithRanges() throws Exception {
+ File overridesFile = new File(createTempDir(), "overrides.xml");
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1L)
+ .addEnableSinceSdkChangeWithId(2, 2L)
+ .build();
+ compatConfig.forceNonDebuggableFinalForTest(true);
+ compatConfig.initOverrides(overridesFile);
+
+ compatConfig.addOverrides(new CompatibilityOverrideConfig(Collections.singletonMap(1L,
+ new PackageOverride.Builder()
+ .setMinVersionCode(99L)
+ .setMaxVersionCode(101L)
+ .setEnabled(true)
+ .build())), "foo.bar");
+
+ assertThat(readFile(overridesFile)).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<overrides>\n"
+ + " <change-overrides changeId=\"1\">\n"
+ + " <validated>\n"
+ + " </validated>\n"
+ + " <raw>\n"
+ + " <raw-override-value packageName=\"foo.bar\" "
+ + "minVersionCode=\"99\" maxVersionCode=\"101\" enabled=\"true\">\n"
+ + " </raw-override-value>\n"
+ + " </raw>\n"
+ " </change-overrides>\n"
+ "</overrides>\n");
}
@Test
- public void testLoadOverrides() throws Exception {
+ public void testLoadOverridesRaw() throws Exception {
+ File tempDir = createTempDir();
+ File overridesFile = new File(tempDir, "overrides.xml");
+ // Change 1 is enabled for foo.bar (validated)
+ // Change 2 is disabled for bar.baz (deferred)
+ String xmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ + "<overrides>\n"
+ + " <change-overrides changeId=\"1\">\n"
+ + " <validated>\n"
+ + " <override-value packageName=\"foo.bar\" enabled=\"true\">\n"
+ + " </override-value>\n"
+ + " </validated>\n"
+ + " <raw>\n"
+ + " <raw-override-value packageName=\"foo.bar\" "
+ + "minVersionCode=\"-9223372036854775808\" "
+ + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ + " </raw-override-value>\n"
+ + " </raw>\n"
+ + " </change-overrides>\n"
+ + " <change-overrides changeId=\"2\">\n"
+ + " <validated>\n"
+ + " </validated>\n"
+ + " <raw>\n"
+ + " <raw-override-value packageName=\"bar.baz\" "
+ + "minVersionCode=\"-9223372036854775808\" "
+ + "maxVersionCode=\"9223372036854775807\" enabled=\"false\">\n"
+ + " </raw-override-value>\n"
+ + " </raw>\n"
+ + " </change-overrides>\n"
+ + "</overrides>\n";
+ writeToFile(tempDir, "overrides.xml", xmlData);
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1L)
+ .addEnableSinceSdkChangeWithId(2, 2L)
+ .build();
+ compatConfig.forceNonDebuggableFinalForTest(true);
+ compatConfig.initOverrides(overridesFile);
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("foo.bar")
+ .withVersionCode(100L)
+ .debuggable()
+ .build();
+ when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
+ .thenReturn(applicationInfo);
+ when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
+ .thenThrow(new NameNotFoundException());
+
+ assertThat(compatConfig.isChangeEnabled(1L, applicationInfo)).isTrue();
+ assertThat(compatConfig.willChangeBeEnabled(2L, "bar.baz")).isFalse();
+
+ compatConfig.recheckOverrides("foo.bar");
+ assertThat(compatConfig.isChangeEnabled(1L, applicationInfo)).isTrue();
+ }
+
+ @Test
+ public void testLoadOverridesDeferred() throws Exception {
File tempDir = createTempDir();
File overridesFile = new File(tempDir, "overrides.xml");
// Change 1 is enabled for foo.bar (validated)
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 a1b2dc8bd82d..799b06734b54 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -196,6 +196,9 @@ public class PlatformCompatTest {
mPlatformCompat.registerListener(1, mListener1);
mPlatformCompat.registerListener(2, mListener1);
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
+
mPlatformCompat.setOverrides(
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
PACKAGE_NAME);
@@ -208,6 +211,9 @@ public class PlatformCompatTest {
mPlatformCompat.registerListener(1, mListener1);
mPlatformCompat.registerListener(2, mListener1);
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
+
mPlatformCompat.setOverrides(
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
PACKAGE_NAME);
@@ -219,6 +225,9 @@ public class PlatformCompatTest {
public void testListenerCalledOnSetOverridesTwoListeners() throws Exception {
mPlatformCompat.registerListener(1, mListener1);
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
+
mPlatformCompat.setOverrides(
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
PACKAGE_NAME);
@@ -244,6 +253,9 @@ public class PlatformCompatTest {
mPlatformCompat.registerListener(1, mListener1);
mPlatformCompat.registerListener(2, mListener1);
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
+
mPlatformCompat.setOverrides(
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
PACKAGE_NAME);
@@ -252,9 +264,12 @@ public class PlatformCompatTest {
}
@Test
- public void testListenerCalledOnSetOverridesTwoListenersForTest() throws Exception {
+ public void testListenerCalledOnSetOverridesForTestTwoListeners() throws Exception {
mPlatformCompat.registerListener(1, mListener1);
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
+
mPlatformCompat.setOverrides(
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
PACKAGE_NAME);
@@ -280,6 +295,9 @@ public class PlatformCompatTest {
mPlatformCompat.registerListener(1, mListener1);
mPlatformCompat.registerListener(2, mListener2);
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
+
mPlatformCompat.setOverrides(
CompatibilityChangeConfigBuilder.create().enable(1L).build(),
PACKAGE_NAME);
@@ -299,6 +317,9 @@ public class PlatformCompatTest {
mPlatformCompat.registerListener(1, mListener1);
mPlatformCompat.registerListener(2, mListener2);
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
+
mPlatformCompat.setOverrides(
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
PACKAGE_NAME);
@@ -318,6 +339,9 @@ public class PlatformCompatTest {
mPlatformCompat.registerListener(1, mListener1);
mPlatformCompat.registerListener(2, mListener2);
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
+
mPlatformCompat.setOverrides(
CompatibilityChangeConfigBuilder.create().enable(1L).build(),
PACKAGE_NAME);
@@ -336,6 +360,9 @@ public class PlatformCompatTest {
public void testListenerCalledOnClearOverrideDoesntExist() throws Exception {
mPlatformCompat.registerListener(1, mListener1);
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
+
mPlatformCompat.clearOverride(1, PACKAGE_NAME);
// Listener not called when a non existing override is removed.
verify(mListener1, never()).onCompatChange(PACKAGE_NAME);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 5b0704d69a68..61d7ede98f45 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -28,6 +28,7 @@ import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
import android.database.ContentObserver;
+import android.hardware.usb.UsbManager;
import android.media.IAudioService;
import android.net.IIpConnectivityMetrics;
import android.net.Uri;
@@ -244,6 +245,11 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
}
@Override
+ UsbManager getUsbManager() {
+ return services.usbManager;
+ }
+
+ @Override
boolean storageManagerIsFileBasedEncryptionEnabled() {
return services.storageManager.isFileBasedEncryptionEnabled();
}
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 7597cbf322f5..6add8d18aa3e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -89,6 +89,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.StringParceledListSlice;
import android.content.pm.UserInfo;
import android.graphics.Color;
+import android.hardware.usb.UsbManager;
import android.net.Uri;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -3992,6 +3993,30 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}
@Test
+ public void testGetSetNetworkSlicing() throws Exception {
+ assertExpectException(SecurityException.class, null,
+ () -> dpm.setNetworkSlicingEnabled(false));
+
+ assertExpectException(SecurityException.class, null,
+ () -> dpm.isNetworkSlicingEnabled());
+
+ assertExpectException(SecurityException.class, null,
+ () -> dpm.isNetworkSlicingEnabledForUser(UserHandle.of(CALLER_USER_HANDLE)));
+
+ mContext.callerPermissions.add(permission.READ_NETWORK_DEVICE_CONFIG);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+ try {
+ dpm.isNetworkSlicingEnabledForUser(UserHandle.of(CALLER_USER_HANDLE));
+ } catch (SecurityException se) {
+ fail("Threw SecurityException with right permission");
+ }
+
+ setupProfileOwner();
+ dpm.setNetworkSlicingEnabled(false);
+ assertThat(dpm.isNetworkSlicingEnabled()).isFalse();
+ }
+
+ @Test
public void testSetSystemSettingFailWithNonWhitelistedSettings() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
@@ -4329,6 +4354,68 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}
@Test
+ public void testSetNetworkLoggingEnabled_asPo() throws Exception {
+ final int managedProfileUserId = CALLER_USER_HANDLE;
+ final int managedProfileAdminUid =
+ UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
+ mContext.binder.callingUid = managedProfileAdminUid;
+ mContext.applicationInfo = new ApplicationInfo();
+ mContext.packageName = admin1.getPackageName();
+ addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.S);
+ when(getServices().iipConnectivityMetrics
+ .addNetdEventCallback(anyInt(), anyObject())).thenReturn(true);
+
+ // Check no logs have been retrieved so far.
+ assertThat(dpm.getLastNetworkLogRetrievalTime()).isEqualTo(-1);
+
+ // Enable network logging
+ dpm.setNetworkLoggingEnabled(admin1, true);
+ assertThat(dpm.getLastNetworkLogRetrievalTime()).isEqualTo(-1);
+
+ // Retrieve the network logs and verify timestamp has been updated.
+ final long beforeRetrieval = System.currentTimeMillis();
+
+ dpm.retrieveNetworkLogs(admin1, 0 /* batchToken */);
+
+ final long networkLogRetrievalTime = dpm.getLastNetworkLogRetrievalTime();
+ final long afterRetrieval = System.currentTimeMillis();
+ assertThat(networkLogRetrievalTime >= beforeRetrieval).isTrue();
+ assertThat(networkLogRetrievalTime <= afterRetrieval).isTrue();
+ }
+
+ @Test
+ public void testSetNetworkLoggingEnabled_asPoOfOrgOwnedDevice() throws Exception {
+ // Setup profile owner on organization-owned device
+ final int MANAGED_PROFILE_ADMIN_UID =
+ UserHandle.getUid(CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID);
+ addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+ configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
+
+ mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+ mContext.packageName = admin1.getPackageName();
+ mContext.applicationInfo = new ApplicationInfo();
+ when(getServices().iipConnectivityMetrics
+ .addNetdEventCallback(anyInt(), anyObject())).thenReturn(true);
+
+ // Check no logs have been retrieved so far.
+ assertThat(dpm.getLastNetworkLogRetrievalTime()).isEqualTo(-1);
+
+ // Enable network logging
+ dpm.setNetworkLoggingEnabled(admin1, true);
+ assertThat(dpm.getLastNetworkLogRetrievalTime()).isEqualTo(-1);
+
+ // Retrieve the network logs and verify timestamp has been updated.
+ final long beforeRetrieval = System.currentTimeMillis();
+
+ dpm.retrieveNetworkLogs(admin1, 0 /* batchToken */);
+
+ final long networkLogRetrievalTime = dpm.getLastNetworkLogRetrievalTime();
+ final long afterRetrieval = System.currentTimeMillis();
+ assertThat(networkLogRetrievalTime >= beforeRetrieval).isTrue();
+ assertThat(networkLogRetrievalTime <= afterRetrieval).isTrue();
+ }
+
+ @Test
public void testGetBindDeviceAdminTargetUsers() throws Exception {
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS);
@@ -6940,6 +7027,82 @@ public class DevicePolicyManagerTest extends DpmTestBase {
DevicePolicyManager.PASSWORD_QUALITY_COMPLEX));
}
+ @Test
+ public void testSetUsbDataSignalingEnabled_noDeviceOwnerOrPoOfOrgOwnedDevice() {
+ assertThrows(SecurityException.class,
+ () -> dpm.setUsbDataSignalingEnabled(true));
+ }
+
+ @Test
+ public void testSetUsbDataSignalingEnabled_asDeviceOwner() throws Exception {
+ setDeviceOwner();
+ when(getServices().usbManager.enableUsbDataSignal(false)).thenReturn(true);
+ when(getServices().usbManager.getUsbHalVersion()).thenReturn(UsbManager.USB_HAL_V1_3);
+
+ assertThat(dpm.isUsbDataSignalingEnabled()).isTrue();
+
+ dpm.setUsbDataSignalingEnabled(false);
+
+ assertThat(dpm.isUsbDataSignalingEnabled()).isFalse();
+ }
+
+ @Test
+ public void testIsUsbDataSignalingEnabledForUser_systemUser() throws Exception {
+ when(getServices().usbManager.enableUsbDataSignal(false)).thenReturn(true);
+ when(getServices().usbManager.getUsbHalVersion()).thenReturn(UsbManager.USB_HAL_V1_3);
+ setDeviceOwner();
+ dpm.setUsbDataSignalingEnabled(false);
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+
+ assertThat(dpm.isUsbDataSignalingEnabledForUser(UserHandle.myUserId())).isFalse();
+ }
+
+ @Test
+ public void testSetUsbDataSignalingEnabled_asPoOfOrgOwnedDevice() throws Exception {
+ final int managedProfileUserId = 15;
+ final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+ addManagedProfile(admin1, managedProfileAdminUid, admin1);
+ configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId);
+ mContext.binder.callingUid = managedProfileAdminUid;
+ when(getServices().usbManager.enableUsbDataSignal(false)).thenReturn(true);
+ when(getServices().usbManager.getUsbHalVersion()).thenReturn(UsbManager.USB_HAL_V1_3);
+
+ assertThat(dpm.isUsbDataSignalingEnabled()).isTrue();
+
+ dpm.setUsbDataSignalingEnabled(false);
+
+ assertThat(dpm.isUsbDataSignalingEnabled()).isFalse();
+ }
+
+ @Test
+ public void testCanUsbDataSignalingBeDisabled_canBeDisabled() throws Exception {
+ when(getServices().usbManager.getUsbHalVersion()).thenReturn(UsbManager.USB_HAL_V1_3);
+
+ assertThat(dpm.canUsbDataSignalingBeDisabled()).isTrue();
+ }
+
+ @Test
+ public void testCanUsbDataSignalingBeDisabled_cannotBeDisabled() throws Exception {
+ setDeviceOwner();
+ when(getServices().usbManager.getUsbHalVersion()).thenReturn(UsbManager.USB_HAL_V1_2);
+
+ assertThat(dpm.canUsbDataSignalingBeDisabled()).isFalse();
+ assertThrows(IllegalStateException.class,
+ () -> dpm.setUsbDataSignalingEnabled(true));
+ }
+
+ @Test
+ public void testSetUsbDataSignalingEnabled_noChangeToActiveAdmin()
+ throws Exception {
+ setDeviceOwner();
+ when(getServices().usbManager.getUsbHalVersion()).thenReturn(UsbManager.USB_HAL_V1_3);
+ boolean enabled = dpm.isUsbDataSignalingEnabled();
+
+ dpm.setUsbDataSignalingEnabled(true);
+
+ assertThat(dpm.isUsbDataSignalingEnabled()).isEqualTo(enabled);
+ }
+
private void setUserUnlocked(int userHandle, boolean unlocked) {
when(getServices().userManager.isUserUnlocked(eq(userHandle))).thenReturn(unlocked);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 944ef8db4e0d..f6dee385ca07 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -45,6 +45,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.database.Cursor;
+import android.hardware.usb.UsbManager;
import android.media.IAudioService;
import android.net.IIpConnectivityMetrics;
import android.net.Uri;
@@ -119,6 +120,7 @@ public class MockSystemServices {
public final CrossProfileApps crossProfileApps;
public final PersistentDataBlockManagerInternal persistentDataBlockManagerInternal;
public final AppOpsManager appOpsManager;
+ public final UsbManager usbManager;
/** Note this is a partial mock, not a real mock. */
public final PackageManager packageManager;
public final BuildMock buildMock = new BuildMock();
@@ -163,6 +165,7 @@ public class MockSystemServices {
crossProfileApps = mock(CrossProfileApps.class);
persistentDataBlockManagerInternal = mock(PersistentDataBlockManagerInternal.class);
appOpsManager = mock(AppOpsManager.class);
+ usbManager = mock(UsbManager.class);
// Package manager is huge, so we use a partial mock instead.
packageManager = spy(realContext.getPackageManager());
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java
index 7506dd45ad82..743b25f5c2b4 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java
@@ -125,7 +125,7 @@ public class NetworkEventTest extends DpmTestBase {
private List<NetworkEvent> fillHandlerWithFullBatchOfEvents(long startingId) throws Exception {
// GIVEN a handler with events
NetworkLoggingHandler handler = new NetworkLoggingHandler(new TestLooper().getLooper(),
- mDpmTestable, startingId);
+ mDpmTestable, startingId, DpmMockContext.CALLER_USER_HANDLE);
// GIVEN network events are sent to the handler.
for (int i = 0; i < MAX_EVENTS_PER_BATCH; i++) {
ConnectEvent event = new ConnectEvent("some_ip_address", 800, "com.google.foo",
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 1a2266139405..a078a77b4498 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -52,7 +52,8 @@ import javax.annotation.Nullable;
public final class DeviceStateManagerServiceTest {
private static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(0, "DEFAULT");
private static final DeviceState OTHER_DEVICE_STATE = new DeviceState(1, "OTHER");
- private static final DeviceState UNSUPPORTED_DEVICE_STATE = new DeviceState(999, "UNSUPPORTED");
+ // A device state that is not reported as being supported for the default test provider.
+ private static final DeviceState UNSUPPORTED_DEVICE_STATE = new DeviceState(255, "UNSUPPORTED");
private TestDeviceStatePolicy mPolicy;
private TestDeviceStateProvider mProvider;
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java
new file mode 100644
index 000000000000..b5c8053ad77e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.devicestate;
+
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for {@link DeviceState}.
+ * <p/>
+ * Run with <code>atest DeviceStateTest</code>.
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public final class DeviceStateTest {
+ @Test
+ public void testConstruct() {
+ final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE /* identifier */,
+ "CLOSED" /* name */);
+ assertEquals(state.getIdentifier(), MINIMUM_DEVICE_STATE);
+ assertEquals(state.getName(), "CLOSED");
+ }
+
+ @Test
+ public void testConstruct_nullName() {
+ final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE /* identifier */,
+ null /* name */);
+ assertEquals(state.getIdentifier(), MAXIMUM_DEVICE_STATE);
+ assertNull(state.getName());
+ }
+
+ @Test
+ public void testConstruct_tooLargeIdentifier() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE + 1 /* identifier */,
+ null /* name */);
+ });
+ }
+
+ @Test
+ public void testConstruct_tooSmallIdentifier() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE - 1 /* identifier */,
+ null /* name */);
+ });
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 640d6e599736..1c55072cab2c 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -45,7 +45,9 @@ import android.hardware.display.VirtualDisplayConfig;
import android.hardware.input.InputManagerInternal;
import android.os.Handler;
import android.os.IBinder;
+import android.os.MessageQueue;
import android.os.Process;
+import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
@@ -76,10 +78,15 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.time.Duration;
+import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.stream.LongStream;
@SmallTest
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class DisplayManagerServiceTest {
private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
@@ -193,8 +200,8 @@ public class DisplayManagerServiceTest {
verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture());
List<DisplayViewport> viewports = viewportCaptor.getValue();
- // Expect to receive 2 viewports: internal, and virtual
- assertEquals(2, viewports.size());
+ // Expect to receive at least 2 viewports: at least 1 internal, and 1 virtual
+ assertTrue(viewports.size() >= 2);
DisplayViewport virtualViewport = null;
DisplayViewport internalViewport = null;
@@ -202,7 +209,10 @@ public class DisplayManagerServiceTest {
DisplayViewport v = viewports.get(i);
switch (v.type) {
case DisplayViewport.VIEWPORT_INTERNAL: {
+ // If more than one internal viewport, this will get overwritten several times,
+ // which for the purposes of this test is fine.
internalViewport = v;
+ assertTrue(internalViewport.valid);
break;
}
case DisplayViewport.VIEWPORT_EXTERNAL: {
@@ -219,9 +229,6 @@ public class DisplayManagerServiceTest {
assertNotNull(internalViewport);
assertNotNull(virtualViewport);
- // INTERNAL
- assertTrue(internalViewport.valid);
-
// VIRTUAL
assertEquals(height, virtualViewport.deviceHeight);
assertEquals(width, virtualViewport.deviceWidth);
@@ -243,10 +250,12 @@ public class DisplayManagerServiceTest {
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
final int displayIds[] = bs.getDisplayIds();
- assertEquals(1, displayIds.length);
- final int displayId = displayIds[0];
- DisplayInfo info = bs.getDisplayInfo(displayId);
- assertEquals(info.type, Display.TYPE_INTERNAL);
+ final int size = displayIds.length;
+ assertTrue(size > 0);
+ for (int i = 0; i < size; i++) {
+ DisplayInfo info = bs.getDisplayInfo(displayIds[i]);
+ assertEquals(info.type, Display.TYPE_INTERNAL);
+ }
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -257,16 +266,22 @@ public class DisplayManagerServiceTest {
verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture());
List<DisplayViewport> viewports = viewportCaptor.getValue();
- // Expect to receive actual viewports: 1 internal
- assertEquals(1, viewports.size());
-
- DisplayViewport internalViewport = viewports.get(0);
-
- // INTERNAL is the only one actual display.
- assertNotNull(internalViewport);
- assertEquals(DisplayViewport.VIEWPORT_INTERNAL, internalViewport.type);
- assertTrue(internalViewport.valid);
- assertEquals(displayId, internalViewport.displayId);
+ // Due to the nature of foldables, we may have a different number of viewports than
+ // displays, just verify there's at least one.
+ final int viewportSize = viewports.size();
+ assertTrue(viewportSize > 0);
+
+ // Now verify that each viewport's displayId is valid.
+ Arrays.sort(displayIds);
+ for (int i = 0; i < viewportSize; i++) {
+ DisplayViewport internalViewport = viewports.get(i);
+
+ // INTERNAL is the only one actual display.
+ assertNotNull(internalViewport);
+ assertEquals(DisplayViewport.VIEWPORT_INTERNAL, internalViewport.type);
+ assertTrue(internalViewport.valid);
+ assertTrue(Arrays.binarySearch(displayIds, internalViewport.displayId) >= 0);
+ }
}
@Test
@@ -486,7 +501,6 @@ public class DisplayManagerServiceTest {
* Tests that collection of display color sampling results are sensible.
*/
@Test
- @FlakyTest(bugId = 172555744)
public void testDisplayedContentSampling() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
@@ -937,8 +951,22 @@ public class DisplayManagerServiceTest {
// Would prefer to call displayManager.onStart() directly here but it performs binderService
// registration which triggers security exceptions when running from a test.
handler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS);
- // flush the handler
- handler.runWithScissors(() -> {}, 0 /* now */);
+ waitForIdleHandler(handler, Duration.ofSeconds(1));
+ }
+
+ private void waitForIdleHandler(Handler handler, Duration timeout) {
+ final MessageQueue queue = handler.getLooper().getQueue();
+ final CountDownLatch latch = new CountDownLatch(1);
+ queue.addIdleHandler(() -> {
+ latch.countDown();
+ // Remove idle handler
+ return false;
+ });
+ try {
+ latch.await(timeout.toMillis(), TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ fail("Interrupted unexpectedly: " + e);
+ }
}
private class FakeDisplayManagerCallback extends IDisplayManagerCallback.Stub {
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 2e3178b13892..81b2381cd629 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -41,10 +41,10 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.database.ContentObserver;
-import android.hardware.display.DisplayManager;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.Looper;
import android.provider.DeviceConfig;
@@ -588,6 +588,12 @@ public class DisplayModeDirectorTest {
@Test
public void testSensorRegistration() {
+ // First, configure brightness zones or DMD won't register for sensor data.
+ final FakeDeviceConfig config = mInjector.getDeviceConfig();
+ config.setRefreshRateInHighZone(60);
+ config.setHighDisplayBrightnessThresholds(new int[] { 255 });
+ config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
+
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
setPeakRefreshRate(90 /*fps*/);
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
index ac4501723c90..ece0a627f051 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
@@ -20,17 +20,23 @@ import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.app.PropertyInvalidatedCache;
import android.graphics.Point;
+import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
+import androidx.test.filters.SmallTest;
+
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.io.OutputStream;
+@SmallTest
+@Presubmit
public class LogicalDisplayTest {
private static final int DISPLAY_ID = 0;
private static final int LAYER_STACK = 0;
@@ -52,6 +58,9 @@ public class LogicalDisplayTest {
mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice);
when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(displayDeviceInfo);
+ // Disable binder caches in this process.
+ PropertyInvalidatedCache.disableForTestMode();
+
DisplayDeviceRepository repo = new DisplayDeviceRepository(
new DisplayManagerService.SyncRoot(),
new PersistentDataStore(new PersistentDataStore.Injector() {
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
index 86054e48fe6d..27fce3c37fd9 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
@@ -18,13 +18,17 @@ package com.android.server.graphics.fonts;
import static com.google.common.truth.Truth.assertThat;
+import android.graphics.FontListParser;
import android.platform.test.annotations.Presubmit;
+import android.text.FontConfig;
+import android.util.Xml;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
@@ -38,13 +42,19 @@ import java.nio.charset.StandardCharsets;
public final class PersistentSystemFontConfigTest {
@Test
- public void testWriteRead() throws IOException, XmlPullParserException {
+ public void testWriteRead() throws Exception {
long expectedModifiedDate = 1234567890;
PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
config.lastModifiedDate = expectedModifiedDate;
config.updatedFontDirs.add("~~abc");
config.updatedFontDirs.add("~~def");
+ FontConfig.FontFamily fontFamily = parseFontFamily(
+ "<family name='test'>"
+ + " <font>test.ttf</font>"
+ + "</family>");
+ config.fontFamilies.add(fontFamily);
+
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
PersistentSystemFontConfig.writeToXml(baos, config);
@@ -57,6 +67,7 @@ public final class PersistentSystemFontConfigTest {
assertThat(another.lastModifiedDate).isEqualTo(expectedModifiedDate);
assertThat(another.updatedFontDirs).containsExactly("~~abc", "~~def");
+ assertThat(another.fontFamilies).containsExactly(fontFamily);
}
}
}
@@ -75,4 +86,11 @@ public final class PersistentSystemFontConfigTest {
}
}
+ private static FontConfig.FontFamily parseFontFamily(String xml) throws Exception {
+ XmlPullParser parser = Xml.newPullParser();
+ ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
+ parser.setInput(is, "UTF-8");
+ parser.nextTag();
+ return FontListParser.readFamily(parser, "", null);
+ }
}
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 cb83b0f7f2f7..7771afc8c7f1 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
@@ -22,12 +22,15 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
import android.content.Context;
+import android.graphics.FontListParser;
import android.graphics.fonts.FontManager;
import android.graphics.fonts.FontUpdateRequest;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.Presubmit;
import android.system.Os;
+import android.text.FontConfig;
+import android.util.Xml;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -37,7 +40,9 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -49,6 +54,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
@Presubmit
@SmallTest
@@ -157,7 +163,11 @@ public final class UpdatableFontDirTest {
newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
- newFontUpdateRequest("bar,4", GOOD_SIGNATURE)));
+ newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+ newAddFontFamilyRequest("<family name='foobar'>"
+ + " <font>foo.ttf</font>"
+ + " <font>bar.ttf</font>"
+ + "</family>")));
// Four font dirs are created.
assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis())
@@ -173,6 +183,14 @@ public final class UpdatableFontDirTest {
assertThat(parser.getRevision(dir.getFontFileMap().get("bar.ttf"))).isEqualTo(4);
// Outdated font dir should be deleted.
assertThat(mUpdatableFontFilesDir.list()).hasLength(2);
+ assertNamedFamilyExists(dir.getSystemFontConfig(), "foobar");
+ assertThat(dir.getFontFamilyMap()).containsKey("foobar");
+ FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar");
+ assertThat(foobar.getFontList()).hasSize(2);
+ assertThat(foobar.getFontList().get(0).getFile())
+ .isEqualTo(dir.getFontFileMap().get("foo.ttf"));
+ assertThat(foobar.getFontList().get(1).getFile())
+ .isEqualTo(dir.getFontFileMap().get("bar.ttf"));
}
@Test
@@ -184,6 +202,7 @@ public final class UpdatableFontDirTest {
mConfigFile);
dir.loadFontFileMap();
assertThat(dir.getFontFileMap()).isEmpty();
+ assertThat(dir.getFontFamilyMap()).isEmpty();
}
@Test
@@ -198,7 +217,11 @@ public final class UpdatableFontDirTest {
newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
- newFontUpdateRequest("bar,4", GOOD_SIGNATURE)));
+ newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+ newAddFontFamilyRequest("<family name='foobar'>"
+ + " <font>foo.ttf</font>"
+ + " <font>bar.ttf</font>"
+ + "</family>")));
// Four font dirs are created.
assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
@@ -211,6 +234,7 @@ public final class UpdatableFontDirTest {
assertThat(dir.getFontFileMap()).isEmpty();
// All font dirs (including dir for "bar.ttf") should be deleted.
assertThat(mUpdatableFontFilesDir.list()).hasLength(0);
+ assertThat(dir.getFontFamilyMap()).isEmpty();
}
@Test
@@ -225,7 +249,11 @@ public final class UpdatableFontDirTest {
newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
- newFontUpdateRequest("bar,4", GOOD_SIGNATURE)));
+ newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+ newAddFontFamilyRequest("<family name='foobar'>"
+ + " <font>foo.ttf</font>"
+ + " <font>bar.ttf</font>"
+ + "</family>")));
// Four font dirs are created.
assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
@@ -239,6 +267,7 @@ public final class UpdatableFontDirTest {
assertThat(dir.getFontFileMap()).isEmpty();
// All font dirs (including dir for "bar.ttf") should be deleted.
assertThat(mUpdatableFontFilesDir.list()).hasLength(0);
+ assertThat(dir.getFontFamilyMap()).isEmpty();
}
@Test
@@ -253,7 +282,11 @@ public final class UpdatableFontDirTest {
newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
- newFontUpdateRequest("bar,4", GOOD_SIGNATURE)));
+ newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+ newAddFontFamilyRequest("<family name='foobar'>"
+ + " <font>foo.ttf</font>"
+ + " <font>bar.ttf</font>"
+ + "</family>")));
// Four font dirs are created.
assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
@@ -274,6 +307,8 @@ public final class UpdatableFontDirTest {
// We don't delete bar.ttf in this case, because it's normal that OTA updates preinstalled
// fonts.
assertThat(mUpdatableFontFilesDir.list()).hasLength(1);
+ // Font family depending on obsoleted font should be removed.
+ assertThat(dir.getFontFamilyMap()).isEmpty();
}
@Test
@@ -285,6 +320,7 @@ public final class UpdatableFontDirTest {
new File("/dev/null"));
dir.loadFontFileMap();
assertThat(dir.getFontFileMap()).isEmpty();
+ assertThat(dir.getFontFamilyMap()).isEmpty();
}
@Test
@@ -295,12 +331,19 @@ public final class UpdatableFontDirTest {
mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
mConfigFile);
dirForPreparation.loadFontFileMap();
- dirForPreparation.update(
- Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+ dirForPreparation.update(Arrays.asList(
+ newFontUpdateRequest("foo,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("bar,2", "Invalid signature"),
+ newAddFontFamilyRequest("<family name='foobar'>"
+ + " <font>foo.ttf</font>"
+ + " <font>bar.ttf</font>"
+ + "</family>")));
fail("Batch update with invalid signature should fail");
} catch (FontManagerService.SystemFontException e) {
// Expected
@@ -313,6 +356,11 @@ public final class UpdatableFontDirTest {
// The state should be rolled back as a whole if one of the update requests fail.
assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
+ assertThat(dir.getFontFamilyMap()).containsKey("foobar");
+ FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar");
+ assertThat(foobar.getFontList()).hasSize(1);
+ assertThat(foobar.getFontList().get(0).getFile())
+ .isEqualTo(dir.getFontFileMap().get("foo.ttf"));
}
@Test
@@ -364,7 +412,7 @@ public final class UpdatableFontDirTest {
dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
try {
dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
- fail("Expect IllegalArgumentException");
+ fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
}
@@ -440,7 +488,7 @@ public final class UpdatableFontDirTest {
try {
dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
- fail("Expect IllegalArgumentException");
+ fail("Expect SystemFontException");
} catch (FontManagerService.SystemFontException e) {
assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
}
@@ -599,6 +647,128 @@ public final class UpdatableFontDirTest {
assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
}
+ @Test
+ public void addFontFamily() throws Exception {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+ UpdatableFontDir dir = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
+ dir.loadFontFileMap();
+
+ dir.update(Arrays.asList(
+ newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+ newAddFontFamilyRequest("<family name='test'>"
+ + " <font>test.ttf</font>"
+ + "</family>")));
+ assertThat(dir.getFontFileMap()).containsKey("test.ttf");
+ assertThat(dir.getFontFamilyMap()).containsKey("test");
+ FontConfig.FontFamily test = dir.getFontFamilyMap().get("test");
+ assertThat(test.getFontList()).hasSize(1);
+ assertThat(test.getFontList().get(0).getFile())
+ .isEqualTo(dir.getFontFileMap().get("test.ttf"));
+ }
+
+ @Test
+ public void addFontFamily_noName() throws Exception {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+ UpdatableFontDir dir = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
+ dir.loadFontFileMap();
+
+ try {
+ dir.update(Arrays.asList(
+ newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+ newAddFontFamilyRequest("<family lang='en'>"
+ + " <font>test.ttf</font>"
+ + "</family>")));
+ fail("Expect NullPointerException");
+ } catch (NullPointerException e) {
+ // Expect
+ }
+ }
+
+ @Test
+ public void addFontFamily_fontNotAvailable() throws Exception {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+ UpdatableFontDir dir = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
+ dir.loadFontFileMap();
+
+ try {
+ dir.update(Arrays.asList(newAddFontFamilyRequest("<family name='test'>"
+ + " <font>test.ttf</font>"
+ + "</family>")));
+ fail("Expect SystemFontException");
+ } catch (FontManagerService.SystemFontException e) {
+ assertThat(e.getErrorCode())
+ .isEqualTo(FontManager.RESULT_ERROR_FONT_NOT_FOUND);
+ }
+ }
+
+ @Test
+ public void getSystemFontConfig() throws Exception {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+ UpdatableFontDir dir = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
+ dir.loadFontFileMap();
+ // We assume we have monospace.
+ assertNamedFamilyExists(dir.getSystemFontConfig(), "monospace");
+
+ dir.update(Arrays.asList(
+ newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+ // Updating an existing font family.
+ newAddFontFamilyRequest("<family name='monospace'>"
+ + " <font>test.ttf</font>"
+ + "</family>"),
+ // Adding a new font family.
+ newAddFontFamilyRequest("<family name='test'>"
+ + " <font>test.ttf</font>"
+ + "</family>")));
+ FontConfig fontConfig = dir.getSystemFontConfig();
+ assertNamedFamilyExists(fontConfig, "monospace");
+ FontConfig.FontFamily monospace = getLastFamily(fontConfig, "monospace");
+ assertThat(monospace.getFontList()).hasSize(1);
+ assertThat(monospace.getFontList().get(0).getFile())
+ .isEqualTo(dir.getFontFileMap().get("test.ttf"));
+ assertNamedFamilyExists(fontConfig, "test");
+ assertThat(getLastFamily(fontConfig, "test").getFontList())
+ .isEqualTo(monospace.getFontList());
+ }
+
+ @Test
+ public void getSystemFontConfig_preserveFirstFontFamily() throws Exception {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+ UpdatableFontDir dir = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
+ dir.loadFontFileMap();
+ assertThat(dir.getSystemFontConfig().getFontFamilies()).isNotEmpty();
+ FontConfig.FontFamily firstFontFamily = dir.getSystemFontConfig().getFontFamilies().get(0);
+ assertThat(firstFontFamily.getName()).isNotEmpty();
+
+ dir.update(Arrays.asList(
+ newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+ newAddFontFamilyRequest("<family name='" + firstFontFamily.getName() + "'>"
+ + " <font>test.ttf</font>"
+ + "</family>")));
+ FontConfig fontConfig = dir.getSystemFontConfig();
+ assertThat(dir.getSystemFontConfig().getFontFamilies()).isNotEmpty();
+ assertThat(fontConfig.getFontFamilies().get(0)).isEqualTo(firstFontFamily);
+ FontConfig.FontFamily updated = getLastFamily(fontConfig, firstFontFamily.getName());
+ assertThat(updated.getFontList()).hasSize(1);
+ assertThat(updated.getFontList().get(0).getFile())
+ .isEqualTo(dir.getFontFileMap().get("test.ttf"));
+ assertThat(updated).isNotEqualTo(firstFontFamily);
+ }
+
private FontUpdateRequest newFontUpdateRequest(String content, String signature)
throws Exception {
File file = File.createTempFile("font", "ttf", mCacheDir);
@@ -608,10 +778,36 @@ public final class UpdatableFontDirTest {
signature.getBytes());
}
+ private static FontUpdateRequest newAddFontFamilyRequest(String xml) throws Exception {
+ XmlPullParser parser = Xml.newPullParser();
+ ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
+ parser.setInput(is, "UTF-8");
+ parser.nextTag();
+ FontConfig.FontFamily fontFamily = FontListParser.readFamily(parser, "", null);
+ return new FontUpdateRequest(fontFamily);
+ }
+
private void writeConfig(PersistentSystemFontConfig.Config config,
File file) throws IOException {
try (FileOutputStream fos = new FileOutputStream(file)) {
PersistentSystemFontConfig.writeToXml(fos, config);
}
}
+
+ // Returns the last family with the given name, which will be used for creating Typeface.
+ private static FontConfig.FontFamily getLastFamily(FontConfig fontConfig, String familyName) {
+ List<FontConfig.FontFamily> fontFamilies = fontConfig.getFontFamilies();
+ for (int i = fontFamilies.size() - 1; i >= 0; i--) {
+ if (familyName.equals(fontFamilies.get(i).getName())) {
+ return fontFamilies.get(i);
+ }
+ }
+ return null;
+ }
+
+ private static void assertNamedFamilyExists(FontConfig fontConfig, String familyName) {
+ assertThat(fontConfig.getFontFamilies().stream()
+ .map(FontConfig.FontFamily::getName)
+ .collect(Collectors.toSet())).contains(familyName);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
index da9dcb79fb76..7cb72c414e52 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -16,6 +16,7 @@
package com.android.server.hdmi;
+import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
@@ -28,6 +29,7 @@ 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.hardware.tv.cec.V1_0.SendMessageResult;
import android.media.AudioManager;
@@ -214,4 +216,75 @@ public class DevicePowerStatusActionTest {
verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_UNKNOWN);
}
+
+ @Test
+ public void queryDisplayStatus_localDevice_2_0_targetDevice_1_4() throws Exception {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mPlaybackDevice.mAddress, ADDR_TV);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+
+ HdmiCecMessage response = HdmiCecMessageBuilder.buildReportPowerStatus(
+ ADDR_TV, mPlaybackDevice.mAddress, HdmiControlManager.POWER_STATUS_STANDBY);
+ mNativeWrapper.onCecMessage(response);
+ mTestLooper.dispatchAll();
+
+ verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_STANDBY);
+ }
+
+ @Test
+ public void queryDisplayStatus_localDevice_2_0_targetDevice_2_0() throws Exception {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
+ .buildReportPhysicalAddressCommand(ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV);
+ mNativeWrapper.onCecMessage(reportPhysicalAddress);
+ mTestLooper.dispatchAll();
+ HdmiCecMessage reportPowerStatusBroadcast = HdmiCecMessageBuilder.buildReportPowerStatus(
+ ADDR_TV, ADDR_BROADCAST, HdmiControlManager.POWER_STATUS_STANDBY);
+ mNativeWrapper.onCecMessage(reportPowerStatusBroadcast);
+ mTestLooper.dispatchAll();
+ mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mPlaybackDevice.mAddress, ADDR_TV);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+
+ verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_STANDBY);
+ }
+
+ @Test
+ public void queryDisplayStatus_localDevice_2_0_targetDevice_2_0_unknown() throws Exception {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
+ .buildReportPhysicalAddressCommand(ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV);
+ mNativeWrapper.onCecMessage(reportPhysicalAddress);
+ mTestLooper.dispatchAll();
+ HdmiCecMessage reportPowerStatusBroadcast = HdmiCecMessageBuilder.buildReportPowerStatus(
+ ADDR_TV, ADDR_BROADCAST, HdmiControlManager.POWER_STATUS_UNKNOWN);
+ mNativeWrapper.onCecMessage(reportPowerStatusBroadcast);
+ mTestLooper.dispatchAll();
+ mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mPlaybackDevice.mAddress, ADDR_TV);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+
+ HdmiCecMessage response = HdmiCecMessageBuilder.buildReportPowerStatus(
+ ADDR_TV, mPlaybackDevice.mAddress, HdmiControlManager.POWER_STATUS_STANDBY);
+ mNativeWrapper.onCecMessage(response);
+ mTestLooper.dispatchAll();
+
+ verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_STANDBY);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index bb57a69d6f51..1958cb01b510 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -15,12 +15,9 @@
*/
package com.android.server.hdmi;
+import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
-import android.hardware.tv.cec.V1_0.CecMessage;
-import android.hardware.tv.cec.V1_0.HotplugEvent;
import android.hardware.tv.cec.V1_0.SendMessageResult;
-import android.os.RemoteException;
-import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.hdmi.HdmiCecController.NativeWrapper;
@@ -61,6 +58,7 @@ final class FakeNativeWrapper implements NativeWrapper {
private int mMyPhysicalAddress = 0;
private HdmiPortInfo[] mHdmiPortInfo = null;
private HdmiCecController.HdmiCecCallback mCallback = null;
+ private int mCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
@Override
public String nativeInit() {
@@ -99,7 +97,7 @@ final class FakeNativeWrapper implements NativeWrapper {
@Override
public int nativeGetVersion() {
- return 0;
+ return mCecVersion;
}
@Override
@@ -135,24 +133,23 @@ final class FakeNativeWrapper implements NativeWrapper {
mPortConnectionStatus.put(port, connected);
}
+ public void setCecVersion(@HdmiControlManager.HdmiCecVersion int cecVersion) {
+ mCecVersion = cecVersion;
+ }
+
public void onCecMessage(HdmiCecMessage hdmiCecMessage) {
if (mCallback == null) {
return;
}
- CecMessage message = new CecMessage();
- message.initiator = hdmiCecMessage.getSource();
- message.destination = hdmiCecMessage.getDestination();
- ArrayList<Byte> body = new ArrayList<>();
- body.add((byte) hdmiCecMessage.getOpcode());
+ int source = hdmiCecMessage.getSource();
+ int destination = hdmiCecMessage.getDestination();
+ byte[] body = new byte[hdmiCecMessage.getParams().length + 1];
+ int i = 0;
+ body[i++] = (byte) hdmiCecMessage.getOpcode();
for (byte param : hdmiCecMessage.getParams()) {
- body.add(param);
- }
- message.body = body;
- try {
- mCallback.onCecMessage(message);
- } catch (RemoteException e) {
- Log.e(TAG, "Error sending CEC message", e);
+ body[i++] = param;
}
+ mCallback.onCecMessage(source, destination, body);
}
public void onHotplugEvent(int port, boolean connected) {
@@ -160,15 +157,7 @@ final class FakeNativeWrapper implements NativeWrapper {
return;
}
- HotplugEvent hotplugEvent = new HotplugEvent();
- hotplugEvent.portId = port;
- hotplugEvent.connected = connected;
-
- try {
- mCallback.onHotplugEvent(hotplugEvent);
- } catch (RemoteException e) {
- Log.e(TAG, "Error sending hotplug event", e);
- }
+ mCallback.onHotplugEvent(port, connected);
}
public List<HdmiCecMessage> getResultMessages() {
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 e6b56ca3418f..b11ac24b9a29 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -15,6 +15,7 @@
*/
package com.android.server.hdmi;
+import static com.android.server.hdmi.Constants.ABORT_UNRECOGNIZED_OPCODE;
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
import static com.android.server.hdmi.Constants.ADDR_INVALID;
@@ -23,6 +24,8 @@ import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
@@ -33,6 +36,7 @@ import android.os.IPowerManager;
import android.os.IThermalService;
import android.os.Looper;
import android.os.PowerManager;
+import android.os.RemoteException;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.sysprop.HdmiProperties;
@@ -49,6 +53,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
@SmallTest
@Presubmit
@@ -67,6 +72,7 @@ public class HdmiCecLocalDevicePlaybackTest {
private int mPlaybackLogicalAddress;
private boolean mWokenUp;
private boolean mStandby;
+ private boolean mActiveMediaSessionsPaused;
@Mock
private IPowerManager mIPowerManagerMock;
@@ -97,6 +103,11 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Override
+ void pauseActiveMediaSessions() {
+ mActiveMediaSessionsPaused = true;
+ }
+
+ @Override
boolean isStandbyMessageReceived() {
return mStandby;
}
@@ -392,6 +403,54 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
+ public void handleRoutingChange_otherDevice_ActiveSource_mediaSessionsPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
+ 0x5000);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isTrue();
+ }
+
+ @Test
+ public void handleRoutingChange_otherDevice_InactiveSource_mediaSessionsNotPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
+ "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
+ 0x5000);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isFalse();
+ }
+
+ @Test
+ public void handleRoutingChange_sameDevice_ActiveSource_mediaSessionsNotPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
+ mPlaybackPhysicalAddress);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isFalse();
+ }
+
+ @Test
+ public void handleRoutingChange_sameDevice_InactiveSource_mediaSessionsNotPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
+ "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
+ mPlaybackPhysicalAddress);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isFalse();
+ }
+
+ @Test
public void handleRoutingInformation_otherDevice_None() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
@@ -496,6 +555,52 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
+ public void handleRoutingInformation_otherDevice_ActiveSource_mediaSessionsPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x5000);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isTrue();
+ }
+
+ @Test
+ public void handleRoutingInformation_otherDevice_InactiveSource_mediaSessionsNotPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
+ "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x5000);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isFalse();
+ }
+
+ @Test
+ public void handleRoutingInformation_sameDevice_ActiveSource_mediaSessionsNotPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, mPlaybackPhysicalAddress);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isFalse();
+ }
+
+ @Test
+ public void handleRoutingInformation_sameDevice_InactiveSource_mediaSessionsNotPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
+ "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, mPlaybackPhysicalAddress);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isFalse();
+ }
+
+ @Test
public void handleSetStreamPath() {
HdmiCecMessage message =
HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2100);
@@ -831,6 +936,52 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
+ public void handleActiveSource_otherDevice_ActiveSource_mediaSessionsPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isTrue();
+ }
+
+ @Test
+ public void handleActiveSource_otherDevice_InactiveSource_mediaSessionsNotPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
+ "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isFalse();
+ }
+
+ @Test
+ public void handleActiveSource_sameDevice_ActiveSource_mediaSessionsNotPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isFalse();
+ }
+
+ @Test
+ public void handleActiveSource_sameDevice_InactiveSource_mediaSessionsNotPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
+ "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isFalse();
+ }
+
+ @Test
public void losingActiveSource_standbyNow_verifyStandbyMessageIsSentOnNextStandby() {
// As described in b/161097846.
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
@@ -1158,6 +1309,54 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
+ public void handleSetStreamPath_otherDevice_ActiveSource_mediaSessionsPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x5000);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isTrue();
+ }
+
+ @Test
+ public void handleSetStreamPath_otherDevice_InactiveSource_mediaSessionsNotPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
+ "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x5000);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isFalse();
+ }
+
+ @Test
+ public void handleSetStreamPath_sameDevice_ActiveSource_mediaSessionsNotPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, mPlaybackPhysicalAddress);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isFalse();
+ }
+
+ @Test
+ public void handleSetStreamPath_sameDevice_InactiveSource_mediaSessionsNotPaused() {
+ mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
+ "HdmiCecLocalDevicePlaybackTest");
+ mActiveMediaSessionsPaused = false;
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, mPlaybackPhysicalAddress);
+ mHdmiCecLocalDevicePlayback.dispatchMessage(message);
+ mTestLooper.dispatchAll();
+ assertThat(mActiveMediaSessionsPaused).isFalse();
+ }
+
+ @Test
public void oneTouchPlay_SendStandbyOnSleepToTv() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
@@ -1356,6 +1555,31 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
+ public void toggleAndFollowTvPower_isInteractive() throws RemoteException {
+ when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+ mActiveMediaSessionsPaused = false;
+ mWokenUp = false;
+
+ mHdmiControlService.toggleAndFollowTvPower();
+
+ assertThat(mActiveMediaSessionsPaused).isTrue();
+ assertThat(mWokenUp).isFalse();
+ }
+
+ @Test
+ public void toggleAndFollowTvPower_isNotInteractive() throws RemoteException {
+ when(mIPowerManagerMock.isInteractive()).thenReturn(false);
+ mActiveMediaSessionsPaused = false;
+ mWokenUp = false;
+
+ mHdmiControlService.toggleAndFollowTvPower();
+
+ assertThat(mActiveMediaSessionsPaused).isFalse();
+ assertThat(mWokenUp).isTrue();
+ }
+
+
+ @Test
public void shouldHandleTvPowerKey_CecDisabled() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
@@ -1392,4 +1616,46 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(features.contains(
Constants.RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU)).isFalse();
}
+
+ @Test
+ public void doesNotSupportRecordTvScreen() {
+ HdmiCecMessage recordTvScreen = new HdmiCecMessage(ADDR_TV, mPlaybackLogicalAddress,
+ Constants.MESSAGE_RECORD_TV_SCREEN, HdmiCecMessage.EMPTY_PARAM);
+
+ mNativeWrapper.onCecMessage(recordTvScreen);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ mPlaybackLogicalAddress, ADDR_TV, Constants.MESSAGE_RECORD_TV_SCREEN,
+ ABORT_UNRECOGNIZED_OPCODE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(featureAbort);
+ }
+
+ @Test
+ public void shouldHandleUserControlPressedAndReleased() {
+ HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed(
+ ADDR_TV, mPlaybackLogicalAddress,
+ HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
+ HdmiCecMessage userControlReleased = HdmiCecMessageBuilder.buildUserControlReleased(
+ ADDR_TV, mPlaybackLogicalAddress);
+
+ mNativeWrapper.onCecMessage(userControlPressed);
+ mTestLooper.dispatchAll();
+
+ // Move past the follower safety timeout
+ mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(2));
+ mTestLooper.dispatchAll();
+
+ mNativeWrapper.onCecMessage(userControlReleased);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage featureAbortPressed = HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ mPlaybackLogicalAddress, ADDR_TV, Constants.MESSAGE_USER_CONTROL_PRESSED,
+ ABORT_UNRECOGNIZED_OPCODE);
+ HdmiCecMessage featureAbortReleased = HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ mPlaybackLogicalAddress, ADDR_TV, Constants.MESSAGE_USER_CONTROL_RELEASED,
+ ABORT_UNRECOGNIZED_OPCODE);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbortPressed);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbortReleased);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 0f527f3713b2..4623eb5b7d4b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -15,8 +15,11 @@
*/
package com.android.server.hdmi;
+import static com.android.server.hdmi.Constants.ABORT_UNRECOGNIZED_OPCODE;
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
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_RECORDER_1;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
@@ -27,6 +30,7 @@ import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.media.AudioManager;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.IThermalService;
@@ -66,6 +70,7 @@ public class HdmiCecLocalDeviceTvTest {
private IPowerManager mIPowerManagerMock;
@Mock
private IThermalService mIThermalServiceMock;
+ @Mock private AudioManager mAudioManager;
@Before
public void setUp() {
@@ -101,11 +106,21 @@ public class HdmiCecLocalDeviceTvTest {
}
@Override
+ boolean isPowerStandby() {
+ return false;
+ }
+
+ @Override
protected PowerManager getPowerManager() {
return powerManager;
}
@Override
+ AudioManager getAudioManager() {
+ return mAudioManager;
+ }
+
+ @Override
protected HdmiCecConfig getHdmiCecConfig() {
return hdmiCecConfig;
}
@@ -121,9 +136,11 @@ public class HdmiCecLocalDeviceTvTest {
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mLocalDevices.add(mHdmiCecLocalDeviceTv);
- HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
+ 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);
@@ -302,4 +319,185 @@ public class HdmiCecLocalDeviceTvTest {
assertThat(features.contains(Constants.RC_PROFILE_TV_THREE)).isFalse();
assertThat(features.contains(Constants.RC_PROFILE_TV_FOUR)).isFalse();
}
+
+ @Test
+ public void startArcAction_enable_noAudioDevice() {
+ mHdmiCecLocalDeviceTv.startArcAction(true);
+
+ HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildRequestArcInitiation(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage requestArcTermination = HdmiCecMessageBuilder.buildRequestArcTermination(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestArcInitiation);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestArcTermination);
+ }
+
+
+ @Test
+ public void startArcAction_disable_noAudioDevice() {
+ mHdmiCecLocalDeviceTv.startArcAction(false);
+
+ HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildRequestArcInitiation(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage requestArcTermination = HdmiCecMessageBuilder.buildRequestArcTermination(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestArcInitiation);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestArcTermination);
+ }
+
+ @Test
+ public void startArcAction_enable_portDoesNotSupportArc() {
+ // Emulate Audio device on port 0x1000 (does not support ARC)
+ mNativeWrapper.setPortConnectionStatus(1, true);
+ HdmiCecMessage hdmiCecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ ADDR_AUDIO_SYSTEM, 0x1000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ mNativeWrapper.onCecMessage(hdmiCecMessage);
+
+ mHdmiCecLocalDeviceTv.startArcAction(true);
+ HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildRequestArcInitiation(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage requestArcTermination = HdmiCecMessageBuilder.buildRequestArcTermination(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestArcInitiation);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestArcTermination);
+ }
+
+ @Test
+ public void startArcAction_disable_portDoesNotSupportArc() {
+ // Emulate Audio device on port 0x1000 (does not support ARC)
+ mNativeWrapper.setPortConnectionStatus(1, true);
+ HdmiCecMessage hdmiCecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ ADDR_AUDIO_SYSTEM, 0x1000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ mNativeWrapper.onCecMessage(hdmiCecMessage);
+
+ mHdmiCecLocalDeviceTv.startArcAction(false);
+ HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildRequestArcInitiation(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage requestArcTermination = HdmiCecMessageBuilder.buildRequestArcTermination(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestArcInitiation);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestArcTermination);
+ }
+
+ @Test
+ public void startArcAction_enable_portSupportsArc() {
+ // Emulate Audio device on port 0x2000 (supports ARC)
+ mNativeWrapper.setPortConnectionStatus(2, true);
+ HdmiCecMessage hdmiCecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ mNativeWrapper.onCecMessage(hdmiCecMessage);
+ mTestLooper.dispatchAll();
+
+ mHdmiCecLocalDeviceTv.startArcAction(true);
+ mTestLooper.dispatchAll();
+ HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildRequestArcInitiation(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage requestArcTermination = HdmiCecMessageBuilder.buildRequestArcTermination(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM);
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestArcInitiation);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestArcTermination);
+ }
+
+ @Test
+ public void startArcAction_disable_portSupportsArc() {
+ // Emulate Audio device on port 0x2000 (supports ARC)
+ mNativeWrapper.setPortConnectionStatus(2, true);
+ HdmiCecMessage hdmiCecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ mNativeWrapper.onCecMessage(hdmiCecMessage);
+ mTestLooper.dispatchAll();
+
+ mHdmiCecLocalDeviceTv.startArcAction(false);
+ mTestLooper.dispatchAll();
+ HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildRequestArcInitiation(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage requestArcTermination = HdmiCecMessageBuilder.buildRequestArcTermination(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestArcInitiation);
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestArcTermination);
+ }
+
+ @Test
+ public void handleInitiateArc_noAudioDevice() {
+ HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildInitiateArc(
+ ADDR_AUDIO_SYSTEM,
+ ADDR_TV);
+
+ mNativeWrapper.onCecMessage(requestArcInitiation);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);
+ }
+
+ @Test
+ public void handleInitiateArc_portDoesNotSupportArc() {
+ // Emulate Audio device on port 0x1000 (does not support ARC)
+ mNativeWrapper.setPortConnectionStatus(1, true);
+ HdmiCecMessage hdmiCecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ ADDR_AUDIO_SYSTEM, 0x1000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ mNativeWrapper.onCecMessage(hdmiCecMessage);
+
+ HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildInitiateArc(
+ ADDR_AUDIO_SYSTEM,
+ ADDR_TV);
+
+ mNativeWrapper.onCecMessage(requestArcInitiation);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);
+ }
+
+ @Test
+ public void handleInitiateArc_portSupportsArc() {
+ // Emulate Audio device on port 0x2000 (supports ARC)
+ mNativeWrapper.setPortConnectionStatus(2, true);
+ HdmiCecMessage hdmiCecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ mNativeWrapper.onCecMessage(hdmiCecMessage);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildInitiateArc(
+ ADDR_AUDIO_SYSTEM,
+ ADDR_TV);
+
+ mNativeWrapper.onCecMessage(requestArcInitiation);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM);
+ assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
+ }
+
+ @Test
+ public void supportsRecordTvScreen() {
+ HdmiCecMessage recordTvScreen = new HdmiCecMessage(ADDR_RECORDER_1, mTvLogicalAddress,
+ Constants.MESSAGE_RECORD_TV_SCREEN, HdmiCecMessage.EMPTY_PARAM);
+
+ mNativeWrapper.onCecMessage(recordTvScreen);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ mTvLogicalAddress, ADDR_RECORDER_1, Constants.MESSAGE_RECORD_TV_SCREEN,
+ ABORT_UNRECOGNIZED_OPCODE);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbort);
+ }
}
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 be584d7b4591..462f3e32ab3b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -49,6 +49,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -622,6 +623,45 @@ public class HdmiControlServiceTest {
assertEquals(runnerUid, Binder.getCallingWorkSourceUid());
}
+ @Ignore("b/180499471")
+ @Test
+ public void initCecVersion_limitToMinimumSupportedVersion() {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+
+ mHdmiControlService.initService();
+ assertThat(mHdmiControlService.getCecVersion()).isEqualTo(
+ HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+ }
+
+ @Ignore("b/180499471")
+ @Test
+ public void initCecVersion_limitToAtLeast1_4() {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mNativeWrapper.setCecVersion(0x0);
+
+ mHdmiControlService.initService();
+ assertThat(mHdmiControlService.getCecVersion()).isEqualTo(
+ HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+ }
+
+ @Ignore("b/180499471")
+ @Test
+ public void initCecVersion_useHighestMatchingVersion() {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_2_0);
+
+ mHdmiControlService.initService();
+ assertThat(mHdmiControlService.getCecVersion()).isEqualTo(
+ HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ }
+
private static class VolumeControlFeatureCallback extends
IHdmiCecVolumeControlFeatureListener.Stub {
boolean mCallbackReceived = false;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
new file mode 100644
index 000000000000..b8dfd5672056
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.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;
+
+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.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;
+import java.util.concurrent.TimeUnit;
+
+/** Tests for {@link ActiveSourceAction} */
+@SmallTest
+@RunWith(JUnit4.class)
+public class PowerStatusMonitorActionTest {
+
+ private Context mContextSpy;
+ private HdmiControlService mHdmiControlService;
+ private FakeNativeWrapper mNativeWrapper;
+
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private int mPhysicalAddress;
+ private HdmiCecLocalDeviceTv mTvDevice;
+
+ @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);
+
+ HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
+
+ 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
+ }
+
+ @Override
+ protected HdmiCecConfig getHdmiCecConfig() {
+ return hdmiCecConfig;
+ }
+ };
+
+ Looper looper = mTestLooper.getLooper();
+ mHdmiControlService.setIoLooper(looper);
+ 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));
+ mTvDevice = new HdmiCecLocalDeviceTv(mHdmiControlService);
+ mTvDevice.init();
+ mLocalDevices.add(mTvDevice);
+ mTestLooper.dispatchAll();
+ HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[2];
+ hdmiPortInfo[0] =
+ new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x1000, true, false, false);
+ hdmiPortInfo[1] =
+ new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false);
+ mNativeWrapper.setPortInfo(hdmiPortInfo);
+ mHdmiControlService.initService();
+ mPhysicalAddress = 0x0000;
+ mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void sourceDevice_1_4_updatesPowerState() {
+ sendMessageFromPlaybackDevice(ADDR_PLAYBACK_1, 0x1000);
+
+ PowerStatusMonitorAction action = new PowerStatusMonitorAction(mTvDevice);
+ action.start();
+ assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_UNKNOWN);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ ADDR_TV,
+ ADDR_PLAYBACK_1);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+
+ reportPowerStatus(ADDR_PLAYBACK_1, false, HdmiControlManager.POWER_STATUS_ON);
+ assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_ON);
+
+ mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(60));
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+
+ reportPowerStatus(ADDR_PLAYBACK_1, false, HdmiControlManager.POWER_STATUS_STANDBY);
+ assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_STANDBY);
+ }
+
+ private void assertPowerStatus(int logicalAddress, int powerStatus) {
+ HdmiDeviceInfo deviceInfo = mHdmiControlService.getHdmiCecNetwork().getCecDeviceInfo(
+ logicalAddress);
+ assertThat(deviceInfo).isNotNull();
+ assertThat(deviceInfo.getDevicePowerStatus()).isEqualTo(powerStatus);
+ }
+
+ @Test
+ public void sourceDevice_2_0_doesNotUpdatePowerState() {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ sendMessageFromPlaybackDevice(ADDR_PLAYBACK_1, 0x1000);
+ reportPowerStatus(ADDR_PLAYBACK_1, true, HdmiControlManager.POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+
+ PowerStatusMonitorAction action = new PowerStatusMonitorAction(mTvDevice);
+ action.start();
+
+ assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ ADDR_TV,
+ ADDR_PLAYBACK_1);
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+
+ mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(60));
+ mTestLooper.dispatchAll();
+
+ assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_ON);
+ }
+
+ @Test
+ public void mixedSourceDevices_localDevice_1_4_updatesAll() {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+ mTestLooper.dispatchAll();
+ sendMessageFromPlaybackDevice(ADDR_PLAYBACK_1, 0x1000);
+ sendMessageFromPlaybackDevice(ADDR_PLAYBACK_2, 0x2000);
+ reportPowerStatus(ADDR_PLAYBACK_2, true, HdmiControlManager.POWER_STATUS_ON);
+
+ assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_UNKNOWN);
+ assertPowerStatus(ADDR_PLAYBACK_2, HdmiControlManager.POWER_STATUS_ON);
+
+ PowerStatusMonitorAction action = new PowerStatusMonitorAction(mTvDevice);
+ action.start();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ ADDR_TV,
+ ADDR_PLAYBACK_1);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+
+ HdmiCecMessage giveDevicePowerStatus2 = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ ADDR_TV,
+ ADDR_PLAYBACK_2);
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus2);
+ }
+
+ @Test
+ public void mixedSourceDevices_localDevice_2_0_onlyUpdates_1_4() {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mTestLooper.dispatchAll();
+ sendMessageFromPlaybackDevice(ADDR_PLAYBACK_1, 0x1000);
+ sendMessageFromPlaybackDevice(ADDR_PLAYBACK_2, 0x2000);
+ reportPowerStatus(ADDR_PLAYBACK_2, true, HdmiControlManager.POWER_STATUS_ON);
+
+ PowerStatusMonitorAction action = new PowerStatusMonitorAction(mTvDevice);
+ action.start();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ ADDR_TV,
+ ADDR_PLAYBACK_1);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+
+ HdmiCecMessage giveDevicePowerStatus2 = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ ADDR_TV,
+ ADDR_PLAYBACK_2);
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus2);
+ }
+
+ private void sendMessageFromPlaybackDevice(int logicalAddress, int physicalAddress) {
+ HdmiCecMessage playbackDevice = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ logicalAddress, physicalAddress, HdmiDeviceInfo.DEVICE_PLAYBACK);
+ mNativeWrapper.onCecMessage(playbackDevice);
+ mTestLooper.dispatchAll();
+ }
+
+ private void reportPowerStatus(int logicalAddress, boolean broadcast, int powerStatus) {
+ int destination = broadcast ? ADDR_BROADCAST : ADDR_TV;
+ HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
+ logicalAddress, destination,
+ powerStatus);
+ mNativeWrapper.onCecMessage(reportPowerStatus);
+ mTestLooper.dispatchAll();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java b/services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java
new file mode 100644
index 000000000000..1915b8c36892
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.job;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.app.ActivityManagerInternal;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.SystemClock;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.job.JobConcurrencyManager.GracePeriodObserver;
+import com.android.server.pm.UserManagerInternal;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class GracePeriodObserverTest {
+ private GracePeriodObserver mGracePeriodObserver;
+ private UserManagerInternal mUserManagerInternal;
+ private static final int FIRST_USER = 0;
+
+ @BeforeClass
+ public static void setUpOnce() {
+ UserManagerInternal userManagerInternal = mock(UserManagerInternal.class);
+ LocalServices.addService(UserManagerInternal.class, userManagerInternal);
+ ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class);
+ LocalServices.addService(ActivityManagerInternal.class, activityManagerInternal);
+ }
+
+ @AfterClass
+ public static void tearDownOnce() {
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ }
+
+ @Before
+ public void setUp() {
+ final Context context = ApplicationProvider.getApplicationContext();
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
+ doReturn(FIRST_USER)
+ .when(LocalServices.getService(ActivityManagerInternal.class)).getCurrentUserId();
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+ doReturn(true).when(mUserManagerInternal).exists(FIRST_USER);
+ mGracePeriodObserver = new GracePeriodObserver(context);
+ }
+
+ @Test
+ public void testGracePeriod() throws RemoteException {
+ final int oldUser = FIRST_USER;
+ final int newUser = 10;
+ doReturn(true).when(mUserManagerInternal).exists(newUser);
+ mGracePeriodObserver.onUserSwitchComplete(newUser);
+ assertTrue(mGracePeriodObserver.isWithinGracePeriodForUser(oldUser));
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.offset(JobSchedulerService.sElapsedRealtimeClock,
+ Duration.ofMillis(mGracePeriodObserver.mGracePeriod));
+ assertFalse(mGracePeriodObserver.isWithinGracePeriodForUser(oldUser));
+ }
+
+ @Test
+ public void testCleanUp() throws RemoteException {
+ final int removedUser = FIRST_USER;
+ final int newUser = 10;
+ mGracePeriodObserver.onUserSwitchComplete(newUser);
+
+ final int sizeBefore = mGracePeriodObserver.mGracePeriodExpiration.size();
+ doReturn(false).when(mUserManagerInternal).exists(removedUser);
+
+ mGracePeriodObserver.onUserRemoved(removedUser);
+ assertEquals(sizeBefore - 1, mGracePeriodObserver.mGracePeriodExpiration.size());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
index 263cf48a8a18..f87d5993c1b5 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
@@ -16,7 +16,10 @@
package com.android.server.job;
+import static com.android.server.job.JobConcurrencyManager.NUM_WORK_TYPES;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
@@ -27,6 +30,7 @@ import android.annotation.NonNull;
import android.util.Pair;
import android.util.SparseIntArray;
+import androidx.test.filters.LargeTest;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
@@ -36,6 +40,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@@ -47,6 +52,10 @@ import java.util.Random;
public class WorkCountTrackerTest {
private static final String TAG = "WorkerCountTrackerTest";
+ private static final double[] EQUAL_PROBABILITY_CDF =
+ buildWorkTypeCdf(1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES,
+ 1.0 / NUM_WORK_TYPES);
+
private Random mRandom;
private WorkCountTracker mWorkCountTracker;
@@ -56,34 +65,112 @@ public class WorkCountTrackerTest {
mWorkCountTracker = new WorkCountTracker();
}
+ @NonNull
+ private static double[] buildWorkTypeCdf(double pTop, double pEj, double pBg, double pBgUser) {
+ return buildCdf(pTop, pEj, pBg, pBgUser);
+ }
+
+ @NonNull
+ private static double[] buildCdf(double... probs) {
+ double[] cdf = new double[probs.length];
+ double sum = 0;
+
+ for (int i = 0; i < probs.length; ++i) {
+ sum += probs[i];
+ cdf[i] = sum;
+ }
+
+ if (Double.compare(1, sum) != 0) {
+ throw new IllegalArgumentException("probabilities don't sum to one: " + sum);
+ }
+ return cdf;
+ }
+
+ static int getRandomIndex(double[] cdf, double rand) {
+ for (int i = cdf.length - 1; i >= 0; --i) {
+ if (rand < cdf[i] && (i == 0 || rand > cdf[i - 1])) {
+ return i;
+ }
+ }
+ throw new IllegalStateException("Couldn't pick random index");
+ }
+
+ @JobConcurrencyManager.WorkType
+ static int getRandomWorkType(double[] cdf, double rand) {
+ final int index = getRandomIndex(cdf, rand);
+ switch (index) {
+ case 0:
+ return WORK_TYPE_TOP;
+ case 1:
+ return WORK_TYPE_EJ;
+ case 2:
+ return WORK_TYPE_BG;
+ case 3:
+ return WORK_TYPE_BGUSER;
+ default:
+ throw new IllegalStateException("Unknown work type");
+ }
+ }
+
/**
* Represents running and pending jobs.
*/
class Jobs {
public final SparseIntArray running = new SparseIntArray();
public final SparseIntArray pending = new SparseIntArray();
+ public final List<Integer> pendingMultiTypes = new ArrayList<>();
+
+ /**
+ * @param probStart Probability of starting a job
+ * @param typeCdf The CDF representing the probability of each work type
+ * @param numTypesCdf The CDF representing the probability of a job having X different
+ * work types. Each index i represents i+1 work types (ie. index 0 = 1
+ * work type, index 3 = 4 work types).
+ */
+ public void maybeEnqueueJobs(double probStart, double[] typeCdf, double[] numTypesCdf) {
+ assertThat(numTypesCdf.length).isAtMost(NUM_WORK_TYPES);
+ assertThat(numTypesCdf.length).isAtLeast(1);
+
+ while (mRandom.nextDouble() < probStart) {
+ final int numTypes = getRandomIndex(numTypesCdf, mRandom.nextDouble()) + 1;
+ int types = WORK_TYPE_NONE;
+ for (int i = 0; i < numTypes; ++i) {
+ types |= getRandomWorkType(typeCdf, mRandom.nextDouble());
+ }
+ addPending(types, 1);
+ }
+ }
- public void maybeEnqueueJobs(double startRatio, double fgJobRatio) {
- while (mRandom.nextDouble() < startRatio) {
- if (mRandom.nextDouble() < fgJobRatio) {
- pending.put(WORK_TYPE_TOP, pending.get(WORK_TYPE_TOP) + 1);
- } else {
- pending.put(WORK_TYPE_BG, pending.get(WORK_TYPE_BG) + 1);
+ void addPending(int allWorkTypes, int num) {
+ for (int n = 0; n < num; ++n) {
+ for (int i = 0; i < 32; ++i) {
+ final int type = 1 << i;
+ if ((allWorkTypes & type) != 0) {
+ pending.put(type, pending.get(type) + 1);
+ }
}
+ pendingMultiTypes.add(allWorkTypes);
}
}
- public void maybeFinishJobs(double stopRatio) {
- for (int i = running.get(WORK_TYPE_BG); i > 0; i--) {
- if (mRandom.nextDouble() < stopRatio) {
- running.put(WORK_TYPE_BG, running.get(WORK_TYPE_BG) - 1);
- mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
+ void removePending(int allWorkTypes) {
+ for (int i = 0; i < 32; ++i) {
+ final int type = 1 << i;
+ if ((allWorkTypes & type) != 0) {
+ pending.put(type, pending.get(type) - 1);
}
}
- for (int i = running.get(WORK_TYPE_TOP); i > 0; i--) {
- if (mRandom.nextDouble() < stopRatio) {
- running.put(WORK_TYPE_TOP, running.get(WORK_TYPE_TOP) - 1);
- mWorkCountTracker.onJobFinished(WORK_TYPE_TOP);
+ pendingMultiTypes.remove(Integer.valueOf(allWorkTypes));
+ }
+
+ public void maybeFinishJobs(double probStop) {
+ for (int i = running.size() - 1; i >= 0; --i) {
+ final int workType = running.keyAt(i);
+ for (int c = running.valueAt(i); c > 0; --c) {
+ if (mRandom.nextDouble() < probStop) {
+ running.put(workType, running.get(workType) - 1);
+ mWorkCountTracker.onJobFinished(workType);
+ }
}
}
}
@@ -116,29 +203,37 @@ public class WorkCountTrackerTest {
mWorkCountTracker.onCountDone();
}
+ private boolean hasStartablePendingJob(Jobs jobs) {
+ for (int i = 0; i < jobs.pending.size(); ++i) {
+ if (jobs.pending.valueAt(i) > 0
+ && mWorkCountTracker.canJobStart(jobs.pending.keyAt(i)) != WORK_TYPE_NONE) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private int getPendingMultiType(Jobs jobs, @JobConcurrencyManager.WorkType int workType) {
+ for (int multiType : jobs.pendingMultiTypes) {
+ if ((multiType & workType) != 0) {
+ return multiType;
+ }
+ }
+ throw new IllegalStateException("No pending multi type with work type: " + workType);
+ }
+
private void startPendingJobs(Jobs jobs) {
- while ((jobs.pending.get(WORK_TYPE_TOP) > 0
- && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE)
- || (jobs.pending.get(WORK_TYPE_BG) > 0
- && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE)) {
- final boolean isStartingFg = mRandom.nextBoolean();
-
- if (isStartingFg) {
- if (jobs.pending.get(WORK_TYPE_TOP) > 0
- && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) {
- jobs.pending.put(WORK_TYPE_TOP, jobs.pending.get(WORK_TYPE_TOP) - 1);
- jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) + 1);
- mWorkCountTracker.stageJob(WORK_TYPE_TOP);
- mWorkCountTracker.onJobStarted(WORK_TYPE_TOP);
- }
- } else {
- if (jobs.pending.get(WORK_TYPE_BG) > 0
- && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) {
- jobs.pending.put(WORK_TYPE_BG, jobs.pending.get(WORK_TYPE_BG) - 1);
- jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) + 1);
- mWorkCountTracker.stageJob(WORK_TYPE_BG);
- mWorkCountTracker.onJobStarted(WORK_TYPE_BG);
- }
+ while (hasStartablePendingJob(jobs)) {
+ final int startingWorkType =
+ getRandomWorkType(EQUAL_PROBABILITY_CDF, mRandom.nextDouble());
+
+ if (jobs.pending.get(startingWorkType) > 0
+ && mWorkCountTracker.canJobStart(startingWorkType) != WORK_TYPE_NONE) {
+ final int pendingMultiType = getPendingMultiType(jobs, startingWorkType);
+ jobs.removePending(pendingMultiType);
+ jobs.running.put(startingWorkType, jobs.running.get(startingWorkType) + 1);
+ mWorkCountTracker.stageJob(startingWorkType, pendingMultiType);
+ mWorkCountTracker.onJobStarted(startingWorkType);
}
}
}
@@ -149,12 +244,17 @@ public class WorkCountTrackerTest {
private void checkRandom(Jobs jobs, int numTests, int totalMax,
@NonNull List<Pair<Integer, Integer>> minLimits,
@NonNull List<Pair<Integer, Integer>> maxLimits,
- double startRatio, double fgJobRatio, double stopRatio) {
+ double probStart, double[] typeCdf, double[] numTypesCdf, double probStop) {
+ int minExpected = 0;
+ for (Pair<Integer, Integer> minLimit : minLimits) {
+ minExpected = Math.min(minLimit.second, minExpected);
+ }
for (int i = 0; i < numTests; i++) {
- jobs.maybeFinishJobs(stopRatio);
- jobs.maybeEnqueueJobs(startRatio, fgJobRatio);
+ jobs.maybeFinishJobs(probStop);
+ jobs.maybeEnqueueJobs(probStart, typeCdf, numTypesCdf);
recount(jobs, totalMax, minLimits, maxLimits);
+ final int numPending = jobs.pendingMultiTypes.size();
startPendingJobs(jobs);
int totalRunning = 0;
@@ -166,6 +266,7 @@ public class WorkCountTrackerTest {
totalRunning += numRunning;
}
assertThat(totalRunning).isAtMost(totalMax);
+ assertThat(totalRunning).isAtLeast(Math.min(minExpected, numPending));
for (Pair<Integer, Integer> maxLimit : maxLimits) {
assertWithMessage("Work type " + maxLimit.first + " is running too many jobs")
.that(jobs.running.get(maxLimit.first)).isAtMost(maxLimit.second);
@@ -178,18 +279,19 @@ public class WorkCountTrackerTest {
*/
@Test
public void testRandom1() {
+ assertThat(EQUAL_PROBABILITY_CDF.length).isEqualTo(NUM_WORK_TYPES);
+
final Jobs jobs = new Jobs();
final int numTests = 5000;
final int totalMax = 6;
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
- final double stopRatio = 0.1;
- final double fgJobRatio = 0.5;
- final double startRatio = 0.1;
+ final double probStop = 0.1;
+ final double probStart = 0.1;
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ EQUAL_PROBABILITY_CDF, EQUAL_PROBABILITY_CDF, probStop);
}
@Test
@@ -198,14 +300,16 @@ public class WorkCountTrackerTest {
final int numTests = 5000;
final int totalMax = 2;
- final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of();
- final double stopRatio = 0.5;
- final double fgJobRatio = 0.5;
- final double startRatio = 0.5;
+ final double probStop = 0.5;
+ final double[] cdf = buildWorkTypeCdf(0.5, 0, 0.5, 0);
+ final double[] numTypesCdf = buildCdf(.5, .3, .15, .05);
+ final double probStart = 0.5;
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ cdf, numTypesCdf, probStop);
}
@Test
@@ -214,14 +318,16 @@ public class WorkCountTrackerTest {
final int numTests = 5000;
final int totalMax = 2;
- final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
- final double stopRatio = 0.5;
- final double fgJobRatio = 0.5;
- final double startRatio = 0.5;
+ final double probStop = 0.5;
+ final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+ final double[] numTypesCdf = buildCdf(.75, .2, .05);
+ final double probStart = 0.5;
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ cdf, numTypesCdf, probStop);
}
@Test
@@ -230,14 +336,16 @@ public class WorkCountTrackerTest {
final int numTests = 5000;
final int totalMax = 10;
- final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of();
- final double stopRatio = 0.5;
- final double fgJobRatio = 0.5;
- final double startRatio = 0.5;
+ final double probStop = 0.5;
+ final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+ final double[] numTypesCdf = buildCdf(.05, .95);
+ final double probStart = 0.5;
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ cdf, numTypesCdf, probStop);
}
@Test
@@ -246,14 +354,16 @@ public class WorkCountTrackerTest {
final int numTests = 5000;
final int totalMax = 6;
- final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
- final double stopRatio = 0.5;
- final double fgJobRatio = 0.1;
- final double startRatio = 0.5;
+ final double probStop = 0.5;
+ final double[] cdf = buildWorkTypeCdf(0.1, 0, 0.8, .1);
+ final double[] numTypesCdf = buildCdf(.5, .3, .15, .05);
+ final double probStart = 0.5;
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ cdf, numTypesCdf, probStop);
}
@Test
@@ -262,14 +372,16 @@ public class WorkCountTrackerTest {
final int numTests = 5000;
final int totalMax = 6;
- final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
- final double stopRatio = 0.5;
- final double fgJobRatio = 0.9;
- final double startRatio = 0.5;
+ final double probStop = 0.5;
+ final double[] cdf = buildWorkTypeCdf(0.9, 0, 0.1, 0);
+ final double[] numTypesCdf = buildCdf(1);
+ final double probStart = 0.5;
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ cdf, numTypesCdf, probStop);
}
@Test
@@ -278,14 +390,16 @@ public class WorkCountTrackerTest {
final int numTests = 5000;
final int totalMax = 6;
- final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
- final double stopRatio = 0.4;
- final double fgJobRatio = 0.1;
- final double startRatio = 0.5;
+ final double probStop = 0.4;
+ final double[] cdf = buildWorkTypeCdf(0.1, 0, 0.1, .8);
+ final double[] numTypesCdf = buildCdf(0.5, 0.5);
+ final double probStart = 0.5;
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ cdf, numTypesCdf, probStop);
}
@Test
@@ -294,14 +408,152 @@ public class WorkCountTrackerTest {
final int numTests = 5000;
final int totalMax = 6;
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+ final double probStop = 0.4;
+ final double[] cdf = buildWorkTypeCdf(0.9, 0, 0.05, 0.05);
+ final double[] numTypesCdf = buildCdf(1);
+ final double probStart = 0.5;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ cdf, numTypesCdf, probStop);
+ }
+
+ @Test
+ public void testRandom9() {
+ final Jobs jobs = new Jobs();
+
+ final int numTests = 5000;
+ final int totalMax = 6;
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+ final double probStop = 0.5;
+ final double[] cdf = buildWorkTypeCdf(0, 0, 0.5, 0.5);
+ final double[] numTypesCdf = buildCdf(1);
+ final double probStart = 0.5;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ cdf, numTypesCdf, probStop);
+ }
+
+ @Test
+ public void testRandom10() {
+ final Jobs jobs = new Jobs();
+
+ final int numTests = 5000;
+ final int totalMax = 6;
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+ final double probStop = 0.5;
+ final double[] cdf = buildWorkTypeCdf(0, 0, 0.1, 0.9);
+ final double[] numTypesCdf = buildCdf(0.9, 0.1);
+ final double probStart = 0.5;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ cdf, numTypesCdf, probStop);
+ }
+
+ @Test
+ public void testRandom11() {
+ final Jobs jobs = new Jobs();
+
+ final int numTests = 5000;
+ final int totalMax = 6;
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+ final double probStop = 0.5;
+ final double[] cdf = buildWorkTypeCdf(0, 0, 0.9, 0.1);
+ final double[] numTypesCdf = buildCdf(1);
+ final double probStart = 0.5;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ cdf, numTypesCdf, probStop);
+ }
+
+ @Test
+ public void testRandom12() {
+ final Jobs jobs = new Jobs();
+
+ final int numTests = 5000;
+ final int totalMax = 6;
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2));
+ final double probStop = 0.4;
+ final double[] cdf = buildWorkTypeCdf(0.5, 0.5, 0, 0);
+ final double[] numTypesCdf = buildCdf(0.1, 0.7, 0.2);
+ final double probStart = 0.5;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ cdf, numTypesCdf, probStop);
+ }
+
+ @Test
+ @LargeTest
+ public void testRandom13() {
+ assertThat(EQUAL_PROBABILITY_CDF.length).isEqualTo(NUM_WORK_TYPES);
+
+ final Jobs jobs = new Jobs();
+
+ final int numTests = 5000;
+ final int totalMax = 13;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(
+ Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4),
+ Pair.create(WORK_TYPE_BGUSER, 3));
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1));
+ final double probStop = 0.13;
+ final double[] numTypesCdf = buildCdf(0, 0.05, 0.1, 0.85);
+ final double probStart = 0.87;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ EQUAL_PROBABILITY_CDF, numTypesCdf, probStop);
+ }
+
+ @Test
+ public void testRandom14() {
+ final Jobs jobs = new Jobs();
+
+ final int numTests = 5000;
+ final int totalMax = 6;
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
- final double stopRatio = 0.4;
- final double fgJobRatio = 0.9;
- final double startRatio = 0.5;
+ final double probStop = 0.4;
+ final double[] cdf = buildWorkTypeCdf(.1, 0.5, 0.35, 0.05);
+ final double[] numTypesCdf = buildCdf(1);
+ final double probStart = 0.5;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ cdf, numTypesCdf, probStop);
+ }
+
+ @Test
+ public void testRandom15() {
+ final Jobs jobs = new Jobs();
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ final int numTests = 5000;
+ final int totalMax = 6;
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4),
+ Pair.create(WORK_TYPE_BGUSER, 1));
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2));
+ final double probStop = 0.4;
+ final double[] cdf = buildWorkTypeCdf(0.01, 0.49, 0.1, 0.4);
+ final double[] numTypesCdf = buildCdf(0.7, 0.3);
+ final double probStart = 0.5;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ cdf, numTypesCdf, probStop);
}
/** Used by the following tests */
@@ -317,7 +569,7 @@ public class WorkCountTrackerTest {
jobs.running.put(run.first, run.second);
}
for (Pair<Integer, Integer> pend : pending) {
- jobs.pending.put(pend.first, pend.second);
+ jobs.addPending(pend.first, pend.second);
}
recount(jobs, totalMax, minLimits, maxLimits);
@@ -337,7 +589,7 @@ public class WorkCountTrackerTest {
public void testBasic() {
checkSimple(6,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
- /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
/* run */ List.of(),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
@@ -345,7 +597,7 @@ public class WorkCountTrackerTest {
checkSimple(6,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
- /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
/* run */ List.of(),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
@@ -354,7 +606,7 @@ public class WorkCountTrackerTest {
// When there are BG jobs pending, 2 (min-BG) jobs should run.
checkSimple(6,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
- /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
/* run */ List.of(),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)),
@@ -385,6 +637,16 @@ public class WorkCountTrackerTest {
checkSimple(8,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 49), Pair.create(WORK_TYPE_BG, 49)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 4)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 47), Pair.create(WORK_TYPE_BG, 49))
+ );
+
+
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 6)),
/* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
@@ -407,6 +669,14 @@ public class WorkCountTrackerTest {
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
+ /* run */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
+
checkSimple(6,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
@@ -415,6 +685,16 @@ public class WorkCountTrackerTest {
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* run */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_EJ, 5),
+ Pair.create(WORK_TYPE_BG, 3)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 2)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3),
+ Pair.create(WORK_TYPE_BG, 3)));
+
// This could happen if we lower the effective config due to higher memory pressure after
// we've already started running jobs. We shouldn't stop already running jobs, but also
// shouldn't start new ones.
@@ -425,14 +705,64 @@ public class WorkCountTrackerTest {
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)),
/* resRun */ List.of(Pair.create(WORK_TYPE_BG, 6)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
+
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
+ /* run */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10),
+ Pair.create(WORK_TYPE_BG, 3),
+ Pair.create(WORK_TYPE_BGUSER, 3)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 6),
+ Pair.create(WORK_TYPE_BG, 3),
+ Pair.create(WORK_TYPE_BGUSER, 3)));
+
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 3)),
+ /* run */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 3)),
+ /* resRun */ List.of(
+ Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
+ /* resPen */ List.of(
+ Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)));
+
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)),
+ /* run */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 3)),
+ /* resRun */ List.of(
+ Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)),
+ /* resPen */ List.of(
+ Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 2)));
+
+ // Test multi-types
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(
+ Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)),
+ /* run */ List.of(),
+ /* pen */ List.of(
+ // 2 of these as TOP, 1 as EJ
+ Pair.create(WORK_TYPE_TOP | WORK_TYPE_EJ, 3),
+ // 1 as EJ, 2 as BG
+ Pair.create(WORK_TYPE_EJ | WORK_TYPE_BG, 3),
+ Pair.create(WORK_TYPE_BG, 4),
+ Pair.create(WORK_TYPE_BGUSER, 1)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2),
+ Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
+ /* resPen */ List.of(
+ Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)));
}
/** Tests that the counter updates properly when jobs are stopped. */
@Test
public void testJobLifecycleLoop() {
final Jobs jobs = new Jobs();
- jobs.pending.put(WORK_TYPE_TOP, 11);
- jobs.pending.put(WORK_TYPE_BG, 10);
+ jobs.addPending(WORK_TYPE_TOP, 11);
+ jobs.addPending(WORK_TYPE_BG, 10);
final int totalMax = 6;
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 1));
@@ -503,4 +833,149 @@ public class WorkCountTrackerTest {
assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(3);
}
+
+ /** Tests that the counter updates properly when jobs are stopped. */
+ @Test
+ public void testJobLifecycleLoop_Multitype() {
+ final Jobs jobs = new Jobs();
+ jobs.addPending(WORK_TYPE_TOP, 6); // a
+ jobs.addPending(WORK_TYPE_TOP | WORK_TYPE_EJ, 5); // b
+ jobs.addPending(WORK_TYPE_BG, 10); // c
+
+ final int totalMax = 8;
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_EJ, 1), Pair.create(WORK_TYPE_BG, 1));
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 5));
+
+ recount(jobs, totalMax, minLimits, maxLimits);
+
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(11);
+ assertThat(jobs.pending.get(WORK_TYPE_EJ)).isEqualTo(5);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(10);
+
+ startPendingJobs(jobs);
+
+ assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(6);
+ assertThat(jobs.running.get(WORK_TYPE_EJ)).isEqualTo(1);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(4);
+ // Since starting happens in random order, all EJs could have run first.
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(9);
+
+ // Stop all jobs
+ jobs.maybeFinishJobs(1);
+
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_TOP);
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_EJ)).isEqualTo(WORK_TYPE_EJ);
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+
+ startPendingJobs(jobs);
+
+ assertThat(jobs.running.get(WORK_TYPE_TOP) + jobs.running.get(WORK_TYPE_EJ)).isEqualTo(4);
+ // Depending on the order jobs start, we may run all TOP/EJ combos as TOP and reserve a slot
+ // for EJ, which would reduce BG count to 3 instead of 4.
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isAtLeast(3);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isAtMost(4);
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
+ assertThat(jobs.pending.get(WORK_TYPE_EJ)).isEqualTo(0);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isAtLeast(5);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isAtMost(6);
+
+ // Stop only a bg job and make sure the counter only allows another bg job to start.
+ jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) - 1);
+ mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
+
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_NONE);
+ // Depending on the order jobs start, we may run all TOP/EJ combos as TOP and reserve a slot
+ // for EJ, which would reduce BG count to 3 instead of 4.
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+
+ startPendingJobs(jobs);
+
+ assertThat(jobs.running.get(WORK_TYPE_TOP) + jobs.running.get(WORK_TYPE_EJ)).isEqualTo(4);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isAtLeast(3);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isAtMost(4);
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
+ assertThat(jobs.pending.get(WORK_TYPE_EJ)).isEqualTo(0);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isAtLeast(4);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isAtMost(5);
+ }
+
+ /** Tests that the counter updates properly when jobs are stopped. */
+ @Test
+ public void testJobLifecycleLoop_Multitype_RandomOrder() {
+ final Jobs jobs = new Jobs();
+ SparseIntArray multiToCount = new SparseIntArray();
+ multiToCount.put(WORK_TYPE_TOP, 6); // a
+ multiToCount.put(WORK_TYPE_TOP | WORK_TYPE_EJ, 5); // b
+ multiToCount.put(WORK_TYPE_EJ | WORK_TYPE_BG, 5); // c
+ multiToCount.put(WORK_TYPE_BG, 5); // d
+ while (multiToCount.size() > 0) {
+ final int index = mRandom.nextInt(multiToCount.size());
+ final int count = multiToCount.valueAt(index);
+ jobs.addPending(multiToCount.keyAt(index), 1);
+ if (count <= 1) {
+ multiToCount.removeAt(index);
+ } else {
+ multiToCount.put(multiToCount.keyAt(index), count - 1);
+ }
+ }
+
+ final int totalMax = 8;
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_EJ, 1), Pair.create(WORK_TYPE_BG, 1));
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 5));
+
+ recount(jobs, totalMax, minLimits, maxLimits);
+
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(11);
+ assertThat(jobs.pending.get(WORK_TYPE_EJ)).isEqualTo(10);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(10);
+
+ startPendingJobs(jobs);
+
+ // Random order, but we should have 6 TOP, 1 EJ, and 1 BG running.
+ assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(6);
+ assertThat(jobs.running.get(WORK_TYPE_EJ)).isEqualTo(1);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(4);
+ // Can't equate pending EJ since some could be running as TOP and BG
+ assertThat(jobs.pending.get(WORK_TYPE_EJ)).isAtLeast(2);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(9);
+
+ // Stop all jobs
+ jobs.maybeFinishJobs(1);
+
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_TOP);
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_EJ)).isEqualTo(WORK_TYPE_EJ);
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+
+ startPendingJobs(jobs);
+
+ // Random order, but we should have 4 TOP, 1 EJ, and 1 BG running.
+ assertThat(jobs.running.get(WORK_TYPE_TOP)).isAtLeast(1);
+ assertThat(jobs.running.get(WORK_TYPE_EJ)).isAtLeast(1);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+ // At this point, all TOP should be running (or have already run).
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
+ assertThat(jobs.pending.get(WORK_TYPE_EJ)).isAtLeast(2);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(5);
+
+ // Stop only a bg job and make sure the counter only allows another bg job to start.
+ jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) - 1);
+ mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
+
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_TOP)).isEqualTo(WORK_TYPE_NONE);
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_EJ)).isEqualTo(WORK_TYPE_NONE);
+ assertThat(mWorkCountTracker.canJobStart(WORK_TYPE_BG)).isEqualTo(WORK_TYPE_BG);
+
+ startPendingJobs(jobs);
+
+ assertThat(jobs.running.get(WORK_TYPE_TOP)).isAtLeast(1);
+ assertThat(jobs.running.get(WORK_TYPE_EJ)).isAtLeast(1);
+ assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
+ assertThat(jobs.pending.get(WORK_TYPE_EJ)).isAtLeast(2);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(4);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
index c28292f03357..2288a8925561 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
@@ -16,8 +16,11 @@
package com.android.server.job;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import android.annotation.NonNull;
@@ -31,11 +34,9 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.server.job.JobConcurrencyManager.WorkTypeConfig;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@@ -43,9 +44,13 @@ import java.util.List;
public class WorkTypeConfigTest {
private static final String KEY_MAX_TOTAL = "concurrency_max_total_test";
private static final String KEY_MAX_TOP = "concurrency_max_top_test";
+ private static final String KEY_MAX_EJ = "concurrency_max_ej_test";
private static final String KEY_MAX_BG = "concurrency_max_bg_test";
+ private static final String KEY_MAX_BGUSER = "concurrency_max_bguser_test";
private static final String KEY_MIN_TOP = "concurrency_min_top_test";
+ private static final String KEY_MIN_EJ = "concurrency_min_ej_test";
private static final String KEY_MIN_BG = "concurrency_min_bg_test";
+ private static final String KEY_MIN_BGUSER = "concurrency_min_bguser_test";
@After
public void tearDown() throws Exception {
@@ -56,43 +61,27 @@ public class WorkTypeConfigTest {
// DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually.
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_EJ, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_EJ, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, "", false);
}
private void check(@Nullable DeviceConfig.Properties config,
int defaultTotal,
- @Nullable Pair<Integer, Integer> defaultTopLimits,
- @Nullable Pair<Integer, Integer> defaultBgLimits,
+ @NonNull List<Pair<Integer, Integer>> defaultMin,
+ @NonNull List<Pair<Integer, Integer>> defaultMax,
boolean expectedValid, int expectedTotal,
- @NonNull Pair<Integer, Integer> expectedTopLimits,
- @NonNull Pair<Integer, Integer> expectedBgLimits) throws Exception {
+ @NonNull List<Pair<Integer, Integer>> expectedMinLimits,
+ @NonNull List<Pair<Integer, Integer>> expectedMaxLimits) throws Exception {
resetConfig();
if (config != null) {
DeviceConfig.setProperties(config);
}
- List<Pair<Integer, Integer>> defaultMin = new ArrayList<>();
- List<Pair<Integer, Integer>> defaultMax = new ArrayList<>();
- Integer val;
- if (defaultTopLimits != null) {
- if ((val = defaultTopLimits.first) != null) {
- defaultMin.add(Pair.create(WORK_TYPE_TOP, val));
- }
- if ((val = defaultTopLimits.second) != null) {
- defaultMax.add(Pair.create(WORK_TYPE_TOP, val));
- }
- }
- if (defaultBgLimits != null) {
- if ((val = defaultBgLimits.first) != null) {
- defaultMin.add(Pair.create(WORK_TYPE_BG, val));
- }
- if ((val = defaultBgLimits.second) != null) {
- defaultMax.add(Pair.create(WORK_TYPE_BG, val));
- }
- }
-
final WorkTypeConfig counts;
try {
counts = new WorkTypeConfig("test",
@@ -112,40 +101,126 @@ public class WorkTypeConfigTest {
counts.update(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
- Assert.assertEquals(expectedTotal, counts.getMaxTotal());
- Assert.assertEquals((int) expectedTopLimits.first, counts.getMinReserved(WORK_TYPE_TOP));
- Assert.assertEquals((int) expectedTopLimits.second, counts.getMax(WORK_TYPE_TOP));
- Assert.assertEquals((int) expectedBgLimits.first, counts.getMinReserved(WORK_TYPE_BG));
- Assert.assertEquals((int) expectedBgLimits.second, counts.getMax(WORK_TYPE_BG));
+ assertEquals(expectedTotal, counts.getMaxTotal());
+ for (Pair<Integer, Integer> min : expectedMinLimits) {
+ assertEquals((int) min.second, counts.getMinReserved(min.first));
+ }
+ for (Pair<Integer, Integer> max : expectedMaxLimits) {
+ assertEquals((int) max.second, counts.getMax(max.first));
+ }
}
@Test
public void test() throws Exception {
// Tests with various combinations.
- check(null, /*default*/ 5, Pair.create(4, null), Pair.create(0, 1),
- /*expected*/ true, 5, Pair.create(4, 5), Pair.create(0, 1));
- check(null, /*default*/ 5, Pair.create(5, null), Pair.create(0, 0),
- /*expected*/ true, 5, Pair.create(5, 5), Pair.create(0, 1));
- check(null, /*default*/ 0, Pair.create(5, null), Pair.create(0, 0),
- /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1));
- check(null, /*default*/ -1, null, Pair.create(-1, -1),
- /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1));
- check(null, /*default*/ 5, null, Pair.create(5, 5),
- /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5));
- check(null, /*default*/ 6, Pair.create(1, null), Pair.create(6, 5),
- /*expected*/ false, 6, Pair.create(1, 6), Pair.create(5, 5));
- check(null, /*default*/ 4, null, Pair.create(6, 5),
- /*expected*/ false, 4, Pair.create(1, 4), Pair.create(3, 4));
- check(null, /*default*/ 5, Pair.create(4, null), Pair.create(1, 1),
- /*expected*/ true, 5, Pair.create(4, 5), Pair.create(1, 1));
- check(null, /*default*/ 15, null, Pair.create(15, 15),
- /*expected*/ true, 15, Pair.create(1, 15), Pair.create(14, 15));
- check(null, /*default*/ 16, null, Pair.create(16, 16),
- /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16));
- check(null, /*default*/ 20, null, Pair.create(20, 20),
- /*expected*/ false, 16, Pair.create(1, 16), Pair.create(15, 16));
- check(null, /*default*/ 20, null, Pair.create(16, 16),
- /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16));
+ check(null, /*default*/ 13,
+ /* min */ List.of(),
+ /* max */ List.of(),
+ /*expected*/ true, 13,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_EJ, 0),
+ Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 13), Pair.create(WORK_TYPE_EJ, 13),
+ Pair.create(WORK_TYPE_BG, 13), Pair.create(WORK_TYPE_BGUSER, 13)));
+ check(null, /*default*/ 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+ /*expected*/ true, 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)));
+ check(null, /*default*/ 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+ Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(
+ Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 1)),
+ /*expected*/ true, 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+ Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+ Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)));
+ check(null, /*default*/ 0,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 0)),
+ /*expected*/ false, 1,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 1)));
+ check(null, /*default*/ -1,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, -1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, -1)),
+ /*expected*/ false, 1,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 1)));
+ check(null, /*default*/ 5,
+ /* min */ List.of(
+ Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(
+ Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)),
+ /*expected*/ true, 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+ Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+ Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)));
+ check(null, /*default*/ 6,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+ Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 2)),
+ /* max */ List.of(
+ Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 1)),
+ /*expected*/ false, 6,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+ Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6),
+ Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 1)));
+ check(null, /*default*/ 4,
+ /* min */ List.of(
+ Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 6)),
+ /* max */ List.of(
+ Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)),
+ /*expected*/ false, 4,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+ Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 4),
+ Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 4)));
+ check(null, /*default*/ 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+ /*expected*/ true, 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)));
+ check(null, /*default*/ 10,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
+ Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+ /*expected*/ true, 10,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
+ Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)));
+ check(null, /*default*/ 15,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 15)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 15)),
+ /*expected*/ true, 15,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 14)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 15), Pair.create(WORK_TYPE_BG, 15)));
+ check(null, /*default*/ 16,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+ /*expected*/ true, 16,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 15)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
+ check(null, /*default*/ 20,
+ /* min */ List.of(
+ Pair.create(WORK_TYPE_BG, 20), Pair.create(WORK_TYPE_BGUSER, 10)),
+ /* max */ List.of(
+ Pair.create(WORK_TYPE_BG, 20), Pair.create(WORK_TYPE_BGUSER, 20)),
+ /*expected*/ false, 16,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+ Pair.create(WORK_TYPE_BG, 15), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16),
+ Pair.create(WORK_TYPE_BG, 16), Pair.create(WORK_TYPE_BGUSER, 16)));
+ check(null, /*default*/ 20,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+ /*expected*/ true, 16,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 15)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
// Test for overriding with a setting string.
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
@@ -153,26 +228,66 @@ public class WorkTypeConfigTest {
.setInt(KEY_MAX_BG, 4)
.setInt(KEY_MIN_BG, 3)
.build(),
- /*default*/ 9, null, Pair.create(9, 9),
- /*expected*/ true, 5, Pair.create(1, 5), Pair.create(3, 4));
+ /*default*/ 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /* max */ List.of(
+ Pair.create(WORK_TYPE_BG, 9), Pair.create(WORK_TYPE_BGUSER, 2)),
+ /*expected*/ true, 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 3)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+ Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)));
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MAX_TOTAL, 5).build(),
- /*default*/ 9, null, Pair.create(9, 9),
- /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5));
+ /*default*/ 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /*expected*/ true, 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 4)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 5)));
+
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MAX_BG, 4).build(),
- /*default*/ 9, null, Pair.create(9, 9),
- /*expected*/ true, 9, Pair.create(1, 9), Pair.create(4, 4));
+ /*default*/ 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /*expected*/ true, 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 4)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 9), Pair.create(WORK_TYPE_BG, 4)));
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MIN_BG, 3).build(),
- /*default*/ 9, null, Pair.create(9, 9),
- /*expected*/ true, 9, Pair.create(1, 9), Pair.create(3, 9));
+ /*default*/ 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /*expected*/ true, 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 3)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 9), Pair.create(WORK_TYPE_BG, 9)));
+ check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+ .setInt(KEY_MAX_TOTAL, 20)
+ .setInt(KEY_MAX_EJ, 5)
+ .setInt(KEY_MIN_EJ, 2)
+ .setInt(KEY_MAX_BG, 16)
+ .setInt(KEY_MIN_BG, 8)
+ .build(),
+ /*default*/ 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /*expected*/ true, 16,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_EJ, 2),
+ Pair.create(WORK_TYPE_BG, 8)),
+ /* max */
+ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_EJ, 5),
+ Pair.create(WORK_TYPE_BG, 16)));
+
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MAX_TOTAL, 20)
.setInt(KEY_MAX_BG, 20)
.setInt(KEY_MIN_BG, 8)
.build(),
- /*default*/ 9, null, Pair.create(9, 9),
- /*expected*/ true, 16, Pair.create(1, 16), Pair.create(8, 16));
+ /*default*/ 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /*expected*/ true, 16,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 8)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 1f66c7c02658..67d69292b476 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -339,11 +339,11 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
mService.setLockCredential(nonePassword(), newPattern("123654"), PRIMARY_USER_ID);
// Verify fingerprint is removed
- verify(mFingerprintManager).remove(any(), eq(PRIMARY_USER_ID), any());
- verify(mFaceManager).remove(any(), eq(PRIMARY_USER_ID), any());
+ verify(mFingerprintManager).removeAll(eq(PRIMARY_USER_ID), any());
+ verify(mFaceManager).removeAll(eq(PRIMARY_USER_ID), any());
- verify(mFingerprintManager).remove(any(), eq(MANAGED_PROFILE_USER_ID), any());
- verify(mFaceManager).remove(any(), eq(MANAGED_PROFILE_USER_ID), any());
+ verify(mFingerprintManager).removeAll(eq(MANAGED_PROFILE_USER_ID), any());
+ verify(mFaceManager).removeAll(eq(MANAGED_PROFILE_USER_ID), any());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
index 25dbc6b6cd51..33ea7108a705 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -45,6 +45,7 @@ import android.content.Context;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
+import android.os.Process;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.platform.test.annotations.Presubmit;
@@ -89,7 +90,8 @@ public class LockSettingsShellCommandTest {
MockitoAnnotations.initMocks(this);
final Context context = InstrumentationRegistry.getTargetContext();
mUserId = ActivityManager.getCurrentUser();
- mCommand = new LockSettingsShellCommand(mLockPatternUtils);
+ mCommand = new LockSettingsShellCommand(mLockPatternUtils, context, 0,
+ Process.SHELL_UID);
when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(true);
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
index 32445fd1a47d..2eedc3251daa 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
@@ -19,19 +19,17 @@ package com.android.server.locksettings;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
-import android.security.keystore.KeyGenParameterSpec;
-import android.security.keystore.KeyProperties;
-
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.security.GeneralSecurityException;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
-import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
/**
* atest FrameworksServicesTests:RebootEscrowDataTest
@@ -41,22 +39,18 @@ public class RebootEscrowDataTest {
private RebootEscrowKey mKey;
private SecretKey mKeyStoreEncryptionKey;
- private SecretKey generateNewRebootEscrowEncryptionKey() throws GeneralSecurityException {
- KeyGenerator generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
- generator.init(new KeyGenParameterSpec.Builder(
- "reboot_escrow_data_test_key",
- KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
- .setKeySize(256)
- .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
- .build());
- return generator.generateKey();
- }
+ // Hex encoding of a randomly generated AES key for test.
+ private static final byte[] TEST_AES_KEY = new byte[] {
+ 0x44, 0x74, 0x61, 0x54, 0x29, 0x74, 0x37, 0x61,
+ 0x48, 0x19, 0x12, 0x54, 0x13, 0x13, 0x52, 0x31,
+ 0x70, 0x70, 0x75, 0x25, 0x27, 0x31, 0x49, 0x09,
+ 0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23,
+ };
@Before
public void generateKey() throws Exception {
mKey = RebootEscrowKey.generate();
- mKeyStoreEncryptionKey = generateNewRebootEscrowEncryptionKey();
+ mKeyStoreEncryptionKey = new SecretKeySpec(TEST_AES_KEY, "AES");
}
private static byte[] getTestSp() {
@@ -114,4 +108,23 @@ public class RebootEscrowDataTest {
assertThat(decrypted, is(testSp));
}
+ @Test
+ public void fromEncryptedData_legacyVersion_success() throws Exception {
+ byte[] testSp = getTestSp();
+ byte[] ksEncryptedBlob = AesEncryptionUtil.encrypt(mKey.getKey(), testSp);
+
+ // Write a legacy blob encrypted only by k_s.
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(bos);
+ dos.writeInt(1);
+ dos.writeByte(3);
+ dos.write(ksEncryptedBlob);
+ byte[] legacyBlob = bos.toByteArray();
+
+ RebootEscrowData actual = RebootEscrowData.fromEncryptedData(mKey, legacyBlob, null);
+
+ assertThat(actual.getSpVersion(), is((byte) 3));
+ assertThat(actual.getKey().getKeyBytes(), is(mKey.getKeyBytes()));
+ assertThat(actual.getSyntheticPassword(), is(testSp));
+ }
}
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 a4ba4c86a8fd..a896f1b0d60f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -43,6 +43,7 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.UserInfo;
import android.hardware.rebootescrow.IRebootEscrow;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserManager;
@@ -155,6 +156,11 @@ public class RebootEscrowManagerTests {
}
@Override
+ void post(Handler handler, Runnable runnable) {
+ runnable.run();
+ }
+
+ @Override
public UserManager getUserManager() {
return mUserManager;
}
@@ -369,7 +375,7 @@ public class RebootEscrowManagerTests {
@Test
public void loadRebootEscrowDataIfAvailable_NothingAvailable_Success() throws Exception {
- mService.loadRebootEscrowDataIfAvailable();
+ mService.loadRebootEscrowDataIfAvailable(null);
}
@Test
@@ -401,7 +407,7 @@ public class RebootEscrowManagerTests {
doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
- mService.loadRebootEscrowDataIfAvailable();
+ mService.loadRebootEscrowDataIfAvailable(null);
verify(mRebootEscrow).retrieveKey();
assertTrue(metricsSuccessCaptor.getValue());
verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
@@ -435,7 +441,7 @@ public class RebootEscrowManagerTests {
when(mServiceConnection.unwrap(any(), anyLong()))
.thenAnswer(invocation -> invocation.getArgument(0));
- mService.loadRebootEscrowDataIfAvailable();
+ mService.loadRebootEscrowDataIfAvailable(null);
verify(mServiceConnection).unwrap(any(), anyLong());
assertTrue(metricsSuccessCaptor.getValue());
verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
@@ -466,7 +472,7 @@ public class RebootEscrowManagerTests {
when(mInjected.getBootCount()).thenReturn(10);
when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]);
- mService.loadRebootEscrowDataIfAvailable();
+ mService.loadRebootEscrowDataIfAvailable(null);
verify(mRebootEscrow).retrieveKey();
verify(mInjected, never()).reportMetric(anyBoolean());
}
@@ -493,7 +499,7 @@ public class RebootEscrowManagerTests {
when(mInjected.getBootCount()).thenReturn(10);
when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]);
- mService.loadRebootEscrowDataIfAvailable();
+ mService.loadRebootEscrowDataIfAvailable(null);
verify(mInjected, never()).reportMetric(anyBoolean());
}
@@ -527,7 +533,7 @@ public class RebootEscrowManagerTests {
when(mInjected.getBootCount()).thenReturn(10);
when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
- mService.loadRebootEscrowDataIfAvailable();
+ mService.loadRebootEscrowDataIfAvailable(null);
verify(mInjected).reportMetric(eq(true));
}
@@ -557,7 +563,7 @@ public class RebootEscrowManagerTests {
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> new byte[32]);
- mService.loadRebootEscrowDataIfAvailable();
+ mService.loadRebootEscrowDataIfAvailable(null);
verify(mRebootEscrow).retrieveKey();
assertFalse(metricsSuccessCaptor.getValue());
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
index bc1e025dd99f..28b737b412d2 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
@@ -30,6 +30,7 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.ContextWrapper;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -42,7 +43,6 @@ import org.junit.runner.RunWith;
import org.mockito.stubbing.Answer;
import java.io.File;
-import java.io.IOException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
@@ -130,7 +130,7 @@ public class RebootEscrowProviderServerBasedImplTests {
@Test
public void getAndClearRebootEscrowKey_ServiceConnectionException_failure() throws Exception {
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
- doThrow(IOException.class).when(mServiceConnection).unwrap(any(), anyLong());
+ doThrow(RemoteException.class).when(mServiceConnection).unwrap(any(), anyLong());
assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 58ba90726b80..7d7af03ecd3d 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -19,11 +19,13 @@ package com.android.server.net;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.Manifest.permission.NETWORK_STACK;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
import static android.net.INetd.FIREWALL_RULE_ALLOW;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -112,8 +114,6 @@ import android.net.INetworkPolicyListener;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkState;
@@ -1985,13 +1985,6 @@ public class NetworkPolicyManagerServiceTest {
return users;
}
- private NetworkInfo buildNetworkInfo() {
- final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_LTE, null, null);
- ni.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
- return ni;
- }
-
private LinkProperties buildLinkProperties(String iface) {
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(iface);
@@ -2045,13 +2038,12 @@ public class NetworkPolicyManagerServiceTest {
}
private static NetworkState buildWifi() {
- final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
- info.setDetailedState(DetailedState.CONNECTED, null, null);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+ networkCapabilities.addTransportType(TRANSPORT_WIFI);
networkCapabilities.setSSID(TEST_SSID);
- return new NetworkState(info, prop, networkCapabilities, null, null, TEST_SSID);
+ return new NetworkState(TYPE_WIFI, prop, networkCapabilities, null, null);
}
private void expectHasInternetPermission(int uid, boolean hasIt) throws Exception {
@@ -2072,10 +2064,10 @@ public class NetworkPolicyManagerServiceTest {
when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID)))
.thenReturn(mCarrierConfig);
when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
- new NetworkState(buildNetworkInfo(),
+ new NetworkState(TYPE_MOBILE,
buildLinkProperties(TEST_IFACE),
buildNetworkCapabilities(TEST_SUB_ID, roaming),
- new Network(TEST_NET_ID), TEST_IMSI, null)
+ new Network(TEST_NET_ID), TEST_IMSI)
});
}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
index 3a292deeaac9..38125c7847f3 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
@@ -21,7 +21,9 @@ import android.content.om.OverlayableInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.os.Process
+import android.util.ArrayMap
import com.android.server.om.OverlayActorEnforcer.ActorState
+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
@@ -29,6 +31,7 @@ import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
+import org.mockito.Mockito
import org.mockito.Mockito.spy
import java.io.IOException
@@ -125,11 +128,11 @@ class OverlayActorEnforcerTests {
ActorState.TARGET_NOT_FOUND withCases {
failure("nullPkgInfo") { targetPkgInfo = null }
allowed("debuggable") {
- targetPkgInfo = pkgInfo(TARGET_PKG).apply {
- applicationInfo.flags = ApplicationInfo.FLAG_DEBUGGABLE
+ targetPkgInfo = androidPackage(TARGET_PKG).apply {
+ whenever(this.isDebuggable).thenReturn(true)
}
}
- skip { targetPkgInfo = pkgInfo(TARGET_PKG) }
+ skip { targetPkgInfo = androidPackage(TARGET_PKG) }
},
ActorState.NO_PACKAGES_FOR_UID withCases {
failure("empty") { callingUid = EMPTY_UID }
@@ -236,22 +239,20 @@ class OverlayActorEnforcerTests {
mapOf(VALID_ACTOR_NAME to VALID_ACTOR_PKG))
}
},
- ActorState.MISSING_APP_INFO withCases {
+ ActorState.ACTOR_NOT_FOUND withCases {
failure("nullActorPkgInfo") { actorPkgInfo = null }
failure("nullActorAppInfo") {
- actorPkgInfo = PackageInfo().apply { applicationInfo = null }
+ actorPkgInfo = null
}
- skip { actorPkgInfo = pkgInfo(VALID_ACTOR_PKG) }
+ skip { actorPkgInfo = androidPackage(VALID_ACTOR_PKG) }
},
ActorState.ACTOR_NOT_PREINSTALLED withCases {
failure("notSystem") {
- actorPkgInfo = pkgInfo(VALID_ACTOR_PKG).apply {
- applicationInfo.flags = 0
- }
+ actorPkgInfo = androidPackage(VALID_ACTOR_PKG)
}
skip {
- actorPkgInfo = pkgInfo(VALID_ACTOR_PKG).apply {
- applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM
+ actorPkgInfo = androidPackage(VALID_ACTOR_PKG).apply {
+ whenever(this.isSystem).thenReturn(true)
}
}
},
@@ -272,22 +273,22 @@ class OverlayActorEnforcerTests {
) {
fun toOverlayInfo() = OverlayInfo(
OVERLAY_PKG,
+ "",
targetPackageName,
targetOverlayableName,
null,
"/path",
OverlayInfo.STATE_UNKNOWN, 0,
- 0, false)
+ 0, false, false)
}
private infix fun ActorState.withCases(block: TestCase.() -> Unit) =
TestCase(this).apply(block)
- private fun pkgInfo(pkgName: String): PackageInfo = mockThrowOnUnmocked {
- this.packageName = pkgName
- this.applicationInfo = ApplicationInfo().apply {
- this.packageName = pkgName
- }
+ private fun androidPackage(pkgName: String): AndroidPackage = mockThrowOnUnmocked {
+ whenever(this.packageName).thenReturn(pkgName)
+ whenever(this.isDebuggable).thenReturn(false)
+ whenever(this.isSystem).thenReturn(false)
}
private fun makeTestName(testCase: TestCase, caseName: String, type: Params.Type): String {
@@ -363,8 +364,8 @@ class OverlayActorEnforcerTests {
var namedActorsMap: Map<String, Map<String, String>> = emptyMap(),
var hasPermission: Boolean = false,
var targetOverlayableInfo: OverlayableInfo? = null,
- var targetPkgInfo: PackageInfo? = null,
- var actorPkgInfo: PackageInfo? = null,
+ var targetPkgInfo: AndroidPackage? = null,
+ var actorPkgInfo: AndroidPackage? = null,
vararg val packageNames: String = arrayOf("com.test.actor.one")
) : PackageManagerHelper {
@@ -375,6 +376,14 @@ class OverlayActorEnforcerTests {
override fun getNamedActors() = namedActorsMap
+ override fun isInstantApp(packageName: String, userId: Int): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun initializeForUser(userId: Int): ArrayMap<String, AndroidPackage> {
+ throw UnsupportedOperationException()
+ }
+
@Throws(IOException::class)
override fun getOverlayableForTarget(
packageName: String,
@@ -394,9 +403,6 @@ class OverlayActorEnforcerTests {
else -> null
}
- override fun getPackageInfo(packageName: String, userId: Int) =
- listOfNotNull(targetPkgInfo, actorPkgInfo).find { it.packageName == packageName }
-
@Throws(IOException::class) // Mockito requires this checked exception to be declared
override fun doesTargetDefineOverlayable(targetPackageName: String?, userId: Int): Boolean {
return targetOverlayableInfo?.takeIf {
@@ -411,11 +417,10 @@ class OverlayActorEnforcerTests {
}
}
- override fun getConfigSignaturePackage(): String {
- throw UnsupportedOperationException()
- }
+ override fun getPackageForUser(packageName: String, userId: Int) =
+ listOfNotNull(targetPkgInfo, actorPkgInfo).find { it.packageName == packageName }
- override fun getOverlayPackages(userId: Int): MutableList<PackageInfo> {
+ override fun getConfigSignaturePackage(): String {
throw UnsupportedOperationException()
}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
index 5468fba59c10..55cd772ad7e0 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
@@ -21,7 +21,9 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
+import android.util.ArraySet;
import androidx.test.runner.AndroidJUnit4;
@@ -29,77 +31,66 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
-import java.util.List;
import java.util.function.BiConsumer;
@RunWith(AndroidJUnit4.class)
public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceImplTestsBase {
private static final String OVERLAY = "com.test.overlay";
+ private static final OverlayIdentifier IDENTIFIER = new OverlayIdentifier(OVERLAY);
private static final String TARGET = "com.test.target";
private static final int USER = 0;
private static final String OVERLAY2 = OVERLAY + "2";
+ private static final OverlayIdentifier IDENTIFIER2 = new OverlayIdentifier(OVERLAY2);
@Test
public void testUpdateOverlaysForUser() {
final OverlayManagerServiceImpl impl = getImpl();
+ final String otherTarget = "some.other.target";
addPackage(target(TARGET), USER);
- addPackage(target("some.other.target"), USER);
+ addPackage(target(otherTarget), USER);
addPackage(overlay(OVERLAY, TARGET), USER);
// do nothing, expect no change
- final List<String> a = impl.updateOverlaysForUser(USER);
- assertEquals(1, a.size());
- assertTrue(a.contains(TARGET));
-
- // upgrade overlay, keep target
- addPackage(overlay(OVERLAY, TARGET), USER);
-
- final List<String> b = impl.updateOverlaysForUser(USER);
- assertEquals(1, b.size());
- assertTrue(b.contains(TARGET));
-
- // do nothing, expect no change
- final List<String> c = impl.updateOverlaysForUser(USER);
- assertEquals(1, c.size());
- assertTrue(c.contains(TARGET));
-
- // upgrade overlay, switch to new target
- addPackage(overlay(OVERLAY, "some.other.target"), USER);
- final List<String> d = impl.updateOverlaysForUser(USER);
- assertEquals(2, d.size());
- assertTrue(d.containsAll(Arrays.asList(TARGET, "some.other.target")));
-
- // do nothing, expect no change
- final List<String> f = impl.updateOverlaysForUser(USER);
- assertEquals(1, f.size());
- assertTrue(f.contains("some.other.target"));
+ final ArraySet<PackageAndUser> a = impl.updateOverlaysForUser(USER);
+ assertEquals(3, a.size());
+ assertTrue(a.containsAll(Arrays.asList(
+ new PackageAndUser(TARGET, USER),
+ new PackageAndUser(otherTarget, USER),
+ new PackageAndUser(OVERLAY, USER))));
+
+ final ArraySet<PackageAndUser> b = impl.updateOverlaysForUser(USER);
+ assertEquals(3, b.size());
+ assertTrue(b.containsAll(Arrays.asList(
+ new PackageAndUser(TARGET, USER),
+ new PackageAndUser(otherTarget, USER),
+ new PackageAndUser(OVERLAY, USER))));
}
@Test
public void testImmutableEnabledChange() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
- installNewPackage(target(TARGET), USER);
- installNewPackage(overlay(OVERLAY, TARGET), USER);
+ installPackage(target(TARGET), USER);
+ installPackage(overlay(OVERLAY, TARGET), USER);
configureSystemOverlay(OVERLAY, false /* mutable */, false /* enabled */, 0 /* priority */);
impl.updateOverlaysForUser(USER);
- final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER);
assertNotNull(o1);
assertFalse(o1.isEnabled());
assertFalse(o1.isMutable);
configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 0 /* priority */);
impl.updateOverlaysForUser(USER);
- final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER, USER);
assertNotNull(o2);
assertTrue(o2.isEnabled());
assertFalse(o2.isMutable);
configureSystemOverlay(OVERLAY, false /* mutable */, false /* enabled */, 0 /* priority */);
impl.updateOverlaysForUser(USER);
- final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER);
assertNotNull(o3);
assertFalse(o3.isEnabled());
assertFalse(o3.isMutable);
@@ -108,26 +99,26 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
@Test
public void testMutableEnabledChangeHasNoEffect() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
- installNewPackage(target(TARGET), USER);
- installNewPackage(overlay(OVERLAY, TARGET), USER);
+ installPackage(target(TARGET), USER);
+ installPackage(overlay(OVERLAY, TARGET), USER);
configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */);
impl.updateOverlaysForUser(USER);
- final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER);
assertNotNull(o1);
assertFalse(o1.isEnabled());
assertTrue(o1.isMutable);
configureSystemOverlay(OVERLAY, true /* mutable */, true /* enabled */, 0 /* priority */);
impl.updateOverlaysForUser(USER);
- final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER, USER);
assertNotNull(o2);
assertFalse(o2.isEnabled());
assertTrue(o2.isMutable);
configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */);
impl.updateOverlaysForUser(USER);
- final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER);
assertNotNull(o3);
assertFalse(o3.isEnabled());
assertTrue(o3.isMutable);
@@ -136,13 +127,13 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
@Test
public void testMutableEnabledToImmutableEnabled() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
- installNewPackage(target(TARGET), USER);
- installNewPackage(overlay(OVERLAY, TARGET), USER);
+ installPackage(target(TARGET), USER);
+ installPackage(overlay(OVERLAY, TARGET), USER);
final BiConsumer<Boolean, Boolean> setOverlay = (mutable, enabled) -> {
configureSystemOverlay(OVERLAY, mutable, enabled, 0 /* priority */);
impl.updateOverlaysForUser(USER);
- final OverlayInfo o = impl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo o = impl.getOverlayInfo(IDENTIFIER, USER);
assertNotNull(o);
assertEquals(enabled, o.isEnabled());
assertEquals(mutable, o.isMutable);
@@ -180,38 +171,38 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
@Test
public void testMutablePriorityChange() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
- installNewPackage(target(TARGET), USER);
- installNewPackage(overlay(OVERLAY, TARGET), USER);
- installNewPackage(overlay(OVERLAY2, TARGET), USER);
+ installPackage(target(TARGET), USER);
+ installPackage(overlay(OVERLAY, TARGET), USER);
+ installPackage(overlay(OVERLAY2, TARGET), USER);
configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */);
configureSystemOverlay(OVERLAY2, true /* mutable */, false /* enabled */, 1 /* priority */);
impl.updateOverlaysForUser(USER);
- final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER);
assertNotNull(o1);
assertEquals(0, o1.priority);
assertFalse(o1.isEnabled());
- final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER);
+ final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER2, USER);
assertNotNull(o2);
assertEquals(1, o2.priority);
assertFalse(o2.isEnabled());
// Overlay priority changing between reboots should not affect enable state of mutable
// overlays.
- impl.setEnabled(OVERLAY, true, USER);
+ impl.setEnabled(IDENTIFIER, true, USER);
// Reorder the overlays
configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 1 /* priority */);
configureSystemOverlay(OVERLAY2, true /* mutable */, false /* enabled */, 0 /* priority */);
impl.updateOverlaysForUser(USER);
- final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER);
assertNotNull(o3);
assertEquals(1, o3.priority);
assertTrue(o3.isEnabled());
- final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER);
+ final OverlayInfo o4 = impl.getOverlayInfo(IDENTIFIER2, USER);
assertNotNull(o4);
assertEquals(0, o4.priority);
assertFalse(o4.isEnabled());
@@ -220,19 +211,19 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
@Test
public void testImmutablePriorityChange() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
- installNewPackage(target(TARGET), USER);
- installNewPackage(overlay(OVERLAY, TARGET), USER);
- installNewPackage(overlay(OVERLAY2, TARGET), USER);
+ installPackage(target(TARGET), USER);
+ installPackage(overlay(OVERLAY, TARGET), USER);
+ installPackage(overlay(OVERLAY2, TARGET), USER);
configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 0 /* priority */);
configureSystemOverlay(OVERLAY2, false /* mutable */, true /* enabled */, 1 /* priority */);
impl.updateOverlaysForUser(USER);
- final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER);
assertNotNull(o1);
assertEquals(0, o1.priority);
assertTrue(o1.isEnabled());
- final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER);
+ final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER2, USER);
assertNotNull(o2);
assertEquals(1, o2.priority);
assertTrue(o2.isEnabled());
@@ -242,12 +233,12 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
configureSystemOverlay(OVERLAY2, false /* mutable */, true /* enabled */, 0 /* priority */);
impl.updateOverlaysForUser(USER);
- final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER);
assertNotNull(o3);
assertEquals(1, o3.priority);
assertTrue(o3.isEnabled());
- final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER);
+ final OverlayInfo o4 = impl.getOverlayInfo(IDENTIFIER2, USER);
assertNotNull(o4);
assertEquals(0, o4.priority);
assertTrue(o4.isEnabled());
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
index 33dbcc0855be..45f82a36c8ed 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -28,6 +28,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.testng.Assert.assertThrows;
+import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.util.Pair;
@@ -39,19 +40,23 @@ import org.junit.runner.RunWith;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
@RunWith(AndroidJUnit4.class)
public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
private static final String OVERLAY = "com.test.overlay";
+ private static final OverlayIdentifier IDENTIFIER = new OverlayIdentifier(OVERLAY);
private static final String TARGET = "com.test.target";
private static final int USER = 0;
private static final String OVERLAY2 = OVERLAY + "2";
private static final String TARGET2 = TARGET + "2";
+ private static final OverlayIdentifier IDENTIFIER2 = new OverlayIdentifier(OVERLAY2);
private static final int USER2 = USER + 1;
private static final String OVERLAY3 = OVERLAY + "3";
+ private static final OverlayIdentifier IDENTIFIER3 = new OverlayIdentifier(OVERLAY3);
private static final int USER3 = USER2 + 1;
private static final String CONFIG_SIGNATURE_REFERENCE_PKG = "com.test.ref";
@@ -60,10 +65,10 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
@Test
public void testGetOverlayInfo() throws Exception {
- installNewPackage(overlay(OVERLAY, TARGET), USER);
+ installPackage(overlay(OVERLAY, TARGET), USER);
final OverlayManagerServiceImpl impl = getImpl();
- final OverlayInfo oi = impl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo oi = impl.getOverlayInfo(IDENTIFIER, USER);
assertNotNull(oi);
assertEquals(oi.packageName, OVERLAY);
assertEquals(oi.targetPackageName, TARGET);
@@ -72,19 +77,19 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
@Test
public void testGetOverlayInfosForTarget() throws Exception {
- installNewPackage(overlay(OVERLAY, TARGET), USER);
- installNewPackage(overlay(OVERLAY2, TARGET), USER);
- installNewPackage(overlay(OVERLAY3, TARGET), USER2);
+ installPackage(overlay(OVERLAY, TARGET), USER);
+ installPackage(overlay(OVERLAY2, TARGET), USER);
+ installPackage(overlay(OVERLAY3, TARGET), USER2);
final OverlayManagerServiceImpl impl = getImpl();
final List<OverlayInfo> ois = impl.getOverlayInfosForTarget(TARGET, USER);
assertEquals(ois.size(), 2);
- assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY, USER)));
- assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY2, USER)));
+ assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER, USER)));
+ assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER2, USER)));
final List<OverlayInfo> ois2 = impl.getOverlayInfosForTarget(TARGET, USER2);
assertEquals(ois2.size(), 1);
- assertTrue(ois2.contains(impl.getOverlayInfo(OVERLAY3, USER2)));
+ assertTrue(ois2.contains(impl.getOverlayInfo(IDENTIFIER3, USER2)));
final List<OverlayInfo> ois3 = impl.getOverlayInfosForTarget(TARGET, USER3);
assertNotNull(ois3);
@@ -97,10 +102,10 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
@Test
public void testGetOverlayInfosForUser() throws Exception {
- installNewPackage(target(TARGET), USER);
- installNewPackage(overlay(OVERLAY, TARGET), USER);
- installNewPackage(overlay(OVERLAY2, TARGET), USER);
- installNewPackage(overlay(OVERLAY3, TARGET2), USER);
+ installPackage(target(TARGET), USER);
+ installPackage(overlay(OVERLAY, TARGET), USER);
+ installPackage(overlay(OVERLAY2, TARGET), USER);
+ installPackage(overlay(OVERLAY3, TARGET2), USER);
final OverlayManagerServiceImpl impl = getImpl();
final Map<String, List<OverlayInfo>> everything = impl.getOverlaysForUser(USER);
@@ -109,13 +114,13 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
final List<OverlayInfo> ois = everything.get(TARGET);
assertNotNull(ois);
assertEquals(ois.size(), 2);
- assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY, USER)));
- assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY2, USER)));
+ assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER, USER)));
+ assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER2, USER)));
final List<OverlayInfo> ois2 = everything.get(TARGET2);
assertNotNull(ois2);
assertEquals(ois2.size(), 1);
- assertTrue(ois2.contains(impl.getOverlayInfo(OVERLAY3, USER)));
+ assertTrue(ois2.contains(impl.getOverlayInfo(IDENTIFIER3, USER)));
final Map<String, List<OverlayInfo>> everything2 = impl.getOverlaysForUser(USER2);
assertNotNull(everything2);
@@ -124,26 +129,26 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
@Test
public void testPriority() throws Exception {
- installNewPackage(overlay(OVERLAY, TARGET), USER);
- installNewPackage(overlay(OVERLAY2, TARGET), USER);
- installNewPackage(overlay(OVERLAY3, TARGET), USER);
+ installPackage(overlay(OVERLAY, TARGET), USER);
+ installPackage(overlay(OVERLAY2, TARGET), USER);
+ installPackage(overlay(OVERLAY3, TARGET), USER);
final OverlayManagerServiceImpl impl = getImpl();
- final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
- final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER);
- final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY3, USER);
+ final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER);
+ final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER2, USER);
+ final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER3, USER);
assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
- assertEquals(impl.setLowestPriority(OVERLAY3, USER),
+ assertEquals(impl.setLowestPriority(IDENTIFIER3, USER),
Optional.of(new PackageAndUser(TARGET, USER)));
assertOverlayInfoForTarget(TARGET, USER, o3, o1, o2);
- assertEquals(impl.setHighestPriority(OVERLAY3, USER),
- Optional.of(new PackageAndUser(TARGET, USER)));
+ assertEquals(impl.setHighestPriority(IDENTIFIER3, USER),
+ Set.of(new PackageAndUser(TARGET, USER)));
assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
- assertEquals(impl.setPriority(OVERLAY, OVERLAY2, USER),
+ assertEquals(impl.setPriority(IDENTIFIER, IDENTIFIER2, USER),
Optional.of(new PackageAndUser(TARGET, USER)));
assertOverlayInfoForTarget(TARGET, USER, o2, o1, o3);
}
@@ -151,61 +156,63 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
@Test
public void testOverlayInfoStateTransitions() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
- assertNull(impl.getOverlayInfo(OVERLAY, USER));
+ assertNull(impl.getOverlayInfo(IDENTIFIER, USER));
- installNewPackage(overlay(OVERLAY, TARGET), USER);
- assertState(STATE_MISSING_TARGET, OVERLAY, USER);
+ installPackage(overlay(OVERLAY, TARGET), USER);
+ assertState(STATE_MISSING_TARGET, IDENTIFIER, USER);
final FakeDeviceState.PackageBuilder target = target(TARGET);
- installNewPackage(target, USER);
- assertState(STATE_DISABLED, OVERLAY, USER);
+ installPackage(target, USER);
+ assertState(STATE_DISABLED, IDENTIFIER, USER);
- assertEquals(impl.setEnabled(OVERLAY, true, USER),
- Optional.of(new PackageAndUser(TARGET, USER)));
- assertState(STATE_ENABLED, OVERLAY, USER);
+ assertEquals(impl.setEnabled(IDENTIFIER, true, USER),
+ Set.of(new PackageAndUser(TARGET, USER)));
+ assertState(STATE_ENABLED, IDENTIFIER, USER);
// target upgrades do not change the state of the overlay
upgradePackage(target, USER);
- assertState(STATE_ENABLED, OVERLAY, USER);
+ assertState(STATE_ENABLED, IDENTIFIER, USER);
uninstallPackage(TARGET, USER);
- assertState(STATE_MISSING_TARGET, OVERLAY, USER);
+ assertState(STATE_MISSING_TARGET, IDENTIFIER, USER);
- installNewPackage(target, USER);
- assertState(STATE_ENABLED, OVERLAY, USER);
+ installPackage(target, USER);
+ assertState(STATE_ENABLED, IDENTIFIER, USER);
}
@Test
public void testOnOverlayPackageUpgraded() throws Exception {
final FakeDeviceState.PackageBuilder target = target(TARGET);
final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
- installNewPackage(target, USER);
- installNewPackage(overlay, USER);
+ installPackage(target, USER);
+ installPackage(overlay, USER);
upgradePackage(overlay, USER);
// upgrade to a version where the overlay has changed its target
final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
- final Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> pair =
- upgradePackage(overlay2, USER);
- assertEquals(pair.first, Optional.of(new PackageAndUser(TARGET, USER)));
- assertEquals(pair.second, Optional.of(new PackageAndUser("some.other.target", USER)));
+ final Pair<Set<PackageAndUser>, Set<PackageAndUser>> pair = upgradePackage(overlay2, USER);
+ assertEquals(pair.first, Set.of(new PackageAndUser(TARGET, USER)));
+ assertEquals(
+ Set.of(new PackageAndUser(TARGET, USER),
+ new PackageAndUser("some.other.target", USER)),
+ pair.second);
}
@Test
public void testSetEnabledAtVariousConditions() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
assertThrows(OverlayManagerServiceImpl.OperationFailedException.class,
- () -> impl.setEnabled(OVERLAY, true, USER));
+ () -> impl.setEnabled(IDENTIFIER, true, USER));
// request succeeded, and there was a change that needs to be
// propagated to the rest of the system
- installNewPackage(target(TARGET), USER);
- installNewPackage(overlay(OVERLAY, TARGET), USER);
- assertEquals(impl.setEnabled(OVERLAY, true, USER),
- Optional.of(new PackageAndUser(TARGET, USER)));
+ installPackage(target(TARGET), USER);
+ installPackage(overlay(OVERLAY, TARGET), USER);
+ assertEquals(impl.setEnabled(IDENTIFIER, true, USER),
+ Set.of(new PackageAndUser(TARGET, USER)));
// request succeeded, but nothing changed
- assertFalse(impl.setEnabled(OVERLAY, true, USER).isPresent());
+ assertTrue(impl.setEnabled(IDENTIFIER, true, USER).isEmpty());
}
@Test
@@ -214,8 +221,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
reinitializeImpl();
addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
- installNewPackage(target(TARGET), USER);
- installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_OK), USER);
+ installPackage(target(TARGET), USER);
+ installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_OK), USER);
final FakeIdmapDaemon idmapd = getIdmapd();
final FakeDeviceState state = getState();
@@ -232,8 +239,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
reinitializeImpl();
addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
- installNewPackage(target(TARGET), USER);
- installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
+ installPackage(target(TARGET), USER);
+ installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
final FakeIdmapDaemon idmapd = getIdmapd();
final FakeDeviceState state = getState();
@@ -247,8 +254,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
@Test
public void testConfigSignaturePolicyNoConfig() throws Exception {
addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
- installNewPackage(target(TARGET), USER);
- installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
+ installPackage(target(TARGET), USER);
+ installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
final FakeIdmapDaemon idmapd = getIdmapd();
final FakeDeviceState state = getState();
@@ -261,8 +268,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
@Test
public void testConfigSignaturePolicyNoRefPkg() throws Exception {
- installNewPackage(target(TARGET), USER);
- installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
+ installPackage(target(TARGET), USER);
+ installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
final FakeIdmapDaemon idmapd = getIdmapd();
final FakeDeviceState state = getState();
@@ -279,8 +286,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
reinitializeImpl();
addPackage(app(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
- installNewPackage(target(TARGET), USER);
- installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
+ installPackage(target(TARGET), USER);
+ installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
final FakeIdmapDaemon idmapd = getIdmapd();
final FakeDeviceState state = getState();
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
index 2c477c897b30..16e03290b1e4 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
@@ -24,11 +24,12 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
+import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.content.om.OverlayInfo.State;
import android.content.om.OverlayableInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
+import android.os.FabricatedOverlayInfo;
+import android.os.FabricatedOverlayInternal;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -37,17 +38,19 @@ import android.util.Pair;
import androidx.annotation.Nullable;
import com.android.internal.content.om.OverlayConfig;
+import com.android.internal.util.CollectionUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import org.junit.Assert;
import org.junit.Before;
+import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
-import java.util.stream.Collectors;
+import java.util.Set;
/** Base class for creating {@link OverlayManagerServiceImplTests} tests. */
class OverlayManagerServiceImplTestsBase {
@@ -94,12 +97,11 @@ class OverlayManagerServiceImplTestsBase {
mConfigSignaturePackageName = packageName;
}
- void assertState(@State int expected, final String overlayPackageName, int userId) {
- final OverlayInfo info = mImpl.getOverlayInfo(overlayPackageName, userId);
+ void assertState(@State int expected, final OverlayIdentifier overlay, int userId) {
+ final OverlayInfo info = mImpl.getOverlayInfo(overlay, userId);
if (info == null) {
- throw new IllegalStateException("package not installed");
+ throw new IllegalStateException("overlay '" + overlay + "' not installed");
}
-
final String msg = String.format("expected %s but was %s:",
OverlayInfo.stateToString(expected), OverlayInfo.stateToString(info.state));
assertEquals(msg, expected, info.state);
@@ -152,17 +154,13 @@ class OverlayManagerServiceImplTestsBase {
*
* @throws IllegalStateException if the package is currently installed
*/
- void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId)
+ Set<PackageAndUser> installPackage(FakeDeviceState.PackageBuilder pkg, int userId)
throws OperationFailedException {
if (mState.select(pkg.packageName, userId) != null) {
throw new IllegalStateException("package " + pkg.packageName + " already installed");
}
mState.add(pkg, userId);
- if (pkg.targetPackage == null) {
- mImpl.onTargetPackageAdded(pkg.packageName, userId);
- } else {
- mImpl.onOverlayPackageAdded(pkg.packageName, userId);
- }
+ return CollectionUtils.emptyIfNull(mImpl.onPackageAdded(pkg.packageName, userId));
}
/**
@@ -178,26 +176,21 @@ class OverlayManagerServiceImplTestsBase {
*
* @throws IllegalStateException if the package is not currently installed
*/
- Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> upgradePackage(
+ Pair<Set<PackageAndUser>, Set<PackageAndUser>> upgradePackage(
FakeDeviceState.PackageBuilder pkg, int userId) throws OperationFailedException {
final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
if (replacedPackage == null) {
throw new IllegalStateException("package " + pkg.packageName + " not installed");
}
- Optional<PackageAndUser> opt1 = Optional.empty();
- if (replacedPackage.targetPackageName != null) {
- opt1 = mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
- }
+
+ final Set<PackageAndUser> updatedPackages1 =
+ CollectionUtils.emptyIfNull(mImpl.onPackageReplacing(pkg.packageName, userId));
mState.add(pkg, userId);
- Optional<PackageAndUser> opt2;
- if (pkg.targetPackage == null) {
- opt2 = mImpl.onTargetPackageReplaced(pkg.packageName, userId);
- } else {
- opt2 = mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
- }
+ final Set<PackageAndUser> updatedPackages2 =
+ CollectionUtils.emptyIfNull(mImpl.onPackageReplaced(pkg.packageName, userId));
- return Pair.create(opt1, opt2);
+ return Pair.create(updatedPackages1, updatedPackages2);
}
/**
@@ -208,17 +201,13 @@ class OverlayManagerServiceImplTestsBase {
*
* @throws IllegalStateException if the package is not currently installed
*/
- void uninstallPackage(String packageName, int userId) throws OperationFailedException {
+ Set<PackageAndUser> uninstallPackage(String packageName, int userId) {
final FakeDeviceState.Package pkg = mState.select(packageName, userId);
if (pkg == null) {
throw new IllegalStateException("package " + packageName+ " not installed");
}
mState.remove(pkg.packageName);
- if (pkg.targetPackageName == null) {
- mImpl.onTargetPackageRemoved(pkg.packageName, userId);
- } else {
- mImpl.onOverlayPackageRemoved(pkg.packageName, userId);
- }
+ return CollectionUtils.emptyIfNull(mImpl.onPackageRemoved(packageName, userId));
}
/** Represents the state of packages installed on a fake device. */
@@ -247,11 +236,6 @@ class OverlayManagerServiceImplTestsBase {
}
}
- List<Package> select(int userId) {
- return mPackages.values().stream().filter(p -> p.installedUserIds.contains(userId))
- .collect(Collectors.toList());
- }
-
Package select(String packageName, int userId) {
final Package pkg = mPackages.get(packageName);
return pkg != null && pkg.installedUserIds.contains(userId) ? pkg : null;
@@ -335,6 +319,21 @@ class OverlayManagerServiceImplTestsBase {
this.apkPath = apkPath;
this.certificate = certificate;
}
+
+ @Nullable
+ private AndroidPackage getPackageForUser(int user) {
+ if (!installedUserIds.contains(user)) {
+ return null;
+ }
+ final AndroidPackage pkg = Mockito.mock(AndroidPackage.class);
+ when(pkg.getPackageName()).thenReturn(packageName);
+ when(pkg.getBaseApkPath()).thenReturn(apkPath);
+ when(pkg.getLongVersionCode()).thenReturn((long) versionCode);
+ when(pkg.getOverlayTarget()).thenReturn(targetPackageName);
+ when(pkg.getOverlayTargetName()).thenReturn(targetOverlayableName);
+ when(pkg.getOverlayCategory()).thenReturn("Fake-category-" + targetPackageName);
+ return pkg;
+ }
}
}
@@ -345,21 +344,29 @@ class OverlayManagerServiceImplTestsBase {
mState = state;
}
+ @NonNull
@Override
- public PackageInfo getPackageInfo(@NonNull String packageName, int userId) {
- final FakeDeviceState.Package pkg = mState.select(packageName, userId);
- if (pkg == null) {
- return null;
- }
- final ApplicationInfo ai = new ApplicationInfo();
- ai.sourceDir = pkg.apkPath;
- PackageInfo pi = new PackageInfo();
- pi.applicationInfo = ai;
- pi.packageName = pkg.packageName;
- pi.overlayTarget = pkg.targetPackageName;
- pi.targetOverlayableName = pkg.targetOverlayableName;
- pi.overlayCategory = "Fake-category-" + pkg.targetPackageName;
- return pi;
+ public ArrayMap<String, AndroidPackage> initializeForUser(int userId) {
+ final ArrayMap<String, AndroidPackage> packages = new ArrayMap<>();
+ mState.mPackages.forEach((key, value) -> {
+ final AndroidPackage pkg = value.getPackageForUser(userId);
+ if (pkg != null) {
+ packages.put(key, pkg);
+ }
+ });
+ return packages;
+ }
+
+ @Nullable
+ @Override
+ public AndroidPackage getPackageForUser(@NonNull String packageName, int userId) {
+ final FakeDeviceState.Package pkgState = mState.select(packageName, userId);
+ return pkgState == null ? null : pkgState.getPackageForUser(userId);
+ }
+
+ @Override
+ public boolean isInstantApp(@NonNull String packageName, int userId) {
+ return false;
}
@Override
@@ -371,14 +378,6 @@ class OverlayManagerServiceImplTestsBase {
}
@Override
- public List<PackageInfo> getOverlayPackages(int userId) {
- return mState.select(userId).stream()
- .filter(p -> p.targetPackageName != null)
- .map(p -> getPackageInfo(p.packageName, userId))
- .collect(Collectors.toList());
- }
-
- @Override
public @NonNull String getConfigSignaturePackage() {
return mConfigSignaturePackageName;
}
@@ -421,6 +420,9 @@ class OverlayManagerServiceImplTestsBase {
static class FakeIdmapDaemon extends IdmapDaemon {
private final FakeDeviceState mState;
private final ArrayMap<String, IdmapHeader> mIdmapFiles = new ArrayMap<>();
+ private final ArrayMap<String, FabricatedOverlayInfo> mFabricatedOverlays =
+ new ArrayMap<>();
+ private int mFabricatedAssetSeq = 0;
FakeIdmapDaemon(FakeDeviceState state) {
this.mState = state;
@@ -433,10 +435,10 @@ class OverlayManagerServiceImplTestsBase {
}
@Override
- String createIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
- int userId) {
+ String createIdmap(String targetPath, String overlayPath, String overlayName,
+ int policies, boolean enforce, int userId) {
mIdmapFiles.put(overlayPath, new IdmapHeader(getCrc(targetPath),
- getCrc(overlayPath), targetPath, policies, enforce));
+ getCrc(overlayPath), targetPath, overlayName, policies, enforce));
return overlayPath;
}
@@ -446,8 +448,8 @@ class OverlayManagerServiceImplTestsBase {
}
@Override
- boolean verifyIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
- int userId) {
+ boolean verifyIdmap(String targetPath, String overlayPath, String overlayName, int policies,
+ boolean enforce, int userId) {
final IdmapHeader idmap = mIdmapFiles.get(overlayPath);
if (idmap == null) {
return false;
@@ -461,6 +463,29 @@ class OverlayManagerServiceImplTestsBase {
return mIdmapFiles.containsKey(overlayPath);
}
+ @Override
+ FabricatedOverlayInfo createFabricatedOverlay(@NonNull FabricatedOverlayInternal overlay) {
+ final String path = Integer.toString(mFabricatedAssetSeq++);
+ final FabricatedOverlayInfo info = new FabricatedOverlayInfo();
+ info.path = path;
+ info.overlayName = overlay.overlayName;
+ info.packageName = overlay.packageName;
+ info.targetPackageName = overlay.targetPackageName;
+ info.targetOverlayable = overlay.targetOverlayable;
+ mFabricatedOverlays.put(path, info);
+ return info;
+ }
+
+ @Override
+ boolean deleteFabricatedOverlay(@NonNull String path) {
+ return mFabricatedOverlays.remove(path) != null;
+ }
+
+ @Override
+ List<FabricatedOverlayInfo> getFabricatedOverlayInfos() {
+ return new ArrayList<>(mFabricatedOverlays.values());
+ }
+
IdmapHeader getIdmap(String overlayPath) {
return mIdmapFiles.get(overlayPath);
}
@@ -469,14 +494,16 @@ class OverlayManagerServiceImplTestsBase {
private final int targetCrc;
private final int overlayCrc;
final String targetPath;
+ final String overlayName;
final int policies;
final boolean enforceOverlayable;
- private IdmapHeader(int targetCrc, int overlayCrc, String targetPath, int policies,
- boolean enforceOverlayable) {
+ private IdmapHeader(int targetCrc, int overlayCrc, String targetPath,
+ String overlayName, int policies, boolean enforceOverlayable) {
this.targetCrc = targetCrc;
this.overlayCrc = overlayCrc;
this.targetPath = targetPath;
+ this.overlayName = overlayName;
this.policies = policies;
this.enforceOverlayable = enforceOverlayable;
}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
index e3e77689f8e6..0a26f27bc848 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
@@ -19,17 +19,20 @@ package com.android.server.om;
import static android.content.om.OverlayInfo.STATE_DISABLED;
import static android.content.om.OverlayInfo.STATE_ENABLED;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.text.TextUtils;
import android.util.TypedXmlPullParser;
import android.util.Xml;
+import androidx.annotation.NonNull;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
@@ -43,67 +46,32 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.stream.IntStream;
-import java.util.stream.Stream;
+
+import javax.annotation.Nullable;
@RunWith(AndroidJUnit4.class)
public class OverlayManagerSettingsTests {
private OverlayManagerSettings mSettings;
+ private static int USER_0 = 0;
+ private static int USER_1 = 1;
+
+ private static OverlayIdentifier OVERLAY_A = new OverlayIdentifier("com.test.overlay_a",
+ null /* overlayName */);
+ private static OverlayIdentifier OVERLAY_B = new OverlayIdentifier("com.test.overlay_b",
+ null /* overlayName */);
+ private static OverlayIdentifier OVERLAY_C = new OverlayIdentifier("com.test.overlay_c",
+ null /* overlayName */);
+
+ private static final OverlayInfo OVERLAY_A_USER0 = createInfo(OVERLAY_A, USER_0);
+ private static final OverlayInfo OVERLAY_B_USER0 = createInfo(OVERLAY_B, USER_0);
+ private static final OverlayInfo OVERLAY_C_USER0 = createInfo(OVERLAY_C, USER_0);
- private static final OverlayInfo OVERLAY_A0 = new OverlayInfo(
- "com.test.overlay_a",
- "com.test.target",
- null,
- "some-category",
- "/data/app/com.test.overlay_a-1/base.apk",
- STATE_DISABLED,
- 0,
- 0,
- true);
-
- private static final OverlayInfo OVERLAY_B0 = new OverlayInfo(
- "com.test.overlay_b",
- "com.test.target",
- null,
- "some-category",
- "/data/app/com.test.overlay_b-1/base.apk",
- STATE_DISABLED,
- 0,
- 0,
- true);
-
- private static final OverlayInfo OVERLAY_C0 = new OverlayInfo(
- "com.test.overlay_c",
- "com.test.target",
- null,
- "some-category",
- "/data/app/com.test.overlay_c-1/base.apk",
- STATE_DISABLED,
- 0,
- 0,
- true);
-
- private static final OverlayInfo OVERLAY_A1 = new OverlayInfo(
- "com.test.overlay_a",
- "com.test.target",
- null,
- "some-category",
- "/data/app/com.test.overlay_a-1/base.apk",
- STATE_DISABLED,
- 1,
- 0,
- true);
-
- private static final OverlayInfo OVERLAY_B1 = new OverlayInfo(
- "com.test.overlay_b",
- "com.test.target",
- null,
- "some-category",
- "/data/app/com.test.overlay_b-1/base.apk",
- STATE_DISABLED,
- 1,
- 0,
- true);
+ private static final OverlayInfo OVERLAY_A_USER1 = createInfo(OVERLAY_A, USER_1);
+ private static final OverlayInfo OVERLAY_B_USER1 = createInfo(OVERLAY_B, USER_1);
+
+ private static final String TARGET_PACKAGE = "com.test.target";
@Before
public void setUp() throws Exception {
@@ -114,124 +82,112 @@ public class OverlayManagerSettingsTests {
@Test
public void testSettingsInitiallyEmpty() throws Exception {
- final int userId = 0;
- Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(userId);
+ final Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(0 /* userId */);
assertEquals(0, map.size());
}
@Test
public void testBasicSetAndGet() throws Exception {
- assertDoesNotContain(mSettings, OVERLAY_A0.packageName, OVERLAY_A0.userId);
+ assertDoesNotContain(mSettings, OVERLAY_A_USER0);
- insert(OVERLAY_A0);
- assertContains(mSettings, OVERLAY_A0);
- OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_A0.packageName, OVERLAY_A0.userId);
- assertEquals(OVERLAY_A0, oi);
+ insertSetting(OVERLAY_A_USER0);
+ assertContains(mSettings, OVERLAY_A_USER0);
+ final OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_A, USER_0);
+ assertEquals(OVERLAY_A_USER0, oi);
- assertTrue(mSettings.remove(OVERLAY_A0.packageName, OVERLAY_A0.userId));
- assertDoesNotContain(mSettings, OVERLAY_A0.packageName, OVERLAY_A0.userId);
+ assertTrue(mSettings.remove(OVERLAY_A, USER_0));
+ assertDoesNotContain(mSettings, OVERLAY_A, USER_0);
}
@Test
public void testGetUsers() throws Exception {
- int[] users = mSettings.getUsers();
- assertEquals(0, users.length);
-
- insert(OVERLAY_A0);
- users = mSettings.getUsers();
- assertEquals(1, users.length);
- assertContains(users, OVERLAY_A0.userId);
-
- insert(OVERLAY_A1);
- insert(OVERLAY_B1);
- users = mSettings.getUsers();
- assertEquals(2, users.length);
- assertContains(users, OVERLAY_A0.userId);
- assertContains(users, OVERLAY_A1.userId);
+ assertArrayEquals(new int[]{}, mSettings.getUsers());
+
+ insertSetting(OVERLAY_A_USER0);
+ assertArrayEquals(new int[]{USER_0}, mSettings.getUsers());
+
+ insertSetting(OVERLAY_A_USER1);
+ insertSetting(OVERLAY_B_USER1);
+ assertArrayEquals(new int[]{USER_0, USER_1}, mSettings.getUsers());
}
@Test
public void testGetOverlaysForUser() throws Exception {
- insert(OVERLAY_A0);
- insert(OVERLAY_B0);
- insert(OVERLAY_A1);
- insert(OVERLAY_B1);
-
- Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(OVERLAY_A0.userId);
- assertEquals(1, map.keySet().size());
- assertTrue(map.keySet().contains(OVERLAY_A0.targetPackageName));
-
- List<OverlayInfo> list = map.get(OVERLAY_A0.targetPackageName);
- assertEquals(2, list.size());
- assertTrue(list.contains(OVERLAY_A0));
- assertTrue(list.contains(OVERLAY_B0));
-
- // getOverlaysForUser should never return null
- map = mSettings.getOverlaysForUser(-1);
- assertNotNull(map);
- assertEquals(0, map.size());
+ insertSetting(OVERLAY_A_USER0);
+ insertSetting(OVERLAY_B_USER0);
+ insertSetting(OVERLAY_A_USER1);
+ insertSetting(OVERLAY_B_USER0);
+
+ final Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(USER_0);
+ assertEquals(Set.of(TARGET_PACKAGE), map.keySet());
+
+ // Two overlays in user 0 target the same package
+ final List<OverlayInfo> list = map.get(TARGET_PACKAGE);
+ assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0), list);
+
+ // No users installed for user 3
+ assertEquals(Map.<String, List<OverlayInfo>>of(), mSettings.getOverlaysForUser(3));
}
@Test
public void testRemoveUser() throws Exception {
- insert(OVERLAY_A0);
- insert(OVERLAY_B0);
- insert(OVERLAY_A1);
+ insertSetting(OVERLAY_A_USER0);
+ insertSetting(OVERLAY_B_USER0);
+ insertSetting(OVERLAY_A_USER1);
- assertContains(mSettings, OVERLAY_A0);
- assertContains(mSettings, OVERLAY_B0);
- assertContains(mSettings, OVERLAY_A1);
+ assertContains(mSettings, OVERLAY_A_USER0);
+ assertContains(mSettings, OVERLAY_B_USER0);
+ assertContains(mSettings, OVERLAY_A_USER1);
- mSettings.removeUser(OVERLAY_A0.userId);
+ mSettings.removeUser(USER_0);
- assertDoesNotContain(mSettings, OVERLAY_A0);
- assertDoesNotContain(mSettings, OVERLAY_B0);
- assertContains(mSettings, OVERLAY_A1);
+ assertDoesNotContain(mSettings, OVERLAY_A_USER0);
+ assertDoesNotContain(mSettings, OVERLAY_B_USER0);
+ assertContains(mSettings, OVERLAY_A_USER1);
}
@Test
public void testOrderOfNewlyAddedItems() throws Exception {
// new items are appended to the list
- insert(OVERLAY_A0);
- insert(OVERLAY_B0);
- insert(OVERLAY_C0);
+ insertSetting(OVERLAY_A_USER0);
+ insertSetting(OVERLAY_B_USER0);
+ insertSetting(OVERLAY_C_USER0);
- List<OverlayInfo> list =
- mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
- assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
+ assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0),
+ mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
// overlays keep their positions when updated
- mSettings.setState(OVERLAY_B0.packageName, OVERLAY_B0.userId, STATE_ENABLED);
- OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_B0.packageName, OVERLAY_B0.userId);
+ mSettings.setState(OVERLAY_B, USER_0, STATE_ENABLED);
+ final OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_B, USER_0);
+ assertNotNull(oi);
- list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
- assertListsAreEqual(list, OVERLAY_A0, oi, OVERLAY_C0);
+ assertListsAreEqual(List.of(OVERLAY_A_USER0, oi, OVERLAY_C_USER0),
+ mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
}
@Test
public void testSetPriority() throws Exception {
- insert(OVERLAY_A0);
- insert(OVERLAY_B0);
- insert(OVERLAY_C0);
-
- List<OverlayInfo> list =
- mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
- assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
-
- boolean changed = mSettings.setPriority(OVERLAY_B0.packageName, OVERLAY_C0.packageName,
- OVERLAY_B0.userId);
- assertTrue(changed);
- list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
- assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0);
-
- changed =
- mSettings.setPriority(OVERLAY_B0.packageName, "does.not.exist", OVERLAY_B0.userId);
- assertFalse(changed);
- list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
- assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0);
-
- OverlayInfo otherTarget = new OverlayInfo(
+ insertSetting(OVERLAY_A_USER0);
+ insertSetting(OVERLAY_B_USER0);
+ insertSetting(OVERLAY_C_USER0);
+
+ assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0),
+ mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
+
+ assertTrue(mSettings.setPriority(OVERLAY_B, OVERLAY_C, USER_0));
+ assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_C_USER0, OVERLAY_B_USER0),
+ mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
+
+ // Nothing happens if the parent package cannot be found
+ assertFalse(mSettings.setPriority(OVERLAY_B, new OverlayIdentifier("does.not.exist"),
+ USER_0));
+ assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_C_USER0, OVERLAY_B_USER0),
+ mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
+
+ // An overlay should not affect the priority of overlays targeting a different package
+ final OverlayInfo otherTarget = new OverlayInfo(
"com.test.overlay_other",
+ null,
"com.test.some.other.target",
null,
"some-category",
@@ -239,45 +195,36 @@ public class OverlayManagerSettingsTests {
STATE_DISABLED,
0,
0,
- true);
- insert(otherTarget);
- changed = mSettings.setPriority(OVERLAY_A0.packageName, otherTarget.packageName,
- OVERLAY_A0.userId);
- assertFalse(changed);
+ true,
+ false);
+ insertSetting(otherTarget);
+ assertFalse(mSettings.setPriority(OVERLAY_A, otherTarget.getOverlayIdentifier(), USER_0));
}
@Test
public void testSetLowestPriority() throws Exception {
- insert(OVERLAY_A0);
- insert(OVERLAY_B0);
- insert(OVERLAY_C0);
-
- List<OverlayInfo> list =
- mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
- assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
-
- boolean changed = mSettings.setLowestPriority(OVERLAY_B0.packageName, OVERLAY_B0.userId);
- assertTrue(changed);
-
- list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
- assertListsAreEqual(list, OVERLAY_B0, OVERLAY_A0, OVERLAY_C0);
+ insertSetting(OVERLAY_A_USER0);
+ insertSetting(OVERLAY_B_USER0);
+ insertSetting(OVERLAY_C_USER0);
+ assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0),
+ mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
+
+ assertTrue(mSettings.setLowestPriority(OVERLAY_B, USER_0));
+ assertListsAreEqual(List.of(OVERLAY_B_USER0, OVERLAY_A_USER0, OVERLAY_C_USER0),
+ mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
}
@Test
public void testSetHighestPriority() throws Exception {
- insert(OVERLAY_A0);
- insert(OVERLAY_B0);
- insert(OVERLAY_C0);
-
- List<OverlayInfo> list =
- mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
- assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
-
- boolean changed = mSettings.setHighestPriority(OVERLAY_B0.packageName, OVERLAY_B0.userId);
- assertTrue(changed);
-
- list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
- assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0);
+ insertSetting(OVERLAY_A_USER0);
+ insertSetting(OVERLAY_B_USER0);
+ insertSetting(OVERLAY_C_USER0);
+ assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0),
+ mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
+
+ assertTrue(mSettings.setHighestPriority(OVERLAY_B, USER_0));
+ assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_C_USER0, OVERLAY_B_USER0),
+ mSettings.getOverlaysForTarget(OVERLAY_A_USER0.targetPackageName, USER_0));
}
// tests: persist and restore
@@ -294,8 +241,8 @@ public class OverlayManagerSettingsTests {
@Test
public void testPersistDifferentOverlaysSameUser() throws Exception {
- insert(OVERLAY_A0);
- insert(OVERLAY_B0);
+ insertSetting(OVERLAY_A_USER0);
+ insertSetting(OVERLAY_B_USER0);
ByteArrayOutputStream os = new ByteArrayOutputStream();
mSettings.persist(os);
@@ -304,17 +251,17 @@ public class OverlayManagerSettingsTests {
assertEquals(1, countXmlTags(xml, "overlays"));
assertEquals(2, countXmlTags(xml, "item"));
assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName",
- OVERLAY_A0.packageName));
+ OVERLAY_A.getPackageName()));
assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName",
- OVERLAY_B0.packageName));
+ OVERLAY_B.getPackageName()));
assertEquals(2, countXmlAttributesWhere(xml, "item", "userId",
- Integer.toString(OVERLAY_A0.userId)));
+ Integer.toString(USER_0)));
}
@Test
public void testPersistSameOverlayDifferentUsers() throws Exception {
- insert(OVERLAY_A0);
- insert(OVERLAY_A1);
+ insertSetting(OVERLAY_A_USER0);
+ insertSetting(OVERLAY_A_USER1);
ByteArrayOutputStream os = new ByteArrayOutputStream();
mSettings.persist(os);
@@ -323,17 +270,17 @@ public class OverlayManagerSettingsTests {
assertEquals(1, countXmlTags(xml, "overlays"));
assertEquals(2, countXmlTags(xml, "item"));
assertEquals(2, countXmlAttributesWhere(xml, "item", "packageName",
- OVERLAY_A0.packageName));
+ OVERLAY_A.getPackageName()));
assertEquals(1, countXmlAttributesWhere(xml, "item", "userId",
- Integer.toString(OVERLAY_A0.userId)));
+ Integer.toString(USER_0)));
assertEquals(1, countXmlAttributesWhere(xml, "item", "userId",
- Integer.toString(OVERLAY_A1.userId)));
+ Integer.toString(USER_1)));
}
@Test
public void testPersistEnabled() throws Exception {
- insert(OVERLAY_A0);
- mSettings.setEnabled(OVERLAY_A0.packageName, OVERLAY_A0.userId, true);
+ insertSetting(OVERLAY_A_USER0);
+ mSettings.setEnabled(OVERLAY_A, USER_0, true);
ByteArrayOutputStream os = new ByteArrayOutputStream();
mSettings.persist(os);
@@ -351,7 +298,7 @@ public class OverlayManagerSettingsTests {
ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
mSettings.restore(is);
- assertDoesNotContain(mSettings, "com.test.overlay", 0);
+ assertDoesNotContain(mSettings, new OverlayIdentifier("com.test.overlay"), 0);
}
@Test
@@ -361,6 +308,7 @@ public class OverlayManagerSettingsTests {
"<?xml version='1.0' encoding='utf-8' standalone='yes'?>\n"
+ "<overlays version='" + version + "'>\n"
+ "<item packageName='com.test.overlay'\n"
+ + " overlayName='test'\n"
+ " userId='1234'\n"
+ " targetPackageName='com.test.target'\n"
+ " baseCodePath='/data/app/com.test.overlay-1/base.apk'\n"
@@ -373,20 +321,22 @@ public class OverlayManagerSettingsTests {
ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
mSettings.restore(is);
- OverlayInfo oi = mSettings.getOverlayInfo("com.test.overlay", 1234);
+ final OverlayIdentifier identifier = new OverlayIdentifier("com.test.overlay", "test");
+ OverlayInfo oi = mSettings.getOverlayInfo(identifier, 1234);
assertNotNull(oi);
assertEquals("com.test.overlay", oi.packageName);
+ assertEquals("test", oi.overlayName);
assertEquals("com.test.target", oi.targetPackageName);
assertEquals("/data/app/com.test.overlay-1/base.apk", oi.baseCodePath);
assertEquals(1234, oi.userId);
assertEquals(STATE_DISABLED, oi.state);
- assertFalse(mSettings.getEnabled("com.test.overlay", 1234));
+ assertFalse(mSettings.getEnabled(identifier, 1234));
}
@Test
public void testPersistAndRestore() throws Exception {
- insert(OVERLAY_A0);
- insert(OVERLAY_B1);
+ insertSetting(OVERLAY_A_USER0);
+ insertSetting(OVERLAY_B_USER1);
ByteArrayOutputStream os = new ByteArrayOutputStream();
mSettings.persist(os);
@@ -394,11 +344,11 @@ public class OverlayManagerSettingsTests {
OverlayManagerSettings newSettings = new OverlayManagerSettings();
newSettings.restore(is);
- OverlayInfo a = newSettings.getOverlayInfo(OVERLAY_A0.packageName, OVERLAY_A0.userId);
- assertEquals(OVERLAY_A0, a);
+ OverlayInfo a = newSettings.getOverlayInfo(OVERLAY_A, USER_0);
+ assertEquals(OVERLAY_A_USER0, a);
- OverlayInfo b = newSettings.getOverlayInfo(OVERLAY_B1.packageName, OVERLAY_B1.userId);
- assertEquals(OVERLAY_B1, b);
+ OverlayInfo b = newSettings.getOverlayInfo(OVERLAY_B, USER_1);
+ assertEquals(OVERLAY_B_USER1, b);
}
private int countXmlTags(InputStream in, String tagToLookFor) throws Exception {
@@ -433,43 +383,53 @@ public class OverlayManagerSettingsTests {
return count;
}
- private void insert(OverlayInfo oi) throws Exception {
- mSettings.init(oi.packageName, oi.userId, oi.targetPackageName, null, oi.baseCodePath,
- true, false,0, oi.category);
- mSettings.setState(oi.packageName, oi.userId, oi.state);
- mSettings.setEnabled(oi.packageName, oi.userId, false);
+ private void insertSetting(OverlayInfo oi) throws Exception {
+ mSettings.init(oi.getOverlayIdentifier(), oi.userId, oi.targetPackageName, null,
+ oi.baseCodePath, true, false,0, oi.category, oi.isFabricated);
+ mSettings.setState(oi.getOverlayIdentifier(), oi.userId, oi.state);
+ mSettings.setEnabled(oi.getOverlayIdentifier(), oi.userId, false);
}
private static void assertContains(final OverlayManagerSettings settings,
final OverlayInfo oi) {
- assertContains(settings, oi.packageName, oi.userId);
- }
-
- private static void assertContains(final OverlayManagerSettings settings,
- final String packageName, int userId) {
try {
- settings.getOverlayInfo(packageName, userId);
+ settings.getOverlayInfo(oi.getOverlayIdentifier(), oi.userId);
} catch (OverlayManagerSettings.BadKeyException e) {
- fail(String.format("settings does not contain packageName=%s userId=%d",
- packageName, userId));
+ fail(String.format("settings does not contain overlay=%s userId=%d",
+ oi.getOverlayIdentifier(), oi.userId));
}
}
private static void assertDoesNotContain(final OverlayManagerSettings settings,
final OverlayInfo oi) {
- assertDoesNotContain(settings, oi.packageName, oi.userId);
+ assertDoesNotContain(settings, oi.getOverlayIdentifier(), oi.userId);
}
private static void assertDoesNotContain(final OverlayManagerSettings settings,
- final String packageName, int userId) {
+ final OverlayIdentifier overlay, int userId) {
try {
- settings.getOverlayInfo(packageName, userId);
- fail(String.format("settings contains packageName=%s userId=%d", packageName, userId));
+ settings.getOverlayInfo(overlay, userId);
+ fail(String.format("settings contains overlay=%s userId=%d", overlay, userId));
} catch (OverlayManagerSettings.BadKeyException e) {
// do nothing: we expect to end up here
}
}
+ private static OverlayInfo createInfo(@NonNull OverlayIdentifier identifier, int userId) {
+ return new OverlayInfo(
+ identifier.getPackageName(),
+ identifier.getOverlayName(),
+ "com.test.target",
+ null,
+ "some-category",
+ "/data/app/" + identifier + "/base.apk",
+ STATE_DISABLED,
+ userId,
+ 0,
+ true,
+ false);
+ }
+
private static void assertContains(int[] haystack, int needle) {
List<Integer> list = IntStream.of(haystack)
.boxed()
@@ -490,16 +450,11 @@ public class OverlayManagerSettingsTests {
}
}
- private static void assertListsAreEqual(List<OverlayInfo> list, OverlayInfo... array) {
- List<OverlayInfo> other = Stream.of(array)
- .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
- assertListsAreEqual(list, other);
- }
-
- private static void assertListsAreEqual(List<OverlayInfo> list, List<OverlayInfo> other) {
- if (!list.equals(other)) {
+ private static void assertListsAreEqual(
+ @NonNull List<OverlayInfo> expected, @Nullable List<OverlayInfo> actual) {
+ if (!expected.equals(actual)) {
fail(String.format("lists [%s] and [%s] differ",
- TextUtils.join(",", list), TextUtils.join(",", other)));
+ TextUtils.join(",", expected), TextUtils.join(",", actual)));
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
index a112b145fdc9..ecff409ccaf1 100644
--- a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
@@ -16,58 +16,104 @@
package com.android.server.people;
+import static android.app.people.ConversationStatus.ACTIVITY_GAME;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.people.ConversationChannel;
+import android.app.people.ConversationStatus;
+import android.app.people.IConversationListener;
+import android.app.people.IPeopleManager;
+import android.app.people.PeopleManager;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionSessionId;
import android.app.prediction.AppTarget;
import android.app.prediction.IPredictionCallback;
import android.content.Context;
import android.content.pm.ParceledListSlice;
+import android.content.pm.ShortcutInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+
+import androidx.test.InstrumentationRegistry;
import com.android.server.LocalServices;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
-@RunWith(JUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public final class PeopleServiceTest {
-
private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share";
private static final int APP_PREDICTION_TARGET_COUNT = 4;
private static final String TEST_PACKAGE_NAME = "com.example";
private static final int USER_ID = 0;
+ private static final String CONVERSATION_ID_1 = "12";
+ private static final String CONVERSATION_ID_2 = "123";
private PeopleServiceInternal mServiceInternal;
private PeopleService.LocalService mLocalService;
private AppPredictionSessionId mSessionId;
private AppPredictionContext mPredictionContext;
- @Mock private Context mContext;
- @Mock private IPredictionCallback mCallback;
+ @Mock
+ private Context mMockContext;
+
+ @Rule
+ public final TestableContext mContext =
+ new TestableContext(InstrumentationRegistry.getContext(), null);
+
+ protected TestableContext getContext() {
+ return mContext;
+ }
+
+ @Mock
+ private IPredictionCallback mCallback;
+ private TestableLooper mTestableLooper;
+ private final TestLooper mTestLooper = new TestLooper();
+
+ private TestablePeopleService mPeopleService;
+ private IPeopleManager mIPeopleManager;
+ private PeopleManager mPeopleManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mContext.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ mPeopleService = new TestablePeopleService(mContext);
+ mTestableLooper = TestableLooper.get(this);
+ mIPeopleManager = ((IPeopleManager) mPeopleService.mService);
+ mPeopleManager = new PeopleManager(mContext, mIPeopleManager);
+ when(mMockContext.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
when(mCallback.asBinder()).thenReturn(new Binder());
-
PeopleService service = new PeopleService(mContext);
service.onStart(/* isForTesting= */ true);
@@ -75,7 +121,7 @@ public final class PeopleServiceTest {
mLocalService = (PeopleService.LocalService) mServiceInternal;
mSessionId = new AppPredictionSessionId("abc", USER_ID);
- mPredictionContext = new AppPredictionContext.Builder(mContext)
+ mPredictionContext = new AppPredictionContext.Builder(mMockContext)
.setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE)
.setPredictedTargetCount(APP_PREDICTION_TARGET_COUNT)
.setExtras(new Bundle())
@@ -111,4 +157,134 @@ public final class PeopleServiceTest {
mServiceInternal.onDestroyPredictionSession(mSessionId);
}
+
+ @Test
+ public void testRegisterConversationListener() throws Exception {
+ assertEquals(0,
+ mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+
+ mIPeopleManager.registerConversationListener(TEST_PACKAGE_NAME, 0, CONVERSATION_ID_1,
+ new TestableConversationListener());
+ mTestableLooper.processAllMessages();
+ assertEquals(1,
+ mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+
+ mIPeopleManager.registerConversationListener(TEST_PACKAGE_NAME, 0, CONVERSATION_ID_1,
+ new TestableConversationListener());
+ mTestableLooper.processAllMessages();
+ assertEquals(2,
+ mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+
+ mIPeopleManager.registerConversationListener(TEST_PACKAGE_NAME, 0, CONVERSATION_ID_2,
+ new TestableConversationListener());
+ mTestableLooper.processAllMessages();
+ assertEquals(3,
+ mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+ }
+
+ @Test
+ public void testUnregisterConversationListener() throws Exception {
+ TestableConversationListener listener1 = new TestableConversationListener();
+ mIPeopleManager.registerConversationListener(TEST_PACKAGE_NAME, 0, CONVERSATION_ID_1,
+ listener1);
+ TestableConversationListener listener2 = new TestableConversationListener();
+ mIPeopleManager.registerConversationListener(TEST_PACKAGE_NAME, 0, CONVERSATION_ID_1,
+ listener2);
+ TestableConversationListener listener3 = new TestableConversationListener();
+ mIPeopleManager.registerConversationListener(TEST_PACKAGE_NAME, 0, CONVERSATION_ID_2,
+ listener3);
+ mTestableLooper.processAllMessages();
+ assertEquals(3,
+ mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+
+ mIPeopleManager.unregisterConversationListener(
+ listener2);
+ assertEquals(2,
+ mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+ mIPeopleManager.unregisterConversationListener(
+ listener1);
+ assertEquals(1,
+ mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+ mIPeopleManager.unregisterConversationListener(
+ listener3);
+ assertEquals(0,
+ mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+ }
+
+ @Test
+ public void testOnlyTriggersConversationListenersForRegisteredConversation() {
+ PeopleManager.ConversationListener listenerForConversation1 = mock(
+ PeopleManager.ConversationListener.class);
+ registerListener(CONVERSATION_ID_1, listenerForConversation1);
+ PeopleManager.ConversationListener secondListenerForConversation1 = mock(
+ PeopleManager.ConversationListener.class);
+ registerListener(CONVERSATION_ID_1, secondListenerForConversation1);
+ PeopleManager.ConversationListener listenerForConversation2 = mock(
+ PeopleManager.ConversationListener.class);
+ registerListener(CONVERSATION_ID_2, listenerForConversation2);
+ assertEquals(3,
+ mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount());
+
+ // Update conversation with two listeners.
+ ConversationStatus status = new ConversationStatus.Builder(CONVERSATION_ID_1,
+ ACTIVITY_GAME).build();
+ mPeopleService.mConversationListenerHelper.onConversationsUpdate(
+ Arrays.asList(getConversation(CONVERSATION_ID_1, status)));
+ mTestLooper.dispatchAll();
+
+ // Never update listeners for other conversations.
+ verify(listenerForConversation2, never()).onConversationUpdate(any());
+ // Should update both listeners for the conversation.
+ ArgumentCaptor<ConversationChannel> capturedConversation = ArgumentCaptor.forClass(
+ ConversationChannel.class);
+ verify(listenerForConversation1, times(1)).onConversationUpdate(
+ capturedConversation.capture());
+ ConversationChannel conversationChannel = capturedConversation.getValue();
+ verify(secondListenerForConversation1, times(1)).onConversationUpdate(
+ eq(conversationChannel));
+ assertEquals(conversationChannel.getShortcutInfo().getId(), CONVERSATION_ID_1);
+ assertThat(conversationChannel.getStatuses()).containsExactly(status);
+ }
+
+ private void registerListener(String conversationId,
+ PeopleManager.ConversationListener listener) {
+ mPeopleManager.registerConversationListener(mContext.getPackageName(), mContext.getUserId(),
+ conversationId, listener,
+ mTestLooper.getNewExecutor());
+ }
+
+ private ConversationChannel getConversation(String shortcutId, ConversationStatus status) {
+ ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext,
+ shortcutId).setLongLabel(
+ "name").build();
+ NotificationChannel notificationChannel = new NotificationChannel("123",
+ "channel",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ return new ConversationChannel(shortcutInfo, 0,
+ notificationChannel, null,
+ 123L, false, false, Arrays.asList(status));
+ }
+
+ private class TestableConversationListener extends IConversationListener.Stub {
+ @Override
+ public void onConversationUpdate(ConversationChannel conversation) {
+ }
+ }
+
+ // Use a Testable subclass so we can simulate calls from the system without failing.
+ private static class TestablePeopleService extends PeopleService {
+ TestablePeopleService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart(true);
+ }
+
+ @Override
+ protected void enforceSystemRootOrSystemUI(Context context, String message) {
+ return;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 6e5789686c55..7709edbbbb31 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -79,6 +79,7 @@ import android.os.CancellationSignal;
import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.test.TestLooper;
import android.provider.ContactsContract;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
@@ -91,8 +92,11 @@ import com.android.internal.app.ChooserActivity;
import com.android.internal.content.PackageMonitor;
import com.android.server.LocalServices;
import com.android.server.notification.NotificationManagerInternal;
+import com.android.server.people.PeopleService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.google.common.collect.Iterables;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -123,6 +127,7 @@ public final class DataManagerTest {
private static final String TEST_PKG_NAME = "pkg";
private static final String TEST_CLASS_NAME = "class";
private static final String TEST_SHORTCUT_ID = "sc";
+ private static final String TEST_SHORTCUT_ID_2 = "sc2";
private static final int TEST_PKG_UID = 35;
private static final String CONTACT_URI = "content://com.android.contacts/contacts/lookup/123";
private static final String PHONE_NUMBER = "+1234567890";
@@ -157,6 +162,7 @@ public final class DataManagerTest {
private ShortcutChangeCallback mShortcutChangeCallback;
private ShortcutInfo mShortcutInfo;
private TestInjector mInjector;
+ private TestLooper mLooper;
@Before
public void setUp() throws PackageManager.NameNotFoundException {
@@ -237,7 +243,8 @@ public final class DataManagerTest {
mCancellationSignal = new CancellationSignal();
mInjector = new TestInjector();
- mDataManager = new DataManager(mContext, mInjector);
+ mLooper = new TestLooper();
+ mDataManager = new DataManager(mContext, mInjector, mLooper.getLooper());
mDataManager.initialize();
when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
@@ -515,6 +522,84 @@ public final class DataManagerTest {
}
@Test
+ public void testAddConversationsListener() throws Exception {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+ ConversationChannel conversationChannel = mDataManager.getConversation(TEST_PKG_NAME,
+ USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID);
+
+ PeopleService.ConversationsListener listener = mock(
+ PeopleService.ConversationsListener.class);
+ mDataManager.addConversationsListener(listener);
+
+ List<ConversationChannel> changedConversations = Arrays.asList(conversationChannel);
+ verify(listener, times(0)).onConversationsUpdate(eq(changedConversations));
+ mDataManager.notifyConversationsListeners(changedConversations);
+ mLooper.dispatchAll();
+
+ verify(listener, times(1)).onConversationsUpdate(eq(changedConversations));
+ }
+
+ @Test
+ public void testAddConversationListenersNotifiesMultipleConversations() throws Exception {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+ ConversationChannel conversationChannel = mDataManager.getConversation(TEST_PKG_NAME,
+ USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID);
+ ShortcutInfo shortcut2 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID_2,
+ buildPerson());
+ mDataManager.addOrUpdateConversationInfo(shortcut2);
+ ConversationChannel conversationChannel2 = mDataManager.getConversation(TEST_PKG_NAME,
+ USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID_2);
+ PeopleService.ConversationsListener listener = mock(
+ PeopleService.ConversationsListener.class);
+ mDataManager.addConversationsListener(listener);
+
+ List<ConversationChannel> changedConversations = Arrays.asList(conversationChannel,
+ conversationChannel2);
+ verify(listener, times(0)).onConversationsUpdate(eq(changedConversations));
+ mDataManager.notifyConversationsListeners(changedConversations);
+ mLooper.dispatchAll();
+
+ verify(listener, times(1)).onConversationsUpdate(eq(changedConversations));
+ ArgumentCaptor<List<ConversationChannel>> capturedConversation = ArgumentCaptor.forClass(
+ List.class);
+ verify(listener, times(1)).onConversationsUpdate(capturedConversation.capture());
+ assertThat(capturedConversation.getValue()).containsExactly(conversationChannel,
+ conversationChannel2);
+ }
+
+ @Test
+ public void testAddOrUpdateStatusNotifiesConversationsListeners() throws Exception {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+ PeopleService.ConversationsListener listener = mock(
+ PeopleService.ConversationsListener.class);
+ mDataManager.addConversationsListener(listener);
+
+ ConversationStatus status = new ConversationStatus.Builder("cs1", ACTIVITY_GAME).build();
+ mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, status);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<List<ConversationChannel>> capturedConversation = ArgumentCaptor.forClass(
+ List.class);
+ verify(listener, times(1)).onConversationsUpdate(capturedConversation.capture());
+ ConversationChannel result = Iterables.getOnlyElement(capturedConversation.getValue());
+ assertThat(result.getStatuses()).containsExactly(status);
+ assertEquals(result.getShortcutInfo().getId(), TEST_SHORTCUT_ID);
+ }
+
+ @Test
public void testGetConversationReturnsCustomizedConversation() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
@@ -601,6 +686,22 @@ public final class DataManagerTest {
}
@Test
+ public void testIsConversation() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ assertThat(mDataManager.isConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID)).isFalse();
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_PINNED);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+ assertThat(mDataManager.isConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID)).isTrue();
+ assertThat(mDataManager.isConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID + "1")).isFalse();
+ }
+
+ @Test
public void testNotificationChannelCreated() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
mDataManager.onUserUnlocked(USER_ID_SECONDARY);
@@ -959,7 +1060,7 @@ public final class DataManagerTest {
mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
byte[] payload = mDataManager.getBackupPayload(USER_ID_PRIMARY);
- DataManager dataManager = new DataManager(mContext, mInjector);
+ DataManager dataManager = new DataManager(mContext, mInjector, mLooper.getLooper());
dataManager.onUserUnlocked(USER_ID_PRIMARY);
dataManager.restore(USER_ID_PRIMARY, payload);
ConversationInfo conversationInfo = dataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index e6a238a775c6..ba6011140cf1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -814,6 +814,7 @@ public class PackageParserTest {
assertArrayEquals(a.splitSourceDirs, that.splitSourceDirs);
assertArrayEquals(a.splitPublicSourceDirs, that.splitPublicSourceDirs);
assertArrayEquals(a.resourceDirs, that.resourceDirs);
+ assertArrayEquals(a.overlayPaths, that.overlayPaths);
assertEquals(a.seInfo, that.seInfo);
assertArrayEquals(a.sharedLibraryFiles, that.sharedLibraryFiles);
assertEquals(a.dataDir, that.dataDir);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 938e4cc84e62..7297622b523f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -21,11 +21,14 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
import android.content.pm.PackageManager;
import android.content.pm.PackageUserState;
import android.content.pm.SuspendDialogInfo;
+import android.content.pm.overlay.OverlayPaths;
import android.os.PersistableBundle;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -346,4 +349,40 @@ public class PackageUserStateTest {
assertLastPackageUsageSet(
testState6, PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT - 1, 60L);
}
+
+ @Test
+ public void testOverlayPaths() {
+ final PackageUserState testState = new PackageUserState();
+ assertFalse(testState.setOverlayPaths(null));
+ assertFalse(testState.setOverlayPaths(new OverlayPaths.Builder().build()));
+
+ assertTrue(testState.setOverlayPaths(new OverlayPaths.Builder()
+ .addApkPath("/path/to/some.apk").build()));
+ assertFalse(testState.setOverlayPaths(new OverlayPaths.Builder()
+ .addApkPath("/path/to/some.apk").build()));
+
+ assertTrue(testState.setOverlayPaths(new OverlayPaths.Builder().build()));
+ assertFalse(testState.setOverlayPaths(null));
+ }
+ @Test
+ public void testSharedLibOverlayPaths() {
+ final PackageUserState testState = new PackageUserState();
+ final String LIB_ONE = "lib1";
+ final String LIB_TW0 = "lib2";
+ assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, null));
+ assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE,
+ new OverlayPaths.Builder().build()));
+
+ assertTrue(testState.setSharedLibraryOverlayPaths(LIB_ONE, new OverlayPaths.Builder()
+ .addApkPath("/path/to/some.apk").build()));
+ assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, new OverlayPaths.Builder()
+ .addApkPath("/path/to/some.apk").build()));
+ assertTrue(testState.setSharedLibraryOverlayPaths(LIB_TW0, new OverlayPaths.Builder()
+ .addApkPath("/path/to/some.apk").build()));
+
+ assertTrue(testState.setSharedLibraryOverlayPaths(LIB_ONE,
+ new OverlayPaths.Builder().build()));
+ assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, null));
+ }
+
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 23fcf701a8bb..4d0beef99cca 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -103,7 +103,6 @@ import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.frameworks.servicestests.R;
-import com.android.internal.util.FastXmlSerializer;
import com.android.server.pm.ShortcutService.ConfigConstants;
import com.android.server.pm.ShortcutService.FileOutputStreamWithPath;
import com.android.server.pm.ShortcutUser.PackageWithUser;
@@ -111,7 +110,6 @@ import com.android.server.pm.ShortcutUser.PackageWithUser;
import org.mockito.ArgumentCaptor;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -141,6 +139,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
private static final int CACHE_OWNER_0 = LauncherApps.FLAG_CACHE_NOTIFICATION_SHORTCUTS;
private static final int CACHE_OWNER_1 = LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS;
+ private static final int CACHE_OWNER_2 = LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS;
@Override
protected void tearDown() throws Exception {
@@ -1531,7 +1530,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
- makeLongLivedShortcut("s4"))));
+ makeLongLivedShortcut("s4"), makeLongLivedShortcut("s5"),
+ makeLongLivedShortcut("s6"))));
});
// Pin s2
@@ -1545,28 +1545,30 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"),
HANDLE_USER_0, CACHE_OWNER_0);
- mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2", "s4"),
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2", "s4", "s5"),
HANDLE_USER_0, CACHE_OWNER_1);
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s5", "s6"),
+ HANDLE_USER_0, CACHE_OWNER_2);
});
setCaller(CALLING_PACKAGE_1);
// Get dynamic shortcuts
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC),
- "s1", "s2", "s3", "s4");
+ "s1", "s2", "s3", "s4", "s5", "s6");
// Get pinned shortcuts
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_PINNED),
"s2");
// Get cached shortcuts
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
- "s2", "s4");
+ "s2", "s4", "s5", "s6");
// Remove a dynamic cached shortcut
- mManager.removeDynamicShortcuts(list("s4"));
+ mManager.removeDynamicShortcuts(list("s4", "s5"));
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC),
- "s1", "s2", "s3");
+ "s1", "s2", "s3", "s6");
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
- "s2", "s4");
+ "s2", "s4", "s5", "s6");
runWithCaller(LAUNCHER_1, USER_0, () -> {
mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s2", "s4"),
@@ -1574,15 +1576,21 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
// s2 still cached by owner1. s4 wasn't cached by owner0 so didn't get removed.
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
- "s2", "s4");
+ "s2", "s4", "s5", "s6");
// uncache a non-dynamic shortcut. Should be removed.
runWithCaller(LAUNCHER_1, USER_0, () -> {
mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s4"),
HANDLE_USER_0, CACHE_OWNER_1);
});
+
+ // uncache s6 by its only owner. s5 still cached by owner1
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s5", "s6"),
+ HANDLE_USER_0, CACHE_OWNER_2);
+ });
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
- "s2");
+ "s2", "s5");
// Cache another shortcut
runWithCaller(LAUNCHER_1, USER_0, () -> {
@@ -1590,14 +1598,14 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
HANDLE_USER_0, CACHE_OWNER_0);
});
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
- "s2", "s3");
+ "s2", "s3", "s5");
// Remove a dynamic cached pinned long lived shortcut
mManager.removeLongLivedShortcuts(list("s2"));
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC),
- "s1", "s3");
+ "s1", "s3", "s6");
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
- "s3");
+ "s3", "s5");
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_PINNED),
"s2");
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
index 62b6c7165c25..b21b04979424 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -221,11 +221,13 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
// This command is deprecated. Will remove the test later.
public void testLauncherCommands() throws Exception {
+ prepareGetRoleHoldersAsUser(getSystemLauncher().activityInfo.packageName, USER_0);
prepareGetHomeActivitiesAsUser(
/* preferred */ getSystemLauncher().activityInfo.getComponentName(),
list(getSystemLauncher(), getFallbackLauncher()),
USER_0);
+ prepareGetRoleHoldersAsUser(CALLING_PACKAGE_2, USER_10);
prepareGetHomeActivitiesAsUser(
/* preferred */ cn(CALLING_PACKAGE_2, "name"),
list(getSystemLauncher(), getFallbackLauncher(),
@@ -247,6 +249,7 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
"Launcher: ComponentInfo{com.android.test.2/name}");
// Change user-0's launcher.
+ prepareGetRoleHoldersAsUser(CALLING_PACKAGE_1, USER_0);
prepareGetHomeActivitiesAsUser(
/* preferred */ cn(CALLING_PACKAGE_1, "name"),
list(ri(CALLING_PACKAGE_1, "name", false, 0)),
diff --git a/services/tests/servicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/StagingManagerTest.java
deleted file mode 100644
index 79935c23774f..000000000000
--- a/services/tests/servicestests/src/com/android/server/pm/StagingManagerTest.java
+++ /dev/null
@@ -1,135 +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.server.pm;
-
-import android.content.Context;
-import android.content.pm.PackageInstaller;
-import android.os.storage.StorageManager;
-import android.platform.test.annotations.Presubmit;
-
-import com.android.internal.os.BackgroundThread;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.util.function.Predicate;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-
-@Presubmit
-@RunWith(JUnit4.class)
-public class StagingManagerTest {
- @Rule
- public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
-
- private File mTmpDir;
- private StagingManager mStagingManager;
-
- @Before
- public void setup() throws Exception {
- MockitoAnnotations.initMocks(this);
- StorageManager storageManager = Mockito.mock(StorageManager.class);
- Context context = Mockito.mock(Context.class);
- when(storageManager.isCheckpointSupported()).thenReturn(true);
- when(context.getSystemService(eq(Context.POWER_SERVICE))).thenReturn(null);
- when(context.getSystemService(eq(Context.STORAGE_SERVICE))).thenReturn(storageManager);
-
- mTmpDir = mTemporaryFolder.newFolder("StagingManagerTest");
- mStagingManager = new StagingManager(context, null);
- }
-
- /**
- * Tests that sessions committed later shouldn't cause earlier ones to fail the overlapping
- * check.
- */
- @Test
- public void checkNonOverlappingWithStagedSessions_laterSessionShouldNotFailEarlierOnes()
- throws Exception {
- // Create 2 sessions with overlapping packages
- StagingManager.StagedSession session1 = createSession(111, "com.foo", 1);
- StagingManager.StagedSession session2 = createSession(222, "com.foo", 2);
-
- mStagingManager.createSession(session1);
- mStagingManager.createSession(session2);
- // Session1 should not fail in spite of the overlapping packages
- mStagingManager.checkNonOverlappingWithStagedSessions(session1);
- // Session2 should fail due to overlapping packages
- assertThrows(PackageManagerException.class,
- () -> mStagingManager.checkNonOverlappingWithStagedSessions(session2));
- }
-
- private StagingManager.StagedSession createSession(int sessionId, String packageName,
- long committedMillis) {
- PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
- PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- params.isStaged = true;
-
- InstallSource installSource = InstallSource.create("testInstallInitiator",
- "testInstallOriginator", "testInstaller", "testAttributionTag");
-
- PackageInstallerSession session = new PackageInstallerSession(
- /* callback */ null,
- /* context */ null,
- /* pm */ null,
- /* sessionProvider */ null,
- /* looper */ BackgroundThread.getHandler().getLooper(),
- /* stagingManager */ null,
- /* sessionId */ sessionId,
- /* userId */ 456,
- /* installerUid */ -1,
- /* installSource */ installSource,
- /* sessionParams */ params,
- /* createdMillis */ 0L,
- /* committedMillis */ committedMillis,
- /* stageDir */ mTmpDir,
- /* stageCid */ null,
- /* files */ null,
- /* checksums */ null,
- /* prepared */ true,
- /* committed */ true,
- /* destroyed */ false,
- /* sealed */ false, // Setting to true would trigger some PM logic.
- /* childSessionIds */ null,
- /* parentSessionId */ -1,
- /* isReady */ false,
- /* isFailed */ false,
- /* isApplied */false,
- /* stagedSessionErrorCode */ PackageInstaller.SessionInfo.STAGED_SESSION_NO_ERROR,
- /* stagedSessionErrorMessage */ "no error");
-
- StagingManager.StagedSession stagedSession = spy(session.mStagedSession);
- doReturn(packageName).when(stagedSession).getPackageName();
- doAnswer(invocation -> {
- Predicate<StagingManager.StagedSession> filter = invocation.getArgument(0);
- return filter.test(stagedSession);
- }).when(stagedSession).sessionContains(any());
- return stagedSession;
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index 0dcd608e6bbd..a1b2f38af473 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -39,6 +39,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.servicestests.R;
import com.android.server.pm.PackageManagerException;
import com.android.server.pm.parsing.TestPackageParser2;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.ParsedPackage;
@@ -55,6 +56,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -64,6 +67,9 @@ import java.util.zip.ZipOutputStream;
public class DexMetadataHelperTest {
private static final String APK_FILE_EXTENSION = ".apk";
private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
+ private static final String DEX_METADATA_PACKAGE_NAME =
+ "com.android.frameworks.servicestests.install_split";
+ private static long DEX_METADATA_VERSION_CODE = 30;
@Rule
public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
@@ -76,12 +82,46 @@ public class DexMetadataHelperTest {
}
private File createDexMetadataFile(String apkFileName) throws IOException {
+ return createDexMetadataFile(apkFileName, /*validManifest=*/true);
+ }
+
+ private File createDexMetadataFile(String apkFileName, boolean validManifest) throws IOException
+ {
+ return createDexMetadataFile(apkFileName,DEX_METADATA_PACKAGE_NAME,
+ DEX_METADATA_VERSION_CODE, /*emptyManifest=*/false, validManifest);
+ }
+
+ private File createDexMetadataFile(String apkFileName, String packageName, Long versionCode,
+ boolean emptyManifest, boolean validManifest) throws IOException {
File dmFile = new File(mTmpDir, apkFileName.replace(APK_FILE_EXTENSION,
DEX_METADATA_FILE_EXTENSION));
try (FileOutputStream fos = new FileOutputStream(dmFile)) {
try (ZipOutputStream zipOs = new ZipOutputStream(fos)) {
zipOs.putNextEntry(new ZipEntry("primary.prof"));
zipOs.closeEntry();
+
+ if (validManifest) {
+ zipOs.putNextEntry(new ZipEntry("manifest.json"));
+ if (!emptyManifest) {
+ String manifestStr = "{";
+
+ if (packageName != null) {
+ manifestStr += "\"packageName\": " + "\"" + packageName + "\"";
+
+ if (versionCode != null) {
+ manifestStr += ", ";
+ }
+ }
+ if (versionCode != null) {
+ manifestStr += " \"versionCode\": " + versionCode;
+ }
+
+ manifestStr += "}";
+ byte[] bytes = manifestStr.getBytes(StandardCharsets.UTF_8);
+ zipOs.write(bytes, /*off=*/0, /*len=*/bytes.length);
+ }
+ zipOs.closeEntry();
+ }
}
}
return dmFile;
@@ -96,17 +136,38 @@ public class DexMetadataHelperTest {
return outFile;
}
+ private static void validatePackageDexMetadata(AndroidPackage pkg, boolean requireManifest)
+ throws PackageParserException {
+ Collection<String> apkToDexMetadataList =
+ AndroidPackageUtils.getPackageDexMetadata(pkg).values();
+ String packageName = pkg.getPackageName();
+ long versionCode = pkg.toAppInfoWithoutState().longVersionCode;
+ for (String dexMetadata : apkToDexMetadataList) {
+ DexMetadataHelper.validateDexMetadataFile(
+ dexMetadata, packageName, versionCode, requireManifest);
+ }
+ }
+
+ private static void validatePackageDexMetatadataVaryingRequireManifest(ParsedPackage pkg)
+ throws PackageParserException {
+ validatePackageDexMetadata(pkg, /*requireManifest=*/true);
+ validatePackageDexMetadata(pkg, /*requireManifest=*/false);
+ }
+
@Test
public void testParsePackageWithDmFileValid() throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk");
- ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, 0 /* flags */, false);
+ ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
Map<String, String> packageDexMetadata = AndroidPackageUtils.getPackageDexMetadata(pkg);
assertEquals(1, packageDexMetadata.size());
String baseDexMetadata = packageDexMetadata.get(pkg.getBaseApkPath());
assertNotNull(baseDexMetadata);
assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.getBaseApkPath()));
+
+ // Should throw no exceptions.
+ validatePackageDexMetatadataVaryingRequireManifest(pkg);
}
@Test
@@ -116,7 +177,7 @@ public class DexMetadataHelperTest {
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
createDexMetadataFile("install_split_base.apk");
createDexMetadataFile("install_split_feature_a.apk");
- ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, 0 /* flags */, false);
+ ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
Map<String, String> packageDexMetadata = AndroidPackageUtils.getPackageDexMetadata(pkg);
assertEquals(2, packageDexMetadata.size());
@@ -127,6 +188,9 @@ public class DexMetadataHelperTest {
String splitDexMetadata = packageDexMetadata.get(pkg.getSplitCodePaths()[0]);
assertNotNull(splitDexMetadata);
assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.getSplitCodePaths()[0]));
+
+ // Should throw no exceptions.
+ validatePackageDexMetatadataVaryingRequireManifest(pkg);
}
@Test
@@ -135,7 +199,7 @@ public class DexMetadataHelperTest {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
createDexMetadataFile("install_split_feature_a.apk");
- ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, 0 /* flags */, false);
+ ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
Map<String, String> packageDexMetadata = AndroidPackageUtils.getPackageDexMetadata(pkg);
assertEquals(1, packageDexMetadata.size());
@@ -143,6 +207,9 @@ public class DexMetadataHelperTest {
String splitDexMetadata = packageDexMetadata.get(pkg.getSplitCodePaths()[0]);
assertNotNull(splitDexMetadata);
assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.getSplitCodePaths()[0]));
+
+ // Should throw no exceptions.
+ validatePackageDexMetatadataVaryingRequireManifest(pkg);
}
@Test
@@ -151,9 +218,17 @@ public class DexMetadataHelperTest {
File invalidDmFile = new File(mTmpDir, "install_split_base.dm");
Files.createFile(invalidDmFile.toPath());
try {
- ParsedPackage pkg = new TestPackageParser2()
- .parsePackage(mTmpDir, 0 /* flags */, false);
- AndroidPackageUtils.validatePackageDexMetadata(pkg);
+ ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
+ validatePackageDexMetadata(pkg, /*requireManifest=*/true);
+ fail("Should fail validation: empty .dm file");
+ } catch (PackageParserException e) {
+ assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
+ }
+
+ try {
+ ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
+ validatePackageDexMetadata(pkg, /*requireManifest=*/false);
+ fail("Should fail validation: empty .dm file");
} catch (PackageParserException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
}
@@ -169,9 +244,112 @@ public class DexMetadataHelperTest {
Files.createFile(invalidDmFile.toPath());
try {
- ParsedPackage pkg = new TestPackageParser2()
- .parsePackage(mTmpDir, 0 /* flags */, false);
- AndroidPackageUtils.validatePackageDexMetadata(pkg);
+ ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
+ validatePackageDexMetadata(pkg, /*requireManifest=*/true);
+ fail("Should fail validation: empty .dm file");
+ } catch (PackageParserException e) {
+ assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
+ }
+
+ try {
+ ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
+ validatePackageDexMetadata(pkg, /*requireManifest=*/false);
+ fail("Should fail validation: empty .dm file");
+ } catch (PackageParserException e) {
+ assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
+ }
+ }
+
+ @Test
+ public void testParsePackageWithDmFileInvalidManifest()
+ throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ createDexMetadataFile("install_split_base.apk", /*validManifest=*/false);
+
+ try {
+ ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
+ validatePackageDexMetadata(pkg, /*requireManifest=*/true);
+ fail("Should fail validation: missing manifest.json in the .dm archive");
+ } catch (PackageParserException e) {
+ assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
+ }
+ }
+
+ @Test
+ public void testParsePackageWithDmFileEmptyManifest()
+ throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ createDexMetadataFile("install_split_base.apk", /*packageName=*/"doesn't matter",
+ /*versionCode=*/-12345L, /*emptyManifest=*/true, /*validManifest=*/true);
+
+ try {
+ ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
+ validatePackageDexMetadata(pkg, /*requireManifest=*/true);
+ fail("Should fail validation: empty manifest.json in the .dm archive");
+ } catch (PackageParserException e) {
+ assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
+ }
+ }
+
+ @Test
+ public void testParsePackageWithDmFileBadPackageName()
+ throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ createDexMetadataFile("install_split_base.apk", /*packageName=*/"bad package name",
+ DEX_METADATA_VERSION_CODE, /*emptyManifest=*/false, /*validManifest=*/true);
+
+ try {
+ ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
+ validatePackageDexMetadata(pkg, /*requireManifest=*/true);
+ fail("Should fail validation: bad package name in the .dm archive");
+ } catch (PackageParserException e) {
+ assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
+ }
+ }
+
+ @Test
+ public void testParsePackageWithDmFileBadVersionCode()
+ throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ createDexMetadataFile("install_split_base.apk", DEX_METADATA_PACKAGE_NAME,
+ /*versionCode=*/12345L, /*emptyManifest=*/false, /*validManifest=*/true);
+
+ try {
+ ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
+ validatePackageDexMetadata(pkg, /*requireManifest=*/true);
+ fail("Should fail validation: bad version code in the .dm archive");
+ } catch (PackageParserException e) {
+ assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
+ }
+ }
+
+ @Test
+ public void testParsePackageWithDmFileMissingPackageName()
+ throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ createDexMetadataFile("install_split_base.apk", /*packageName=*/null,
+ DEX_METADATA_VERSION_CODE, /*emptyManifest=*/false, /*validManifest=*/true);
+
+ try {
+ ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
+ validatePackageDexMetadata(pkg, /*requireManifest=*/true);
+ fail("Should fail validation: missing package name in the .dm archive");
+ } catch (PackageParserException e) {
+ assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
+ }
+ }
+
+ @Test
+ public void testParsePackageWithDmFileMissingVersionCode()
+ throws IOException, PackageParserException {
+ copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+ createDexMetadataFile("install_split_base.apk", DEX_METADATA_PACKAGE_NAME,
+ /*versionCode=*/null, /*emptyManifest=*/false, /*validManifest=*/true);
+
+ try {
+ ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
+ validatePackageDexMetadata(pkg, /*requireManifest=*/true);
+ fail("Should fail validation: missing version code in the .dm archive");
} catch (PackageParserException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
}
@@ -184,7 +362,7 @@ public class DexMetadataHelperTest {
try {
DexMetadataHelper.validateDexPaths(mTmpDir.list());
- fail("Should fail validation");
+ fail("Should fail validation: .dm filename has no match against .apk");
} catch (IllegalStateException e) {
// expected.
}
@@ -200,7 +378,7 @@ public class DexMetadataHelperTest {
try {
DexMetadataHelper.validateDexPaths(mTmpDir.list());
- fail("Should fail validation");
+ fail("Should fail validation: split .dm filename unmatched against .apk");
} catch (IllegalStateException e) {
// expected.
}
@@ -211,7 +389,7 @@ public class DexMetadataHelperTest {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
final File dm = createDexMetadataFile("install_split_base.apk");
final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
- ParseTypeImpl.forDefaultParsing().reset(), mTmpDir, 0 /* flags */);
+ ParseTypeImpl.forDefaultParsing().reset(), mTmpDir, /*flags=*/0);
if (result.isError()) {
throw new IllegalStateException(result.getErrorMessage(), result.getException());
}
@@ -228,7 +406,7 @@ public class DexMetadataHelperTest {
try (FileInputStream is = new FileInputStream(base)) {
final ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(
ParseTypeImpl.forDefaultParsing().reset(), is.getFD(),
- base.getAbsolutePath(), /* flags */ 0);
+ base.getAbsolutePath(), /*flags=*/0);
if (result.isError()) {
throw new PackageManagerException(result.getErrorCode(),
result.getErrorMessage(), result.getException());
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index 22020ad45666..bc84e350a329 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -96,7 +96,7 @@ public class DexoptOptionsTests {
int[] reasons = new int[] {
PackageManagerService.REASON_FIRST_BOOT,
- PackageManagerService.REASON_BOOT,
+ PackageManagerService.REASON_POST_BOOT,
PackageManagerService.REASON_INSTALL,
PackageManagerService.REASON_BACKGROUND_DEXOPT,
PackageManagerService.REASON_AB_OTA,
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index 61252c473530..ff0aec71a0df 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -248,6 +248,7 @@ open class AndroidPackageParsingTestBase {
.ignored("Deferred pre-R, but assigned immediately in R")}
requiresSmallestWidthDp=${this.requiresSmallestWidthDp}
resourceDirs=${this.resourceDirs?.contentToString()}
+ overlayPaths=${this.overlayPaths?.contentToString()}
roundIconRes=${this.roundIconRes}
scanPublicSourceDir=${this.scanPublicSourceDir
.ignored("Deferred pre-R, but assigned immediately in R")}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
new file mode 100644
index 000000000000..70d85b6e0411
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
@@ -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.server.pm.parsing.library;
+
+import android.os.Build;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link AndroidNetIpSecIkeUpdater}
+ */
+@Presubmit
+@SmallTest
+@RunWith(JUnit4.class)
+public class AndroidNetIpSecIkeUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+ @Test
+ public void otherUsesLibraries() {
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesLibrary("other")
+ .addUsesOptionalLibrary("optional")
+ .addUsesLibrary("android.net.ipsec.ike")
+ .hideAsParsed());
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.O)
+ .addUsesLibrary("other")
+ .addUsesOptionalLibrary("optional")
+ .hideAsParsed())
+ .hideAsFinal();
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void in_usesLibraries() {
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesLibrary("android.net.ipsec.ike")
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .hideAsParsed())
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void in_usesOptionalLibraries() {
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesOptionalLibrary("android.net.ipsec.ike")
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .hideAsParsed())
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+ checkBackwardsCompatibility(before, after, AndroidNetIpSecIkeUpdater::new);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
index 09c8142105cc..9768f176ea85 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
@@ -165,6 +165,23 @@ public class PackageBackwardCompatibilityTest extends PackageSharedLibraryUpdate
checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal());
}
+ /**
+ * Ensures that the {@link PackageBackwardCompatibility} uses a
+ * {@link AndroidNetIpSecIkeUpdater}.
+ */
+ @Test
+ public void android_net_ipsec_ike_in_usesLibraries() {
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .addUsesLibrary("android.net.ipsec.ike")
+ .hideAsParsed());
+
+ ParsingPackage after = PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT);
+
+ checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal());
+ }
+
private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance);
}
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 03e60afe4489..ddbe81c81d6d 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -16,6 +16,7 @@
package com.android.server.powerstats;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -34,6 +35,9 @@ import androidx.test.InstrumentationRegistry;
import com.android.server.SystemService;
import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
+import com.android.server.powerstats.ProtoStreamUtils.ChannelUtils;
+import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerUtils;
+import com.android.server.powerstats.ProtoStreamUtils.PowerEntityUtils;
import com.android.server.powerstats.nano.PowerEntityProto;
import com.android.server.powerstats.nano.PowerStatsServiceMeterProto;
import com.android.server.powerstats.nano.PowerStatsServiceModelProto;
@@ -52,6 +56,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
+import java.util.Arrays;
import java.util.Random;
/**
@@ -63,15 +68,18 @@ import java.util.Random;
public class PowerStatsServiceTest {
private static final String TAG = PowerStatsServiceTest.class.getSimpleName();
private static final String DATA_STORAGE_SUBDIR = "powerstatstest";
- private static final String METER_FILENAME = "metertest";
- private static final String MODEL_FILENAME = "modeltest";
- private static final String RESIDENCY_FILENAME = "residencytest";
+ private static final String METER_FILENAME = "log.powerstats.metertest.0";
+ private static final String MODEL_FILENAME = "log.powerstats.modeltest.0";
+ private static final String RESIDENCY_FILENAME = "log.powerstats.residencytest.0";
private static final String PROTO_OUTPUT_FILENAME = "powerstats.proto";
private static final String CHANNEL_NAME = "channelname";
private static final String CHANNEL_SUBSYSTEM = "channelsubsystem";
private static final String POWER_ENTITY_NAME = "powerentityinfo";
private static final String STATE_NAME = "stateinfo";
private static final String ENERGY_CONSUMER_NAME = "energyconsumer";
+ private static final String METER_CACHE_FILENAME = "meterCacheTest";
+ private static final String MODEL_CACHE_FILENAME = "modelCacheTest";
+ private static final String RESIDENCY_CACHE_FILENAME = "residencyCacheTest";
private static final int ENERGY_METER_COUNT = 8;
private static final int ENERGY_CONSUMER_COUNT = 2;
private static final int ENERGY_CONSUMER_ATTRIBUTION_COUNT = 5;
@@ -90,12 +98,12 @@ public class PowerStatsServiceTest {
private TestPowerStatsHALWrapper mTestPowerStatsHALWrapper = new TestPowerStatsHALWrapper();
@Override
File createDataStoragePath() {
- mDataStorageDir = null;
-
- try {
- mDataStorageDir = Files.createTempDirectory(DATA_STORAGE_SUBDIR).toFile();
- } catch (IOException e) {
- fail("Could not create temp directory.");
+ if (mDataStorageDir == null) {
+ try {
+ mDataStorageDir = Files.createTempDirectory(DATA_STORAGE_SUBDIR).toFile();
+ } catch (IOException e) {
+ fail("Could not create temp directory.");
+ }
}
return mDataStorageDir;
@@ -117,16 +125,36 @@ public class PowerStatsServiceTest {
}
@Override
+ String createMeterCacheFilename() {
+ return METER_CACHE_FILENAME;
+ }
+
+ @Override
+ String createModelCacheFilename() {
+ return MODEL_CACHE_FILENAME;
+ }
+
+ @Override
+ String createResidencyCacheFilename() {
+ return RESIDENCY_CACHE_FILENAME;
+ }
+
+ @Override
IPowerStatsHALWrapper getPowerStatsHALWrapperImpl() {
return mTestPowerStatsHALWrapper;
}
@Override
PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
- String meterFilename, String modelFilename, String residencyFilename,
+ String meterFilename, String meterCacheFilename,
+ String modelFilename, String modelCacheFilename,
+ String residencyFilename, String residencyCacheFilename,
IPowerStatsHALWrapper powerStatsHALWrapper) {
- mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath, meterFilename,
- modelFilename, residencyFilename, powerStatsHALWrapper);
+ mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath,
+ meterFilename, meterCacheFilename,
+ modelFilename, modelCacheFilename,
+ residencyFilename, residencyCacheFilename,
+ powerStatsHALWrapper);
return mPowerStatsLogger;
}
@@ -665,4 +693,315 @@ public class PowerStatsServiceTest {
// input buffer had only length and no data.
assertTrue(pssProto.stateResidencyResult.length == 0);
}
+
+ @Test
+ public void testDataStorageDeletedMeterMismatch() throws IOException {
+ // Create the directory where cached data will be stored.
+ mInjector.createDataStoragePath();
+
+ // In order to create cached data that will match the current data read by the
+ // PowerStatsService we need to write valid data from the TestPowerStatsHALWrapper that is
+ // returned from the Injector.
+ IPowerStatsHALWrapper powerStatsHALWrapper = mInjector.getPowerStatsHALWrapperImpl();
+
+ // Generate random array of bytes to emulate cached meter data. Store to file.
+ Random rd = new Random();
+ byte[] bytes = new byte[100];
+ rd.nextBytes(bytes);
+ File onDeviceStorageFile = new File(mDataStorageDir, mInjector.createMeterCacheFilename());
+ FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
+
+ // Create cached energy consumer data and write to file.
+ EnergyConsumer[] energyConsumers = powerStatsHALWrapper.getEnergyConsumerInfo();
+ bytes = EnergyConsumerUtils.getProtoBytes(energyConsumers);
+ onDeviceStorageFile = new File(mDataStorageDir, mInjector.createModelCacheFilename());
+ onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
+
+ // Create cached power entity info data and write to file.
+ PowerEntity[] powerEntityInfo = powerStatsHALWrapper.getPowerEntityInfo();
+ bytes = PowerEntityUtils.getProtoBytes(powerEntityInfo);
+ onDeviceStorageFile = new File(mDataStorageDir, mInjector.createResidencyCacheFilename());
+ onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
+
+ // Create log files.
+ File meterFile = new File(mDataStorageDir, mInjector.createMeterFilename());
+ File modelFile = new File(mDataStorageDir, mInjector.createModelFilename());
+ File residencyFile = new File(mDataStorageDir, mInjector.createResidencyFilename());
+ meterFile.createNewFile();
+ modelFile.createNewFile();
+ residencyFile.createNewFile();
+
+ // Verify log files exist.
+ assertTrue(meterFile.exists());
+ assertTrue(modelFile.exists());
+ assertTrue(residencyFile.exists());
+
+ // Boot device after creating old cached data.
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Since cached meter data is just random bytes it won't match the data read from the HAL.
+ // This mismatch of cached and current HAL data should force a delete.
+ assertTrue(mService.getDeleteMeterDataOnBoot());
+ assertFalse(mService.getDeleteModelDataOnBoot());
+ assertFalse(mService.getDeleteResidencyDataOnBoot());
+
+ // Verify log files were deleted.
+ assertFalse(meterFile.exists());
+ assertTrue(modelFile.exists());
+ assertTrue(residencyFile.exists());
+
+ // Verify cached meter data was updated to new HAL output.
+ Channel[] channels = powerStatsHALWrapper.getEnergyMeterInfo();
+ byte[] bytesExpected = ChannelUtils.getProtoBytes(channels);
+ onDeviceStorageFile = new File(mDataStorageDir, mInjector.createMeterCacheFilename());
+ byte[] bytesActual = new byte[(int) onDeviceStorageFile.length()];
+ FileInputStream onDeviceStorageFis = new FileInputStream(onDeviceStorageFile);
+ onDeviceStorageFis.read(bytesActual);
+ assertTrue(Arrays.equals(bytesExpected, bytesActual));
+ }
+
+ @Test
+ public void testDataStorageDeletedModelMismatch() throws IOException {
+ // Create the directory where cached data will be stored.
+ mInjector.createDataStoragePath();
+
+ // In order to create cached data that will match the current data read by the
+ // PowerStatsService we need to write valid data from the TestPowerStatsHALWrapper that is
+ // returned from the Injector.
+ IPowerStatsHALWrapper powerStatsHALWrapper = mInjector.getPowerStatsHALWrapperImpl();
+
+ // Create cached channel data and write to file.
+ Channel[] channels = powerStatsHALWrapper.getEnergyMeterInfo();
+ byte[] bytes = ChannelUtils.getProtoBytes(channels);
+ File onDeviceStorageFile = new File(mDataStorageDir, mInjector.createMeterCacheFilename());
+ FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
+
+ // Generate random array of bytes to emulate cached energy consumer data. Store to file.
+ Random rd = new Random();
+ bytes = new byte[100];
+ rd.nextBytes(bytes);
+ onDeviceStorageFile = new File(mDataStorageDir, mInjector.createModelCacheFilename());
+ onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
+
+ // Create cached power entity info data and write to file.
+ PowerEntity[] powerEntityInfo = powerStatsHALWrapper.getPowerEntityInfo();
+ bytes = PowerEntityUtils.getProtoBytes(powerEntityInfo);
+ onDeviceStorageFile = new File(mDataStorageDir, mInjector.createResidencyCacheFilename());
+ onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
+
+ // Create log files.
+ File meterFile = new File(mDataStorageDir, mInjector.createMeterFilename());
+ File modelFile = new File(mDataStorageDir, mInjector.createModelFilename());
+ File residencyFile = new File(mDataStorageDir, mInjector.createResidencyFilename());
+ meterFile.createNewFile();
+ modelFile.createNewFile();
+ residencyFile.createNewFile();
+
+ // Verify log files exist.
+ assertTrue(meterFile.exists());
+ assertTrue(modelFile.exists());
+ assertTrue(residencyFile.exists());
+
+ // Boot device after creating old cached data.
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Since cached energy consumer data is just random bytes it won't match the data read from
+ // the HAL. This mismatch of cached and current HAL data should force a delete.
+ assertFalse(mService.getDeleteMeterDataOnBoot());
+ assertTrue(mService.getDeleteModelDataOnBoot());
+ assertFalse(mService.getDeleteResidencyDataOnBoot());
+
+ // Verify log files were deleted.
+ assertTrue(meterFile.exists());
+ assertFalse(modelFile.exists());
+ assertTrue(residencyFile.exists());
+
+ // Verify cached energy consumer data was updated to new HAL output.
+ EnergyConsumer[] energyConsumers = powerStatsHALWrapper.getEnergyConsumerInfo();
+ byte[] bytesExpected = EnergyConsumerUtils.getProtoBytes(energyConsumers);
+ onDeviceStorageFile = new File(mDataStorageDir, mInjector.createModelCacheFilename());
+ byte[] bytesActual = new byte[(int) onDeviceStorageFile.length()];
+ FileInputStream onDeviceStorageFis = new FileInputStream(onDeviceStorageFile);
+ onDeviceStorageFis.read(bytesActual);
+ assertTrue(Arrays.equals(bytesExpected, bytesActual));
+ }
+
+ @Test
+ public void testDataStorageDeletedResidencyMismatch() throws IOException {
+ // Create the directory where cached data will be stored.
+ mInjector.createDataStoragePath();
+
+ // In order to create cached data that will match the current data read by the
+ // PowerStatsService we need to write valid data from the TestPowerStatsHALWrapper that is
+ // returned from the Injector.
+ IPowerStatsHALWrapper powerStatsHALWrapper = mInjector.getPowerStatsHALWrapperImpl();
+
+ // Create cached channel data and write to file.
+ Channel[] channels = powerStatsHALWrapper.getEnergyMeterInfo();
+ byte[] bytes = ChannelUtils.getProtoBytes(channels);
+ File onDeviceStorageFile = new File(mDataStorageDir, mInjector.createMeterCacheFilename());
+ FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
+
+ // Create cached energy consumer data and write to file.
+ EnergyConsumer[] energyConsumers = powerStatsHALWrapper.getEnergyConsumerInfo();
+ bytes = EnergyConsumerUtils.getProtoBytes(energyConsumers);
+ onDeviceStorageFile = new File(mDataStorageDir, mInjector.createModelCacheFilename());
+ onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
+
+ // Generate random array of bytes to emulate cached power entity info data. Store to file.
+ Random rd = new Random();
+ bytes = new byte[100];
+ rd.nextBytes(bytes);
+ onDeviceStorageFile = new File(mDataStorageDir, mInjector.createResidencyCacheFilename());
+ onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
+
+ // Create log files.
+ File meterFile = new File(mDataStorageDir, mInjector.createMeterFilename());
+ File modelFile = new File(mDataStorageDir, mInjector.createModelFilename());
+ File residencyFile = new File(mDataStorageDir, mInjector.createResidencyFilename());
+ meterFile.createNewFile();
+ modelFile.createNewFile();
+ residencyFile.createNewFile();
+
+ // Verify log files exist.
+ assertTrue(meterFile.exists());
+ assertTrue(modelFile.exists());
+ assertTrue(residencyFile.exists());
+
+ // Boot device after creating old cached data.
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Since cached power entity info data is just random bytes it won't match the data read
+ // from the HAL. This mismatch of cached and current HAL data should force a delete.
+ assertFalse(mService.getDeleteMeterDataOnBoot());
+ assertFalse(mService.getDeleteModelDataOnBoot());
+ assertTrue(mService.getDeleteResidencyDataOnBoot());
+
+ // Verify log files were deleted.
+ assertTrue(meterFile.exists());
+ assertTrue(modelFile.exists());
+ assertFalse(residencyFile.exists());
+
+ // Verify cached power entity data was updated to new HAL output.
+ PowerEntity[] powerEntityInfo = powerStatsHALWrapper.getPowerEntityInfo();
+ byte[] bytesExpected = PowerEntityUtils.getProtoBytes(powerEntityInfo);
+ onDeviceStorageFile = new File(mDataStorageDir, mInjector.createResidencyCacheFilename());
+ byte[] bytesActual = new byte[(int) onDeviceStorageFile.length()];
+ FileInputStream onDeviceStorageFis = new FileInputStream(onDeviceStorageFile);
+ onDeviceStorageFis.read(bytesActual);
+ assertTrue(Arrays.equals(bytesExpected, bytesActual));
+ }
+
+ @Test
+ public void testDataStorageNotDeletedNoCachedData() throws IOException {
+ // Create the directory where log files will be stored.
+ mInjector.createDataStoragePath();
+
+ // Create log files.
+ File meterFile = new File(mDataStorageDir, mInjector.createMeterFilename());
+ File modelFile = new File(mDataStorageDir, mInjector.createModelFilename());
+ File residencyFile = new File(mDataStorageDir, mInjector.createResidencyFilename());
+ meterFile.createNewFile();
+ modelFile.createNewFile();
+ residencyFile.createNewFile();
+
+ // Verify log files exist.
+ assertTrue(meterFile.exists());
+ assertTrue(modelFile.exists());
+ assertTrue(residencyFile.exists());
+
+ // This test mimics the device's first boot where there is no cached data.
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Since there is no cached data on the first boot any log files that happen to exist
+ // should be deleted.
+ assertTrue(mService.getDeleteMeterDataOnBoot());
+ assertTrue(mService.getDeleteModelDataOnBoot());
+ assertTrue(mService.getDeleteResidencyDataOnBoot());
+
+ // Verify log files were deleted.
+ assertFalse(meterFile.exists());
+ assertFalse(modelFile.exists());
+ assertFalse(residencyFile.exists());
+ }
+
+ @Test
+ public void testDataStorageNotDeletedAllDataMatches() throws IOException {
+ // Create the directory where cached data will be stored.
+ mInjector.createDataStoragePath();
+
+ // In order to create cached data that will match the current data read by the
+ // PowerStatsService we need to write valid data from the TestPowerStatsHALWrapper that is
+ // returned from the Injector.
+ IPowerStatsHALWrapper powerStatsHALWrapper = mInjector.getPowerStatsHALWrapperImpl();
+
+ // Create cached channel data and write to file.
+ Channel[] channels = powerStatsHALWrapper.getEnergyMeterInfo();
+ byte[] bytes = ChannelUtils.getProtoBytes(channels);
+ File onDeviceStorageFile = new File(mDataStorageDir, mInjector.createMeterCacheFilename());
+ FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
+
+ // Create cached energy consumer data and write to file.
+ EnergyConsumer[] energyConsumers = powerStatsHALWrapper.getEnergyConsumerInfo();
+ bytes = EnergyConsumerUtils.getProtoBytes(energyConsumers);
+ onDeviceStorageFile = new File(mDataStorageDir, mInjector.createModelCacheFilename());
+ onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
+
+ // Create cached power entity info data and write to file.
+ PowerEntity[] powerEntityInfo = powerStatsHALWrapper.getPowerEntityInfo();
+ bytes = PowerEntityUtils.getProtoBytes(powerEntityInfo);
+ onDeviceStorageFile = new File(mDataStorageDir, mInjector.createResidencyCacheFilename());
+ onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
+
+ // Create log files.
+ File meterFile = new File(mDataStorageDir, mInjector.createMeterFilename());
+ File modelFile = new File(mDataStorageDir, mInjector.createModelFilename());
+ File residencyFile = new File(mDataStorageDir, mInjector.createResidencyFilename());
+ meterFile.createNewFile();
+ modelFile.createNewFile();
+ residencyFile.createNewFile();
+
+ // Verify log files exist.
+ assertTrue(meterFile.exists());
+ assertTrue(modelFile.exists());
+ assertTrue(residencyFile.exists());
+
+ // Boot device after creating old cached data.
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // All cached data created above should match current data read in PowerStatsService so we
+ // expect the data not to be deleted.
+ assertFalse(mService.getDeleteMeterDataOnBoot());
+ assertFalse(mService.getDeleteModelDataOnBoot());
+ assertFalse(mService.getDeleteResidencyDataOnBoot());
+
+ // Verify log files were not deleted.
+ assertTrue(meterFile.exists());
+ assertTrue(modelFile.exists());
+ assertTrue(residencyFile.exists());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/rotationresolver/OWNERS b/services/tests/servicestests/src/com/android/server/rotationresolver/OWNERS
new file mode 100644
index 000000000000..81b6f05a1658
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/rotationresolver/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/rotationresolver/OWNERS
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 5f64249213ec..22c38c1961b8 100644
--- a/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
@@ -19,15 +19,21 @@ package com.android.server.rotationresolver;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.Manifest;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
import android.os.CancellationSignal;
import android.os.RemoteException;
import android.rotationresolver.RotationResolverInternal;
import android.view.Surface;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import org.junit.Before;
@@ -41,75 +47,81 @@ import org.mockito.MockitoAnnotations;
*/
@SmallTest
public class RotationResolverManagerPerUserServiceTest {
-
- @Mock
- Context mContext;
+ private static final String PACKAGE_NAME = "test_pkg";
+ private static final String CLASS_NAME = "test_class";
@Mock
RotationResolverInternal.RotationResolverCallbackInternal mMockCallbackInternal;
-
@Mock
- ComponentName mMockComponentName;
+ PackageManager mMockPackageManager;
+ private Context mContext;
private CancellationSignal mCancellationSignal;
-
- private RotationResolverManagerPerUserService mSpyService;
+ private RotationResolverManagerPerUserService mService;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
- // setup context mock
+ // setup context.
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ doReturn(PACKAGE_NAME).when(mMockPackageManager).getRotationResolverPackageName();
+ doReturn(createTestingResolveInfo()).when(mMockPackageManager).resolveServiceAsUser(any(),
+ anyInt(), anyInt());
+ doReturn(mMockPackageManager).when(mContext).getPackageManager();
doReturn(true).when(mContext).bindServiceAsUser(any(), any(), anyInt(), any());
// setup a spy for the RotationResolverManagerPerUserService.
final RotationResolverManagerService mainService = new RotationResolverManagerService(
mContext);
- final RotationResolverManagerPerUserService mService =
- new RotationResolverManagerPerUserService(mainService, /* Lock */ new Object(),
- mContext.getUserId());
+ mService = new RotationResolverManagerPerUserService(mainService, /* Lock */ new Object(),
+ mContext.getUserId());
mCancellationSignal = new CancellationSignal();
- mSpyService = Mockito.spy(mService);
- mSpyService.mCurrentRequest = new RemoteRotationResolverService.RotationRequest(
+ this.mService.mCurrentRequest = new RemoteRotationResolverService.RotationRequest(
mMockCallbackInternal, Surface.ROTATION_0, Surface.ROTATION_0, "", 1000L,
mCancellationSignal);
- mSpyService.getMaster().mIsServiceEnabled = true;
+ this.mService.getMaster().mIsServiceEnabled = true;
- mSpyService.mRemoteService = new MockRemoteRotationResolverService(mContext,
- mMockComponentName, mContext.getUserId(),
+ ComponentName componentName = new ComponentName(PACKAGE_NAME, CLASS_NAME);
+ this.mService.mRemoteService = new MockRemoteRotationResolverService(mContext,
+ componentName, mContext.getUserId(),
/* idleUnbindTimeoutMs */60000L,
/* Lock */ new Object());
}
@Test
public void testResolveRotation_callOnSuccess() {
- doReturn(true).when(mSpyService).isServiceAvailableLocked();
- mSpyService.mCurrentRequest = null;
+ mService.mCurrentRequest = null;
RotationResolverInternal.RotationResolverCallbackInternal callbackInternal =
Mockito.mock(RotationResolverInternal.RotationResolverCallbackInternal.class);
- mSpyService.resolveRotationLocked(callbackInternal, Surface.ROTATION_0, Surface.ROTATION_0,
+ mService.resolveRotationLocked(callbackInternal, Surface.ROTATION_0, Surface.ROTATION_0,
"", 1000L, mCancellationSignal);
verify(callbackInternal).onSuccess(anyInt());
}
@Test
public void testResolveRotation_noCrashWhenCancelled() {
- doReturn(true).when(mSpyService).isServiceAvailableLocked();
-
RotationResolverInternal.RotationResolverCallbackInternal callbackInternal =
Mockito.mock(RotationResolverInternal.RotationResolverCallbackInternal.class);
final CancellationSignal cancellationSignal = new CancellationSignal();
- mSpyService.resolveRotationLocked(callbackInternal, Surface.ROTATION_0, Surface.ROTATION_0,
+ mService.resolveRotationLocked(callbackInternal, Surface.ROTATION_0, Surface.ROTATION_0,
"", 1000L, cancellationSignal);
cancellationSignal.cancel();
+ }
- verify(mSpyService.mCurrentRequest).cancelInternal();
+ private ResolveInfo createTestingResolveInfo() {
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.serviceInfo = new ServiceInfo();
+ resolveInfo.serviceInfo.packageName = PACKAGE_NAME;
+ resolveInfo.serviceInfo.name = CLASS_NAME;
+ resolveInfo.serviceInfo.permission = Manifest.permission.BIND_ROTATION_RESOLVER_SERVICE;
+ return resolveInfo;
}
static class MockRemoteRotationResolverService extends RemoteRotationResolverService {
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 b28994cafa0f..55748366c1ba 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -28,7 +28,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.timedetector.ExternalTimeSuggestion;
+import android.app.time.ExternalTimeSuggestion;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
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 6dcfb42f7f08..daa1b25de22f 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -27,7 +27,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.app.timedetector.ExternalTimeSuggestion;
+import android.app.time.ExternalTimeSuggestion;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java
index 72c40ea5a2be..014bfd2d40e4 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java
@@ -23,7 +23,7 @@ import android.os.Vibrator;
import androidx.annotation.NonNull;
/** Fake implementation of {@link Vibrator} for service tests. */
-public final class FakeVibrator extends Vibrator {
+final class FakeVibrator extends Vibrator {
private int mDefaultHapticFeedbackIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM;
private int mDefaultNotificationIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM;
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 f562c1613413..4634e12f1530 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -33,7 +33,7 @@ import java.util.Map;
* Provides {@link VibratorController} with controlled vibrator hardware capabilities and
* interactions.
*/
-public final class FakeVibratorControllerProvider {
+final class FakeVibratorControllerProvider {
private static final int EFFECT_DURATION = 20;
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
index e71c2f5ba8da..8c62b7fe235e 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
@@ -67,9 +67,8 @@ public class InputDeviceDelegateTest {
private static final String REASON = "some reason";
private static final VibrationAttributes VIBRATION_ATTRIBUTES =
new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build();
- private static final VibrationEffect EFFECT = VibrationEffect.createOneShot(100, 255);
private static final CombinedVibrationEffect SYNCED_EFFECT =
- CombinedVibrationEffect.createSynced(EFFECT);
+ CombinedVibrationEffect.createSynced(VibrationEffect.createOneShot(100, 255));
@Rule public MockitoRule rule = MockitoJUnit.rule();
@@ -105,6 +104,7 @@ public class InputDeviceDelegateTest {
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ false);
assertFalse(mInputDeviceDelegate.isAvailable());
+ when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[]{1});
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
mInputDeviceDelegate.onInputDeviceAdded(1);
@@ -118,6 +118,7 @@ public class InputDeviceDelegateTest {
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
assertFalse(mInputDeviceDelegate.isAvailable());
+ when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[0]);
when(mIInputManagerMock.getInputDevice(eq(1)))
.thenReturn(createInputDeviceWithoutVibrator(1));
updateInputDevices(new int[]{1});
@@ -132,6 +133,7 @@ public class InputDeviceDelegateTest {
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
assertFalse(mInputDeviceDelegate.isAvailable());
+ when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[]{1});
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
updateInputDevices(new int[]{1});
@@ -142,6 +144,7 @@ public class InputDeviceDelegateTest {
@Test
public void onInputDeviceChanged_withSettingsDisabled_ignoresDevice() throws Exception {
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
+ when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[]{1});
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ false);
@@ -153,6 +156,7 @@ public class InputDeviceDelegateTest {
@Test
public void onInputDeviceChanged_deviceLosesVibrator_removesDevice() throws Exception {
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
+ when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[]{1}, new int[0]);
when(mIInputManagerMock.getInputDevice(eq(1)))
.thenReturn(createInputDeviceWithVibrator(1), createInputDeviceWithoutVibrator(1));
@@ -167,6 +171,7 @@ public class InputDeviceDelegateTest {
@Test
public void onInputDeviceChanged_deviceLost_removesDevice() throws Exception {
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
+ when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[]{1}, new int[0]);
when(mIInputManagerMock.getInputDevice(eq(1)))
.thenReturn(createInputDeviceWithVibrator(1), (InputDevice) null);
@@ -181,6 +186,7 @@ public class InputDeviceDelegateTest {
@Test
public void onInputDeviceChanged_deviceAddsVibrator_addsDevice() throws Exception {
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
+ when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[0], new int[]{1});
when(mIInputManagerMock.getInputDevice(eq(1)))
.thenReturn(createInputDeviceWithoutVibrator(1), createInputDeviceWithVibrator(1));
@@ -195,8 +201,10 @@ public class InputDeviceDelegateTest {
@Test
public void onInputDeviceRemoved_removesDevice() throws Exception {
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2});
+ when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[0]);
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(
createInputDeviceWithoutVibrator(1));
+ when(mIInputManagerMock.getVibratorIds(eq(2))).thenReturn(new int[]{1});
when(mIInputManagerMock.getInputDevice(eq(2))).thenReturn(createInputDeviceWithVibrator(2));
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
@@ -209,7 +217,9 @@ public class InputDeviceDelegateTest {
@Test
public void updateInputDeviceVibrators_usesFlagToLoadDeviceList() throws Exception {
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2});
+ when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[]{1});
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
+ when(mIInputManagerMock.getVibratorIds(eq(2))).thenReturn(new int[]{1});
when(mIInputManagerMock.getInputDevice(eq(2))).thenReturn(createInputDeviceWithVibrator(2));
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
@@ -223,6 +233,7 @@ public class InputDeviceDelegateTest {
public void updateInputDeviceVibrators_withDeviceWithoutVibrator_deviceIsIgnored()
throws Exception {
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
+ when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[0]);
when(mIInputManagerMock.getInputDevice(eq(1)))
.thenReturn(createInputDeviceWithoutVibrator(1));
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
@@ -240,14 +251,16 @@ public class InputDeviceDelegateTest {
public void vibrateIfAvailable_withInputDevices_returnsTrueAndVibratesAllDevices()
throws Exception {
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2});
+ when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[]{1});
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
+ when(mIInputManagerMock.getVibratorIds(eq(2))).thenReturn(new int[]{1});
when(mIInputManagerMock.getInputDevice(eq(2))).thenReturn(createInputDeviceWithVibrator(2));
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
assertTrue(mInputDeviceDelegate.vibrateIfAvailable(
UID, PACKAGE_NAME, SYNCED_EFFECT, REASON, VIBRATION_ATTRIBUTES));
- verify(mIInputManagerMock).vibrate(eq(1), same(EFFECT), any());
- verify(mIInputManagerMock).vibrate(eq(2), same(EFFECT), any());
+ verify(mIInputManagerMock).vibrateCombined(eq(1), same(SYNCED_EFFECT), any());
+ verify(mIInputManagerMock).vibrateCombined(eq(2), same(SYNCED_EFFECT), any());
}
@Test
@@ -261,7 +274,9 @@ public class InputDeviceDelegateTest {
public void cancelVibrateIfAvailable_withInputDevices_returnsTrueAndStopsAllDevices()
throws Exception {
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2});
+ when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[]{1});
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
+ when(mIInputManagerMock.getVibratorIds(eq(2))).thenReturn(new int[]{1});
when(mIInputManagerMock.getInputDevice(eq(2))).thenReturn(createInputDeviceWithVibrator(2));
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
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 d876b05af3d5..1b7e1ca6a014 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -19,6 +19,7 @@ package com.android.server.vibrator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -27,8 +28,10 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.hardware.vibrator.IVibrator;
+import android.hardware.vibrator.IVibratorManager;
import android.os.CombinedVibrationEffect;
import android.os.IBinder;
import android.os.PowerManager;
@@ -37,6 +40,7 @@ import android.os.SystemClock;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.test.TestLooper;
+import android.platform.test.annotations.LargeTest;
import android.platform.test.annotations.Presubmit;
import android.util.SparseArray;
@@ -229,9 +233,9 @@ public class VibrationThreadTest {
.compose();
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
- Thread.sleep(20);
+ assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread,
+ TEST_TIMEOUT_MILLIS));
assertTrue(vibrationThread.isAlive());
- assertTrue(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -254,9 +258,9 @@ public class VibrationThreadTest {
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, 0);
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
- Thread.sleep(20);
+ assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread,
+ TEST_TIMEOUT_MILLIS));
assertTrue(vibrationThread.isAlive());
- assertTrue(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -388,6 +392,20 @@ public class VibrationThreadTest {
}
@Test
+ public void vibrate_singleVibrator_skipsSyncedCallbacks() {
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+
+ long vibrationId = 1;
+ waitForCompletion(startThreadAndDispatcher(vibrationId++,
+ VibrationEffect.createOneShot(10, 100)));
+
+ verify(mThreadCallbacks).onVibrationEnded(anyLong(), eq(Vibration.Status.FINISHED));
+ verify(mThreadCallbacks, never()).prepareSyncedVibration(anyLong(), any());
+ verify(mThreadCallbacks, never()).triggerSyncedVibration(anyLong());
+ verify(mThreadCallbacks, never()).cancelSyncedVibration();
+ }
+
+ @Test
public void vibrate_multipleExistingAndMissingVibrators_vibratesOnlyExistingOnes()
throws Exception {
mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_TICK);
@@ -484,8 +502,7 @@ public class VibrationThreadTest {
}
@Test
- public void vibrate_multipleSequential_runsVibrationInOrderWithDelays()
- throws Exception {
+ public void vibrate_multipleSequential_runsVibrationInOrderWithDelays() throws Exception {
mockVibrators(1, 2, 3);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
@@ -529,6 +546,126 @@ public class VibrationThreadTest {
}
@Test
+ public void vibrate_multipleSyncedCallbackTriggered_finishSteps() throws Exception {
+ int[] vibratorIds = new int[]{1, 2};
+ long vibrationId = 1;
+ mockVibrators(vibratorIds);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ when(mThreadCallbacks.prepareSyncedVibration(anyLong(), eq(vibratorIds))).thenReturn(true);
+ when(mThreadCallbacks.triggerSyncedVibration(eq(vibrationId))).thenReturn(true);
+
+ VibrationEffect composed = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100)
+ .compose();
+ CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(composed);
+ VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+
+ assertTrue(waitUntil(t -> !mVibratorProviders.get(1).getEffects().isEmpty()
+ && !mVibratorProviders.get(2).getEffects().isEmpty(), thread, TEST_TIMEOUT_MILLIS));
+ thread.syncedVibrationComplete();
+ waitForCompletion(thread);
+
+ long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_COMPOSE;
+ verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+ verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
+ verify(mThreadCallbacks, never()).cancelSyncedVibration();
+ verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
+
+ assertEquals(Arrays.asList(composed), mVibratorProviders.get(1).getEffects());
+ assertEquals(Arrays.asList(composed), mVibratorProviders.get(2).getEffects());
+ }
+
+ @Test
+ public void vibrate_multipleSynced_callsPrepareAndTriggerCallbacks() {
+ int[] vibratorIds = new int[]{1, 2, 3, 4};
+ mockVibrators(vibratorIds);
+ mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ mVibratorProviders.get(4).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ when(mThreadCallbacks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
+ when(mThreadCallbacks.triggerSyncedVibration(anyLong())).thenReturn(true);
+
+ long vibrationId = 1;
+ VibrationEffect composed = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .compose();
+ CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addVibrator(2, VibrationEffect.createOneShot(10, 100))
+ .addVibrator(3, VibrationEffect.createWaveform(new long[]{10}, new int[]{100}, -1))
+ .addVibrator(4, composed)
+ .combine();
+ VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+ waitForCompletion(thread);
+
+ long expectedCap = IVibratorManager.CAP_SYNC
+ | IVibratorManager.CAP_PREPARE_ON
+ | IVibratorManager.CAP_PREPARE_PERFORM
+ | IVibratorManager.CAP_PREPARE_COMPOSE
+ | IVibratorManager.CAP_MIXED_TRIGGER_ON
+ | IVibratorManager.CAP_MIXED_TRIGGER_PERFORM
+ | IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE;
+ verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+ verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
+ verify(mThreadCallbacks, never()).cancelSyncedVibration();
+ verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
+ }
+
+ @Test
+ public void vibrate_multipleSyncedPrepareFailed_skipTriggerStepAndVibrates() {
+ int[] vibratorIds = new int[]{1, 2};
+ mockVibrators(vibratorIds);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ when(mThreadCallbacks.prepareSyncedVibration(anyLong(), any())).thenReturn(false);
+
+ long vibrationId = 1;
+ CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.createOneShot(10, 100))
+ .addVibrator(2, VibrationEffect.createWaveform(new long[]{5}, new int[]{200}, -1))
+ .combine();
+ VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+ waitForCompletion(thread);
+
+ long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_ON;
+ verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+ verify(mThreadCallbacks, never()).triggerSyncedVibration(eq(vibrationId));
+ verify(mThreadCallbacks, never()).cancelSyncedVibration();
+
+ assertEquals(Arrays.asList(expectedOneShot(10)), mVibratorProviders.get(1).getEffects());
+ assertEquals(Arrays.asList(100), mVibratorProviders.get(1).getAmplitudes());
+ assertEquals(Arrays.asList(expectedOneShot(5)), mVibratorProviders.get(2).getEffects());
+ assertEquals(Arrays.asList(200), mVibratorProviders.get(2).getAmplitudes());
+ }
+
+ @Test
+ public void vibrate_multipleSyncedTriggerFailed_cancelPreparedVibrationAndSkipSetAmplitude() {
+ int[] vibratorIds = new int[]{1, 2};
+ mockVibrators(vibratorIds);
+ mVibratorProviders.get(2).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ when(mThreadCallbacks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
+ when(mThreadCallbacks.triggerSyncedVibration(anyLong())).thenReturn(false);
+
+ long vibrationId = 1;
+ CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.createOneShot(10, 100))
+ .addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .combine();
+ VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+ waitForCompletion(thread);
+
+ long expectedCap = IVibratorManager.CAP_SYNC
+ | IVibratorManager.CAP_PREPARE_ON
+ | IVibratorManager.CAP_PREPARE_PERFORM
+ | IVibratorManager.CAP_MIXED_TRIGGER_ON
+ | IVibratorManager.CAP_MIXED_TRIGGER_PERFORM;
+ verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
+ verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
+ verify(mThreadCallbacks).cancelSyncedVibration();
+ assertTrue(mVibratorProviders.get(1).getAmplitudes().isEmpty());
+ }
+
+ @Test
public void vibrate_multipleWaveforms_playsWaveformsInParallel() throws Exception {
mockVibrators(1, 2, 3);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
@@ -573,6 +710,7 @@ public class VibrationThreadTest {
assertEquals(Arrays.asList(6), mVibratorProviders.get(3).getAmplitudes());
}
+ @LargeTest
@Test
public void vibrate_withWaveform_totalVibrationTimeRespected() {
int totalDuration = 10_000; // 10s
@@ -621,10 +759,9 @@ public class VibrationThreadTest {
.combine();
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
- Thread.sleep(10);
+ assertTrue(waitUntil(t -> t.getVibrators().get(2).isVibrating(), vibrationThread,
+ TEST_TIMEOUT_MILLIS));
assertTrue(vibrationThread.isAlive());
- assertTrue(vibrationThread.getVibrators().get(1).isVibrating());
- assertTrue(vibrationThread.getVibrators().get(2).isVibrating());
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
diff --git a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index f7b24920f903..ba0a472c80dd 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.vibrator;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
@@ -32,6 +33,7 @@ import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -44,6 +46,7 @@ import android.content.pm.PackageManagerInternal;
import android.hardware.input.IInputManager;
import android.hardware.input.InputManager;
import android.hardware.vibrator.IVibrator;
+import android.hardware.vibrator.IVibratorManager;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.os.CombinedVibrationEffect;
@@ -70,9 +73,7 @@ import androidx.test.InstrumentationRegistry;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
-import com.android.server.vibrator.FakeVibrator;
-import com.android.server.vibrator.FakeVibratorControllerProvider;
-import com.android.server.vibrator.VibratorController;
+import com.android.server.LocalServices;
import org.junit.After;
import org.junit.Before;
@@ -195,6 +196,10 @@ public class VibratorManagerServiceTest {
return mVibratorProviders.get(vibratorId)
.newVibratorController(vibratorId, listener);
}
+
+ @Override
+ void addService(String name, IBinder service) {
+ }
});
service.systemReady();
return service;
@@ -204,7 +209,7 @@ public class VibratorManagerServiceTest {
public void createService_initializesNativeManagerServiceAndVibrators() {
mockVibrators(1, 2);
createService();
- verify(mNativeWrapperMock).init();
+ verify(mNativeWrapperMock).init(any());
assertTrue(mVibratorProviders.get(1).isInitialized());
assertTrue(mVibratorProviders.get(2).isInitialized());
}
@@ -526,28 +531,15 @@ public class VibratorManagerServiceTest {
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
- when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
+ when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[]{1});
+ when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
VibratorManagerService service = createService();
- // Prebaked vibration will play fallback waveform on input device.
- ArgumentCaptor<VibrationEffect> captor = ArgumentCaptor.forClass(VibrationEffect.class);
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
- verify(mIInputManagerMock).vibrate(eq(1), captor.capture(), any());
- assertTrue(captor.getValue() instanceof VibrationEffect.Waveform);
-
- VibrationEffect[] effects = new VibrationEffect[]{
- VibrationEffect.createOneShot(100, 128),
- VibrationEffect.createWaveform(new long[]{10}, new int[]{100}, -1),
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .compose(),
- };
-
- for (VibrationEffect effect : effects) {
- vibrate(service, effect, ALARM_ATTRS);
- verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
- }
+ CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
+ VibrationEffect.createOneShot(10, 10));
+ vibrate(service, effect, ALARM_ATTRS);
+ verify(mIInputManagerMock).vibrateCombined(eq(1), eq(effect), any());
// VibrationThread will start this vibration async, so wait before checking it never played.
assertFalse(waitUntil(s -> !mVibratorProviders.get(1).getEffects().isEmpty(),
@@ -557,8 +549,6 @@ public class VibratorManagerServiceTest {
@Test
public void vibrate_withNativeCallbackTriggered_finishesVibration() throws Exception {
mockVibrators(1);
- mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS,
- IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
VibratorManagerService service = createService();
// The native callback will be dispatched manually in this test.
@@ -577,6 +567,139 @@ public class VibratorManagerServiceTest {
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
}
+ @Test
+ public void vibrate_withTriggerCallback_finishesVibration() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_COMPOSE);
+ mockVibrators(1, 2);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ VibratorManagerService service = createService();
+ // The native callback will be dispatched manually in this test.
+ mTestLooper.stopAutoDispatchAndIgnoreExceptions();
+
+ ArgumentCaptor<VibratorManagerService.OnSyncedVibrationCompleteListener> listenerCaptor =
+ ArgumentCaptor.forClass(
+ VibratorManagerService.OnSyncedVibrationCompleteListener.class);
+ verify(mNativeWrapperMock).init(listenerCaptor.capture());
+
+ // Mock trigger callback on registered listener.
+ when(mNativeWrapperMock.prepareSynced(eq(new int[]{1, 2}))).thenReturn(true);
+ when(mNativeWrapperMock.triggerSynced(anyLong())).then(answer -> {
+ listenerCaptor.getValue().onComplete(answer.getArgument(0));
+ return true;
+ });
+
+ VibrationEffect composed = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100)
+ .compose();
+ CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(composed);
+
+ // Wait for vibration to start, it should finish right away with trigger callback.
+ vibrate(service, effect, ALARM_ATTRS);
+
+ // VibrationThread will start this vibration async, so wait until callback is triggered.
+ assertTrue(waitUntil(s -> !listenerCaptor.getAllValues().isEmpty(), service,
+ TEST_TIMEOUT_MILLIS));
+
+ verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
+ verify(mNativeWrapperMock).triggerSynced(anyLong());
+ assertEquals(Arrays.asList(composed), mVibratorProviders.get(1).getEffects());
+ assertEquals(Arrays.asList(composed), mVibratorProviders.get(2).getEffects());
+ }
+
+ @Test
+ public void vibrate_withMultipleVibratorsAndCapabilities_prepareAndTriggerCalled()
+ throws Exception {
+ mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_PERFORM,
+ IVibratorManager.CAP_PREPARE_COMPOSE, IVibratorManager.CAP_MIXED_TRIGGER_PERFORM,
+ IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE);
+ mockVibrators(1, 2);
+ when(mNativeWrapperMock.prepareSynced(eq(new int[]{1, 2}))).thenReturn(true);
+ when(mNativeWrapperMock.triggerSynced(anyLong())).thenReturn(true);
+ FakeVibratorControllerProvider fakeVibrator1 = mVibratorProviders.get(1);
+ fakeVibrator1.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ VibratorManagerService service = createService();
+
+ CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addVibrator(2, VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .compose())
+ .combine();
+ vibrate(service, effect, ALARM_ATTRS);
+ assertTrue(waitUntil(s -> !fakeVibrator1.getEffects().isEmpty(), service,
+ TEST_TIMEOUT_MILLIS));
+
+ verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
+ verify(mNativeWrapperMock).triggerSynced(anyLong());
+ verify(mNativeWrapperMock).cancelSynced(); // Trigger on service creation only.
+ }
+
+ @Test
+ public void vibrate_withMultipleVibratorsWithoutCapabilities_skipPrepareAndTrigger()
+ throws Exception {
+ // Missing CAP_MIXED_TRIGGER_ON and CAP_MIXED_TRIGGER_PERFORM.
+ mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_ON,
+ IVibratorManager.CAP_PREPARE_PERFORM);
+ mockVibrators(1, 2);
+ FakeVibratorControllerProvider fakeVibrator1 = mVibratorProviders.get(1);
+ fakeVibrator1.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ VibratorManagerService service = createService();
+
+ CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addVibrator(2, VibrationEffect.createOneShot(10, 100))
+ .combine();
+ vibrate(service, effect, ALARM_ATTRS);
+ assertTrue(waitUntil(s -> !fakeVibrator1.getEffects().isEmpty(), service,
+ TEST_TIMEOUT_MILLIS));
+
+ verify(mNativeWrapperMock, never()).prepareSynced(any());
+ verify(mNativeWrapperMock, never()).triggerSynced(anyLong());
+ verify(mNativeWrapperMock).cancelSynced(); // Trigger on service creation only.
+ }
+
+ @Test
+ public void vibrate_withMultipleVibratorsPrepareFailed_skipTrigger() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_ON);
+ mockVibrators(1, 2);
+ when(mNativeWrapperMock.prepareSynced(any())).thenReturn(false);
+ VibratorManagerService service = createService();
+
+ CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.createOneShot(10, 50))
+ .addVibrator(2, VibrationEffect.createOneShot(10, 100))
+ .combine();
+ vibrate(service, effect, ALARM_ATTRS);
+ assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getEffects().isEmpty(), service,
+ TEST_TIMEOUT_MILLIS));
+
+ verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
+ verify(mNativeWrapperMock, never()).triggerSynced(anyLong());
+ verify(mNativeWrapperMock).cancelSynced(); // Trigger on service creation only.
+ }
+
+ @Test
+ public void vibrate_withMultipleVibratorsTriggerFailed_cancelPreparedSynced() throws Exception {
+ mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_ON);
+ mockVibrators(1, 2);
+ when(mNativeWrapperMock.prepareSynced(eq(new int[]{1, 2}))).thenReturn(true);
+ when(mNativeWrapperMock.triggerSynced(anyLong())).thenReturn(false);
+ VibratorManagerService service = createService();
+
+ CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
+ .addVibrator(1, VibrationEffect.createOneShot(10, 50))
+ .addVibrator(2, VibrationEffect.createOneShot(10, 100))
+ .combine();
+ vibrate(service, effect, ALARM_ATTRS);
+ assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getEffects().isEmpty(), service,
+ TEST_TIMEOUT_MILLIS));
+
+ verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
+ verify(mNativeWrapperMock).triggerSynced(anyLong());
+ verify(mNativeWrapperMock, times(2)).cancelSynced(); // Trigger on service creation too.
+ }
@Test
public void vibrate_withIntensitySettings_appliesSettingsToScaleVibrations() throws Exception {
@@ -682,6 +805,11 @@ public class VibratorManagerServiceTest {
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
}
+ private void mockCapabilities(long... capabilities) {
+ when(mNativeWrapperMock.getCapabilities()).thenReturn(
+ Arrays.stream(capabilities).reduce(0, (a, b) -> a | b));
+ }
+
private void mockVibrators(int... vibratorIds) {
for (int vibratorId : vibratorIds) {
mVibratorProviders.put(vibratorId,
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
new file mode 100644
index 000000000000..f5d0ca7c5f9f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorManager;
+import android.hardware.input.InputSensorInfo;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.rotationresolver.RotationResolverInternal;
+import android.view.Surface;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link com.android.server.wm.WindowOrientationListener}
+ */
+public class WindowOrientationListenerTest {
+
+ @Mock
+ private Context mMockContext;
+ @Mock
+ private Handler mMockHandler;
+ @Mock
+ private InputSensorInfo mMockInputSensorInfo;
+ @Mock
+ private SensorManager mMockSensorManager;
+ @Mock
+ private WindowManagerService mMockWindowManagerService;
+
+ private TestableRotationResolver mFakeRotationResolverInternal;
+ private com.android.server.wm.WindowOrientationListener mWindowOrientationListener;
+ private int mFinalizedRotation;
+ private boolean mRotationResolverEnabled;
+ private boolean mCanUseRotationResolver;
+ private SensorEvent mFakeSensorEvent;
+ private Sensor mFakeSensor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mRotationResolverEnabled = true;
+ mCanUseRotationResolver = true;
+
+ mFakeRotationResolverInternal = new TestableRotationResolver();
+ doReturn(mMockSensorManager).when(mMockContext).getSystemService(Context.SENSOR_SERVICE);
+ mWindowOrientationListener = new TestableWindowOrientationListener(mMockContext,
+ mMockHandler, mMockWindowManagerService);
+ mWindowOrientationListener.mRotationResolverService = mFakeRotationResolverInternal;
+
+ mFakeSensor = new Sensor(mMockInputSensorInfo);
+ mFakeSensorEvent = new SensorEvent(mFakeSensor, /* accuracy */ 1, /* timestamp */ 1L,
+ new float[]{(float) Surface.ROTATION_90});
+ }
+
+ @Test
+ public void testOnSensorChanged_rotationResolverDisabled_useSensorResult() {
+ mRotationResolverEnabled = false;
+
+ mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+ assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_90);
+ }
+
+ @Test
+ public void testOnSensorChanged_cannotUseRotationResolver_useSensorResult() {
+ mCanUseRotationResolver = false;
+
+ mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+ assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_90);
+
+ }
+
+ @Test
+ public void testOnSensorChanged_normalCase() {
+ mFakeRotationResolverInternal.mResult = Surface.ROTATION_180;
+
+ mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+ assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_180);
+ }
+
+ final class TestableRotationResolver extends RotationResolverInternal {
+ @Surface.Rotation
+ int mResult;
+
+ @Override
+ public boolean isRotationResolverSupported() {
+ return true;
+ }
+
+ @Override
+ public void resolveRotation(@NonNull RotationResolverCallbackInternal callback,
+ @Surface.Rotation int proposedRotation, @Surface.Rotation int currentRotation,
+ @DurationMillisLong long timeoutMillis,
+ @NonNull CancellationSignal cancellationSignal) {
+ callback.onSuccess(mResult);
+ }
+ }
+
+ final class TestableWindowOrientationListener extends WindowOrientationListener {
+
+ TestableWindowOrientationListener(Context context, Handler handler,
+ WindowManagerService service) {
+ super(context, handler, service);
+ this.mOrientationJudge = new OrientationSensorJudge();
+ }
+
+ @Override
+ public void onProposedRotationChanged(int rotation) {
+ mFinalizedRotation = rotation;
+ }
+
+ @Override
+ public boolean canUseRotationResolver() {
+ return mCanUseRotationResolver;
+ }
+
+ @Override
+ public boolean isRotationResolverEnabled() {
+ return mRotationResolverEnabled;
+ }
+ }
+}
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.bp b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp
index 13e664446418..0731e2ca1f41 100644
--- a/services/tests/servicestests/test-apps/ConnTestApp/Android.bp
+++ b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "ConnTestApp",
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/OWNERS b/services/tests/servicestests/test-apps/ConnTestApp/OWNERS
new file mode 100644
index 000000000000..aa87958f1d53
--- /dev/null
+++ b/services/tests/servicestests/test-apps/ConnTestApp/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/net/OWNERS
diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.bp b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
index b29e187576c3..6458bcd4fc8f 100644
--- a/services/tests/servicestests/test-apps/JobTestApp/Android.bp
+++ b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "JobTestApp",
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
index f69dfe9d03c9..b11c85c008ad 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
+++ b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "PackageParserTestApp1",
sdk_version: "current",
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp b/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp
index 5cbd39c39434..50b89d484ee8 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "SimpleServiceTestApp",
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp
index 7257275971ab..5e77498862be 100644
--- a/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "SuspendTestApp",
diff --git a/services/tests/shortcutmanagerutils/Android.bp b/services/tests/shortcutmanagerutils/Android.bp
index c2cb688175b2..deabb6c3c438 100644
--- a/services/tests/shortcutmanagerutils/Android.bp
+++ b/services/tests/shortcutmanagerutils/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "ShortcutManagerTestUtils",
@@ -22,5 +31,9 @@ java_library {
"android.test.runner.stubs",
],
+ static_libs: [
+ "compatibility-device-util-axt",
+ ],
+
sdk_version: "test_current",
}
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 9f1fff7b67ad..5182b3b69655 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
@@ -15,6 +15,8 @@
*/
package com.android.server.pm.shortcutmanagertest;
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -34,6 +36,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.Instrumentation;
+import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.LocusId;
@@ -50,6 +53,7 @@ import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.UserHandle;
+import android.os.UserManager;
import android.test.MoreAsserts;
import android.util.Log;
@@ -217,30 +221,60 @@ public class ShortcutManagerTestUtils {
return runShortcutCommand(instrumentation, command, result -> result.contains("Success"));
}
- public static String getDefaultLauncher(Instrumentation instrumentation) {
- final String PREFIX = "Launcher: ComponentInfo{";
- final String POSTFIX = "}";
- final List<String> result = runShortcutCommandForSuccess(
- instrumentation, "get-default-launcher --user "
- + instrumentation.getContext().getUserId());
- for (String s : result) {
- if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) {
- return s.substring(PREFIX.length(), s.length() - POSTFIX.length());
+ private static UserHandle getParentUser(Context context) {
+ final UserHandle user = context.getUser();
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ if (!userManager.isManagedProfile(user.getIdentifier())) {
+ return user;
+ }
+
+ final List<UserHandle> profiles = userManager.getUserProfiles();
+ for (UserHandle handle : profiles) {
+ if (!userManager.isManagedProfile(handle.getIdentifier())) {
+ return handle;
}
}
- fail("Default launcher not found");
return null;
}
- public static void setDefaultLauncher(Instrumentation instrumentation, String component) {
- runCommand(instrumentation, "cmd package set-home-activity --user "
- + instrumentation.getContext().getUserId() + " " + component,
- result -> result.contains("Success"));
+ public static String getDefaultLauncher(Instrumentation instrumentation) throws Exception {
+ final Context context = instrumentation.getContext();
+ final RoleManager roleManager = context.getSystemService(RoleManager.class);
+ final UserHandle user = getParentUser(context);
+ List<String> roleHolders = callWithShellPermissionIdentity(
+ () -> roleManager.getRoleHoldersAsUser(RoleManager.ROLE_HOME, user));
+ if (roleHolders.size() == 1) {
+ return roleHolders.get(0);
+ }
+ fail("Failed to get the default launcher for user " + context.getUserId());
+ return null;
+ }
+
+ public static void setDefaultLauncher(Instrumentation instrumentation, String packageName) {
+ runCommandForNoOutput(instrumentation, "cmd role add-role-holder --user "
+ + instrumentation.getContext().getUserId() + " " + RoleManager.ROLE_HOME + " "
+ + packageName + " 0");
+ waitUntil("Failed to get shortcut access",
+ () -> hasShortcutAccess(instrumentation, packageName), 20);
}
public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
- setDefaultLauncher(instrumentation, packageContext.getPackageName()
- + "/android.content.pm.cts.shortcutmanager.packages.Launcher");
+ setDefaultLauncher(instrumentation, packageContext.getPackageName());
+ }
+
+ public static boolean hasShortcutAccess(Instrumentation instrumentation, String packageName) {
+ final List<String> result = runShortcutCommandForSuccess(instrumentation,
+ "has-shortcut-access --user " + instrumentation.getContext().getUserId()
+ + " " + packageName);
+ for (String s : result) {
+ if (s.startsWith("true")) {
+ return true;
+ } else if (s.startsWith("false")) {
+ return false;
+ }
+ }
+ fail("Failed to check shortcut access");
+ return false;
}
public static void overrideConfig(Instrumentation instrumentation, String config) {
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 1dd42127ec06..1ba414e33d1b 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -2,6 +2,15 @@
// Build FrameworksUiServicesTests package
//########################################################################
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksUiServicesTests",
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index ec28baf53d3c..07475e955785 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -1686,6 +1686,11 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Override
+ protected void ensureFilters(ServiceInfo si, int userId) {
+
+ }
+
+ @Override
protected void loadDefaultsFromConfig() {
mDefaultComponents.addAll(mDefaults);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index b3116d970725..17c941179853 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -15,6 +15,9 @@
*/
package com.android.server.notification;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
@@ -138,6 +141,30 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
}
@Test
+ public void testXmlMigratingAllowedAdjustments() throws Exception {
+ // Old tag, need migration
+ String xml = "<q_allowed_adjustments types=\"adj_1\"/>";
+
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ parser.nextTag();
+ mAssistants.readExtraTag("q_allowed_adjustments", parser);
+ assertTrue(mAssistants.isAdjustmentAllowed("adj_1"));
+ assertEquals(mNm.DEFAULT_ALLOWED_ADJUSTMENTS.length + 1,
+ mAssistants.getAllowedAssistantAdjustments().size());
+
+ // New TAG
+ xml = "<s_allowed_adjustments types=\"adj_2\"/>";
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ parser.nextTag();
+ mAssistants.readExtraTag("s_allowed_adjustments", parser);
+ assertTrue(mAssistants.isAdjustmentAllowed("adj_2"));
+ assertEquals(1, mAssistants.getAllowedAssistantAdjustments().size());
+ }
+
+ @Test
public void testSetPackageOrComponentEnabled_onlyOnePackage() throws Exception {
ComponentName component1 = ComponentName.unflattenFromString("package/Component1");
ComponentName component2 = ComponentName.unflattenFromString("package/Component2");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index be489c3bcd12..5614aa2a165d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -394,7 +394,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
"disabledMessage", 0, "disabledMessageResName",
null, null, 0, null, 0, 0,
0, "iconResName", "bitmapPath", null, 0,
- null, null);
+ null, null, 0);
return si;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index afcf08ef1562..80a046a1e8bb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -15,6 +15,11 @@
*/
package com.android.server.notification;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
+
+import static com.android.server.notification.NotificationManagerService.NotificationListeners.TAG_REQUESTED_LISTENERS;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -27,11 +32,14 @@ import android.app.INotificationManager;
import android.content.ComponentName;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.VersionedPackage;
+import android.os.Bundle;
import android.service.notification.NotificationListenerFilter;
+import android.service.notification.NotificationListenerService;
import android.util.ArraySet;
import android.util.Pair;
+import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
@@ -47,8 +55,6 @@ import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
-import java.util.List;
public class NotificationListenersTest extends UiServiceTestCase {
@@ -80,22 +86,21 @@ public class NotificationListenersTest extends UiServiceTestCase {
@Test
public void testReadExtraTag() throws Exception {
- String xml = "<req_listeners>"
+ String xml = "<" + TAG_REQUESTED_LISTENERS+ ">"
+ "<listener component=\"" + mCn1.flattenToString() + "\" user=\"0\">"
+ "<allowed types=\"7\" />"
- + "<disallowed pkgs=\"\" />"
+ "</listener>"
+ "<listener component=\"" + mCn2.flattenToString() + "\" user=\"10\">"
+ "<allowed types=\"4\" />"
- + "<disallowed pkgs=\"something\" />"
+ + "<disallowed pkg=\"pkg1\" uid=\"243\"/>"
+ "</listener>"
- + "</req_listeners>";
+ + "</" + TAG_REQUESTED_LISTENERS + ">";
TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(xml.getBytes())), null);
parser.nextTag();
- mListeners.readExtraTag("req_listeners", parser);
+ mListeners.readExtraTag(TAG_REQUESTED_LISTENERS, parser);
validateListenersFromXml();
}
@@ -103,8 +108,9 @@ public class NotificationListenersTest extends UiServiceTestCase {
@Test
public void testWriteExtraTag() throws Exception {
NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+ VersionedPackage a1 = new VersionedPackage("pkg1", 243);
NotificationListenerFilter nlf2 =
- new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
+ new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1}));
mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2);
@@ -134,16 +140,18 @@ public class NotificationListenersTest extends UiServiceTestCase {
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10)).getTypes())
.isEqualTo(4);
+ VersionedPackage a1 = new VersionedPackage("pkg1", 243);
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10))
.getDisallowedPackages())
- .contains("something");
+ .contains(a1);
}
@Test
public void testOnUserRemoved() {
NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+ VersionedPackage a1 = new VersionedPackage("pkg1", 243);
NotificationListenerFilter nlf2 =
- new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
+ new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1}));
mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2);
@@ -155,58 +163,68 @@ public class NotificationListenersTest extends UiServiceTestCase {
}
@Test
- public void testOnUserUnlocked() {
+ public void testEnsureFilters_newServiceNoMetadata() {
+ ServiceInfo si = new ServiceInfo();
+ si.packageName = "new2";
+ si.name = "comp2";
+
+ mListeners.ensureFilters(si, 0);
+
+ assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))).isNull();
+ }
+
+ @Test
+ public void testEnsureFilters_preExisting() {
// one exists already, say from xml
+ VersionedPackage a1 = new VersionedPackage("pkg1", 243);
NotificationListenerFilter nlf =
- new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
+ new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1}));
mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf);
+ ServiceInfo siOld = new ServiceInfo();
+ siOld.packageName = mCn2.getPackageName();
+ siOld.name = mCn2.getClassName();
+
+ mListeners.ensureFilters(siOld, 0);
- // new service exists or backfilling on upgrade to S
+ assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))).isEqualTo(nlf);
+ }
+
+ @Test
+ public void testEnsureFilters_newServiceWithMetadata() {
ServiceInfo si = new ServiceInfo();
- si.permission = mListeners.getConfig().bindPermission;
si.packageName = "new";
si.name = "comp";
- ResolveInfo ri = new ResolveInfo();
- ri.serviceInfo = si;
+ si.metaData = new Bundle();
+ si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "1,2");
- // incorrect service
- ServiceInfo si2 = new ServiceInfo();
- ResolveInfo ri2 = new ResolveInfo();
- ri2.serviceInfo = si2;
- si2.packageName = "new2";
- si2.name = "comp2";
+ mListeners.ensureFilters(si, 0);
- List<ResolveInfo> ris = new ArrayList<>();
- ris.add(ri);
- ris.add(ri2);
-
- when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(ris);
+ assertThat(mListeners.getNotificationListenerFilter(
+ Pair.create(si.getComponentName(), 0)).getTypes())
+ .isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ALERTING);
+ }
- mListeners.onUserUnlocked(0);
+ @Test
+ public void testEnsureFilters_newServiceWithEmptyMetadata() {
+ ServiceInfo si = new ServiceInfo();
+ si.packageName = "new";
+ si.name = "comp";
+ si.metaData = new Bundle();
+ si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "");
- assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0)).getTypes())
- .isEqualTo(4);
- assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))
- .getDisallowedPackages())
- .contains("something");
+ mListeners.ensureFilters(si, 0);
assertThat(mListeners.getNotificationListenerFilter(
Pair.create(si.getComponentName(), 0)).getTypes())
- .isEqualTo(15);
- assertThat(mListeners.getNotificationListenerFilter(Pair.create(si.getComponentName(), 0))
- .getDisallowedPackages())
- .isEmpty();
-
- assertThat(mListeners.getNotificationListenerFilter(Pair.create(si2.getComponentName(), 0)))
- .isNull();
-
+ .isEqualTo(0);
}
@Test
public void testOnPackageChanged() {
NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+ VersionedPackage a1 = new VersionedPackage("pkg1", 243);
NotificationListenerFilter nlf2 =
- new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
+ new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1}));
mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2);
@@ -224,8 +242,9 @@ public class NotificationListenersTest extends UiServiceTestCase {
@Test
public void testOnPackageChanged_removing() {
NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+ VersionedPackage a1 = new VersionedPackage("pkg1", 243);
NotificationListenerFilter nlf2 =
- new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
+ new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1}));
mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf2);
@@ -239,4 +258,21 @@ public class NotificationListenersTest extends UiServiceTestCase {
.isEqualTo(4);
}
+ @Test
+ public void testOnPackageChanged_removingDisallowedPackage() {
+ NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+ VersionedPackage a1 = new VersionedPackage("pkg1", 243);
+ NotificationListenerFilter nlf2 =
+ new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1}));
+ mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
+ mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf2);
+
+ String[] pkgs = new String[] {"pkg1"};
+ int[] uids = new int[] {243};
+ mListeners.onPackagesChanged(true, pkgs, uids);
+
+ assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0))
+ .getDisallowedPackages()).isEmpty();
+ }
+
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index e8888f496a01..a64050996d42 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -464,7 +464,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Setup managed services
when(mNlf.isTypeAllowed(anyInt())).thenReturn(true);
- when(mNlf.isPackageAllowed(anyString())).thenReturn(true);
+ when(mNlf.isPackageAllowed(any())).thenReturn(true);
when(mNlf.isPackageAllowed(null)).thenReturn(true);
when(mListeners.getNotificationListenerFilter(any())).thenReturn(mNlf);
mListener = mListeners.new ManagedServiceInfo(
@@ -7307,7 +7307,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testIsVisibleToListener_disallowedPackage() {
- when(mNlf.isPackageAllowed(null)).thenReturn(false);
+ when(mNlf.isPackageAllowed(any())).thenReturn(false);
StatusBarNotification sbn = mock(StatusBarNotification.class);
when(sbn.getUserId()).thenReturn(10);
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index ddf2844012e0..90dac47e353c 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -2,6 +2,15 @@
// Build WmTests package
//########################################################################
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
// Include all test java files.
filegroup {
name: "wmtests-sources",
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 6b69ee921c8b..002859e3366b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -96,6 +96,8 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
.setTask(mTrampolineActivity.getTask())
.setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "TopActivity"))
.build();
+ // becomes invisible when covered by mTopActivity
+ mTrampolineActivity.mVisibleRequested = false;
}
@After
@@ -230,7 +232,6 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
@Test
public void testOnActivityLaunchCancelled_finishedBeforeDrawn() {
- mTopActivity.mVisibleRequested = true;
doReturn(true).when(mTopActivity).isReportedDrawn();
// Create an activity with different process that meets process switch.
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 93851109200b..aa1110cd55a7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -719,7 +719,7 @@ public class ActivityRecordTests extends WindowTestsBase {
final ActivityRecord activity = createActivityWithTask();
assertTrue(activity.hasSavedState());
- ActivityRecord.activityResumedLocked(activity.appToken);
+ ActivityRecord.activityResumedLocked(activity.appToken, false /* handleSplashScreenExit */);
assertFalse(activity.hasSavedState());
assertNull(activity.getSavedState());
}
@@ -2180,6 +2180,7 @@ public class ActivityRecordTests extends WindowTestsBase {
activity.setOccludesParent(true);
activity.setVisible(false);
+ activity.mVisibleRequested = false;
// Can not specify orientation if app isn't visible even though it occludes parent.
assertEquals(SCREEN_ORIENTATION_UNSET, activity.getOrientation());
// Can specify orientation if the current orientation candidate is orientation behind.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 22ee886c46fe..3a7954b3a5ce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1424,6 +1424,9 @@ public class ActivityStackTests extends WindowTestsBase {
final ActivityRecord nonTopVisibleActivity =
new ActivityBuilder(mAtm).setTask(task).build();
new ActivityBuilder(mAtm).setTask(task).build();
+ // The scenario we are testing is when the app isn't visible yet.
+ nonTopVisibleActivity.setVisible(false);
+ nonTopVisibleActivity.mVisibleRequested = false;
doReturn(false).when(nonTopVisibleActivity).attachedToProcess();
doReturn(true).when(nonTopVisibleActivity).shouldBeVisibleUnchecked();
doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(),
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 70c0d35c7eaf..bc3b358c7a0a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -849,6 +849,9 @@ public class ActivityStarterTests extends WindowTestsBase {
final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, false /* onTop */));
+ // Activity should start invisible since we are bringing it to front.
+ singleTaskActivity.setVisible(false);
+ singleTaskActivity.mVisibleRequested = false;
// Create another activity on top of the secondary display.
final Task topStack = secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
index 5597be93c1f1..2686a2429492 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
@@ -27,6 +27,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -118,6 +119,19 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase {
}
@Test
+ public void testRegisterOrganizer_ignoreUntrustedDisplay() throws RemoteException {
+ doReturn(false).when(mDisplayContent).isTrusted();
+
+ final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder());
+ List<DisplayAreaAppearedInfo> infos = mOrganizerController
+ .registerOrganizer(organizer, FEATURE_VENDOR_FIRST).getList();
+
+ assertThat(infos).isEmpty();
+ verify(organizer, never()).onDisplayAreaAppeared(any(DisplayAreaInfo.class),
+ any(SurfaceControl.class));
+ }
+
+ @Test
public void testCreateTaskDisplayArea_topBelowRoot() {
final String newTdaName = "testTda";
final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder());
@@ -186,13 +200,20 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase {
@Test
public void testCreateTaskDisplayArea_invalidDisplayAndRoot() {
final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder());
+
assertThrows(IllegalArgumentException.class, () ->
mOrganizerController.createTaskDisplayArea(
organizer, SystemServicesTestRule.sNextDisplayId + 1, FEATURE_ROOT,
"testTda"));
+
assertThrows(IllegalArgumentException.class, () ->
mOrganizerController.createTaskDisplayArea(
organizer, DEFAULT_DISPLAY, FEATURE_ROOT - 1, "testTda"));
+
+ doReturn(false).when(mDisplayContent).isTrusted();
+ assertThrows(IllegalArgumentException.class, () ->
+ mOrganizerController.createTaskDisplayArea(
+ organizer, DEFAULT_DISPLAY, FEATURE_ROOT, "testTda"));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 89b962b96baf..d4c956db90a9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -43,11 +43,15 @@ import static com.android.server.wm.testing.Assert.assertThrows;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+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.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -57,6 +61,7 @@ import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
+import android.window.IDisplayAreaOrganizer;
import com.google.android.collect.Lists;
@@ -525,6 +530,42 @@ public class DisplayAreaTest extends WindowTestsBase {
assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda);
}
+ @Test
+ public void testDisplayContentUpdateDisplayAreaOrganizers_onDisplayAreaAppeared() {
+ final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(
+ mWm, BELOW_TASKS, "NewArea", FEATURE_VENDOR_FIRST);
+ final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
+ spyOn(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController);
+ when(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController
+ .getOrganizerByFeature(FEATURE_VENDOR_FIRST))
+ .thenReturn(mockDisplayAreaOrganizer);
+
+ mDisplayContent.addChild(displayArea, 0);
+ mDisplayContent.updateDisplayAreaOrganizers();
+
+ assertEquals(mockDisplayAreaOrganizer, displayArea.mOrganizer);
+ verify(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController)
+ .onDisplayAreaAppeared(
+ eq(mockDisplayAreaOrganizer),
+ argThat(it -> it == displayArea && it.getSurfaceControl() != null));
+ }
+
+ @Test
+ public void testRemoveImmediately_onDisplayAreaVanished() {
+ final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(
+ mWm, BELOW_TASKS, "NewArea", FEATURE_VENDOR_FIRST);
+ final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
+ displayArea.mOrganizer = mockDisplayAreaOrganizer;
+ spyOn(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController);
+ mDisplayContent.addChild(displayArea, 0);
+
+ displayArea.removeImmediately();
+
+ assertNull(displayArea.mOrganizer);
+ verify(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController)
+ .onDisplayAreaVanished(mockDisplayAreaOrganizer, displayArea);
+ }
+
private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
private TestDisplayArea(WindowManagerService wms, Rect bounds) {
super(wms, ANY, "half display area");
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 4bea9a2eea45..781cfec77f08 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1379,7 +1379,7 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
- public void testNoFixedRotationWithPip() {
+ public void testFixedRotationWithPip() {
final DisplayContent displayContent = mDefaultDisplay;
unblockDisplayRotation(displayContent);
// Make resume-top really update the activity state.
@@ -1406,15 +1406,20 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(homeConfigOrientation, displayConfig.orientation);
clearInvocations(mWm);
- // Leave PiP to fullscreen. The orientation can be updated from
- // ActivityRecord#reportDescendantOrientationChangeIfNeeded.
- pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ // Leave PiP to fullscreen. Simulate the step of PipTaskOrganizer that sets the activity
+ // to fullscreen, so fixed rotation will apply on it.
+ pinnedActivity.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
homeActivity.setState(Task.ActivityState.STOPPED, "test");
- assertFalse(displayContent.hasTopFixedRotationLaunchingApp());
- verify(mWm, atLeastOnce()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
- assertEquals(pinnedConfigOrientation, displayConfig.orientation);
+ assertTrue(displayContent.hasTopFixedRotationLaunchingApp());
+ verify(mWm, never()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
+ assertNotEquals(pinnedConfigOrientation, displayConfig.orientation);
+
+ // Assume the animation of PipTaskOrganizer is done and then commit fullscreen to task.
+ pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ displayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
assertFalse(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging());
+ assertEquals(pinnedConfigOrientation, displayConfig.orientation);
clearInvocations(mWm);
// Enter PiP from fullscreen. The orientation can be updated from
@@ -1608,6 +1613,16 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
+ public void testGetOrCreateRootHomeTask_dontMoveToTop() {
+ DisplayContent display = createNewDisplay();
+ display.mDontMoveToTop = true;
+ TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
+
+ assertNull(taskDisplayArea.getRootHomeTask());
+ assertNull(taskDisplayArea.getOrCreateRootHomeTask());
+ }
+
+ @Test
public void testValidWindowingLayer() {
final SurfaceControl windowingLayer = mDisplayContent.getWindowingLayer();
assertNotNull(windowingLayer);
@@ -1819,7 +1834,7 @@ public class DisplayContentTests extends WindowTestsBase {
mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
}
- private void performLayout(DisplayContent dc) {
+ static void performLayout(DisplayContent dc) {
dc.setLayoutNeeded();
dc.performLayout(true /* initial */, false /* updateImeWindows */);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
index 33e8fc0bd94b..3e05c86898e2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
@@ -217,6 +217,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase {
SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo);
overrideSettings.mShouldShowSystemDecors = true;
overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL;
+ overrideSettings.mDontMoveToTop = true;
provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings);
assertTrue(mOverrideSettingsStorage.wasWriteSuccessful());
@@ -227,6 +228,8 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase {
getStoredDisplayAttributeValue(mOverrideSettingsStorage, "shouldShowSystemDecors"));
assertEquals("Attribute value must be stored", "0",
getStoredDisplayAttributeValue(mOverrideSettingsStorage, "imePolicy"));
+ assertEquals("Attribute value must be stored", "true",
+ getStoredDisplayAttributeValue(mOverrideSettingsStorage, "dontMoveToTop"));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index f935bfcd9068..d663b649fbba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -122,6 +122,7 @@ public class LockTaskControllerTest {
@Mock private StatusBarManagerInternal mStatusBarManagerInternal;
@Mock private TelecomManager mTelecomManager;
@Mock private RecentTasks mRecentTasks;
+ @Mock private TaskChangeNotificationController mTaskChangeNotificationController;
private LockTaskController mLockTaskController;
private Context mContext;
@@ -145,7 +146,7 @@ public class LockTaskControllerTest {
mSupervisor.mRootWindowContainer = mRootWindowContainer;
mLockTaskController = new LockTaskController(mContext, mSupervisor,
- new ImmediatelyExecuteHandler());
+ new ImmediatelyExecuteHandler(), mTaskChangeNotificationController);
mLockTaskController.setWindowManager(mWindowManager);
mLockTaskController.mStatusBarService = mStatusBarService;
mLockTaskController.mDevicePolicyManager = mDevicePolicyManager;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 21fd04ee3ae9..925b6f9601be 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -29,6 +29,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -59,6 +60,10 @@ import android.app.ActivityTaskManager;
import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
+import android.graphics.ColorSpace;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -66,6 +71,8 @@ import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
import android.util.SparseBooleanArray;
+import android.view.Surface;
+import android.window.TaskSnapshot;
import androidx.test.filters.MediumTest;
@@ -446,12 +453,8 @@ public class RecentTasksTest extends WindowTestsBase {
doReturn(false).when(child2).isOrganized();
mRecentTasks.add(root);
- doNothing().when(mRecentTasks).loadUserRecentsLocked(anyInt());
- doReturn(true).when(mRecentTasks).isUserRunning(anyInt(), anyInt());
- final List<RecentTaskInfo> infos = mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
- true /* getTasksAllowed */, TEST_USER_0_ID, 0 /* callingUid */).getList();
-
// Make sure only organized child will be appended.
+ final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
final List<RecentTaskInfo> childrenTaskInfos = infos.get(0).childrenTaskInfos;
assertEquals(childrenTaskInfos.size(), 1);
assertEquals(childrenTaskInfos.get(0).taskId, child1.mTaskId);
@@ -1051,9 +1054,6 @@ public class RecentTasksTest extends WindowTestsBase {
@Test
public void testTaskInfo_expectNoExtras() {
- doNothing().when(mRecentTasks).loadUserRecentsLocked(anyInt());
- doReturn(true).when(mRecentTasks).isUserRunning(anyInt(), anyInt());
-
final Bundle data = new Bundle();
data.putInt("key", 100);
final Task task1 = createTaskBuilder(".Task").build();
@@ -1063,8 +1063,7 @@ public class RecentTasksTest extends WindowTestsBase {
.build();
mRecentTasks.add(r1.getTask());
- final List<RecentTaskInfo> infos = mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
- true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList();
+ final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
assertTrue(infos.size() == 1);
for (int i = 0; i < infos.size(); i++) {
final Bundle extras = infos.get(i).baseIntent.getExtras();
@@ -1072,6 +1071,60 @@ public class RecentTasksTest extends WindowTestsBase {
}
}
+ @Test
+ public void testLastSnapshotData_snapshotSaved() {
+ final TaskSnapshot snapshot = createSnapshot(new Point(100, 100), new Point(80, 80));
+ final Task task1 = createTaskBuilder(".Task").build();
+ task1.onSnapshotChanged(snapshot);
+
+ mRecentTasks.add(task1);
+ final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
+ final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
+ infos.get(0).lastSnapshotData;
+ assertTrue(lastSnapshotData.taskSize.equals(100, 100));
+ assertTrue(lastSnapshotData.bufferSize.equals(80, 80));
+ }
+
+ @Test
+ public void testLastSnapshotData_noBuffer() {
+ final Task task1 = createTaskBuilder(".Task").build();
+ final TaskSnapshot snapshot = createSnapshot(new Point(100, 100), null);
+ task1.onSnapshotChanged(snapshot);
+
+ mRecentTasks.add(task1);
+ final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
+ final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
+ infos.get(0).lastSnapshotData;
+ assertTrue(lastSnapshotData.taskSize.equals(100, 100));
+ assertNull(lastSnapshotData.bufferSize);
+ }
+
+ @Test
+ public void testLastSnapshotData_notSet() {
+ final Task task1 = createTaskBuilder(".Task").build();
+
+ mRecentTasks.add(task1);
+ final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */);
+ final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
+ infos.get(0).lastSnapshotData;
+ assertNull(lastSnapshotData.taskSize);
+ assertNull(lastSnapshotData.bufferSize);
+ }
+
+ private TaskSnapshot createSnapshot(Point taskSize, Point bufferSize) {
+ HardwareBuffer buffer = null;
+ if (bufferSize != null) {
+ buffer = mock(HardwareBuffer.class);
+ doReturn(bufferSize.x).when(buffer).getWidth();
+ doReturn(bufferSize.y).when(buffer).getHeight();
+ }
+ return new TaskSnapshot(1, new ComponentName("", ""), buffer,
+ ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
+ Surface.ROTATION_0, taskSize, new Rect() /* insets */, false /* isLowResolution */,
+ true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* mSystemUiVisibility */,
+ false /* isTranslucent */, false /* hasImeSurface */);
+ }
+
/**
* Ensures that the raw recent tasks list is in the provided order. Note that the expected tasks
* should be ordered from least to most recent.
@@ -1084,15 +1137,19 @@ public class RecentTasksTest extends WindowTestsBase {
}
}
+ private List<RecentTaskInfo> getRecentTasks(int flags) {
+ doNothing().when(mRecentTasks).loadUserRecentsLocked(anyInt());
+ doReturn(true).when(mRecentTasks).isUserRunning(anyInt(), anyInt());
+ return mRecentTasks.getRecentTasks(MAX_VALUE, flags, true /* getTasksAllowed */,
+ TEST_USER_0_ID, 0 /* callingUid */).getList();
+ }
+
/**
* Ensures that the recent tasks list is in the provided order. Note that the expected tasks
* should be ordered from least to most recent.
*/
private void assertGetRecentTasksOrder(int getRecentTaskFlags, Task... expectedTasks) {
- doNothing().when(mRecentTasks).loadUserRecentsLocked(anyInt());
- doReturn(true).when(mRecentTasks).isUserRunning(anyInt(), anyInt());
- List<RecentTaskInfo> infos = mRecentTasks.getRecentTasks(MAX_VALUE, getRecentTaskFlags,
- true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList();
+ List<RecentTaskInfo> infos = getRecentTasks(getRecentTaskFlags);
assertTrue(expectedTasks.length == infos.size());
for (int i = 0; i < infos.size(); i++) {
assertTrue(expectedTasks[i].mTaskId == infos.get(i).taskId);
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 15e045c85c29..2fdd63ed93d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -16,8 +16,11 @@
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.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;
@@ -94,10 +97,19 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mController = new RemoteAnimationController(mWm, mAdapter, mHandler);
}
+ private WindowState createAppOverlayWindow() {
+ final WindowState win = createWindow(null /* parent */, TYPE_APPLICATION_OVERLAY,
+ "testOverlayWindow");
+ win.mActivityRecord = null;
+ win.mHasSurface = true;
+ return win;
+ }
+
@Test
public void testRun() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+ final WindowState overlayWin = createAppOverlayWindow();
try {
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
@@ -109,12 +121,12 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
- final ArgumentCaptor<RemoteAnimationTarget[]> nonApsCaptor =
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
- appsCaptor.capture(), wallpapersCaptor.capture(), nonApsCaptor.capture(),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -130,6 +142,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
finishedCaptor.getValue().onAnimationFinished();
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
eq(adapter));
+ assertEquals(0, nonAppsCaptor.getValue().length);
} finally {
mDisplayContent.mOpeningApps.clear();
}
@@ -424,6 +437,89 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
}
}
+ @Test
+ public void testNonAppIncluded_keygaurdGoingAway() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+ // Add overlay window hidden by the keyguard.
+ final WindowState overlayWin = createAppOverlayWindow();
+ overlayWin.hide(false /* doAnimation */, false /* requestAnim */);
+ try {
+ 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_OLD_KEYGUARD_GOING_AWAY);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_KEYGUARD_GOING_AWAY),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
+ finishedCaptor.capture());
+ assertEquals(1, appsCaptor.getValue().length);
+ final RemoteAnimationTarget app = appsCaptor.getValue()[0];
+ assertEquals(new Point(50, 100), app.position);
+ assertEquals(new Rect(50, 100, 150, 150), app.sourceContainerBounds);
+ assertEquals(win.mActivityRecord.getPrefixOrderIndex(), app.prefixOrderIndex);
+ assertEquals(win.mActivityRecord.getTask().mTaskId, app.taskId);
+ assertEquals(mMockLeash, app.leash);
+ assertEquals(false, app.isTranslucent);
+ verify(mMockTransaction).setPosition(mMockLeash, app.position.x, app.position.y);
+ verify(mMockTransaction).setWindowCrop(mMockLeash, 100, 50);
+
+ finishedCaptor.getValue().onAnimationFinished();
+ verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
+ eq(adapter));
+ assertEquals(1, nonAppsCaptor.getValue().length);
+ } finally {
+ mDisplayContent.mOpeningApps.clear();
+ }
+ }
+
+ @Test
+ public void testNonAppIncluded_keygaurdGoingAwayToWallpaper() throws Exception {
+ final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
+ true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+ // Add overlay window hidden by the keyguard.
+ final WindowState overlayWin = createAppOverlayWindow();
+ overlayWin.hide(false /* doAnimation */, false /* requestAnim */);
+ try {
+ 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_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
+ finishedCaptor.capture());
+ assertEquals(1, wallpapersCaptor.getValue().length);
+ assertEquals(1, nonAppsCaptor.getValue().length);
+ } finally {
+ mDisplayContent.mOpeningApps.clear();
+ }
+ }
+
private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
verify(binder, atLeast(0)).asBinder();
verifyNoMoreInteractions(binder);
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 cc4d4eaa9e8b..e843dd71381f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -482,8 +482,7 @@ public class SizeCompatTests extends WindowTestsBase {
public void testHandleActivitySizeCompatModeChanged() {
setUpDisplaySizeWithApp(1000, 2000);
doReturn(true).when(mTask).isOrganized();
- ActivityRecord activity = mActivity;
- activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+ mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
@@ -499,12 +498,12 @@ public class SizeCompatTests extends WindowTestsBase {
// Make the activity resizable again by restarting it
clearInvocations(mTask);
- activity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
- activity.mVisibleRequested = true;
- activity.restartProcessIfVisible();
+ mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
+ mActivity.mVisibleRequested = true;
+ mActivity.restartProcessIfVisible();
// The full lifecycle isn't hooked up so manually set state to resumed
- activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
- mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity);
+ mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+ mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(mActivity);
// Expect null token when switching to non-size-compat mode activity.
verify(mTask).onSizeCompatActivityChanged();
@@ -515,6 +514,46 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testHandleActivitySizeCompatModeChangedOnDifferentTask() {
+ setUpDisplaySizeWithApp(1000, 2000);
+ doReturn(true).when(mTask).isOrganized();
+ mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+ prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ assertFitted();
+
+ // Resize the display so that the activity exercises size-compat mode.
+ resizeDisplay(mTask.mDisplayContent, 1000, 2500);
+
+ // Expect the exact token when the activity is in size compatibility mode.
+ verify(mTask).onSizeCompatActivityChanged();
+ ActivityManager.RunningTaskInfo taskInfo = mTask.getTaskInfo();
+
+ assertEquals(mActivity.appToken, taskInfo.topActivityToken);
+ assertTrue(taskInfo.topActivityInSizeCompat);
+
+ // Create another Task to hold another size compat activity.
+ clearInvocations(mTask);
+ final Task secondTask = new TaskBuilder(mSupervisor).setDisplay(mTask.getDisplayContent())
+ .setCreateActivity(true).build();
+ final ActivityRecord secondActivity = secondTask.getTopNonFinishingActivity();
+ doReturn(true).when(secondTask).isOrganized();
+ secondActivity.setState(Task.ActivityState.RESUMED,
+ "testHandleActivitySizeCompatModeChanged");
+ prepareUnresizable(secondActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Resize the display so that the activity exercises size-compat mode.
+ resizeDisplay(mTask.mDisplayContent, 1000, 3000);
+
+ // Expect the exact token when the activity is in size compatibility mode.
+ verify(secondTask).onSizeCompatActivityChanged();
+ verify(mTask, never()).onSizeCompatActivityChanged();
+ taskInfo = secondTask.getTaskInfo();
+
+ assertEquals(secondActivity.appToken, taskInfo.topActivityToken);
+ assertTrue(taskInfo.topActivityInSizeCompat);
+ }
+
+ @Test
public void testShouldUseSizeCompatModeOnResizableTask() {
setUpDisplaySizeWithApp(1000, 2500);
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 d83e9c21fae7..e22cda60d423 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -156,6 +156,9 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
spyOn(mDisplayContent);
doReturn(true).when(mDisplayContent).isTrusted();
+ // Allow child stack to move to top.
+ mDisplayContent.mDontMoveToTop = false;
+
// The display contains pinned stack that was added in {@link #setUp}.
final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
@@ -173,6 +176,65 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
}
@Test
+ public void testMovingChildTaskOnTop() {
+ // Make sure the display is trusted display which capable to move the stack to top.
+ spyOn(mDisplayContent);
+ doReturn(true).when(mDisplayContent).isTrusted();
+
+ // Allow child stack to move to top.
+ mDisplayContent.mDontMoveToTop = false;
+
+ // The display contains pinned stack that was added in {@link #setUp}.
+ Task stack = createTaskStackOnDisplay(mDisplayContent);
+ Task task = createTaskInStack(stack, 0 /* userId */);
+
+ // Add another display at top.
+ mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
+ false /* includingParents */);
+
+ // Ensure that original display ({@code mDisplayContent}) is not on top.
+ assertEquals("Testing DisplayContent should not be on the top",
+ mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent));
+
+ // Move the task of {@code mDisplayContent} to top.
+ stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
+
+ // Ensure that original display ({@code mDisplayContent}) is now on the top.
+ assertEquals("The testing DisplayContent should be moved to top with task",
+ mWm.mRoot.getChildCount() - 1, mWm.mRoot.mChildren.indexOf(mDisplayContent));
+ }
+
+ @Test
+ public void testDontMovingChildTaskOnTop() {
+ // Make sure the display is trusted display which capable to move the stack to top.
+ spyOn(mDisplayContent);
+ doReturn(true).when(mDisplayContent).isTrusted();
+
+ // Allow child stack to move to top.
+ mDisplayContent.mDontMoveToTop = true;
+
+ // The display contains pinned stack that was added in {@link #setUp}.
+ Task stack = createTaskStackOnDisplay(mDisplayContent);
+ Task task = createTaskInStack(stack, 0 /* userId */);
+
+ // Add another display at top.
+ mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
+ false /* includingParents */);
+
+ // Ensure that original display ({@code mDisplayContent}) is not on top.
+ assertEquals("Testing DisplayContent should not be on the top",
+ mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent));
+
+ // Try moving the task of {@code mDisplayContent} to top.
+ stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
+
+ // Ensure that original display ({@code mDisplayContent}) hasn't moved and is not
+ // on the top.
+ assertEquals("The testing DisplayContent should not be moved to top with task",
+ mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent));
+ }
+
+ @Test
public void testReuseTaskAsRootTask() {
final Task candidateTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, mDisplayContent);
@@ -233,7 +295,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
homeActivity.mVisibleRequested = true;
assertFalse(rootHomeTask.isVisible());
- assertEquals(rootWindowContainer.getOrientation(), rootHomeTask.getOrientation());
+ assertEquals(defaultTaskDisplayArea.getOrientation(), rootHomeTask.getOrientation());
}
@Test
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 e4b865fd2941..86d8eee878fd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -91,11 +91,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
return attrs.type == TYPE_NOTIFICATION_SHADE;
}
- @Override
- public boolean canBeHiddenByKeyguardLw(WindowState win) {
- return false;
- }
-
/**
* Sets a runnable to run when adding a splash screen which gets executed after the window has
* been added but before returning the surface.
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 77fca3d2fdeb..2c2c09a5750a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -61,6 +61,7 @@ import static org.mockito.Mockito.clearInvocations;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager.RootTaskInfo;
+import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.content.pm.ActivityInfo;
import android.content.pm.ParceledListSlice;
@@ -549,6 +550,9 @@ public class WindowOrganizerTests extends WindowTestsBase {
public void removeStartingWindow(int taskId) { }
@Override
+ public void copySplashScreenView(int taskId) { }
+
+ @Override
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { }
@Override
@@ -609,10 +613,10 @@ public class WindowOrganizerTests extends WindowTestsBase {
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
}
-
@Override
public void removeStartingWindow(int taskId) { }
-
+ @Override
+ public void copySplashScreenView(int taskId) { }
@Override
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { }
@@ -685,7 +689,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Override
public void removeStartingWindow(int taskId) { }
-
+ @Override
+ public void copySplashScreenView(int taskId) { }
@Override
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { }
@@ -829,6 +834,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Override
public void removeStartingWindow(int taskId) { }
@Override
+ public void copySplashScreenView(int taskId) { }
+ @Override
public void onTaskAppeared(RunningTaskInfo info, SurfaceControl leash) {
mInfo = info;
}
@@ -970,7 +977,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertTrue(stack2.isOrganized());
// Verify a back pressed does not call the organizer
- mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token);
+ mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token,
+ new IRequestFinishCallback.Default());
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, never()).onBackPressedOnTaskRoot(any());
@@ -980,7 +988,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
stack.mRemoteToken.toWindowContainerToken(), true);
// Verify now that the back press does call the organizer
- mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token);
+ mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token,
+ new IRequestFinishCallback.Default());
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
@@ -990,7 +999,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
stack.mRemoteToken.toWindowContainerToken(), false);
// Verify now that the back press no longer calls the organizer
- mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token);
+ mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token,
+ new IRequestFinishCallback.Default());
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
@@ -1195,7 +1205,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
mWm.mWindowPlacerLocked.deferLayout();
stack.removeImmediately();
- mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token);
+ mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token,
+ new IRequestFinishCallback.Default());
waitUntilHandlersIdle();
ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
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 3231f8b6551a..896969548af3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -67,11 +67,14 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.when;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.view.Gravity;
import android.view.InputWindowHandle;
import android.view.InsetsState;
import android.view.SurfaceControl;
@@ -559,6 +562,46 @@ public class WindowStateTests extends WindowTestsBase {
assertTrue(window.isVisibleByPolicy());
}
+ @Test
+ public void testCompatOverrideScale() {
+ final float overrideScale = 2; // 0.5x on client side.
+ final CompatModePackages cmp = mWm.mAtmService.mCompatModePackages;
+ spyOn(cmp);
+ doReturn(overrideScale).when(cmp).getCompatScale(anyString(), anyInt());
+ final WindowState w = createWindow(null, TYPE_APPLICATION_OVERLAY, "win");
+ makeWindowVisible(w);
+ w.setRequestedSize(100, 200);
+ w.mAttrs.width = w.mAttrs.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ w.mAttrs.gravity = Gravity.TOP | Gravity.LEFT;
+ DisplayContentTests.performLayout(mDisplayContent);
+
+ // Frame on screen = 100x200. Compat frame on client = 50x100.
+ final Rect unscaledCompatFrame = new Rect(w.getWindowFrames().mCompatFrame);
+ unscaledCompatFrame.scale(overrideScale);
+ assertEquals(w.getWindowFrames().mFrame, unscaledCompatFrame);
+
+ // Surface should apply the scale.
+ w.prepareSurfaces();
+ verify(w.getPendingTransaction()).setMatrix(w.getSurfaceControl(),
+ overrideScale, 0, 0, overrideScale);
+
+ // According to "dp * density / 160 = px", density is scaled and the size in dp is the same.
+ final CompatibilityInfo compatInfo = cmp.compatibilityInfoForPackageLocked(
+ mContext.getApplicationInfo());
+ final Configuration winConfig = w.getConfiguration();
+ final Configuration clientConfig = new Configuration(w.getConfiguration());
+ compatInfo.applyToConfiguration(clientConfig.densityDpi, clientConfig);
+
+ assertEquals(winConfig.screenWidthDp, clientConfig.screenWidthDp);
+ assertEquals(winConfig.screenHeightDp, clientConfig.screenHeightDp);
+ assertEquals(winConfig.smallestScreenWidthDp, clientConfig.smallestScreenWidthDp);
+ assertEquals(winConfig.densityDpi, (int) (clientConfig.densityDpi * overrideScale));
+
+ final Rect unscaledClientBounds = new Rect(clientConfig.windowConfiguration.getBounds());
+ unscaledClientBounds.scale(overrideScale);
+ assertEquals(w.getWindowConfiguration().getBounds(), unscaledClientBounds);
+ }
+
@UseTestDisplay(addWindows = { W_ABOVE_ACTIVITY, W_NOTIFICATION_SHADE })
@Test
public void testRequestDrawIfNeeded() {
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 3492d90cc0f3..c13d6b19bf1d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -337,6 +337,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
attrs.setTitle(name);
+ attrs.packageName = "test";
final WindowState w = new WindowState(service, session, iWindow, token, parent,
OP_NONE, attrs, VISIBLE, ownerId, userId,
@@ -890,6 +891,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
mTask.moveToFront("createActivity");
}
// Make visible by default...
+ activity.mVisibleRequested = true;
activity.setVisible(true);
}
@@ -1142,6 +1144,9 @@ class WindowTestsBase extends SystemServiceTestsBase {
public void removeStartingWindow(int taskId) {
}
@Override
+ public void copySplashScreenView(int taskId) {
+ }
+ @Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) {
}
@Override
diff --git a/services/texttospeech/Android.bp b/services/texttospeech/Android.bp
new file mode 100644
index 000000000000..bacc932f760f
--- /dev/null
+++ b/services/texttospeech/Android.bp
@@ -0,0 +1,13 @@
+filegroup {
+ name: "services.texttospeech-sources",
+ srcs: ["java/**/*.java"],
+ path: "java",
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+ name: "services.texttospeech",
+ defaults: ["platform_service_defaults"],
+ srcs: [":services.texttospeech-sources"],
+ libs: ["services.core"],
+} \ No newline at end of file
diff --git a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
new file mode 100644
index 000000000000..f80590420d09
--- /dev/null
+++ b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.texttospeech;
+
+import static com.android.internal.infra.AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import android.speech.tts.ITextToSpeechService;
+import android.speech.tts.ITextToSpeechSession;
+import android.speech.tts.ITextToSpeechSessionCallback;
+import android.speech.tts.TextToSpeech;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.ServiceConnector;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Manages per-user text to speech session activated by {@link TextToSpeechManagerService}.
+ * Creates {@link TtsClient} interface object with direct connection to
+ * {@link android.speech.tts.TextToSpeechService} provider.
+ *
+ * @see ITextToSpeechSession
+ * @see TextToSpeech
+ */
+final class TextToSpeechManagerPerUserService extends
+ AbstractPerUserSystemService<TextToSpeechManagerPerUserService,
+ TextToSpeechManagerService> {
+
+ private static final String TAG = TextToSpeechManagerPerUserService.class.getSimpleName();
+
+ TextToSpeechManagerPerUserService(
+ @NonNull TextToSpeechManagerService master,
+ @NonNull Object lock, @UserIdInt int userId) {
+ super(master, lock, userId);
+ }
+
+ void createSessionLocked(String engine, ITextToSpeechSessionCallback sessionCallback) {
+ TextToSpeechSessionConnection.start(getContext(), mUserId, engine, sessionCallback);
+ }
+
+ @GuardedBy("mLock")
+ @Override // from PerUserSystemService
+ @NonNull
+ protected ServiceInfo newServiceInfoLocked(
+ @SuppressWarnings("unused") @NonNull ComponentName serviceComponent)
+ throws PackageManager.NameNotFoundException {
+ try {
+ return AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+ PackageManager.GET_META_DATA, mUserId);
+ } catch (RemoteException e) {
+ throw new PackageManager.NameNotFoundException(
+ "Could not get service for " + serviceComponent);
+ }
+ }
+
+ private static class TextToSpeechSessionConnection extends
+ ServiceConnector.Impl<ITextToSpeechService> {
+
+ private final String mEngine;
+ private final ITextToSpeechSessionCallback mCallback;
+ private final DeathRecipient mUnbindOnDeathHandler;
+
+ static void start(Context context, @UserIdInt int userId, String engine,
+ ITextToSpeechSessionCallback callback) {
+ new TextToSpeechSessionConnection(context, userId, engine, callback).start();
+ }
+
+ private TextToSpeechSessionConnection(Context context, @UserIdInt int userId, String engine,
+ ITextToSpeechSessionCallback callback) {
+ super(context,
+ new Intent(TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE).setPackage(engine),
+ Context.BIND_AUTO_CREATE,
+ userId,
+ ITextToSpeechService.Stub::asInterface);
+ mEngine = engine;
+ mCallback = callback;
+ mUnbindOnDeathHandler = () -> unbindEngine("client process death is reported");
+ }
+
+ private void start() {
+ Slog.d(TAG, "Trying to start connection to TTS engine: " + mEngine);
+
+ connect()
+ .thenAccept(
+ serviceBinder -> {
+ if (serviceBinder != null) {
+ Slog.d(TAG,
+ "Connected successfully to TTS engine: " + mEngine);
+ try {
+ mCallback.onConnected(new ITextToSpeechSession.Stub() {
+ @Override
+ public void disconnect() {
+ unbindEngine("client disconnection request");
+ }
+ }, serviceBinder.asBinder());
+
+ mCallback.asBinder().linkToDeath(mUnbindOnDeathHandler, 0);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Error notifying the client on connection", ex);
+
+ unbindEngine(
+ "failed communicating with the client - process "
+ + "is dead");
+ }
+ } else {
+ Slog.w(TAG, "Failed to obtain TTS engine binder");
+ runSessionCallbackMethod(
+ () -> mCallback.onError("Failed creating TTS session"));
+ }
+ })
+ .exceptionally(ex -> {
+ Slog.w(TAG, "TTS engine binding error", ex);
+ runSessionCallbackMethod(
+ () -> mCallback.onError(
+ "Failed creating TTS session: " + ex.getCause()));
+
+ return null;
+ });
+ }
+
+ @Override // from ServiceConnector.Impl
+ protected void onServiceConnectionStatusChanged(
+ ITextToSpeechService service, boolean connected) {
+ if (!connected) {
+ Slog.w(TAG, "Disconnected from TTS engine");
+ runSessionCallbackMethod(mCallback::onDisconnected);
+
+ try {
+ mCallback.asBinder().unlinkToDeath(mUnbindOnDeathHandler, 0);
+ } catch (NoSuchElementException ex) {
+ Slog.d(TAG, "The death recipient was not linked.");
+ }
+ }
+ }
+
+ @Override // from ServiceConnector.Impl
+ protected long getAutoDisconnectTimeoutMs() {
+ return PERMANENT_BOUND_TIMEOUT_MS;
+ }
+
+ private void unbindEngine(String reason) {
+ Slog.d(TAG, "Unbinding TTS engine: " + mEngine + ". Reason: " + reason);
+ unbind();
+ }
+ }
+
+ static void runSessionCallbackMethod(ThrowingRunnable callbackRunnable) {
+ try {
+ callbackRunnable.runOrThrow();
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed running callback method", ex);
+ }
+ }
+
+ interface ThrowingRunnable {
+ void runOrThrow() throws RemoteException;
+ }
+}
diff --git a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerService.java b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerService.java
new file mode 100644
index 000000000000..9015563f439e
--- /dev/null
+++ b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerService.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.texttospeech;
+
+import static com.android.server.texttospeech.TextToSpeechManagerPerUserService.runSessionCallbackMethod;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.UserHandle;
+import android.speech.tts.ITextToSpeechManager;
+import android.speech.tts.ITextToSpeechSessionCallback;
+
+import com.android.server.infra.AbstractMasterSystemService;
+
+
+/**
+ * A service that allows secured synthesizing of text to speech audio. Upon request creates a
+ * session
+ * that is managed by {@link TextToSpeechManagerPerUserService}.
+ *
+ * @see ITextToSpeechManager
+ */
+public final class TextToSpeechManagerService extends
+ AbstractMasterSystemService<TextToSpeechManagerService,
+ TextToSpeechManagerPerUserService> {
+
+ private static final String TAG = TextToSpeechManagerService.class.getSimpleName();
+
+ public TextToSpeechManagerService(@NonNull Context context) {
+ super(context, /* serviceNameResolver= */ null,
+ /* disallowProperty = */null);
+ }
+
+ @Override // from SystemService
+ public void onStart() {
+ publishBinderService(Context.TEXT_TO_SPEECH_MANAGER_SERVICE,
+ new TextToSpeechManagerServiceStub());
+ }
+
+ @Override
+ protected TextToSpeechManagerPerUserService newServiceLocked(
+ @UserIdInt int resolvedUserId, boolean disabled) {
+ return new TextToSpeechManagerPerUserService(this, mLock, resolvedUserId);
+ }
+
+ private final class TextToSpeechManagerServiceStub extends ITextToSpeechManager.Stub {
+ @Override
+ public void createSession(String engine,
+ ITextToSpeechSessionCallback sessionCallback) {
+ synchronized (mLock) {
+ TextToSpeechManagerPerUserService perUserService = getServiceForUserLocked(
+ UserHandle.getCallingUserId());
+ if (perUserService != null) {
+ perUserService.createSessionLocked(engine, sessionCallback);
+ } else {
+ runSessionCallbackMethod(
+ () -> sessionCallback.onError("Service is not available for user"));
+ }
+ }
+ }
+ }
+}
diff --git a/services/usage/Android.bp b/services/usage/Android.bp
index 80f040b1e15e..867773da7ba9 100644
--- a/services/usage/Android.bp
+++ b/services/usage/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.usage-sources",
srcs: ["java/**/*.java"],
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 0cb1255c6830..9e419d4c3f3b 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1526,15 +1526,19 @@ public class UsageStatsService extends SystemService implements
@Override
public ParceledListSlice<UsageStats> queryUsageStats(int bucketType, long beginTime,
- long endTime, String callingPackage) {
+ long endTime, String callingPackage, int userId) {
if (!hasPermission(callingPackage)) {
return null;
}
+ final int callingUid = Binder.getCallingUid();
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
+ userId, false, true, "queryUsageStats", callingPackage);
+
+ // Check the caller's userId for obfuscation decision, not the user being queried
final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
- Binder.getCallingUid(), UserHandle.getCallingUserId());
+ callingUid, UserHandle.getCallingUserId());
- final int userId = UserHandle.getCallingUserId();
final long token = Binder.clearCallingIdentity();
try {
final List<UsageStats> results = UsageStatsService.this.queryUsageStats(
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index aa8bbde8b32a..01feacd826c5 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.usb-sources",
srcs: ["java/**/*.java"],
@@ -19,6 +28,7 @@ java_library_static {
"android.hardware.usb-V1.0-java",
"android.hardware.usb-V1.1-java",
"android.hardware.usb-V1.2-java",
+ "android.hardware.usb-V1.3-java",
"android.hardware.usb.gadget-V1.0-java",
"android.hardware.usb.gadget-V1.1-java",
"android.hardware.usb.gadget-V1.2-java",
diff --git a/services/usb/OWNERS b/services/usb/OWNERS
index 8ee72b577f3c..60172a36128e 100644
--- a/services/usb/OWNERS
+++ b/services/usb/OWNERS
@@ -1,6 +1,5 @@
badhri@google.com
elaurent@google.com
-moltmann@google.com
albertccwang@google.com
jameswei@google.com
howardyen@google.com \ No newline at end of file
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index ca18c57d6f4b..c53c95cc982e 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -156,6 +156,9 @@ public class UsbPortManager {
*/
private int mIsPortContaminatedNotificationId;
+ private boolean mEnableUsbDataSignaling;
+ protected int mCurrentUsbHalVersion;
+
public UsbPortManager(Context context) {
mContext = context;
try {
@@ -181,6 +184,7 @@ public class UsbPortManager {
if (mProxy != null) {
try {
mProxy.queryPortStatus();
+ mEnableUsbDataSignaling = true;
} catch (RemoteException e) {
logAndPrintException(null,
"ServiceStart: Failed to query port status", e);
@@ -346,6 +350,66 @@ public class UsbPortManager {
}
}
+ /**
+ * Enable/disable the USB data signaling
+ *
+ * @param enable enable or disable USB data signaling
+ */
+ public boolean enableUsbDataSignal(boolean enable) {
+ try {
+ mEnableUsbDataSignaling = enable;
+ // Call into the hal. Use the castFrom method from HIDL.
+ android.hardware.usb.V1_3.IUsb proxy = android.hardware.usb.V1_3.IUsb.castFrom(mProxy);
+ return proxy.enableUsbDataSignal(enable);
+ } catch (RemoteException e) {
+ logAndPrintException(null, "Failed to set USB data signaling", e);
+ return false;
+ } catch (ClassCastException e) {
+ logAndPrintException(null, "Method only applicable to V1.3 or above implementation", e);
+ return false;
+ }
+ }
+
+ /**
+ * Get USB HAL version
+ *
+ * @param none
+ */
+ public int getUsbHalVersion() {
+ return mCurrentUsbHalVersion;
+ }
+
+ /**
+ * update USB HAL version
+ *
+ * @param none
+ */
+ private void updateUsbHalVersion() {
+ android.hardware.usb.V1_3.IUsb usbProxy_V1_3 =
+ android.hardware.usb.V1_3.IUsb.castFrom(mProxy);
+ if (usbProxy_V1_3 != null) {
+ mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_3;
+ return;
+ }
+
+ android.hardware.usb.V1_2.IUsb usbProxy_V1_2 =
+ android.hardware.usb.V1_2.IUsb.castFrom(mProxy);
+ if (usbProxy_V1_2 != null) {
+ mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_2;
+ return;
+ }
+
+ android.hardware.usb.V1_1.IUsb usbProxy_V1_1 =
+ android.hardware.usb.V1_1.IUsb.castFrom(mProxy);
+ if (usbProxy_V1_1 != null) {
+ mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_1;
+ return;
+ }
+
+ mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_0;
+ return;
+ }
+
public void setPortRoles(String portId, int newPowerRole, int newDataRole,
IndentingPrintWriter pw) {
synchronized (mLock) {
@@ -610,6 +674,9 @@ public class UsbPortManager {
for (PortInfo portInfo : mPorts.values()) {
portInfo.dump(dump, "usb_ports", UsbPortManagerProto.USB_PORTS);
}
+
+ dump.write("enable_usb_data_signaling", UsbPortManagerProto.ENABLE_USB_DATA_SIGNALING,
+ mEnableUsbDataSignaling);
}
dump.end(token);
@@ -783,6 +850,7 @@ public class UsbPortManager {
mProxy.linkToDeath(new DeathRecipient(pw), USB_HAL_DEATH_COOKIE);
mProxy.setCallback(mHALCallback);
mProxy.queryPortStatus();
+ mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_0;
} catch (NoSuchElementException e) {
logAndPrintException(pw, "connectToProxy: usb hal service not found."
+ " Did the service fail to start?", e);
@@ -1115,6 +1183,7 @@ public class UsbPortManager {
case MSG_SYSTEM_READY: {
mNotificationManager = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ updateUsbHalVersion();
break;
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index edd4a3874c94..9a13d7648be2 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -745,6 +745,38 @@ public class UsbService extends IUsbManager.Stub {
}
@Override
+ public int getUsbHalVersion() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mPortManager != null) {
+ return mPortManager.getUsbHalVersion();
+ } else {
+ return UsbManager.USB_HAL_NOT_SUPPORTED;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public boolean enableUsbDataSignal(boolean enable) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mPortManager != null) {
+ return mPortManager.enableUsbDataSignal(enable);
+ } else {
+ return false;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public void setUsbDeviceConnectionHandler(ComponentName usbDeviceConnectionHandler) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
synchronized (mLock) {
diff --git a/services/voiceinteraction/Android.bp b/services/voiceinteraction/Android.bp
index 02061be73920..702ddb1b98fe 100644
--- a/services/voiceinteraction/Android.bp
+++ b/services/voiceinteraction/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.voiceinteraction-sources",
srcs: ["java/**/*.java"],
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index be36f82cce7d..8628f89a49c1 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -430,25 +430,17 @@ public class VoiceInteractionManagerService extends SystemService {
// Eventually it will be an error to not specify this.
setCurInteractor(new ComponentName(curInteractorInfo.getServiceInfo().packageName,
curInteractorInfo.getServiceInfo().name), userHandle);
- if (curInteractorInfo.getRecognitionService() != null) {
- setCurRecognizer(
- new ComponentName(curInteractorInfo.getServiceInfo().packageName,
- curInteractorInfo.getRecognitionService()), userHandle);
- return;
- }
+ } else {
+ // No voice interactor, so clear the setting.
+ setCurInteractor(null, userHandle);
}
- // No voice interactor, we'll just set up a simple recognizer.
- initSimpleRecognizer(curInteractorInfo, userHandle);
+ initRecognizer(userHandle);
}
- public void initSimpleRecognizer(VoiceInteractionServiceInfo curInteractorInfo,
- int userHandle) {
+ public void initRecognizer(int userHandle) {
ComponentName curRecognizer = findAvailRecognizer(null, userHandle);
if (curRecognizer != null) {
- if (curInteractorInfo == null) {
- setCurInteractor(null, userHandle);
- }
setCurRecognizer(curRecognizer, userHandle);
}
}
@@ -581,17 +573,24 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
- VoiceInteractionServiceInfo findAvailInteractor(int userHandle, String packageName) {
- List<ResolveInfo> available =
- mContext.getPackageManager().queryIntentServicesAsUser(
- new Intent(VoiceInteractionService.SERVICE_INTERFACE)
- .setPackage(packageName),
- PackageManager.GET_META_DATA
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
+ private List<ResolveInfo> queryInteractorServices(
+ @UserIdInt int user,
+ @Nullable String packageName) {
+ return mContext.getPackageManager().queryIntentServicesAsUser(
+ new Intent(VoiceInteractionService.SERVICE_INTERFACE).setPackage(packageName),
+ PackageManager.GET_META_DATA
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ user);
+ }
+
+ VoiceInteractionServiceInfo findAvailInteractor(
+ @UserIdInt int user,
+ @Nullable String packageName) {
+ List<ResolveInfo> available = queryInteractorServices(user, packageName);
int numAvailable = available.size();
if (numAvailable == 0) {
- Slog.w(TAG, "no available voice interaction services found for user " + userHandle);
+ Slog.w(TAG, "no available voice interaction services found for user " + user);
return null;
}
// Find first system package. We never want to allow third party services to
@@ -1600,20 +1599,6 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
- private @NonNull String getDefaultRecognizer(@NonNull UserHandle user) {
- ResolveInfo resolveInfo = mPm.resolveServiceAsUser(
- new Intent(RecognitionService.SERVICE_INTERFACE),
- PackageManager.GET_META_DATA, user.getIdentifier());
-
- if (resolveInfo == null || resolveInfo.serviceInfo == null) {
- Log.w(TAG, "Unable to resolve default voice recognition service.");
- return "";
- }
-
- return new ComponentName(resolveInfo.serviceInfo.packageName,
- resolveInfo.serviceInfo.name).flattenToShortString();
- }
-
/**
* Convert the assistant-role holder into settings. The rest of the system uses the
* settings.
@@ -1635,21 +1620,12 @@ public class VoiceInteractionManagerService extends SystemService {
Settings.Secure.ASSISTANT, "", userId);
Settings.Secure.putStringForUser(getContext().getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE, "", userId);
- Settings.Secure.putStringForUser(getContext().getContentResolver(),
- Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer(user),
- userId);
} else {
// Assistant is singleton role
String pkg = roleHolders.get(0);
// Try to set role holder as VoiceInteractionService
- List<ResolveInfo> services = mPm.queryIntentServicesAsUser(
- new Intent(VoiceInteractionService.SERVICE_INTERFACE).setPackage(pkg),
- PackageManager.GET_META_DATA
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
-
- for (ResolveInfo resolveInfo : services) {
+ for (ResolveInfo resolveInfo : queryInteractorServices(userId, pkg)) {
ServiceInfo serviceInfo = resolveInfo.serviceInfo;
VoiceInteractionServiceInfo voiceInteractionServiceInfo =
@@ -1670,9 +1646,6 @@ public class VoiceInteractionManagerService extends SystemService {
Settings.Secure.putStringForUser(getContext().getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName,
userId);
- Settings.Secure.putStringForUser(getContext().getContentResolver(),
- Settings.Secure.VOICE_RECOGNITION_SERVICE, serviceRecognizerName,
- userId);
return;
}
@@ -1692,9 +1665,6 @@ public class VoiceInteractionManagerService extends SystemService {
activityInfo.getComponentName().flattenToShortString(), userId);
Settings.Secure.putStringForUser(getContext().getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE, "", userId);
- Settings.Secure.putStringForUser(getContext().getContentResolver(),
- Settings.Secure.VOICE_RECOGNITION_SERVICE,
- getDefaultRecognizer(user), userId);
return;
}
}
@@ -1771,7 +1741,9 @@ public class VoiceInteractionManagerService extends SystemService {
synchronized (VoiceInteractionManagerServiceStub.this) {
Slog.i(TAG, "Force stopping current voice recognizer: "
+ getCurRecognizer(userHandle));
- initSimpleRecognizer(null, userHandle);
+ // TODO: Figure out why the interactor was being cleared and document it.
+ setCurInteractor(null, userHandle);
+ initRecognizer(userHandle);
}
}
return hitInt || hitRec;
@@ -1792,6 +1764,9 @@ public class VoiceInteractionManagerService extends SystemService {
if (isPackageAppearing(pkgName) != PACKAGE_UNCHANGED) {
return;
}
+ if (getCurRecognizer(mCurUser) == null) {
+ initRecognizer(mCurUser);
+ }
final String curInteractorStr = Settings.Secure.getStringForUser(
mContext.getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE, mCurUser);
@@ -1806,12 +1781,6 @@ public class VoiceInteractionManagerService extends SystemService {
availInteractorInfo.getServiceInfo().packageName,
availInteractorInfo.getServiceInfo().name);
setCurInteractor(availInteractor, mCurUser);
- if (getCurRecognizer(mCurUser) == null &&
- availInteractorInfo.getRecognitionService() != null) {
- setCurRecognizer(new ComponentName(
- availInteractorInfo.getServiceInfo().packageName,
- availInteractorInfo.getRecognitionService()), mCurUser);
- }
}
} else {
if (didSomePackagesChange()) {
@@ -1842,10 +1811,7 @@ public class VoiceInteractionManagerService extends SystemService {
if (curRecognizer == null) {
// Could a new recognizer appear when we don't have one pre-installed?
if (anyPackagesAppearing()) {
- curRecognizer = findAvailRecognizer(null, userHandle);
- if (curRecognizer != null) {
- setCurRecognizer(curRecognizer, userHandle);
- }
+ initRecognizer(userHandle);
}
return;
}
diff --git a/services/wifi/Android.bp b/services/wifi/Android.bp
index fcfcbeb72b0b..7f22bd8bd3a4 100644
--- a/services/wifi/Android.bp
+++ b/services/wifi/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.wifi-sources",
srcs: ["java/**/*.java"],
diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp
index f42c755e3efd..98b9b64e1242 100644
--- a/startop/apps/test/Android.bp
+++ b/startop/apps/test/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "startop_test_app",
srcs: [
diff --git a/startop/iorap/Android.bp b/startop/iorap/Android.bp
index 993d1e1092b9..4fdf34cc1814 100644
--- a/startop/iorap/Android.bp
+++ b/startop/iorap/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "services.startop.iorap-javasources",
srcs: ["src/**/*.java"],
diff --git a/startop/iorap/functional_tests/Android.bp b/startop/iorap/functional_tests/Android.bp
index 8a5bd34af653..43c61551cdec 100644
--- a/startop/iorap/functional_tests/Android.bp
+++ b/startop/iorap/functional_tests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "iorap-functional-tests",
srcs: ["src/**/*.java"],
@@ -39,4 +48,3 @@ android_test {
certificate: "platform",
platform_apis: true,
}
-
diff --git a/startop/iorap/stress/Android.bp b/startop/iorap/stress/Android.bp
index f9f251bdd889..6e8725d091fb 100644
--- a/startop/iorap/stress/Android.bp
+++ b/startop/iorap/stress/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_binary {
name: "iorap.stress.memory",
srcs: ["main_memory.cc"],
diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp
index 3e60ad448cae..ad3d001ad7d4 100644
--- a/startop/iorap/tests/Android.bp
+++ b/startop/iorap/tests/Android.bp
@@ -13,6 +13,15 @@
// limitations under the License.
// TODO: once b/80095087 is fixed, rewrite this back to android_test
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "libiorap-java-test-lib",
srcs: ["src/**/*.kt"],
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index cac7b82adc2c..90239212f665 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_defaults {
name: "viewcompiler_defaults",
header_libs: [
diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp
index f783aa68fe92..2048964f4af2 100644
--- a/startop/view_compiler/dex_builder_test/Android.bp
+++ b/startop/view_compiler/dex_builder_test/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
genrule {
name: "generate_compiled_layout1",
tools: [":viewcompiler"],
diff --git a/telecomm/java/android/telecom/CallScreeningService.aidl b/telecomm/java/android/telecom/CallScreeningService.aidl
new file mode 100644
index 000000000000..87b5138745f2
--- /dev/null
+++ b/telecomm/java/android/telecom/CallScreeningService.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+/**
+ * {@hide}
+ */
+parcelable CallScreeningService.ParcelableCallResponse;
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 7988b036ccd3..deeb4331bcaa 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -17,6 +17,7 @@
package android.telecom;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
@@ -30,12 +31,18 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
import com.android.internal.os.SomeArgs;
import com.android.internal.telecom.ICallScreeningAdapter;
import com.android.internal.telecom.ICallScreeningService;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
/**
* This service can be implemented by the default dialer (see
* {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
@@ -132,7 +139,10 @@ public abstract class CallScreeningService extends Service {
.createFromParcelableCall((ParcelableCall) args.arg2);
onScreenCall(callDetails);
if (callDetails.getCallDirection() == Call.Details.DIRECTION_OUTGOING) {
- mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
+ mCallScreeningAdapter.onScreeningResponse(
+ callDetails.getTelecomCallId(),
+ new ComponentName(getPackageName(), getClass().getName()),
+ null);
}
} catch (RemoteException e) {
Log.w(this, "Exception when screening call: " + e);
@@ -157,16 +167,171 @@ public abstract class CallScreeningService extends Service {
private ICallScreeningAdapter mCallScreeningAdapter;
- /*
- * Information about how to respond to an incoming call.
+ /**
+ * Parcelable version of {@link CallResponse} used to do IPC.
+ * @hide
+ */
+ public static class ParcelableCallResponse implements Parcelable {
+ private final boolean mShouldDisallowCall;
+ private final boolean mShouldRejectCall;
+ private final boolean mShouldSilenceCall;
+ private final boolean mShouldSkipCallLog;
+ private final boolean mShouldSkipNotification;
+ private final boolean mShouldScreenCallViaAudioProcessing;
+
+ private final int mCallComposerAttachmentsToShow;
+
+ private ParcelableCallResponse(
+ boolean shouldDisallowCall,
+ boolean shouldRejectCall,
+ boolean shouldSilenceCall,
+ boolean shouldSkipCallLog,
+ boolean shouldSkipNotification,
+ boolean shouldScreenCallViaAudioProcessing,
+ int callComposerAttachmentsToShow) {
+ mShouldDisallowCall = shouldDisallowCall;
+ mShouldRejectCall = shouldRejectCall;
+ mShouldSilenceCall = shouldSilenceCall;
+ mShouldSkipCallLog = shouldSkipCallLog;
+ mShouldSkipNotification = shouldSkipNotification;
+ mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
+ mCallComposerAttachmentsToShow = callComposerAttachmentsToShow;
+ }
+
+ protected ParcelableCallResponse(Parcel in) {
+ mShouldDisallowCall = in.readBoolean();
+ mShouldRejectCall = in.readBoolean();
+ mShouldSilenceCall = in.readBoolean();
+ mShouldSkipCallLog = in.readBoolean();
+ mShouldSkipNotification = in.readBoolean();
+ mShouldScreenCallViaAudioProcessing = in.readBoolean();
+ mCallComposerAttachmentsToShow = in.readInt();
+ }
+
+ public CallResponse toCallResponse() {
+ return new CallResponse.Builder()
+ .setDisallowCall(mShouldDisallowCall)
+ .setRejectCall(mShouldRejectCall)
+ .setSilenceCall(mShouldSilenceCall)
+ .setSkipCallLog(mShouldSkipCallLog)
+ .setSkipNotification(mShouldSkipNotification)
+ .setShouldScreenCallViaAudioProcessing(mShouldScreenCallViaAudioProcessing)
+ .setCallComposerAttachmentsToShow(mCallComposerAttachmentsToShow)
+ .build();
+ }
+
+ public boolean shouldDisallowCall() {
+ return mShouldDisallowCall;
+ }
+
+ public boolean shouldRejectCall() {
+ return mShouldRejectCall;
+ }
+
+ public boolean shouldSilenceCall() {
+ return mShouldSilenceCall;
+ }
+
+ public boolean shouldSkipCallLog() {
+ return mShouldSkipCallLog;
+ }
+
+ public boolean shouldSkipNotification() {
+ return mShouldSkipNotification;
+ }
+
+ public boolean shouldScreenCallViaAudioProcessing() {
+ return mShouldScreenCallViaAudioProcessing;
+ }
+
+ public int getCallComposerAttachmentsToShow() {
+ return mCallComposerAttachmentsToShow;
+ }
+
+ public static final Creator<ParcelableCallResponse> CREATOR =
+ new Creator<ParcelableCallResponse>() {
+ @Override
+ public ParcelableCallResponse createFromParcel(Parcel in) {
+ return new ParcelableCallResponse(in);
+ }
+
+ @Override
+ public ParcelableCallResponse[] newArray(int size) {
+ return new ParcelableCallResponse[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(mShouldDisallowCall);
+ dest.writeBoolean(mShouldRejectCall);
+ dest.writeBoolean(mShouldSilenceCall);
+ dest.writeBoolean(mShouldSkipCallLog);
+ dest.writeBoolean(mShouldSkipNotification);
+ dest.writeBoolean(mShouldScreenCallViaAudioProcessing);
+ dest.writeInt(mCallComposerAttachmentsToShow);
+ }
+ }
+
+ /**
+ * Information about how to respond to an incoming call. Call screening apps can construct an
+ * instance of this class using {@link CallResponse.Builder}.
*/
public static class CallResponse {
+ /**
+ * Bit flag indicating whether to show the picture attachment for call composer.
+ *
+ * Used with {@link Builder#setCallComposerAttachmentsToShow(int)}.
+ */
+ public static final int CALL_COMPOSER_ATTACHMENT_PICTURE = 1;
+
+ /**
+ * Bit flag indicating whether to show the location attachment for call composer.
+ *
+ * Used with {@link Builder#setCallComposerAttachmentsToShow(int)}.
+ */
+ public static final int CALL_COMPOSER_ATTACHMENT_LOCATION = 1 << 1;
+
+ /**
+ * Bit flag indicating whether to show the subject attachment for call composer.
+ *
+ * Used with {@link Builder#setCallComposerAttachmentsToShow(int)}.
+ */
+ public static final int CALL_COMPOSER_ATTACHMENT_SUBJECT = 1 << 2;
+
+ /**
+ * Bit flag indicating whether to show the priority attachment for call composer.
+ *
+ * Used with {@link Builder#setCallComposerAttachmentsToShow(int)}.
+ */
+ public static final int CALL_COMPOSER_ATTACHMENT_PRIORITY = 1 << 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "CALL_COMPOSER_ATTACHMENT_", flag = true,
+ value = {
+ CALL_COMPOSER_ATTACHMENT_PICTURE,
+ CALL_COMPOSER_ATTACHMENT_LOCATION,
+ CALL_COMPOSER_ATTACHMENT_SUBJECT,
+ CALL_COMPOSER_ATTACHMENT_PRIORITY
+ }
+ )
+ public @interface CallComposerAttachmentType {}
+
+ private static final int NUM_CALL_COMPOSER_ATTACHMENT_TYPES = 4;
+
private final boolean mShouldDisallowCall;
private final boolean mShouldRejectCall;
private final boolean mShouldSilenceCall;
private final boolean mShouldSkipCallLog;
private final boolean mShouldSkipNotification;
private final boolean mShouldScreenCallViaAudioProcessing;
+ private final int mCallComposerAttachmentsToShow;
private CallResponse(
boolean shouldDisallowCall,
@@ -174,7 +339,8 @@ public abstract class CallScreeningService extends Service {
boolean shouldSilenceCall,
boolean shouldSkipCallLog,
boolean shouldSkipNotification,
- boolean shouldScreenCallViaAudioProcessing) {
+ boolean shouldScreenCallViaAudioProcessing,
+ int callComposerAttachmentsToShow) {
if (!shouldDisallowCall
&& (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) {
throw new IllegalStateException("Invalid response state for allowed call.");
@@ -190,6 +356,7 @@ public abstract class CallScreeningService extends Service {
mShouldSkipNotification = shouldSkipNotification;
mShouldSilenceCall = shouldSilenceCall;
mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
+ mCallComposerAttachmentsToShow = callComposerAttachmentsToShow;
}
/*
@@ -237,6 +404,49 @@ public abstract class CallScreeningService extends Service {
return mShouldScreenCallViaAudioProcessing;
}
+ /**
+ * @return A bitmask of call composer attachments that should be shown to the user.
+ */
+ public @CallComposerAttachmentType int getCallComposerAttachmentsToShow() {
+ return mCallComposerAttachmentsToShow;
+ }
+
+ /** @hide */
+ public ParcelableCallResponse toParcelable() {
+ return new ParcelableCallResponse(
+ mShouldDisallowCall,
+ mShouldRejectCall,
+ mShouldSilenceCall,
+ mShouldSkipCallLog,
+ mShouldSkipNotification,
+ mShouldScreenCallViaAudioProcessing,
+ mCallComposerAttachmentsToShow
+ );
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CallResponse that = (CallResponse) o;
+ return mShouldDisallowCall == that.mShouldDisallowCall &&
+ mShouldRejectCall == that.mShouldRejectCall &&
+ mShouldSilenceCall == that.mShouldSilenceCall &&
+ mShouldSkipCallLog == that.mShouldSkipCallLog &&
+ mShouldSkipNotification == that.mShouldSkipNotification &&
+ mShouldScreenCallViaAudioProcessing
+ == that.mShouldScreenCallViaAudioProcessing &&
+ mCallComposerAttachmentsToShow == that.mCallComposerAttachmentsToShow;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mShouldDisallowCall, mShouldRejectCall, mShouldSilenceCall,
+ mShouldSkipCallLog, mShouldSkipNotification,
+ mShouldScreenCallViaAudioProcessing,
+ mCallComposerAttachmentsToShow);
+ }
+
public static class Builder {
private boolean mShouldDisallowCall;
private boolean mShouldRejectCall;
@@ -244,6 +454,7 @@ public abstract class CallScreeningService extends Service {
private boolean mShouldSkipCallLog;
private boolean mShouldSkipNotification;
private boolean mShouldScreenCallViaAudioProcessing;
+ private int mCallComposerAttachmentsToShow = -1;
/**
* Sets whether the incoming call should be blocked.
@@ -329,6 +540,38 @@ public abstract class CallScreeningService extends Service {
return this;
}
+ /**
+ * Sets the call composer attachments that should be shown to the user.
+ *
+ * Attachments that are not shown will not be passed to the in-call UI responsible for
+ * displaying the call to the user.
+ *
+ * If this method is not called on a {@link Builder}, all attachments will be shown,
+ * except pictures, which will only be shown to users if the call is from a contact.
+ *
+ * Setting attachments to show will have no effect if the call screening service does
+ * not belong to the same package as the system dialer (as returned by
+ * {@link TelecomManager#getSystemDialerPackage()}).
+ *
+ * @param callComposerAttachmentsToShow A bitmask of call composer attachments to show.
+ */
+ public @NonNull Builder setCallComposerAttachmentsToShow(
+ @CallComposerAttachmentType int callComposerAttachmentsToShow) {
+ // If the argument is less than zero (meaning unset), no-op since the conversion
+ // to/from the parcelable version may call with that value.
+ if (callComposerAttachmentsToShow < 0) {
+ return this;
+ }
+
+ if ((callComposerAttachmentsToShow
+ & (1 << NUM_CALL_COMPOSER_ATTACHMENT_TYPES)) != 0) {
+ throw new IllegalArgumentException("Attachment types must match the ones"
+ + " defined in CallResponse");
+ }
+ mCallComposerAttachmentsToShow = callComposerAttachmentsToShow;
+ return this;
+ }
+
public CallResponse build() {
return new CallResponse(
mShouldDisallowCall,
@@ -336,7 +579,8 @@ public abstract class CallScreeningService extends Service {
mShouldSilenceCall,
mShouldSkipCallLog,
mShouldSkipNotification,
- mShouldScreenCallViaAudioProcessing);
+ mShouldScreenCallViaAudioProcessing,
+ mCallComposerAttachmentsToShow);
}
}
}
@@ -423,21 +667,12 @@ public abstract class CallScreeningService extends Service {
public final void respondToCall(@NonNull Call.Details callDetails,
@NonNull CallResponse response) {
try {
- if (response.getDisallowCall()) {
- mCallScreeningAdapter.disallowCall(
- callDetails.getTelecomCallId(),
- response.getRejectCall(),
- !response.getSkipCallLog(),
- !response.getSkipNotification(),
- new ComponentName(getPackageName(), getClass().getName()));
- } else if (response.getSilenceCall()) {
- mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId());
- } else if (response.getShouldScreenCallViaAudioProcessing()) {
- mCallScreeningAdapter.screenCallFurther(callDetails.getTelecomCallId());
- } else {
- mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
- }
+ mCallScreeningAdapter.onScreeningResponse(
+ callDetails.getTelecomCallId(),
+ new ComponentName(getPackageName(), getClass().getName()),
+ response.toParcelable());
} catch (RemoteException e) {
+ Log.e(this, e, "Got remote exception when returning response");
}
}
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index e4eb12d8327e..e143160694a7 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -3409,11 +3409,20 @@ public abstract class Connection extends Conferenceable {
* {@code true}, {@link #onDisconnect()} will be called soon after
* this is called.
* @param isInContacts Indicates whether the caller is in the user's contacts list.
+ * @param callScreeningResponse The response that was returned from the
+ * {@link CallScreeningService} that handled this call. If no
+ * response was received from a call screening service,
+ * this will be {@code null}.
+ * @param isResponseFromSystemDialer Whether {@code callScreeningResponse} was sent from the
+ * system dialer. If {@code callScreeningResponse} is
+ * {@code null}, this will be {@code false}.
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_CONTACTS)
- public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) { }
+ public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts,
+ @Nullable CallScreeningService.CallResponse callScreeningResponse,
+ boolean isResponseFromSystemDialer) { }
static String toLogSafePhoneNumber(String number) {
// For unknown number, log empty string.
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index e941101b9903..ce9ce7b6c092 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -759,6 +759,8 @@ public abstract class ConnectionService extends Service {
@Override
public void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
+ CallScreeningService.ParcelableCallResponse callScreeningResponse,
+ boolean isResponseFromSystemDialer,
Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_CALL_FILTERING_COMPLETED);
try {
@@ -766,7 +768,9 @@ public abstract class ConnectionService extends Service {
args.arg1 = callId;
args.arg2 = isBlocked;
args.arg3 = isInContacts;
- args.arg4 = Log.createSubsession();
+ args.arg4 = callScreeningResponse;
+ args.arg5 = isResponseFromSystemDialer;
+ args.arg6 = Log.createSubsession();
mHandler.obtainMessage(MSG_ON_CALL_FILTERING_COMPLETED, args).sendToTarget();
} finally {
Log.endSession();
@@ -1437,12 +1441,16 @@ public abstract class ConnectionService extends Service {
case MSG_ON_CALL_FILTERING_COMPLETED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- Log.continueSession((Session) args.arg4,
+ Log.continueSession((Session) args.arg6,
SESSION_HANDLER + SESSION_CALL_FILTERING_COMPLETED);
String callId = (String) args.arg1;
boolean isBlocked = (boolean) args.arg2;
boolean isInContacts = (boolean) args.arg3;
- onCallFilteringCompleted(callId, isBlocked, isInContacts);
+ CallScreeningService.ParcelableCallResponse callScreeningResponse =
+ (CallScreeningService.ParcelableCallResponse) args.arg4;
+ boolean isResponseFromSystemDialer = (boolean) args.arg5;
+ onCallFilteringCompleted(callId, isBlocked, isInContacts,
+ callScreeningResponse, isResponseFromSystemDialer);
} finally {
args.recycle();
Log.endSession();
@@ -2024,7 +2032,7 @@ public abstract class ConnectionService extends Service {
boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean(
TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false);
boolean addSelfManaged = request.getExtras() != null && request.getExtras().getBoolean(
- PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, false);
+ PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, true);
Log.i(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, "
+ "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b, "
+ " addSelfManaged: %b", callManagerAccount, callId, request, isIncoming,
@@ -2462,11 +2470,16 @@ public abstract class ConnectionService extends Service {
}
}
- private void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts) {
- Log.i(this, "onCallFilteringCompleted(%b, %b)", isBlocked, isInContacts);
+ private void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
+ CallScreeningService.ParcelableCallResponse callScreeningResponse,
+ boolean isResponseFromSystemDialer) {
+ Log.i(this, "onCallFilteringCompleted(%s, %b, %b, %s, %b)", callId,
+ isBlocked, isInContacts, callScreeningResponse, isResponseFromSystemDialer);
Connection connection = findConnectionForAction(callId, "onCallFilteringCompleted");
if (connection != null) {
- connection.onCallFilteringCompleted(isBlocked, isInContacts);
+ connection.onCallFilteringCompleted(isBlocked, isInContacts,
+ callScreeningResponse == null ? null : callScreeningResponse.toCallResponse(),
+ isResponseFromSystemDialer);
}
}
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index feb2ca53bbbe..6c6097ac71e5 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -1204,15 +1204,25 @@ public final class RemoteConnection {
* the results of a contacts lookup for the remote party.
* @param isBlocked Whether call filtering indicates that the call should be blocked
* @param isInContacts Whether the remote party is in the user's contacts
+ * @param callScreeningResponse The response that was returned from the
+ * {@link CallScreeningService} that handled this call. If no
+ * response was received from a call screening service,
+ * this will be {@code null}.
+ * @param isResponseFromSystemDialer Whether {@code callScreeningResponse} was sent from the
+ * system dialer. If {@code callScreeningResponse} is
+ * {@code null}, this will be {@code false}.
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_CONTACTS)
- public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) {
+ public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts,
+ @Nullable CallScreeningService.CallResponse callScreeningResponse,
+ boolean isResponseFromSystemDialer) {
Log.startSession("RC.oCFC", getActiveOwnerInfo());
try {
if (mConnected) {
mConnectionService.onCallFilteringCompleted(mConnectionId, isBlocked, isInContacts,
+ callScreeningResponse.toParcelable(), isResponseFromSystemDialer,
null /*Session.Info*/);
}
} catch (RemoteException ignored) {
diff --git a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
index 83c8f62bb3db..0f2e178f1970 100644
--- a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
@@ -17,6 +17,7 @@
package com.android.internal.telecom;
import android.content.ComponentName;
+import android.telecom.CallScreeningService;
/**
* Internal remote callback interface for call screening services.
@@ -26,16 +27,6 @@ import android.content.ComponentName;
* {@hide}
*/
oneway interface ICallScreeningAdapter {
- void allowCall(String callId);
-
- void silenceCall(String callId);
-
- void screenCallFurther(String callId);
-
- void disallowCall(
- String callId,
- boolean shouldReject,
- boolean shouldAddToCallLog,
- boolean shouldShowNotification,
- in ComponentName componentName);
+ void onScreeningResponse(String callId, in ComponentName componentName,
+ in CallScreeningService.ParcelableCallResponse response);
}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 301c2bb6cdb2..7599e189cc37 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -20,6 +20,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.telecom.CallAudioState;
+import android.telecom.CallScreeningService;
import android.telecom.ConnectionRequest;
import android.telecom.Logging.Session;
import android.telecom.PhoneAccountHandle;
@@ -119,7 +120,8 @@ oneway interface IConnectionService {
void sendCallEvent(String callId, String event, in Bundle extras, in Session.Info sessionInfo);
void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
- in Session.Info sessionInfo);
+ in CallScreeningService.ParcelableCallResponse callScreeningResponse,
+ boolean isResponseFromSystemDialer, in Session.Info sessionInfo);
void onExtrasChanged(String callId, in Bundle extras, in Session.Info sessionInfo);
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 48a5ab6d204b..5e50bea05057 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -686,6 +686,67 @@ public final class TelephonyPermissions {
}
/**
+ * Given a list of permissions, check to see if the caller has at least one of them. If the
+ * caller has none of these permissions, throw a SecurityException.
+ */
+ public static void enforceAnyPermissionGranted(Context context, int uid, String message,
+ String... permissions) {
+ if (permissions.length == 0) return;
+ boolean isGranted = false;
+ for (String perm : permissions) {
+ if (context.checkCallingOrSelfPermission(perm) == PERMISSION_GRANTED) {
+ isGranted = true;
+ break;
+ }
+ }
+
+ if (isGranted) return;
+
+ StringBuilder b = new StringBuilder(message);
+ b.append(": Neither user ");
+ b.append(uid);
+ b.append(" nor current process has ");
+ b.append(permissions[0]);
+ for (int i = 1; i < permissions.length; i++) {
+ b.append(" or ");
+ b.append(permissions[i]);
+ }
+ throw new SecurityException(b.toString());
+ }
+
+ /**
+ * Given a list of permissions, check to see if the caller has at least one of them granted. If
+ * not, check to see if the caller has carrier privileges. If the caller does not have any of
+ * these permissions, throw a SecurityException.
+ */
+ public static void enforceAnyPermissionGrantedOrCarrierPrivileges(Context context, int subId,
+ int uid, String message, String... permissions) {
+ if (permissions.length == 0) return;
+ boolean isGranted = false;
+ for (String perm : permissions) {
+ if (context.checkCallingOrSelfPermission(perm) == PERMISSION_GRANTED) {
+ isGranted = true;
+ break;
+ }
+ }
+
+ if (isGranted) return;
+ if (checkCarrierPrivilegeForSubId(context, subId)) return;
+
+ StringBuilder b = new StringBuilder(message);
+ b.append(": Neither user ");
+ b.append(uid);
+ b.append(" nor current process has ");
+ b.append(permissions[0]);
+ for (int i = 1; i < permissions.length; i++) {
+ b.append(" or ");
+ b.append(permissions[i]);
+ }
+ b.append(" or carrier privileges");
+ throw new SecurityException(b.toString());
+ }
+
+ /**
* Throws if the caller is not of a shell (or root) UID.
*
* @param callingUid pass Binder.callingUid().
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index 99f2e5ee0755..c6757fba4d53 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -646,7 +646,7 @@ public class Annotation {
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO,
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE})
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED})
public @interface OverrideNetworkType {}
/**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 32876f188d9c..0115c4e04180 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4273,10 +4273,6 @@ public class CarrierConfigManager {
public static final String KEY_RETRANSMIT_TIMER_MSEC_INT_ARRAY =
KEY_PREFIX + "retransmit_timer_sec_int_array";
- /** Controls if wifi mac Id should be added to network access identifier(NAI) */
- public static final String KEY_ADD_WIFI_MAC_ADDR_TO_NAI_BOOL =
- KEY_PREFIX + "add_wifi_mac_addr_to_nai_bool";
-
/**
* Specifies the local identity type for IKE negotiations. Possible values are {@link
* #ID_TYPE_FQDN}, {@link #ID_TYPE_RFC822_ADDR}, {@link #ID_TYPE_KEY_ID}
@@ -4600,7 +4596,6 @@ public class CarrierConfigManager {
KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
new int[] {EPDG_ADDRESS_PLMN, EPDG_ADDRESS_STATIC});
defaults.putStringArray(KEY_MCC_MNCS_STRING_ARRAY, new String[] {});
- defaults.putBoolean(KEY_ADD_WIFI_MAC_ADDR_TO_NAI_BOOL, false);
defaults.putInt(KEY_IKE_LOCAL_ID_TYPE_INT, ID_TYPE_RFC822_ADDR);
defaults.putInt(KEY_IKE_REMOTE_ID_TYPE_INT, ID_TYPE_FQDN);
defaults.putBoolean(KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL, false);
@@ -4644,8 +4639,9 @@ public class CarrierConfigManager {
"mmi_two_digit_number_pattern_string_array";
/**
- * Holds the list of carrier certificate hashes.
- * Note that each carrier has its own certificates.
+ * Holds the list of carrier certificate hashes, followed by optional package names.
+ * Format: "sha1/256" or "sha1/256:package1,package2,package3..."
+ * Note that each carrier has its own hashes.
*/
public static final String KEY_CARRIER_CERTIFICATE_STRING_ARRAY =
"carrier_certificate_string_array";
@@ -4791,6 +4787,39 @@ public class CarrierConfigManager {
public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL =
"network_temp_not_metered_supported_bool";
+ /**
+ * Boolean indicating whether the SIM PIN can be stored and verified
+ * seamlessly after an unattended reboot.
+ *
+ * The device configuration value {@code config_allow_pin_storage_for_unattended_reboot}
+ * ultimately controls whether this carrier configuration option is used. Where
+ * {@code config_allow_pin_storage_for_unattended_reboot} is false, the value of the
+ * {@link #KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL} carrier configuration option is
+ * ignored.
+ *
+ * @hide
+ */
+ public static final String KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL =
+ "store_sim_pin_for_unattended_reboot_bool";
+
+ /**
+ * Determine whether "Enable 2G" toggle can be shown.
+ *
+ * Used to trade privacy/security against potentially reduced carrier coverage for some
+ * carriers.
+ */
+ public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool";
+
+ /**
+ * Indicates the allowed APN types that can be used for LTE initial attach. The order of APN
+ * types in the configuration is the order of APN types that will be used for initial attach.
+ * Empty list indicates that no APN types are allowed for initial attach.
+ *
+ * @hide
+ */
+ public static final String KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY =
+ "allowed_initial_attach_apn_types_string_array";
+
/**
* Flag indicating whether carrier supports multianchor conference.
* In multianchor conference, a participant of a conference can add
@@ -5362,6 +5391,10 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false);
sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true);
sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0);
+ sDefaults.putBoolean(KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, true);
+ sDefaults.putBoolean(KEY_HIDE_ENABLE_2G, false);
+ sDefaults.putStringArray(KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY,
+ new String[]{"ia", "default", "ims", "mms", "dun", "emergency"});
sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_MULTIANCHOR_CONFERENCE, false);
}
@@ -5394,11 +5427,28 @@ public class CarrierConfigManager {
public static final String KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED =
KEY_PREFIX + "suggestion_ssid_list_with_mac_randomization_disabled";
+ /**
+ * Avoid SoftAp in 5GHz if cellular is on unlicensed 5Ghz using License Assisted Access
+ * (LAA).
+ */
+ public static final String KEY_AVOID_5GHZ_SOFTAP_FOR_LAA_BOOL =
+ KEY_PREFIX + "avoid_5ghz_softap_for_laa_bool";
+
+ /**
+ * Avoid Wifi Direct in 5GHz if cellular is on unlicensed 5Ghz using License Assisted
+ * Access (LAA).
+ */
+ public static final String KEY_AVOID_5GHZ_WIFI_DIRECT_FOR_LAA_BOOL =
+ KEY_PREFIX + "avoid_5ghz_wifi_direct_for_laa_bool";
+
+
private static PersistableBundle getDefaults() {
PersistableBundle defaults = new PersistableBundle();
defaults.putInt(KEY_HOTSPOT_MAX_CLIENT_COUNT, 0);
defaults.putStringArray(KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED,
new String[0]);
+ defaults.putBoolean(KEY_AVOID_5GHZ_SOFTAP_FOR_LAA_BOOL, false);
+ defaults.putBoolean(KEY_AVOID_5GHZ_WIFI_DIRECT_FOR_LAA_BOOL, false);
return defaults;
}
diff --git a/telephony/java/android/telephony/RadioInterfaceCapabilities.java b/telephony/java/android/telephony/RadioInterfaceCapabilities.java
deleted file mode 100644
index 7c7eb9fbbeb2..000000000000
--- a/telephony/java/android/telephony/RadioInterfaceCapabilities.java
+++ /dev/null
@@ -1,53 +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 android.telephony;
-
-import android.util.ArraySet;
-
-/**
- * Contains the set of supported capabilities that the Radio Interface supports on this device.
- *
- * @hide
- */
-public class RadioInterfaceCapabilities {
-
- private final ArraySet<String> mSupportedCapabilities;
-
-
- public RadioInterfaceCapabilities() {
- mSupportedCapabilities = new ArraySet<>();
- }
-
- /**
- * Marks a capability as supported
- *
- * @param capabilityName the name of the capability
- */
- public void addSupportedCapability(
- @TelephonyManager.RadioInterfaceCapability String capabilityName) {
- mSupportedCapabilities.add(capabilityName);
- }
-
- /**
- * Whether the capability is supported
- *
- * @param capabilityName the name of the capability
- */
- public boolean isSupported(String capabilityName) {
- return mSupportedCapabilities.contains(capabilityName);
- }
-}
diff --git a/telephony/java/android/telephony/SignalThresholdInfo.java b/telephony/java/android/telephony/SignalThresholdInfo.java
index 0059ad6c2426..ae7d20929d58 100644
--- a/telephony/java/android/telephony/SignalThresholdInfo.java
+++ b/telephony/java/android/telephony/SignalThresholdInfo.java
@@ -402,29 +402,27 @@ public final class SignalThresholdInfo implements Parcelable {
* @see #getThresholds() for more details on signal strength thresholds
*/
public @NonNull Builder setThresholds(@NonNull int[] thresholds) {
- Objects.requireNonNull(thresholds, "thresholds must not be null");
- if (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
- || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED) {
- throw new IllegalArgumentException(
- "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
- + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED);
- }
- mThresholds = thresholds.clone();
- Arrays.sort(mThresholds);
- return this;
+ return setThresholds(thresholds, false /*isSystem*/);
}
/**
- * Set the signal strength thresholds for the corresponding signal measurement type without
- * the length limitation.
+ * Set the signal strength thresholds for the corresponding signal measurement type.
*
* @param thresholds array of integer as the signal threshold values
+ * @param isSystem true is the caller is system which does not have restrictions on
+ * the length of thresholds array.
* @return the builder to facilitate the chaining
*
* @hide
*/
- public @NonNull Builder setThresholdsUnlimited(@NonNull int[] thresholds) {
+ public @NonNull Builder setThresholds(@NonNull int[] thresholds, boolean isSystem) {
Objects.requireNonNull(thresholds, "thresholds must not be null");
+ if (!isSystem && (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+ || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED)) {
+ throw new IllegalArgumentException(
+ "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+ + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED);
+ }
mThresholds = thresholds.clone();
Arrays.sort(mThresholds);
return this;
diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java
index 1fcb504e7895..5b5570b27a9b 100644
--- a/telephony/java/android/telephony/TelephonyDisplayInfo.java
+++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java
@@ -66,9 +66,26 @@ public final class TelephonyDisplayInfo implements Parcelable {
* {@link TelephonyManager#NETWORK_TYPE_LTE} network and has E-UTRA-NR Dual Connectivity(EN-DC)
* capability or is currently connected to the secondary
* {@link TelephonyManager#NETWORK_TYPE_NR} cellular network on millimeter wave bands.
+ * @deprecated Use{@link #OVERRIDE_NETWORK_TYPE_NR_ADVANCED} instead.
*/
+ @Deprecated
public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4;
+ /**
+ * Override network type when the device is connected NR cellular network and the data rate is
+ * higher than the generic 5G date rate.
+ * Including but not limited to
+ * <ul>
+ * <li>The device is connected to the NR cellular network on millimeter wave bands. </li>
+ * <li>The device is connected to the specific network which the carrier is using
+ * proprietary means to provide a faster overall data connection than would be otherwise
+ * possible. This may include using other bands unique to the carrier, or carrier
+ * aggregation, for example.</li>
+ * </ul>
+ * One of the use case is that UX can show a different icon, for example, "5G+"
+ */
+ public static final int OVERRIDE_NETWORK_TYPE_NR_ADVANCED = 4;
+
@NetworkType
private final int mNetworkType;
@@ -169,7 +186,7 @@ public final class TelephonyDisplayInfo implements Parcelable {
case OVERRIDE_NETWORK_TYPE_LTE_CA: return "LTE_CA";
case OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO: return "LTE_ADV_PRO";
case OVERRIDE_NETWORK_TYPE_NR_NSA: return "NR_NSA";
- case OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE: return "NR_NSA_MMWAVE";
+ case OVERRIDE_NETWORK_TYPE_NR_ADVANCED: return "NR_NSA_MMWAVE";
default: return "UNKNOWN";
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index eb9b0a041d92..aefc11c7a4c4 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4466,6 +4466,10 @@ public class TelephonyManager {
* This functionality is only available to the app filling the {@link RoleManager#ROLE_DIALER}
* role on the device.
*
+ * This functionality is only available when
+ * {@link CarrierConfigManager#KEY_SUPPORTS_CALL_COMPOSER_BOOL} is set to {@code true} in the
+ * bundle returned from {@link #getCarrierConfig()}.
+ *
* @param pictureToUpload An {@link InputStream} that supplies the bytes representing the
* picture to upload. The client bears responsibility for closing this
* stream after {@code callback} is called with success or failure.
@@ -4474,7 +4478,9 @@ public class TelephonyManager {
* of {@link #getMaximumCallComposerPictureSize()}, the upload will be
* aborted and the callback will be called with an exception containing
* {@link CallComposerException#ERROR_FILE_TOO_LARGE}.
- * @param contentType The MIME type of the picture you're uploading (e.g. image/jpeg)
+ * @param contentType The MIME type of the picture you're uploading (e.g. image/jpeg). The list
+ * of acceptable content types can be found at 3GPP TS 26.141 sections
+ * 4.2 and 4.3.
* @param executor The {@link Executor} on which the {@code pictureToUpload} stream will be
* read, as well as on which the callback will be called.
* @param callback A callback called when the upload operation terminates, either in success
@@ -8497,6 +8503,11 @@ public class TelephonyManager {
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * <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,
+ * setPreferredNetworkTypesBitmap is used instead.
*
* @param subId the id of the subscription to set the preferred network type for.
* @param networkType the preferred network type
@@ -8530,6 +8541,11 @@ public class TelephonyManager {
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * <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,
+ * setPreferredNetworkTypesBitmap is used instead.
*
* @param networkTypeBitmask The bitmask of preferred network types.
* @return true on success; false on any failure.
@@ -8556,6 +8572,11 @@ public class TelephonyManager {
* Set the allowed network types of the device. This is for carrier or privileged apps to
* enable/disable certain network types on the device. The user preferred network types should
* be set through {@link #setPreferredNetworkTypeBitmask}.
+ * <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,
+ * setPreferredNetworkTypesBitmap is used instead.
*
* @param allowedNetworkTypes The bitmask of allowed network types.
* @return true on success; false on any failure.
@@ -8582,7 +8603,8 @@ public class TelephonyManager {
@IntDef({
ALLOWED_NETWORK_TYPES_REASON_USER,
ALLOWED_NETWORK_TYPES_REASON_POWER,
- ALLOWED_NETWORK_TYPES_REASON_CARRIER
+ ALLOWED_NETWORK_TYPES_REASON_CARRIER,
+ ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G
})
@Retention(RetentionPolicy.SOURCE)
public @interface AllowedNetworkTypesReason {
@@ -8619,6 +8641,14 @@ public class TelephonyManager {
public static final int ALLOWED_NETWORK_TYPES_REASON_CARRIER = 2;
/**
+ * To indicate allowed network type change is requested by the user via the 2G toggle.
+ *
+ * @hide
+ */
+ @SystemApi
+ 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.
* This can be called for following reasons
@@ -8627,9 +8657,16 @@ public class TelephonyManager {
* <li>Allowed network types control by power manager
* {@link #ALLOWED_NETWORK_TYPES_REASON_POWER}
* <li>Allowed network types control by carrier {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER}
+ * <li>Allowed network types control by the user-controlled "Allow 2G" toggle
+ * {@link #ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}
* </ol>
* This API will result in allowing an intersection of allowed network types for all reasons,
* including the configuration done through other reasons.
+ * <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,
+ * setPreferredNetworkTypesBitmap is used instead.
*
* @param reason the reason the allowed network type change is taking place
* @param allowedNetworkTypes The bitmask of allowed network types.
@@ -8705,6 +8742,7 @@ public class TelephonyManager {
case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER:
case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER:
case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER:
+ case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
return true;
}
return false;
@@ -11261,16 +11299,21 @@ public class TelephonyManager {
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
- * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+ * Additionally, depending on the level of location permissions the caller holds (i.e. no
+ * location permissions, {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, or
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}), location-sensitive fields will
+ * be cleared from the return value.
+ *
+ * <p>Note also that if the caller holds any sort of location permission, a usage event for the
+ * {@link android.app.AppOpsManager#OPSTR_FINE_LOCATION} or
+ * {@link android.app.AppOpsManager#OPSTR_FINE_LOCATION}
+ * will be logged against the caller when calling this method.
*
* May return {@code null} when the subscription is inactive or when there was an error
* communicating with the phone process.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
- @RequiresPermission(allOf = {
- Manifest.permission.READ_PHONE_STATE,
- Manifest.permission.ACCESS_COARSE_LOCATION
- })
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
public @Nullable ServiceState getServiceState() {
return getServiceStateForSubscriber(getSubId());
}
@@ -14862,10 +14905,24 @@ public class TelephonyManager {
public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE =
"CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
+ /**
+ * Indicates whether {@link #setPreferredNetworkType}, {@link
+ * #setPreferredNetworkTypeBitmask}, {@link #setAllowedNetworkTypes} and
+ * {@link #setAllowedNetworkTypesForReason} rely on
+ * setAllowedNetworkTypesBitmap instead of setPreferredNetworkTypesBitmap on the radio
+ * interface.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED =
+ "CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@StringDef(prefix = "CAPABILITY_", value = {
CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE,
+ CAPABILITY_ALLOWED_NETWORK_TYPES_USED,
})
public @interface RadioInterfaceCapability {}
@@ -15000,6 +15057,7 @@ public class TelephonyManager {
}
return THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR;
}
+
/**
* Registers a listener object to receive notification of changes
* in specified telephony states.
@@ -15164,8 +15222,13 @@ public class TelephonyManager {
* <li>Generate the ks_NAF/ ks_Ext_NAF to be returned via the callback.</li>
* </ol>
*
- * <p> Requires Permission: MODIFY_PHONE_STATE or that the calling app has carrier
- * privileges (see {@link #hasCarrierPrivileges}).
+ * <p> Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE},</li>
+ * <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
+ * <li>or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </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
@@ -15192,7 +15255,8 @@ public class TelephonyManager {
*/
@SystemApi
@WorkerThread
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresPermission(anyOf = {android.Manifest.permission.MODIFY_PHONE_STATE,
+ Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
public void bootstrapAuthenticationRequest(
@UiccAppTypeExt int appType, @NonNull Uri nafId,
@NonNull UaSecurityProtocolIdentifier securityProtocol,
@@ -15356,4 +15420,66 @@ public class TelephonyManager {
return PhoneCapability.DEFAULT_SSSS_CAPABILITY;
}
}
+
+ /**
+ * The unattended reboot was prepared successfully.
+ * @hide
+ */
+ @SystemApi
+ public static final int PREPARE_UNATTENDED_REBOOT_SUCCESS = 0;
+
+ /**
+ * The unattended reboot was prepared, but the user will need to manually
+ * enter the PIN code of at least one SIM card present in the device.
+ * @hide
+ */
+ @SystemApi
+ public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1;
+
+ /**
+ * The unattended reboot was not prepared due to generic error.
+ * @hide
+ */
+ @SystemApi
+ public static final int PREPARE_UNATTENDED_REBOOT_ERROR = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"PREPARE_UNATTENDED_REBOOT_"},
+ value = {
+ PREPARE_UNATTENDED_REBOOT_SUCCESS,
+ PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED,
+ PREPARE_UNATTENDED_REBOOT_ERROR
+ })
+ public @interface PrepareUnattendedRebootResult {}
+
+ /**
+ * Prepare TelephonyManager for an unattended reboot. The reboot is required to be done
+ * shortly (e.g. within 15 seconds) after the API is invoked.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#REBOOT}
+ *
+ * @return {@link #PREPARE_UNATTENDED_REBOOT_SUCCESS} in case of success.
+ * {@link #PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED} if the device contains
+ * at least one SIM card for which the user needs to manually enter the PIN
+ * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case
+ * of error.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.REBOOT)
+ @PrepareUnattendedRebootResult
+ public int prepareForUnattendedReboot() {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.prepareForUnattendedReboot();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Telephony#prepareForUnattendedReboot RemoteException", e);
+ e.rethrowFromSystemServer();
+ }
+ return PREPARE_UNATTENDED_REBOOT_ERROR;
+ }
}
diff --git a/telephony/java/android/telephony/UiccAccessRule.java b/telephony/java/android/telephony/UiccAccessRule.java
index 7773c0aef252..38b551bff9a7 100644
--- a/telephony/java/android/telephony/UiccAccessRule.java
+++ b/telephony/java/android/telephony/UiccAccessRule.java
@@ -35,6 +35,7 @@ import java.io.DataOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -52,6 +53,16 @@ public final class UiccAccessRule implements Parcelable {
private static final int ENCODING_VERSION = 1;
+ /**
+ * Delimiter used to decode {@link CarrierConfigManager#KEY_CARRIER_CERTIFICATE_STRING_ARRAY}.
+ */
+ private static final String DELIMITER_CERTIFICATE_HASH_PACKAGE_NAMES = ":";
+
+ /**
+ * Delimiter used to decode {@link CarrierConfigManager#KEY_CARRIER_CERTIFICATE_STRING_ARRAY}.
+ */
+ private static final String DELIMITER_INDIVIDUAL_PACKAGE_NAMES = ",";
+
public static final @android.annotation.NonNull Creator<UiccAccessRule> CREATOR = new Creator<UiccAccessRule>() {
@Override
public UiccAccessRule createFromParcel(Parcel in) {
@@ -98,6 +109,36 @@ public final class UiccAccessRule implements Parcelable {
}
/**
+ * Decodes {@link CarrierConfigManager#KEY_CARRIER_CERTIFICATE_STRING_ARRAY} values.
+ * @hide
+ */
+ @Nullable
+ public static UiccAccessRule[] decodeRulesFromCarrierConfig(@Nullable String[] certs) {
+ if (certs == null) {
+ return null;
+ }
+ List<UiccAccessRule> carrierConfigAccessRulesArray = new ArrayList();
+ for (String cert : certs) {
+ String[] splitStr = cert.split(DELIMITER_CERTIFICATE_HASH_PACKAGE_NAMES);
+ byte[] certificateHash = IccUtils.hexStringToBytes(splitStr[0]);
+ if (splitStr.length == 1) {
+ // The value is a certificate hash, without any package name
+ carrierConfigAccessRulesArray.add(new UiccAccessRule(certificateHash, null, 0));
+ } else {
+ // The value is composed of the certificate hash followed by at least one
+ // package name
+ String[] packageNames = splitStr[1].split(DELIMITER_INDIVIDUAL_PACKAGE_NAMES);
+ for (String packageName : packageNames) {
+ carrierConfigAccessRulesArray.add(
+ new UiccAccessRule(certificateHash, packageName, 0));
+ }
+ }
+ }
+ return carrierConfigAccessRulesArray.toArray(
+ new UiccAccessRule[carrierConfigAccessRulesArray.size()]);
+ }
+
+ /**
* Decodes a byte array generated with {@link #encodeRules}.
* @hide
*/
@@ -222,6 +263,14 @@ public final class UiccAccessRule implements Parcelable {
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
}
+ /**
+ * Returns true if the given certificate and package name match this rule's values.
+ * @hide
+ */
+ public boolean matches(@Nullable String certHash, @Nullable String packageName) {
+ return matches(IccUtils.hexStringToBytes(certHash), packageName);
+ }
+
private boolean matches(byte[] certHash, String packageName) {
return certHash != null && Arrays.equals(this.mCertificateHash, certHash) &&
(TextUtils.isEmpty(this.mPackageName) || this.mPackageName.equals(packageName));
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index f48ddb0aaca6..bd4bf0740ca1 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -136,7 +136,7 @@ public final class DataCallResponse implements Parcelable {
private final @HandoverFailureMode int mHandoverFailureMode;
private final int mPduSessionId;
private final Qos mDefaultQos;
- private final List<QosSession> mQosSessions;
+ private final List<QosBearerSession> mQosBearerSessions;
private final SliceInfo mSliceInfo;
/**
@@ -187,7 +187,7 @@ public final class DataCallResponse implements Parcelable {
mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY;
mPduSessionId = PDU_SESSION_ID_NOT_SET;
mDefaultQos = null;
- mQosSessions = new ArrayList<>();
+ mQosBearerSessions = new ArrayList<>();
mSliceInfo = null;
}
@@ -197,7 +197,7 @@ public final class DataCallResponse implements Parcelable {
@Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
@Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
@HandoverFailureMode int handoverFailureMode, int pduSessionId,
- @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions,
+ @Nullable Qos defaultQos, @Nullable List<QosBearerSession> qosBearerSessions,
@Nullable SliceInfo sliceInfo) {
mCause = cause;
mSuggestedRetryTime = suggestedRetryTime;
@@ -219,7 +219,7 @@ public final class DataCallResponse implements Parcelable {
mHandoverFailureMode = handoverFailureMode;
mPduSessionId = pduSessionId;
mDefaultQos = defaultQos;
- mQosSessions = qosSessions;
+ mQosBearerSessions = qosBearerSessions;
mSliceInfo = sliceInfo;
}
@@ -246,8 +246,8 @@ public final class DataCallResponse implements Parcelable {
mHandoverFailureMode = source.readInt();
mPduSessionId = source.readInt();
mDefaultQos = source.readParcelable(Qos.class.getClassLoader());
- mQosSessions = new ArrayList<>();
- source.readList(mQosSessions, QosSession.class.getClassLoader());
+ mQosBearerSessions = new ArrayList<>();
+ source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader());
mSliceInfo = source.readParcelable(SliceInfo.class.getClassLoader());
}
@@ -394,8 +394,8 @@ public final class DataCallResponse implements Parcelable {
* @hide
*/
@NonNull
- public List<QosSession> getQosSessions() {
- return mQosSessions;
+ public List<QosBearerSession> getQosBearerSessions() {
+ return mQosBearerSessions;
}
/**
@@ -427,7 +427,7 @@ public final class DataCallResponse implements Parcelable {
.append(" handoverFailureMode=").append(failureModeToString(mHandoverFailureMode))
.append(" pduSessionId=").append(getPduSessionId())
.append(" defaultQos=").append(mDefaultQos)
- .append(" qosSessions=").append(mQosSessions)
+ .append(" qosBearerSessions=").append(mQosBearerSessions)
.append(" sliceInfo=").append(mSliceInfo)
.append("}");
return sb.toString();
@@ -447,10 +447,10 @@ public final class DataCallResponse implements Parcelable {
mDefaultQos == other.mDefaultQos :
mDefaultQos.equals(other.mDefaultQos);
- final boolean isQosSessionsSame = (mQosSessions == null || mQosSessions == null) ?
- mQosSessions == other.mQosSessions :
- mQosSessions.size() == other.mQosSessions.size()
- && mQosSessions.containsAll(other.mQosSessions);
+ final boolean isQosBearerSessionsSame = (mQosBearerSessions == null || mQosBearerSessions == null) ?
+ mQosBearerSessions == other.mQosBearerSessions :
+ mQosBearerSessions.size() == other.mQosBearerSessions.size()
+ && mQosBearerSessions.containsAll(other.mQosBearerSessions);
return mCause == other.mCause
&& mSuggestedRetryTime == other.mSuggestedRetryTime
@@ -472,7 +472,7 @@ public final class DataCallResponse implements Parcelable {
&& mHandoverFailureMode == other.mHandoverFailureMode
&& mPduSessionId == other.mPduSessionId
&& isQosSame
- && isQosSessionsSame
+ && isQosBearerSessionsSame
&& Objects.equals(mSliceInfo, other.mSliceInfo);
}
@@ -481,7 +481,7 @@ public final class DataCallResponse implements Parcelable {
return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos,
- mQosSessions, mSliceInfo);
+ mQosBearerSessions, mSliceInfo);
}
@Override
@@ -515,7 +515,7 @@ public final class DataCallResponse implements Parcelable {
} else {
dest.writeParcelable(null, flags);
}
- dest.writeList(mQosSessions);
+ dest.writeList(mQosBearerSessions);
dest.writeParcelable(mSliceInfo, flags);
}
@@ -598,7 +598,7 @@ public final class DataCallResponse implements Parcelable {
private Qos mDefaultQos;
- private List<QosSession> mQosSessions = new ArrayList<>();
+ private List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
private SliceInfo mSliceInfo;
@@ -812,15 +812,16 @@ public final class DataCallResponse implements Parcelable {
/**
* Set the dedicated bearer QOS sessions for this data connection.
*
- * @param qosSessions Dedicated bearer QOS (Quality Of Service) sessions received
+ * @param qosBearerSessions Dedicated bearer QOS (Quality Of Service) sessions received
* from network.
*
* @return The same instance of the builder.
*
* @hide
*/
- public @NonNull Builder setQosSessions(@NonNull List<QosSession> qosSessions) {
- mQosSessions = qosSessions;
+ public @NonNull Builder setQosBearerSessions(
+ @NonNull List<QosBearerSession> qosBearerSessions) {
+ mQosBearerSessions = qosBearerSessions;
return this;
}
@@ -848,7 +849,7 @@ public final class DataCallResponse implements Parcelable {
return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
- mDefaultQos, mQosSessions, mSliceInfo);
+ mDefaultQos, mQosBearerSessions, mSliceInfo);
}
}
}
diff --git a/telephony/java/android/telephony/data/EpsQos.java b/telephony/java/android/telephony/data/EpsQos.java
index ad43068b2f11..22c8b0a0a74f 100644
--- a/telephony/java/android/telephony/data/EpsQos.java
+++ b/telephony/java/android/telephony/data/EpsQos.java
@@ -48,6 +48,10 @@ public final class EpsQos extends Qos implements Parcelable {
qosClassId = source.readInt();
}
+ public int getQci() {
+ return qosClassId;
+ }
+
public static @NonNull EpsQos createFromParcelBody(@NonNull Parcel in) {
return new EpsQos(in);
}
diff --git a/telephony/java/android/telephony/data/Qos.java b/telephony/java/android/telephony/data/Qos.java
index c8bb91e28bf2..c286c6217450 100644
--- a/telephony/java/android/telephony/data/Qos.java
+++ b/telephony/java/android/telephony/data/Qos.java
@@ -56,7 +56,15 @@ public abstract class Qos {
this.uplink = new QosBandwidth(uplink.maxBitrateKbps, uplink.guaranteedBitrateKbps);
}
- static class QosBandwidth implements Parcelable {
+ public QosBandwidth getDownlinkBandwidth() {
+ return downlink;
+ }
+
+ public QosBandwidth getUplinkBandwidth() {
+ return uplink;
+ }
+
+ public static class QosBandwidth implements Parcelable {
int maxBitrateKbps;
int guaranteedBitrateKbps;
@@ -73,6 +81,14 @@ public abstract class Qos {
guaranteedBitrateKbps = source.readInt();
}
+ public int getMaxBitrateKbps() {
+ return maxBitrateKbps;
+ }
+
+ public int getGuaranteedBitrateKbps() {
+ return guaranteedBitrateKbps;
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(maxBitrateKbps);
diff --git a/telephony/java/android/telephony/data/QosFilter.java b/telephony/java/android/telephony/data/QosBearerFilter.java
index 69277445634d..6c1c653f13d0 100644
--- a/telephony/java/android/telephony/data/QosFilter.java
+++ b/telephony/java/android/telephony/data/QosBearerFilter.java
@@ -38,7 +38,7 @@ import java.util.Objects;
*
* @hide
*/
-public final class QosFilter implements Parcelable {
+public final class QosBearerFilter implements Parcelable {
private List<LinkAddress> localAddresses;
private List<LinkAddress> remoteAddresses;
@@ -74,7 +74,7 @@ public final class QosFilter implements Parcelable {
@IntDef(prefix = "QOS_FILTER_DIRECTION_",
value = {QOS_FILTER_DIRECTION_DOWNLINK, QOS_FILTER_DIRECTION_UPLINK,
QOS_FILTER_DIRECTION_BIDIRECTIONAL})
- public @interface QosFilterDirection {}
+ public @interface QosBearerFilterDirection {}
public static final int QOS_FILTER_DIRECTION_DOWNLINK =
android.hardware.radio.V1_6.QosFilterDirection.DOWNLINK;
@@ -83,7 +83,7 @@ public final class QosFilter implements Parcelable {
public static final int QOS_FILTER_DIRECTION_BIDIRECTIONAL =
android.hardware.radio.V1_6.QosFilterDirection.BIDIRECTIONAL;
- @QosFilterDirection
+ @QosBearerFilterDirection
private int filterDirection;
/**
@@ -92,7 +92,7 @@ public final class QosFilter implements Parcelable {
*/
private int precedence;
- QosFilter() {
+ QosBearerFilter() {
localAddresses = new ArrayList<>();
remoteAddresses = new ArrayList<>();
localPort = new PortRange();
@@ -101,7 +101,7 @@ public final class QosFilter implements Parcelable {
filterDirection = QOS_FILTER_DIRECTION_BIDIRECTIONAL;
}
- public QosFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses,
+ public QosBearerFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses,
PortRange localPort, PortRange remotePort, int protocol, int tos,
long flowLabel, long spi, int direction, int precedence) {
this.localAddresses = localAddresses;
@@ -116,10 +116,30 @@ public final class QosFilter implements Parcelable {
this.precedence = precedence;
}
+ public List<LinkAddress> getLocalAddresses() {
+ return localAddresses;
+ }
+
+ public List<LinkAddress> getRemoteAddresses() {
+ return remoteAddresses;
+ }
+
+ public PortRange getLocalPortRange() {
+ return localPort;
+ }
+
+ public PortRange getRemotePortRange() {
+ return remotePort;
+ }
+
+ public int getPrecedence() {
+ return precedence;
+ }
+
/** @hide */
- public static @NonNull QosFilter create(
+ public static @NonNull QosBearerFilter create(
@NonNull android.hardware.radio.V1_6.QosFilter qosFilter) {
- QosFilter ret = new QosFilter();
+ QosBearerFilter ret = new QosBearerFilter();
String[] localAddresses = qosFilter.localAddresses.stream().toArray(String[]::new);
if (localAddresses != null) {
@@ -202,6 +222,14 @@ public final class QosFilter implements Parcelable {
this.end = end;
}
+ public int getStart() {
+ return start;
+ }
+
+ public int getEnd() {
+ return end;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(start);
@@ -254,7 +282,7 @@ public final class QosFilter implements Parcelable {
@Override
public String toString() {
- return "QosFilter {"
+ return "QosBearerFilter {"
+ " localAddresses=" + localAddresses
+ " remoteAddresses=" + remoteAddresses
+ " localPort=" + localPort
@@ -278,11 +306,11 @@ public final class QosFilter implements Parcelable {
public boolean equals(Object o) {
if (this == o) return true;
- if (o == null || !(o instanceof QosFilter)) {
+ if (o == null || !(o instanceof QosBearerFilter)) {
return false;
}
- QosFilter other = (QosFilter) o;
+ QosBearerFilter other = (QosBearerFilter) o;
return localAddresses.size() == other.localAddresses.size()
&& localAddresses.containsAll(other.localAddresses)
@@ -324,7 +352,7 @@ public final class QosFilter implements Parcelable {
LinkAddress.LIFETIME_UNKNOWN, LinkAddress.LIFETIME_UNKNOWN);
}
- private QosFilter(Parcel source) {
+ private QosBearerFilter(Parcel source) {
localAddresses = new ArrayList<>();
source.readList(localAddresses, LinkAddress.class.getClassLoader());
remoteAddresses = new ArrayList<>();
@@ -358,16 +386,16 @@ public final class QosFilter implements Parcelable {
return 0;
}
- public static final @NonNull Parcelable.Creator<QosFilter> CREATOR =
- new Parcelable.Creator<QosFilter>() {
+ public static final @NonNull Parcelable.Creator<QosBearerFilter> CREATOR =
+ new Parcelable.Creator<QosBearerFilter>() {
@Override
- public QosFilter createFromParcel(Parcel source) {
- return new QosFilter(source);
+ public QosBearerFilter createFromParcel(Parcel source) {
+ return new QosBearerFilter(source);
}
@Override
- public QosFilter[] newArray(int size) {
- return new QosFilter[size];
+ public QosBearerFilter[] newArray(int size) {
+ return new QosBearerFilter[size];
}
};
}
diff --git a/telephony/java/android/telephony/data/QosBearerSession.java b/telephony/java/android/telephony/data/QosBearerSession.java
new file mode 100644
index 000000000000..30effc9273d7
--- /dev/null
+++ b/telephony/java/android/telephony/data/QosBearerSession.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to QOS session.
+ *
+ * @hide
+ */
+public final class QosBearerSession implements Parcelable{
+
+ final int qosBearerSessionId;
+ final Qos qos;
+ final List<QosBearerFilter> qosBearerFilterList;
+
+ public QosBearerSession(int qosBearerSessionId, @NonNull Qos qos, @NonNull List<QosBearerFilter> qosBearerFilterList) {
+ this.qosBearerSessionId = qosBearerSessionId;
+ this.qos = qos;
+ this.qosBearerFilterList = qosBearerFilterList;
+ }
+
+ private QosBearerSession(Parcel source) {
+ qosBearerSessionId = source.readInt();
+ qos = source.readParcelable(Qos.class.getClassLoader());
+ qosBearerFilterList = new ArrayList<>();
+ source.readList(qosBearerFilterList, QosBearerFilter.class.getClassLoader());
+ }
+
+ public int getQosBearerSessionId() {
+ return qosBearerSessionId;
+ }
+
+ public Qos getQos() {
+ return qos;
+ }
+
+ public List<QosBearerFilter> getQosBearerFilterList() {
+ return qosBearerFilterList;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(qosBearerSessionId);
+ if (qos.getType() == Qos.QOS_TYPE_EPS) {
+ dest.writeParcelable((EpsQos)qos, flags);
+ } else {
+ dest.writeParcelable((NrQos)qos, flags);
+ }
+ dest.writeList(qosBearerFilterList);
+ }
+
+ public static @NonNull QosBearerSession create(
+ @NonNull android.hardware.radio.V1_6.QosSession qosSession) {
+ List<QosBearerFilter> qosBearerFilters = new ArrayList<>();
+
+ if (qosSession.qosFilters != null) {
+ for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) {
+ qosBearerFilters.add(QosBearerFilter.create(filter));
+ }
+ }
+
+ return new QosBearerSession(
+ qosSession.qosSessionId,
+ Qos.create(qosSession.qos),
+ qosBearerFilters);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "QosBearerSession {"
+ + " qosBearerSessionId=" + qosBearerSessionId
+ + " qos=" + qos
+ + " qosBearerFilterList=" + qosBearerFilterList + "}";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(qosBearerSessionId, qos, qosBearerFilterList);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (o == null || !(o instanceof QosBearerSession)) {
+ return false;
+ }
+
+ QosBearerSession other = (QosBearerSession) o;
+ return this.qosBearerSessionId == other.qosBearerSessionId
+ && this.qos.equals(other.qos)
+ && this.qosBearerFilterList.size() == other.qosBearerFilterList.size()
+ && this.qosBearerFilterList.containsAll(other.qosBearerFilterList);
+ }
+
+
+ public static final @NonNull Parcelable.Creator<QosBearerSession> CREATOR =
+ new Parcelable.Creator<QosBearerSession>() {
+ @Override
+ public QosBearerSession createFromParcel(Parcel source) {
+ return new QosBearerSession(source);
+ }
+
+ @Override
+ public QosBearerSession[] newArray(int size) {
+ return new QosBearerSession[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/data/QosSession.java b/telephony/java/android/telephony/data/QosSession.java
deleted file mode 100644
index f07b6a9f6725..000000000000
--- a/telephony/java/android/telephony/data/QosSession.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/**
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.data;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-
-/**
- * Class that stores information specific to QOS session.
- *
- * @hide
- */
-public final class QosSession implements Parcelable{
-
- final int qosSessionId;
- final Qos qos;
- final List<QosFilter> qosFilterList;
-
- public QosSession(int qosSessionId, @NonNull Qos qos, @NonNull List<QosFilter> qosFilterList) {
- this.qosSessionId = qosSessionId;
- this.qos = qos;
- this.qosFilterList = qosFilterList;
- }
-
- private QosSession(Parcel source) {
- qosSessionId = source.readInt();
- qos = source.readParcelable(Qos.class.getClassLoader());
- qosFilterList = new ArrayList<>();
- source.readList(qosFilterList, QosFilter.class.getClassLoader());
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(qosSessionId);
- if (qos.getType() == Qos.QOS_TYPE_EPS) {
- dest.writeParcelable((EpsQos)qos, flags);
- } else {
- dest.writeParcelable((NrQos)qos, flags);
- }
- dest.writeList(qosFilterList);
- }
-
- public static @NonNull QosSession create(
- @NonNull android.hardware.radio.V1_6.QosSession qosSession) {
- List<QosFilter> qosFilters = new ArrayList<>();
-
- if (qosSession.qosFilters != null) {
- for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) {
- qosFilters.add(QosFilter.create(filter));
- }
- }
-
- return new QosSession(
- qosSession.qosSessionId,
- Qos.create(qosSession.qos),
- qosFilters);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public String toString() {
- return "QosSession {"
- + " qosSessionId=" + qosSessionId
- + " qos=" + qos
- + " qosFilterList=" + qosFilterList + "}";
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(qosSessionId, qos, qosFilterList);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
-
- if (o == null || !(o instanceof QosSession)) {
- return false;
- }
-
- QosSession other = (QosSession) o;
- return this.qosSessionId == other.qosSessionId
- && this.qos.equals(other.qos)
- && this.qosFilterList.size() == other.qosFilterList.size()
- && this.qosFilterList.containsAll(other.qosFilterList);
- }
-
-
- public static final @NonNull Parcelable.Creator<QosSession> CREATOR =
- new Parcelable.Creator<QosSession>() {
- @Override
- public QosSession createFromParcel(Parcel source) {
- return new QosSession(source);
- }
-
- @Override
- public QosSession[] newArray(int size) {
- return new QosSession[size];
- }
- };
-}
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index f39e30b6d61f..814ce18c51e3 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -21,6 +21,7 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
@@ -39,6 +40,8 @@ import android.util.Log;
import com.android.internal.telephony.IIntegerConsumer;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -77,31 +80,49 @@ public class ImsRcsManager {
"android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
/**
- * Receives RCS Feature availability status updates from the ImsService.
- *
- * @see #isAvailable(int)
- * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
- * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+ * An application can use {@link #addOnAvailabilityChangedListener} to register a
+ * {@link OnAvailabilityChangedListener}, which will notify the user when the RCS feature
+ * availability status updates from the ImsService.
* @hide
*/
- public static class AvailabilityCallback {
+ @SystemApi
+ public interface OnAvailabilityChangedListener {
+ /**
+ * The availability of the feature's capabilities has changed to either available or
+ * unavailable.
+ * <p>
+ * If unavailable, the feature does not support the capability at the current time. This may
+ * be due to network or subscription provisioning changes, such as the IMS registration
+ * being lost, network type changing, or OMA-DM provisioning updates.
+ *
+ * @param capabilities The new availability of the capabilities.
+ */
+ void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities);
+ }
- private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+ /**
+ * Receive the availability status changed from the ImsService and pass the status change to
+ * the associated {@link OnAvailabilityChangedListener}
+ */
+ private static class AvailabilityCallbackAdapter {
- private final AvailabilityCallback mLocalCallback;
- private Executor mExecutor;
+ private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+ private final OnAvailabilityChangedListener mOnAvailabilityChangedListener;
+ private final Executor mExecutor;
- CapabilityBinder(AvailabilityCallback c) {
- mLocalCallback = c;
+ CapabilityBinder(OnAvailabilityChangedListener listener, Executor executor) {
+ mExecutor = executor;
+ mOnAvailabilityChangedListener = listener;
}
@Override
public void onCapabilitiesStatusChanged(int config) {
- if (mLocalCallback == null) return;
+ if (mOnAvailabilityChangedListener == null) return;
final long callingIdentity = Binder.clearCallingIdentity();
try {
- mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged(config));
+ mExecutor.execute(() ->
+ mOnAvailabilityChangedListener.onAvailabilityChanged(config));
} finally {
restoreCallingIdentity(callingIdentity);
}
@@ -110,48 +131,34 @@ public class ImsRcsManager {
@Override
public void onQueryCapabilityConfiguration(int capability, int radioTech,
boolean isEnabled) {
- // This is not used for public interfaces.
+ // This is not used.
}
@Override
public void onChangeCapabilityConfigurationError(int capability, int radioTech,
@ImsFeature.ImsCapabilityError int reason) {
- // This is not used for public interfaces
- }
-
- private void setExecutor(Executor executor) {
- mExecutor = executor;
+ // This is not used.
}
}
- private final CapabilityBinder mBinder = new CapabilityBinder(this);
+ private final CapabilityBinder mBinder;
- /**
- * The availability of the feature's capabilities has changed to either available or
- * unavailable.
- * <p>
- * If unavailable, the feature does not support the capability at the current time. This may
- * be due to network or subscription provisioning changes, such as the IMS registration
- * being lost, network type changing, or OMA-DM provisioning updates.
- *
- * @param capabilities The new availability of the capabilities.
- */
- public void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
+ AvailabilityCallbackAdapter(@NonNull Executor executor,
+ @NonNull OnAvailabilityChangedListener listener) {
+ mBinder = new CapabilityBinder(listener, executor);
}
/**@hide*/
public final IImsCapabilityCallback getBinder() {
return mBinder;
}
-
- private void setExecutor(Executor executor) {
- mBinder.setExecutor(executor);
- }
}
private final int mSubId;
private final Context mContext;
private final BinderCacheManager<IImsRcsController> mBinderCache;
+ private final Map<OnAvailabilityChangedListener, AvailabilityCallbackAdapter>
+ mAvailabilityChangedCallbacks;
/**
* Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this class.
@@ -162,6 +169,7 @@ public class ImsRcsManager {
mSubId = subId;
mContext = context;
mBinderCache = binderCache;
+ mAvailabilityChangedCallbacks = new HashMap<>();
}
/**
@@ -174,10 +182,23 @@ public class ImsRcsManager {
}
/**
- * @hide
+ * Registers a {@link RegistrationManager.RegistrationCallback} with the system. When the
+ * callback is registered, it will initiate the callback c to be called with the current
+ * registration state.
+ *
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param executor The executor the callback events should be run on.
+ * @param c The {@link RegistrationManager.RegistrationCallback} to be added.
+ * @see #unregisterImsRegistrationCallback(RegistrationManager.RegistrationCallback)
+ * @throws ImsException if the subscription associated with this callback is valid, but
+ * the {@link ImsService} associated with the subscription is not available. This can happen if
+ * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+ * reason.
*/
- // @Override add back to RegistrationManager interface once public.
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public void registerImsRegistrationCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull RegistrationManager.RegistrationCallback c)
@@ -191,7 +212,7 @@ public class ImsRcsManager {
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "Register registration callback: IImsRcsController is null");
+ Log.w(TAG, "Register registration callback: IImsRcsController is null");
throw new ImsException("Cannot find remote IMS service",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
@@ -207,10 +228,21 @@ public class ImsRcsManager {
}
/**
- * @hide
+ * Removes an existing {@link RegistrationManager.RegistrationCallback}.
+ *
+ * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+ * etc...), this callback will automatically be removed. If this method is called for an
+ * inactive subscription, it will result in a no-op.
+ *
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param c The {@link RegistrationManager.RegistrationCallback} to be removed.
+ * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener
+ * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
*/
- // @Override add back to RegistrationManager interface once public.
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public void unregisterImsRegistrationCallback(
@NonNull RegistrationManager.RegistrationCallback c) {
if (c == null) {
@@ -219,7 +251,7 @@ public class ImsRcsManager {
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "Unregister registration callback: IImsRcsController is null");
+ Log.w(TAG, "Unregister registration callback: IImsRcsController is null");
throw new IllegalStateException("Cannot find remote IMS service");
}
@@ -231,10 +263,21 @@ public class ImsRcsManager {
}
/**
- * @hide
+ * Gets the registration state of the IMS service.
+ *
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param executor The {@link Executor} that will be used to call the IMS registration state
+ * callback.
+ * @param stateCallback A callback called on the supplied {@link Executor} that will contain the
+ * registration state of the IMS service, which will be one of the
+ * following: {@link RegistrationManager#REGISTRATION_STATE_NOT_REGISTERED},
+ * {@link RegistrationManager#REGISTRATION_STATE_REGISTERING}, or
+ * {@link RegistrationManager#REGISTRATION_STATE_REGISTERED}.
*/
- // @Override add back to RegistrationManager interface once public.
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
@NonNull @RegistrationManager.ImsRegistrationState Consumer<Integer> stateCallback) {
if (stateCallback == null) {
@@ -246,7 +289,7 @@ public class ImsRcsManager {
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "Get registration state error: IImsRcsController is null");
+ Log.w(TAG, "Get registration state error: IImsRcsController is null");
throw new IllegalStateException("Cannot find remote IMS service");
}
@@ -263,9 +306,20 @@ public class ImsRcsManager {
}
/**
- * @hide
+ * Gets the Transport Type associated with the current IMS registration.
+ *
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param executor The {@link Executor} that will be used to call the transportTypeCallback.
+ * @param transportTypeCallback The transport type associated with the current IMS registration,
+ * which will be one of following:
+ * {@see AccessNetworkConstants#TRANSPORT_TYPE_WWAN},
+ * {@see AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or
+ * {@see AccessNetworkConstants#TRANSPORT_TYPE_INVALID}.
*/
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
@NonNull @AccessNetworkConstants.TransportType
Consumer<Integer> transportTypeCallback) {
@@ -278,7 +332,7 @@ public class ImsRcsManager {
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "Get registration transport type error: IImsRcsController is null");
+ Log.w(TAG, "Get registration transport type error: IImsRcsController is null");
throw new IllegalStateException("Cannot find remote IMS service");
}
@@ -296,31 +350,33 @@ public class ImsRcsManager {
}
/**
- * Registers an {@link AvailabilityCallback} with the system, which will provide RCS
+ * Add an {@link OnAvailabilityChangedListener} with the system, which will provide RCS
* availability updates for the subscription specified.
*
* Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
* subscription changed events and call
- * {@link #unregisterRcsAvailabilityCallback(AvailabilityCallback)} to clean up after a
- * subscription is removed.
+ * {@link #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)} to clean up
+ * after a subscription is removed.
* <p>
- * When the callback is registered, it will initiate the callback c to be called with the
- * current capabilities.
+ * When the listener is registered, it will initiate the callback listener to be called with
+ * the current capabilities.
*
* @param executor The executor the callback events should be run on.
- * @param c The RCS {@link AvailabilityCallback} to be registered.
- * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+ * @param listener The RCS {@link OnAvailabilityChangedListener} to be registered.
+ * @see #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)
* @throws ImsException if the subscription associated with this instance of
* {@link ImsRcsManager} 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(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public void registerRcsAvailabilityCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull AvailabilityCallback c) throws ImsException {
- if (c == null) {
- throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
+ public void addOnAvailabilityChangedListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull OnAvailabilityChangedListener listener) throws ImsException {
+ if (listener == null) {
+ throw new IllegalArgumentException("Must include a non-null"
+ + "OnAvailabilityChangedListener.");
}
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
@@ -328,56 +384,61 @@ public class ImsRcsManager {
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "Register availability callback: IImsRcsController is null");
+ Log.w(TAG, "Add availability changed listener: IImsRcsController is null");
throw new ImsException("Cannot find remote IMS service",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
- c.setExecutor(executor);
+ AvailabilityCallbackAdapter adapter =
+ addAvailabilityChangedListenerToCollection(executor, listener);
try {
- imsRcsController.registerRcsAvailabilityCallback(mSubId, c.getBinder());
-
+ imsRcsController.registerRcsAvailabilityCallback(mSubId, adapter.getBinder());
} catch (ServiceSpecificException e) {
throw new ImsException(e.toString(), e.errorCode);
} catch (RemoteException e) {
- Log.e(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e);
+ Log.w(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e);
throw new ImsException("Remote IMS Service is not available",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
- /**
- * Removes an existing RCS {@link AvailabilityCallback}.
+ /**
+ * Removes an existing RCS {@link OnAvailabilityChangedListener}.
* <p>
* When the subscription associated with this callback is removed (SIM removed, ESIM swap,
* etc...), this callback will automatically be unregistered. If this method is called for an
* inactive subscription, it will result in a no-op.
- * @param c The RCS {@link AvailabilityCallback} to be removed.
- * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
+ * @param listener The RCS {@link OnAvailabilityChangedListener} to be removed.
+ * @see #addOnAvailabilityChangedListener(Executor, OnAvailabilityChangedListener)
* @throws ImsException if the IMS service is not available when calling this method.
* See {@link ImsException#getCode()} for more information on the error codes.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public void unregisterRcsAvailabilityCallback(@NonNull AvailabilityCallback c)
- throws ImsException {
- if (c == null) {
- throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
+ public void removeOnAvailabilityChangedListener(
+ @NonNull OnAvailabilityChangedListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("Must include a non-null"
+ + "OnAvailabilityChangedListener.");
}
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "Unregister availability callback: IImsRcsController is null");
- throw new ImsException("Cannot find remote IMS service",
- ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ Log.w(TAG, "Remove availability changed listener: IImsRcsController is null");
+ return;
+ }
+
+ AvailabilityCallbackAdapter callback =
+ removeAvailabilityChangedListenerFromCollection(listener);
+ if (callback == null) {
+ return;
}
try {
- imsRcsController.unregisterRcsAvailabilityCallback(mSubId, c.getBinder());
+ imsRcsController.unregisterRcsAvailabilityCallback(mSubId, callback.getBinder());
} catch (RemoteException e) {
- Log.e(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e);
- throw new ImsException("Remote IMS Service is not available",
- ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ Log.w(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e);
}
}
@@ -388,26 +449,24 @@ public class ImsRcsManager {
* RCS capabilities provided over-the-top by applications.
*
* @param capability The RCS capability to query.
- * @param radioTech The radio tech that this capability failed for, defined as
- * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
- * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} or
- * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}.
+ * @param radioTech The radio technology type that we are querying.
* @return true if the RCS capability is capable for this subscription, false otherwise. This
* does not necessarily mean that we are registered for IMS and the capability is available, but
* rather the subscription is capable of this service over IMS.
- * @see #isAvailable(int)
+ * @see #isAvailable(int, int)
* @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL
* @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL
* @throws ImsException if the IMS service is not available when calling this method.
* See {@link ImsException#getCode()} for more information on the error codes.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "isCapable: IImsRcsController is null");
+ Log.w(TAG, "isCapable: IImsRcsController is null");
throw new ImsException("Cannot find remote IMS service",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
@@ -415,7 +474,7 @@ public class ImsRcsManager {
try {
return imsRcsController.isCapable(mSubId, capability, radioTech);
} catch (RemoteException e) {
- Log.e(TAG, "Error calling IImsRcsController#isCapable", e);
+ Log.w(TAG, "Error calling IImsRcsController#isCapable", e);
throw new ImsException("Remote IMS Service is not available",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
@@ -428,6 +487,7 @@ public class ImsRcsManager {
* RCS capabilities provided by over-the-top by applications.
*
* @param capability the RCS capability to query.
+ * @param radioTech The radio technology type that we are querying.
* @return true if the RCS capability is currently available for the associated subscription,
* false otherwise. If the capability is available, IMS is registered and the service is
* currently available over IMS.
@@ -436,25 +496,57 @@ public class ImsRcsManager {
* See {@link ImsException#getCode()} for more information on the error codes.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability)
+ public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)
throws ImsException {
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "isAvailable: IImsRcsController is null");
+ Log.w(TAG, "isAvailable: IImsRcsController is null");
throw new ImsException("Cannot find remote IMS service",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
try {
- return imsRcsController.isAvailable(mSubId, capability);
+ return imsRcsController.isAvailable(mSubId, capability, radioTech);
} catch (RemoteException e) {
- Log.e(TAG, "Error calling IImsRcsController#isAvailable", e);
+ Log.w(TAG, "Error calling IImsRcsController#isAvailable", e);
throw new ImsException("Remote IMS Service is not available",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
+ /**
+ * Add the {@link OnAvailabilityChangedListener} to collection for tracking.
+ * @param executor The executor that will be used when the publish state is changed and the
+ * {@link OnAvailabilityChangedListener} is called.
+ * @param listener The {@link OnAvailabilityChangedListener} to call the publish state changed.
+ * @return The {@link AvailabilityCallbackAdapter} to wrapper the
+ * {@link OnAvailabilityChangedListener}
+ */
+ private AvailabilityCallbackAdapter addAvailabilityChangedListenerToCollection(
+ @NonNull Executor executor, @NonNull OnAvailabilityChangedListener listener) {
+ AvailabilityCallbackAdapter adapter = new AvailabilityCallbackAdapter(executor, listener);
+ synchronized (mAvailabilityChangedCallbacks) {
+ mAvailabilityChangedCallbacks.put(listener, adapter);
+ }
+ return adapter;
+ }
+
+ /**
+ * Remove the existing {@link OnAvailabilityChangedListener} from the collection.
+ * @param listener The {@link OnAvailabilityChangedListener} to remove from the collection.
+ * @return The wrapper class {@link AvailabilityCallbackAdapter} associated with the
+ * {@link OnAvailabilityChangedListener}.
+ */
+ private AvailabilityCallbackAdapter removeAvailabilityChangedListenerFromCollection(
+ @NonNull OnAvailabilityChangedListener listener) {
+ synchronized (mAvailabilityChangedCallbacks) {
+ return mAvailabilityChangedCallbacks.remove(listener);
+ }
+ }
+
private IImsRcsController getIImsRcsController() {
IBinder binder = TelephonyFrameworkInitializer
.getTelephonyServiceManager()
diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl
new file mode 100644
index 000000000000..0830ff2ff050
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.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.ims;
+
+parcelable ImsRegistrationAttributes;
diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
new file mode 100644
index 000000000000..ccb3231526dd
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
@@ -0,0 +1,252 @@
+/*
+ * 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.ims;
+
+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 android.telephony.AccessNetworkConstants;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.ArraySet;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Contains the attributes associated with the current IMS registration.
+ */
+public final class ImsRegistrationAttributes implements Parcelable {
+
+ /**
+ * Attribute to specify if an EPDG tunnel is setup over the cellular internet APN.
+ * <p>
+ * If IMS is registered through an EPDG tunnel is setup over the cellular internet APN then this
+ * bit will be set. If IMS is registered through the IMS APN, then this bit will not be set.
+ *
+ */
+ public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1 << 0;
+
+ /** @hide */
+ // Defines the underlying radio technology type that we have registered for IMS over.
+ @IntDef(prefix = "ATTR_",
+ value = {
+ ATTR_EPDG_OVER_CELL_INTERNET,
+ },
+ flag = true)
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsAttributeFlag {}
+
+ /**
+ * Builder for creating {@link ImsRegistrationAttributes} instances.
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ private final int mRegistrationTech;
+ private Set<String> mFeatureTags = Collections.emptySet();
+
+ /**
+ * Build a new instance of {@link ImsRegistrationAttributes}.
+ *
+ * @param registrationTech The Radio Access Technology that IMS is registered on.
+ */
+ public Builder(@ImsRegistrationImplBase.ImsRegistrationTech int registrationTech) {
+ mRegistrationTech = registrationTech;
+ }
+
+ /**
+ * Optional IMS feature tags included in this IMS registration.
+ * @param tags A set of Strings containing the MMTEL and RCS feature tags associated with
+ * the IMS registration. This information is used for services such as the UCE
+ * service to ascertain the complete IMS registration state to ensure the SIP
+ * PUBLISH is accurate. The format of the set of feature tags must be one feature
+ * tag key and value per entry. Each feature tag will contain the feature tag name
+ * and string value (if applicable), even if they have the same feature tag name.
+ * For example,
+ * {@code +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg,
+ * urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", +g.gsma.callcomposer} must
+ * be split into three feature tag entries:
+ * {@code {+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg",
+ * +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session",
+ * +g.gsma.callcomposer}}.
+ */
+ public @NonNull Builder setFeatureTags(@NonNull Set<String> tags) {
+ if (tags == null) {
+ throw new IllegalArgumentException("feature tag set must not be null");
+ }
+ mFeatureTags = new ArraySet<>(tags);
+ return this;
+ }
+
+ /**
+ * @return A new instance created from this builder.
+ */
+ public @NonNull ImsRegistrationAttributes build() {
+ return new ImsRegistrationAttributes(mRegistrationTech,
+ RegistrationManager.getAccessType(mRegistrationTech),
+ getAttributeFlags(mRegistrationTech),
+ mFeatureTags);
+ }
+
+ /**
+ * @return attribute flags from the registration technology.
+ */
+ private static int getAttributeFlags(int imsRadioTech) {
+ int attributes = 0;
+ if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
+ attributes |= ATTR_EPDG_OVER_CELL_INTERNET;
+ }
+ return attributes;
+ }
+ }
+
+ private final int mRegistrationTech;
+ private final int mTransportType;
+ private final int mImsAttributeFlags;
+ private final ArrayList<String> mFeatureTags;
+
+ /**
+ * Create a new {@link ImsRegistrationAttributes} instance.
+ *
+ * @param registrationTech The technology that IMS has been registered on.
+ * @param transportType The transport type that IMS has been registered on.
+ * @param imsAttributeFlags The attributes associated with the IMS registration.
+ * @param featureTags The feature tags included in the IMS registration.
+ * @see Builder
+ * @hide
+ */
+ public ImsRegistrationAttributes(
+ @ImsRegistrationImplBase.ImsRegistrationTech int registrationTech,
+ @AccessNetworkConstants.TransportType int transportType,
+ @ImsAttributeFlag int imsAttributeFlags,
+ @Nullable Set<String> featureTags) {
+ mRegistrationTech = registrationTech;
+ mTransportType = transportType;
+ mImsAttributeFlags = imsAttributeFlags;
+ mFeatureTags = new ArrayList<>(featureTags);
+ }
+
+ /**@hide*/
+ public ImsRegistrationAttributes(Parcel source) {
+ mRegistrationTech = source.readInt();
+ mTransportType = source.readInt();
+ mImsAttributeFlags = source.readInt();
+ mFeatureTags = new ArrayList<>();
+ source.readList(mFeatureTags, null /*classloader*/);
+ }
+
+ /**
+ * @return The Radio Access Technology that the IMS registration has been registered over.
+ * @hide
+ */
+ @SystemApi
+ public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTechnology() {
+ return mRegistrationTech;
+ }
+
+ /**
+ * @return The access network transport type that IMS has been registered over.
+ */
+ public @AccessNetworkConstants.TransportType int getTransportType() {
+ return mTransportType;
+ }
+
+ /**
+ * @return A bit-mask containing attributes associated with the IMS registration.
+ */
+ public @ImsAttributeFlag int getAttributeFlags() {
+ return mImsAttributeFlags;
+ }
+
+ /**
+ * Gets the Set of feature tags associated with the current IMS registration, if the IMS
+ * service supports supplying this information.
+ * <p>
+ * The format of the set of feature tags will be one feature tag key and value per entry and
+ * will potentially contain MMTEL and RCS feature tags, depending the configuration of the IMS
+ * service associated with the registration indications. Each feature tag will contain the
+ * feature tag name and string value (if applicable), even if they have the same feature tag
+ * name. For example, {@code +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg,
+ * urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", +g.gsma.callcomposer} will be split
+ * into three feature tag entries:
+ * {@code {+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg",
+ * +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session",
+ * +g.gsma.callcomposer}}.
+ * @return The Set of feature tags associated with the current IMS registration.
+ */
+ public @NonNull Set<String> getFeatureTags() {
+ if (mFeatureTags == null) {
+ return Collections.emptySet();
+ }
+ return new ArraySet<>(mFeatureTags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mRegistrationTech);
+ dest.writeInt(mTransportType);
+ dest.writeInt(mImsAttributeFlags);
+ dest.writeList(mFeatureTags);
+ }
+
+ public static final @NonNull Creator<ImsRegistrationAttributes> CREATOR =
+ new Creator<ImsRegistrationAttributes>() {
+ @Override
+ public ImsRegistrationAttributes createFromParcel(Parcel source) {
+ return new ImsRegistrationAttributes(source);
+ }
+
+ @Override
+ public ImsRegistrationAttributes[] newArray(int size) {
+ return new ImsRegistrationAttributes[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ImsRegistrationAttributes that = (ImsRegistrationAttributes) o;
+ return mRegistrationTech == that.mRegistrationTech
+ && mTransportType == that.mTransportType
+ && mImsAttributeFlags == that.mImsAttributeFlags
+ && Objects.equals(mFeatureTags, that.mFeatureTags);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRegistrationTech, mTransportType, mImsAttributeFlags, mFeatureTags);
+ }
+
+ @Override
+ public String toString() {
+ return "ImsRegistrationAttributes { transportType= " + mTransportType + ", attributeFlags="
+ + mImsAttributeFlags + ", featureTags=[" + mFeatureTags + "]}";
+ }
+}
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 31ba8530e623..6fda2e12fffd 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -32,6 +32,7 @@ import android.os.ServiceSpecificException;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.TelephonyManager;
import android.telephony.ims.aidl.IImsConfigCallback;
import android.telephony.ims.aidl.IRcsConfigCallback;
import android.telephony.ims.feature.MmTelFeature;
@@ -1300,7 +1301,7 @@ public class ProvisioningManager {
* provisioning.
* <p>
* Requires Permission: Manifest.permission.MODIFY_PHONE_STATE or that the calling app has
- * carrier privileges (see {@link #hasCarrierPrivileges}).
+ * carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
* @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed.
* @param isCompressed The XML file is compressed in gzip format and must be decompressed
* before being read.
@@ -1330,7 +1331,7 @@ public class ProvisioningManager {
* the intent is valid. and {@link #EXTRA_STATUS} to specify RCS VoLTE single registration
* status.
*/
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE =
"android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE";
@@ -1375,7 +1376,7 @@ public class ProvisioningManager {
* provisioning status events {@link #registerRcsProvisioningChangedCallback}
* @param rcc RCS client configuration {@link RcsClientConfiguration}
*/
- @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
public void setRcsClientConfiguration(
@NonNull RcsClientConfiguration rcc) throws ImsException {
try {
@@ -1390,6 +1391,14 @@ public class ProvisioningManager {
/**
* Returns a flag to indicate whether or not the device supports IMS single registration for
* MMTEL and RCS features as well as if the carrier has provisioned the feature.
+ *
+ * <p> Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
+ * <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
+ * <li>or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
* @return true if IMS single registration is capable at this time, or false otherwise
* @throws ImsException If the remote ImsService is not available for
* any reason or the subscription associated with this instance is no
@@ -1398,7 +1407,8 @@ public class ProvisioningManager {
* @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION for whether or not this
* device supports IMS single registration.
*/
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
public boolean isRcsVolteSingleRegistrationCapable() throws ImsException {
try {
return getITelephony().isRcsVolteSingleRegistrationCapable(mSubId);
@@ -1408,36 +1418,44 @@ public class ProvisioningManager {
}
/**
- * Registers a new {@link RcsProvisioningCallback} to listen to changes to
- * RCS provisioning xml.
- *
- * <p>RCS application must be the default messaging application and must
- * have already registered its {@link RcsClientConfiguration} by using
- * {@link #setRcsClientConfiguration} before it registers the provisioning
- * callback. If ProvisioningManager has a valid RCS configuration at the
- * time of callback registration and a reconfiguration is not required
- * due to RCS client parameters change, then the callback shall be invoked
- * immediately with the xml.
- * When the subscription associated with this callback is removed (SIM removed,
- * ESIM swap,etc...), this callback will automatically be removed.
- *
- * @param executor The {@link Executor} to call the callback methods on
- * @param callback The rcs provisioning callback to be registered.
- * @see #unregisterRcsProvisioningChangedCallback(RcsProvisioningCallback)
- * @see SubscriptionManager.OnSubscriptionsChangedListener
- * @throws IllegalArgumentException if the subscription associated with this
- * callback is not active (SIM is not inserted, ESIM inactive) or the
- * subscription is invalid.
- * @throws ImsException if the subscription associated with this callback is
- * valid, but the {@link ImsService} associated with the subscription is not
- * available. This can happen if the service crashed, for example.
- * It shall also throw this exception when the RCS client parameters for the
- * application are not valid. In that case application must set the client
- * params (See {@link #setRcsClientConfiguration}) and re register the
- * callback.
- * See {@link ImsException#getCode()} for a more detailed reason.
- */
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ * Registers a new {@link RcsProvisioningCallback} to listen to changes to
+ * RCS provisioning xml.
+ *
+ * <p>RCS application must be the default messaging application and must
+ * have already registered its {@link RcsClientConfiguration} by using
+ * {@link #setRcsClientConfiguration} before it registers the provisioning
+ * callback. If ProvisioningManager has a valid RCS configuration at the
+ * time of callback registration and a reconfiguration is not required
+ * due to RCS client parameters change, then the callback shall be invoked
+ * immediately with the xml.
+ * When the subscription associated with this callback is removed (SIM removed,
+ * ESIM swap,etc...), this callback will automatically be removed.
+ * <p> Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
+ * <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
+ * <li>or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
+ * @param executor The {@link Executor} to call the callback methods on
+ * @param callback The rcs provisioning callback to be registered.
+ * @see #unregisterRcsProvisioningChangedCallback(RcsProvisioningCallback)
+ * @see SubscriptionManager.OnSubscriptionsChangedListener
+ * @throws IllegalArgumentException if the subscription associated with this
+ * callback is not active (SIM is not inserted, ESIM inactive) or the
+ * subscription is invalid.
+ * @throws ImsException if the subscription associated with this callback is
+ * valid, but the {@link ImsService} associated with the subscription is not
+ * available. This can happen if the service crashed, for example.
+ * It shall also throw this exception when the RCS client parameters for the
+ * application are not valid. In that case application must set the client
+ * params (See {@link #setRcsClientConfiguration}) and re register the
+ * callback.
+ * See {@link ImsException#getCode()} for a more detailed reason.
+ */
+ @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
public void registerRcsProvisioningChangedCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull RcsProvisioningCallback callback) throws ImsException {
@@ -1459,13 +1477,22 @@ public class ProvisioningManager {
* removed, ESIM swap, etc...), this callback will automatically be
* removed. If this method is called for an inactive subscription, it
* will result in a no-op.
+ * <p> Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
+ * <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
+ * <li>or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
* @param callback The existing {@link RcsProvisioningCallback} to be
* removed.
* @see #registerRcsProvisioningChangedCallback
* @throws IllegalArgumentException if the subscription associated with this callback is
* invalid.
*/
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
public void unregisterRcsProvisioningChangedCallback(
@NonNull RcsProvisioningCallback callback) {
try {
@@ -1480,7 +1507,7 @@ public class ProvisioningManager {
* Reconfiguration triggered by the RCS application. Most likely cause
* is the 403 forbidden to a HTTP request.
*/
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
public void triggerRcsReconfiguration() {
try {
getITelephony().triggerRcsReconfiguration(mSubId);
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 070fd799d6cc..09c07d3f203c 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -32,6 +32,7 @@ import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsRcsController;
import android.telephony.ims.aidl.IRcsUceControllerCallback;
import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.telephony.ims.feature.RcsFeature;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -467,7 +468,7 @@ public class RcsUceAdapter {
* 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)} and ensuring
+ * {@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}.
@@ -484,7 +485,8 @@ public class RcsUceAdapter {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @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 {
@@ -557,7 +559,7 @@ public class RcsUceAdapter {
*
* <p>
* Be sure to check the availability of this feature using
- * {@link ImsRcsManager#isAvailable(int)} and ensuring
+ * {@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
@@ -571,7 +573,8 @@ public class RcsUceAdapter {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
+ Manifest.permission.READ_CONTACTS})
public void requestAvailability(@NonNull Uri contactNumber,
@NonNull @CallbackExecutor Executor executor,
@NonNull CapabilitiesCallback c) throws ImsException {
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index b430befbc024..c682afe8178a 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -72,30 +72,6 @@ public interface RegistrationManager {
*/
int REGISTRATION_STATE_REGISTERED = 2;
- /**
- * @hide
- */
- // Defines the underlying radio technology type that we have registered for IMS over.
- @IntDef(prefix = "ATTR_",
- value = {
- ATTR_EPDG_OVER_CELL_INTERNET,
- },
- flag = true)
- @Retention(RetentionPolicy.SOURCE)
- public @interface ImsAttributes {}
-
- /**
- * Attribute to specify if EPDG tunnel is setup over cellular internet.
- * if EPDG tunnel is setup over cellular internet then this bit will be set else the same will
- * not be set.
- */
- int ATTR_EPDG_OVER_CELL_INTERNET = 0x00000001;
-
- //******************************************************************************************
- // Next attribute value: 0x00000002
- //******************************************************************************************
-
-
/**@hide*/
// Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
// and WWAN are more accurate constants.
@@ -103,7 +79,8 @@ public interface RegistrationManager {
new HashMap<Integer, Integer>() {{
// Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE
// case, since it is defined.
- put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1);
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE,
+ AccessNetworkConstants.TRANSPORT_TYPE_INVALID);
put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
@@ -132,6 +109,20 @@ public interface RegistrationManager {
}
/**
+ * @param regtech The registration technology.
+ * @return The Access Network type from registration technology.
+ * @hide
+ */
+ static int getAccessType(int regtech) {
+ if (!RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regtech)) {
+ Log.w("RegistrationManager", "getAccessType - invalid regType returned: "
+ + regtech);
+ return AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+ }
+ return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regtech);
+ }
+
+ /**
* Callback class for receiving IMS network Registration callback events.
* @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
* @see #unregisterImsRegistrationCallback(RegistrationCallback)
@@ -149,45 +140,24 @@ public interface RegistrationManager {
}
@Override
- public void onRegistered(int imsRadioTech) {
+ public void onRegistered(ImsRegistrationAttributes attr) {
if (mLocalCallback == null) return;
final long callingIdentity = Binder.clearCallingIdentity();
try {
- mExecutor.execute(() -> {
- mLocalCallback.onRegistered(getAccessType(imsRadioTech));
- });
- int attributes = 0;
- if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
- attributes = changeBitmask(attributes, ATTR_EPDG_OVER_CELL_INTERNET,
- true);
- }
- final int finalattributes = attributes;
- mExecutor.execute(() ->
- mLocalCallback.onRegistered(getAccessType(imsRadioTech),
- finalattributes));
+ mExecutor.execute(() -> mLocalCallback.onRegistered(attr));
} finally {
restoreCallingIdentity(callingIdentity);
}
}
@Override
- public void onRegistering(int imsRadioTech) {
+ public void onRegistering(ImsRegistrationAttributes attr) {
if (mLocalCallback == null) return;
final long callingIdentity = Binder.clearCallingIdentity();
try {
- mExecutor.execute(() ->
- mLocalCallback.onRegistering(getAccessType(imsRadioTech)));
- int attributes = 0;
- if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
- attributes = changeBitmask(attributes, ATTR_EPDG_OVER_CELL_INTERNET,
- true);
- }
- final int finalattributes = attributes;
- mExecutor.execute(() ->
- mLocalCallback.onRegistering(getAccessType(imsRadioTech),
- finalattributes));
+ mExecutor.execute(() -> mLocalCallback.onRegistering(attr));
} finally {
restoreCallingIdentity(callingIdentity);
}
@@ -232,31 +202,6 @@ public interface RegistrationManager {
private void setExecutor(Executor executor) {
mExecutor = executor;
}
-
- private static int getAccessType(int regType) {
- if (!RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) {
- Log.w("RegistrationManager", "RegistrationBinder - invalid regType returned: "
- + regType);
- return -1;
- }
- return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regType);
- }
-
- /**
- * Changes a attribute bit-mask to add or remove an attribute.
- *
- * @param bitmask The bit-mask.
- * @param bitfield The bit-field to change.
- * @param enabled Whether the bit-field should be set or removed.
- * @return The bit-mask with the bit-field changed.
- */
- private int changeBitmask(int bitmask, int bitfield, boolean enabled) {
- if (enabled) {
- return bitmask | bitfield;
- } else {
- return bitmask & ~bitfield;
- }
- }
}
private final RegistrationBinder mBinder = new RegistrationBinder(this);
@@ -265,7 +210,7 @@ public interface RegistrationManager {
* Notifies the framework when the IMS Provider is registered to the IMS network.
*
* @param imsTransportType the radio access technology.
- * @deprecated Use {@link #onRegistered(int, int)} instead.
+ * @deprecated Use {@link #onRegistered(ImsRegistrationAttributes)} instead.
*/
@Deprecated
public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
@@ -273,25 +218,20 @@ public interface RegistrationManager {
/**
* Notifies the framework when the IMS Provider is registered to the IMS network
- * with corresponding attributes
- *
- * @param imsTransportType the radio access technology.
- * @param registrationAttributes IMS registration attributes as a bitmap of attributes.
- * Possible attributes are following
- * <ul>
- * <li>{@link #ATTR_EPDG_OVER_CELL_INTERNET}</li>
- * </ul>
+ * with corresponding attributes.
*
+ * @param attributes The attributes associated with this IMS registration.
*/
- public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType,
- @ImsAttributes int registrationAttributes) {
+ public void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
+ // Default impl to keep backwards compatibility with old implementations
+ onRegistered(attributes.getTransportType());
}
/**
* Notifies the framework when the IMS Provider is trying to register the IMS network.
*
* @param imsTransportType the radio access technology.
- * @deprecated Use {@link #onRegistering(int, int)} instead.
+ * @deprecated Use {@link #onRegistering(ImsRegistrationAttributes)} instead.
*/
public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
}
@@ -299,12 +239,11 @@ public interface RegistrationManager {
/**
* Notifies the framework when the IMS Provider is trying to register the IMS network.
*
- * @param imsTransportType the radio access technology.
- * @param registrationAttributes IMS registration attributes as a bitmap of attributes.
- * Possible attributes are following
+ * @param attributes The attributes associated with this IMS registration.
*/
- public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType,
- @ImsAttributes int registrationAttributes) {
+ public void onRegistering(@NonNull ImsRegistrationAttributes attributes) {
+ // Default impl to keep backwards compatibility with old implementations
+ onRegistering(attributes.getTransportType());
}
/**
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 04421c9a2449..171842b26cb0 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -275,7 +275,8 @@ public class SipDelegateManager {
* @see CarrierConfigManager.Ims#KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL
* @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION
*/
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
public boolean isSupported() throws ImsException {
try {
IImsRcsController controller = mBinderCache.getBinder();
@@ -317,7 +318,7 @@ public class SipDelegateManager {
* @throws ImsException Thrown if there was a problem communicating with the ImsService
* associated with this SipDelegateManager. See {@link ImsException#getCode()}.
*/
- @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
public void createSipDelegate(@NonNull DelegateRequest request, @NonNull Executor executor,
@NonNull DelegateConnectionStateCallback dc,
@NonNull DelegateConnectionMessageCallback mc) throws ImsException {
@@ -351,7 +352,7 @@ public class SipDelegateManager {
* @param delegateConnection The SipDelegateConnection to destroy.
* @param reason The reason for why this SipDelegateConnection was destroyed.
*/
- @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
public void destroySipDelegate(@NonNull SipDelegateConnection delegateConnection,
@SipDelegateDestroyReason int reason) {
@@ -392,6 +393,7 @@ public class SipDelegateManager {
* this condition. May be {@code null} if there was no reason String provided from the
* network.
*/
+ @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
public void triggerFullNetworkRegistration(@NonNull SipDelegateConnection connection,
@IntRange(from = 100, to = 699) int sipCode, @Nullable String sipReason) {
if (connection == null) {
diff --git a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
index 4435640e008c..a217d1321342 100644
--- a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.net.Uri;
import android.os.Binder;
import android.os.RemoteException;
+import android.telephony.ims.ImsException;
import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.stub.CapabilityExchangeEventListener;
import android.util.Log;
@@ -47,7 +48,7 @@ public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventLis
* Receives the request of publishing capabilities from the network and deliver this request
* to the framework via the registered capability exchange event listener.
*/
- public void onRequestPublishCapabilities(int publishTriggerType) {
+ public void onRequestPublishCapabilities(int publishTriggerType) throws ImsException {
ICapabilityExchangeEventListener listener = mListenerBinder;
if (listener == null) {
return;
@@ -56,13 +57,15 @@ public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventLis
listener.onRequestPublishCapabilities(publishTriggerType);
} catch (RemoteException e) {
Log.w(LOG_TAG, "request publish capabilities exception: " + e);
+ throw new ImsException("Remote is not available",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
/**
* Receives the unpublish notification and deliver this callback to the framework.
*/
- public void onUnpublish() {
+ public void onUnpublish() throws ImsException {
ICapabilityExchangeEventListener listener = mListenerBinder;
if (listener == null) {
return;
@@ -71,6 +74,8 @@ public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventLis
listener.onUnpublish();
} catch (RemoteException e) {
Log.w(LOG_TAG, "Unpublish exception: " + e);
+ throw new ImsException("Remote is not available",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@@ -79,7 +84,8 @@ public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventLis
* request to the framework.
*/
public void onRemoteCapabilityRequest(@NonNull Uri contactUri,
- @NonNull List<String> remoteCapabilities, @NonNull OptionsRequestCallback callback) {
+ @NonNull List<String> remoteCapabilities, @NonNull OptionsRequestCallback callback)
+ throws ImsException {
ICapabilityExchangeEventListener listener = mListenerBinder;
if (listener == null) {
return;
@@ -87,10 +93,11 @@ public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventLis
IOptionsRequestCallback internalCallback = new IOptionsRequestCallback.Stub() {
@Override
- public void respondToCapabilityRequest(RcsContactUceCapability ownCapabilities) {
+ public void respondToCapabilityRequest(RcsContactUceCapability ownCapabilities,
+ boolean isBlocked) {
final long callingIdentity = Binder.clearCallingIdentity();
try {
- callback.onRespondToCapabilityRequest(ownCapabilities);
+ callback.onRespondToCapabilityRequest(ownCapabilities, isBlocked);
} finally {
restoreCallingIdentity(callingIdentity);
}
@@ -110,6 +117,8 @@ public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventLis
listener.onRemoteCapabilityRequest(contactUri, remoteCapabilities, internalCallback);
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote capability request exception: " + e);
+ throw new ImsException("Remote is not available",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index 7a6c28bddd09..8931a78709ed 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -47,7 +47,7 @@ interface IImsRcsController {
void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
boolean isCapable(int subId, int capability, int radioTech);
- boolean isAvailable(int subId, int capability);
+ boolean isAvailable(int subId, int capability, int radioTech);
// ImsUceAdapter specific
void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
index 749b1916962e..179407c983e5 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
@@ -21,6 +21,7 @@ import android.net.Uri;
import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
/**
* See {@link ImsManager#RegistrationCallback} for more information.
@@ -28,8 +29,8 @@ import android.telephony.ims.ImsReasonInfo;
* {@hide}
*/
oneway interface IImsRegistrationCallback {
- void onRegistered(int imsRadioTech);
- void onRegistering(int imsRadioTech);
+ void onRegistered(in ImsRegistrationAttributes attr);
+ void onRegistering(in ImsRegistrationAttributes attr);
void onDeregistered(in ImsReasonInfo info);
void onTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info);
void onSubscriberAssociatedUriChanged(in Uri[] uris);
diff --git a/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl b/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl
index d4d5301f38fa..8eecbca7e6a7 100644
--- a/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl
@@ -27,8 +27,9 @@ oneway interface IOptionsRequestCallback {
* Respond to a remote capability request from the contact specified with the capabilities
* of this device.
* @param ownCapabilities The capabilities of this device.
+ * @param isBlocked True if the user has blocked the number sending this request.
*/
- void respondToCapabilityRequest(in RcsContactUceCapability ownCapabilities);
+ void respondToCapabilityRequest(in RcsContactUceCapability ownCapabilities, boolean isBlocked);
/**
* Respond to a remote capability request from the contact specified with the
diff --git a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
index c5b1c90ced12..f3791d1c6f96 100644
--- a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
+++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
@@ -36,12 +36,9 @@ import java.util.Set;
public final class CapabilityChangeRequest implements Parcelable {
/**
- * Contains a feature capability, defined as
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS},
- * along with an associated technology, defined as
+ * Contains a MMTEL feature capability {@link MmTelFeature.MmTelCapabilities} and RCS feature
+ * capability {@link RcsFeature.RcsImsCapabilities}, along with an associated technology,
+ * defined as
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
@@ -50,7 +47,7 @@ public final class CapabilityChangeRequest implements Parcelable {
private final int mCapability;
private final int radioTech;
- public CapabilityPair(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ public CapabilityPair(int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
this.mCapability = capability;
this.radioTech = radioTech;
@@ -81,13 +78,10 @@ public final class CapabilityChangeRequest implements Parcelable {
}
/**
- * @return The stored capability, defined as
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
+ * @return The stored capability, defined as {@link MmTelFeature.MmTelCapabilities} and
+ * {@link RcsFeature.RcsImsCapabilities}
*/
- public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() {
+ public int getCapability() {
return mCapability;
}
@@ -125,12 +119,11 @@ public final class CapabilityChangeRequest implements Parcelable {
* Add one or many capabilities to the request to be enabled.
*
* @param capabilities A bitfield of capabilities to enable, valid values are defined in
- * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+ * {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}.
* @param radioTech the radio tech that these capabilities should be enabled for, valid
* values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
*/
- public void addCapabilitiesToEnableForTech(
- @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
+ public void addCapabilitiesToEnableForTech(int capabilities,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech);
}
@@ -138,12 +131,11 @@ public final class CapabilityChangeRequest implements Parcelable {
/**
* Add one or many capabilities to the request to be disabled.
* @param capabilities A bitfield of capabilities to diable, valid values are defined in
- * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+ * {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}.
* @param radioTech the radio tech that these capabilities should be disabled for, valid
* values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
*/
- public void addCapabilitiesToDisableForTech(
- @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
+ public void addCapabilitiesToDisableForTech(int capabilities,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech);
}
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 22df921c4214..85703f8de5e5 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -194,7 +194,6 @@ public class RcsFeature extends ImsFeature {
* of the capability and notify the capability status as true using
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
* framework that the capability is available for usage.
- * @hide
*/
public static class RcsImsCapabilities extends Capabilities {
/** @hide*/
@@ -226,12 +225,21 @@ public class RcsFeature extends ImsFeature {
*/
public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
+ /**
+ * Create a new {@link RcsImsCapabilities} instance with the provided capabilities.
+ * @param capabilities The capabilities that are supported for RCS in the form of a
+ * bitfield.
+ */
public RcsImsCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
super(capabilities);
}
- private RcsImsCapabilities(Capabilities c) {
- super(c.getMask());
+ /**
+ * Create a new {@link RcsImsCapabilities} instance with the provided capabilities.
+ * @param capabilities The capabilities instance that are supported for RCS
+ */
+ private RcsImsCapabilities(Capabilities capabilities) {
+ super(capabilities.getMask());
}
@Override
@@ -307,7 +315,7 @@ public class RcsFeature extends ImsFeature {
* set, the {@link RcsFeature} has brought up the capability and is ready for framework
* requests. To change the status of the capabilities
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
- * @hide
+ * @return A copy of the current RcsFeature capability status.
*/
@Override
public @NonNull final RcsImsCapabilities queryCapabilityStatus() {
@@ -318,13 +326,13 @@ public class RcsFeature extends ImsFeature {
* Notify the framework that the capabilities status has changed. If a capability is enabled,
* this signals to the framework that the capability has been initialized and is ready.
* Call {@link #queryCapabilityStatus()} to return the current capability status.
- * @hide
+ * @param capabilities The current capability status of the RcsFeature.
*/
- public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) {
- if (c == null) {
+ public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities capabilities) {
+ if (capabilities == null) {
throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
}
- super.notifyCapabilitiesStatusChanged(c);
+ super.notifyCapabilitiesStatusChanged(capabilities);
}
/**
@@ -333,7 +341,9 @@ public class RcsFeature extends ImsFeature {
* {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to
* enable or disable capability A, this method should return the correct configuration for
* capability A afterwards (until it has changed).
- * @hide
+ * @param capability The capability that we are querying the configuration for.
+ * @param radioTech The radio technology type that we are querying.
+ * @return true if the capability is enabled, false otherwise.
*/
public boolean queryCapabilityConfiguration(
@RcsUceAdapter.RcsImsCapabilityFlag int capability,
@@ -355,11 +365,12 @@ public class RcsFeature extends ImsFeature {
* If for some reason one or more of these capabilities can not be enabled/disabled,
* {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should
* be called for each capability change that resulted in an error.
- * @hide
+ * @param request The request to change the capability.
+ * @param callback To notify the framework that the result of the capability changes.
*/
@Override
public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
- @NonNull CapabilityCallbackProxy c) {
+ @NonNull CapabilityCallbackProxy callback) {
// Base Implementation - Override to provide functionality
}
diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
index d9734a7475c0..4967e5da7c9a 100644
--- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
+++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -16,32 +16,58 @@
package android.telephony.ims.stub;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.net.Uri;
import android.telephony.ims.ImsException;
import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.RcsUceAdapter;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
+import android.util.Log;
+
+import java.util.List;
/**
- * The interface of the capabilities event listener for ImsService to notify the framework of the
- * UCE request and status updated.
+ * The interface that is used by the framework to listen to events from the vendor RCS stack
+ * regarding capabilities exchange using presence server and OPTIONS.
* @hide
*/
@SystemApi
public interface CapabilityExchangeEventListener {
/**
* Interface used by the framework to respond to OPTIONS requests.
- * @hide
*/
interface OptionsRequestCallback {
/**
* Respond to a remote capability request from the contact specified with the
* capabilities of this device.
* @param ownCapabilities The capabilities of this device.
+ * @hide
*/
- void onRespondToCapabilityRequest(@NonNull RcsContactUceCapability ownCapabilities);
+ default void onRespondToCapabilityRequest(
+ @NonNull RcsContactUceCapability ownCapabilities) {}
+
+ /**
+ * Respond to a remote capability request from the contact specified with the
+ * capabilities of this device.
+ * @param ownCapabilities The capabilities of this device.
+ * @param isBlocked Whether or not the user has blocked the number requesting the
+ * capabilities of this device. If true, the device should respond to the OPTIONS
+ * request with a 200 OK response and no capabilities.
+ */
+ default void onRespondToCapabilityRequest(@NonNull RcsContactUceCapability ownCapabilities,
+ boolean isBlocked) {
+ Log.w("CapabilityExchangeEventListener", "implement "
+ + "onRespondToCapabilityRequest(RcsContactUceCapability, boolean) instead!");
+ // Fall back to old implementation
+ if (isBlocked) {
+ onRespondToCapabilityRequestWithError(200, "OK");
+ } else {
+ onRespondToCapabilityRequest(ownCapabilities);
+ }
+ }
/**
* Respond to a remote capability request from the contact specified with the
@@ -49,7 +75,8 @@ public interface CapabilityExchangeEventListener {
* @param code The SIP response code to respond with.
* @param reason A non-null String containing the reason associated with the SIP code.
*/
- void onRespondToCapabilityRequestWithError(int code, @NonNull String reason);
+ void onRespondToCapabilityRequestWithError(@IntRange(from = 100, to = 699) int code,
+ @NonNull String reason);
}
/**
@@ -59,8 +86,7 @@ public interface CapabilityExchangeEventListener {
* This is typically used when trying to generate an initial PUBLISH for a new subscription to
* the network. The device will cache all presence publications after boot until this method is
* called the first time.
- * @param publishTriggerType {@link RcsUceAdapter#StackPublishTriggerType} The reason for the
- * capability update request.
+ * @param publishTriggerType The reason for the capability update request.
* @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not currently
* connected to the framework. This can happen if the {@link RcsFeature} is not
* {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
@@ -81,4 +107,25 @@ public interface CapabilityExchangeEventListener {
* Telephony stack has crashed.
*/
void onUnpublish() throws ImsException;
+
+ /**
+ * Inform the framework of an OPTIONS query from a remote device for this device's UCE
+ * capabilities.
+ * <p>
+ * The framework will respond via the
+ * {@link OptionsRequestCallback#onRespondToCapabilityRequest} or
+ * {@link OptionsRequestCallback#onRespondToCapabilityRequestWithError}.
+ * @param contactUri The URI associated with the remote contact that is
+ * requesting capabilities.
+ * @param remoteCapabilities The remote contact's capability information.
+ * @param callback The callback of this request which is sent from the remote user.
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not
+ * currently connected to the framework. This can happen if the {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+ * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
+ * cases when the Telephony stack has crashed.
+ */
+ void onRemoteCapabilityRequest(@NonNull Uri contactUri,
+ @NonNull List<String> remoteCapabilities,
+ @NonNull OptionsRequestCallback callback) throws ImsException;
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 4f753c308f7e..39994be34865 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -18,17 +18,18 @@ package android.telephony.ims.stub;
import android.annotation.IntDef;
import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.Uri;
import android.os.RemoteException;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.util.RemoteCallbackListExt;
import com.android.internal.util.ArrayUtils;
@@ -89,7 +90,10 @@ public class ImsRegistrationImplBase {
@Override
public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException {
- return getConnectionType();
+ synchronized (mLock) {
+ return (mRegistrationAttributes == null) ? REGISTRATION_TECH_NONE
+ : mRegistrationAttributes.getRegistrationTechnology();
+ }
}
@Override
@@ -122,8 +126,7 @@ public class ImsRegistrationImplBase {
new RemoteCallbackListExt<>();
private final Object mLock = new Object();
// Locked on mLock
- private @ImsRegistrationTech
- int mConnectionType = REGISTRATION_TECH_NONE;
+ private ImsRegistrationAttributes mRegistrationAttributes;
// Locked on mLock
private int mRegistrationState = REGISTRATION_STATE_UNKNOWN;
// Locked on mLock, create unspecified disconnect cause.
@@ -201,18 +204,24 @@ public class ImsRegistrationImplBase {
/**
* Notify the framework that the device is connected to the IMS network.
*
- * @param imsRadioTech the radio access technology. Valid values are defined as
- * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
- * {@link #REGISTRATION_TECH_CROSS_SIM}.
+ * @param imsRadioTech the radio access technology.
*/
public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
- updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERED);
+ onRegistered(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
+ }
+
+ /**
+ * Notify the framework that the device is connected to the IMS network.
+ *
+ * @param attributes The attributes associated with the IMS registration.
+ */
+ public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
+ updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED);
mCallbacks.broadcastAction((c) -> {
try {
- c.onRegistered(imsRadioTech);
+ c.onRegistered(attributes);
} catch (RemoteException e) {
- Log.w(LOG_TAG, e + " " + "onRegistrationConnected() - Skipping " +
- "callback.");
+ Log.w(LOG_TAG, e + "onRegistered(int, Set) - Skipping callback.");
}
});
}
@@ -220,18 +229,24 @@ public class ImsRegistrationImplBase {
/**
* Notify the framework that the device is trying to connect the IMS network.
*
- * @param imsRadioTech the radio access technology. Valid values are defined as
- * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
- * {@link #REGISTRATION_TECH_CROSS_SIM}.
+ * @param imsRadioTech the radio access technology.
*/
public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
- updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERING);
+ onRegistering(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
+ }
+
+ /**
+ * Notify the framework that the device is trying to connect the IMS network.
+ *
+ * @param attributes The attributes associated with the IMS registration.
+ */
+ public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) {
+ updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING);
mCallbacks.broadcastAction((c) -> {
try {
- c.onRegistering(imsRadioTech);
+ c.onRegistering(attributes);
} catch (RemoteException e) {
- Log.w(LOG_TAG, e + " " + "onRegistrationProcessing() - Skipping " +
- "callback.");
+ Log.w(LOG_TAG, e + "onRegistering(int, Set) - Skipping callback.");
}
});
}
@@ -260,8 +275,7 @@ public class ImsRegistrationImplBase {
try {
c.onDeregistered(reasonInfo);
} catch (RemoteException e) {
- Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " +
- "callback.");
+ Log.w(LOG_TAG, e + "onDeregistered() - Skipping callback.");
}
});
}
@@ -281,8 +295,7 @@ public class ImsRegistrationImplBase {
try {
c.onTechnologyChangeFailed(imsRadioTech, reasonInfo);
} catch (RemoteException e) {
- Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " +
- "callback.");
+ Log.w(LOG_TAG, e + "onTechnologyChangeFailed() - Skipping callback.");
}
});
}
@@ -306,14 +319,13 @@ public class ImsRegistrationImplBase {
try {
callback.onSubscriberAssociatedUriChanged(uris);
} catch (RemoteException e) {
- Log.w(LOG_TAG, e + " " + "onSubscriberAssociatedUriChanged() - Skipping "
- + "callback.");
+ Log.w(LOG_TAG, e + "onSubscriberAssociatedUriChanged() - Skipping callback.");
}
}
- private void updateToState(@ImsRegistrationTech int connType, int newState) {
+ private void updateToState(ImsRegistrationAttributes attributes, int newState) {
synchronized (mLock) {
- mConnectionType = connType;
+ mRegistrationAttributes = attributes;
mRegistrationState = newState;
mLastDisconnectCause = null;
}
@@ -325,7 +337,7 @@ public class ImsRegistrationImplBase {
mUrisSet = false;
mUris = null;
- updateToState(REGISTRATION_TECH_NONE,
+ updateToState(new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_NONE).build(),
RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
if (info != null) {
mLastDisconnectCause = info;
@@ -337,30 +349,19 @@ public class ImsRegistrationImplBase {
}
/**
- * @return the current registration connection type. Valid values are
- * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
- * {@link #REGISTRATION_TECH_CROSS_SIM}.
- * @hide
- */
- @VisibleForTesting
- public final @ImsRegistrationTech int getConnectionType() {
- synchronized (mLock) {
- return mConnectionType;
- }
- }
-
- /**
* @param c the newly registered callback that will be updated with the current registration
* state.
*/
private void updateNewCallbackWithState(IImsRegistrationCallback c)
throws RemoteException {
int state;
+ ImsRegistrationAttributes attributes;
ImsReasonInfo disconnectInfo;
boolean urisSet;
Uri[] uris;
synchronized (mLock) {
state = mRegistrationState;
+ attributes = mRegistrationAttributes;
disconnectInfo = mLastDisconnectCause;
urisSet = mUrisSet;
uris = mUris;
@@ -371,11 +372,11 @@ public class ImsRegistrationImplBase {
break;
}
case RegistrationManager.REGISTRATION_STATE_REGISTERING: {
- c.onRegistering(getConnectionType());
+ c.onRegistering(attributes);
break;
}
case RegistrationManager.REGISTRATION_STATE_REGISTERED: {
- c.onRegistered(getConnectionType());
+ c.onRegistered(attributes);
break;
}
case REGISTRATION_STATE_UNKNOWN: {
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index ec98be6e5062..908869beb607 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -19,7 +19,6 @@ package android.telephony.ims.stub;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.net.Uri;
@@ -141,7 +140,7 @@ public class RcsCapabilityExchangeImplBase {
* {@link #publishCapabilities(String, PublishResponseCallback)}.
*
* If this network response also contains a “Reason” header, then the
- * {@link onNetworkResponse(int, String, int, String)} method should be used instead.
+ * {@link #onNetworkResponse(int, String, int, String)} method should be used instead.
*
* @param sipCode The SIP response code sent from the network for the operation
* token specified.
@@ -160,7 +159,7 @@ public class RcsCapabilityExchangeImplBase {
/**
* Provide the framework with a subsequent network response update to
- * {@link #publishCapabilities(RcsContactUceCapability, int)} that also
+ * {@link #publishCapabilities(String, PublishResponseCallback)} that also
* includes a reason provided in the “reason” header. See RFC3326 for more
* information.
*
@@ -186,7 +185,6 @@ public class RcsCapabilityExchangeImplBase {
/**
* Interface used by the framework to respond to OPTIONS requests.
- * @hide
*/
public interface OptionsResponseCallback {
/**
@@ -217,7 +215,7 @@ public class RcsCapabilityExchangeImplBase {
* cases when the Telephony stack has crashed.
*/
void onNetworkResponse(int sipCode, @NonNull String reason,
- @Nullable List<String> theirCaps) throws ImsException;
+ @NonNull List<String> theirCaps) throws ImsException;
}
/**
@@ -243,7 +241,7 @@ public class RcsCapabilityExchangeImplBase {
/**
* Notify the framework of the response to the SUBSCRIBE request from
- * {@link #subscribeForCapabilities(List<Uri>, SubscribeResponseCallback)}.
+ * {@link #subscribeForCapabilities(List, SubscribeResponseCallback)}.
* <p>
* If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the
* framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate},
@@ -251,7 +249,7 @@ public class RcsCapabilityExchangeImplBase {
* subsequent NOTIFY responses to the subscription.
*
* If this network response also contains a “Reason” header, then the
- * {@link onNetworkResponse(int, String, int, String)} method should be used instead.
+ * {@link #onNetworkResponse(int, String, int, String)} method should be used instead.
*
* @param sipCode The SIP response code sent from the network for the operation
* token specified.
@@ -268,7 +266,7 @@ public class RcsCapabilityExchangeImplBase {
/**
* Notify the framework of the response to the SUBSCRIBE request from
- * {@link #subscribeForCapabilities(RcsContactUceCapability, int)} that also
+ * {@link #subscribeForCapabilities(List, SubscribeResponseCallback)} that also
* includes a reason provided in the “reason” header. See RFC3326 for more
* information.
*
@@ -294,7 +292,8 @@ public class RcsCapabilityExchangeImplBase {
/**
* Notify the framework of the latest XML PIDF documents included in the network response
* for the requested contacts' capabilities requested by the Framework using
- * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)}.
+ * {@link RcsUceAdapter#requestCapabilities(List, Executor,
+ * RcsUceAdapter.CapabilitiesCallback)}.
* <p>
* The expected format for the PIDF XML is defined in RFC3861. Each XML document must be a
* "application/pidf+xml" object and start with a root <presence> element. For NOTIFY
@@ -336,7 +335,8 @@ public class RcsCapabilityExchangeImplBase {
/**
* The subscription associated with a previous
- * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)}
+ * {@link RcsUceAdapter#requestCapabilities(List, Executor,
+ * RcsUceAdapter.CapabilitiesCallback)}
* operation has been terminated. This will mostly be due to the network sending a final
* NOTIFY response due to the subscription expiring, but this may also happen due to a
* network error.
@@ -427,12 +427,11 @@ public class RcsCapabilityExchangeImplBase {
* Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism
* in order to receive the capabilities of the remote user in response.
* <p>
- * The implementer must call {@link #onNetworkResponse} to send the response of this
- * query back to the framework.
+ * The implementer must use {@link OptionsResponseCallback} to send the response of
+ * this query from the network back to the framework.
* @param contactUri The URI of the remote user that we wish to get the capabilities of.
* @param myCapabilities The capabilities of this device to send to the remote user.
* @param callback The callback of this request which is sent from the remote user.
- * @hide
*/
// executor used is defined in the constructor.
@SuppressLint("ExecutorRegistration")
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 6fbde503c3a0..15d19a49ee56 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -125,6 +125,7 @@ public class DctConstants {
public static final String PROVISIONING_URL_KEY = "provisioningUrl";
public static final String BANDWIDTH_SOURCE_MODEM_KEY = "modem";
public static final String BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY = "carrier_config";
+ public static final String BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY = "bandwidth_estimator";
public static final String RAT_NAME_LTE = "LTE";
public static final String RAT_NAME_NR_NSA = "NR_NSA";
public static final String RAT_NAME_NR_NSA_MMWAVE = "NR_NSA_MMWAVE";
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0cd17da3c0c5..1d049530ba77 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2387,4 +2387,18 @@ interface ITelephony {
* Gets the current phone capability.
*/
PhoneCapability getPhoneCapability();
+
+ /**
+ * Prepare TelephonyManager for an unattended reboot. The reboot is
+ * required to be done shortly after the API is invoked.
+ *
+ * Requires system privileges.
+ *
+ * @return {@link #PREPARE_UNATTENDED_REBOOT_SUCCESS} in case of success.
+ * {@link #PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED} if the device contains
+ * at least one SIM card for which the user needs to manually enter the PIN
+ * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case
+ * of error.
+ */
+ int prepareForUnattendedReboot();
}
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index f44e32e1b8d9..4515c329987b 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.uicc;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
@@ -30,6 +31,7 @@ import com.android.telephony.Rlog;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.util.List;
/**
@@ -255,13 +257,48 @@ public class IccUtils {
}
if ((b & 0x0f) <= 0x09) {
- ret += (b & 0xf);
+ ret += (b & 0xf);
}
return ret;
}
/**
+ * Encodes a string to be formatted like the EF[ADN] alpha identifier.
+ *
+ * <p>See javadoc for {@link #adnStringFieldToString(byte[], int, int)} for more details on
+ * the relevant specs.
+ *
+ * <p>This will attempt to encode using the GSM 7-bit alphabet but will fallback to UCS-2 if
+ * there are characters that are not supported by it.
+ *
+ * @return the encoded string including the prefix byte necessary to identify the encoding.
+ * @see #adnStringFieldToString(byte[], int, int)
+ */
+ @NonNull
+ public static byte[] stringToAdnStringField(@NonNull String alphaTag) {
+ int septets = GsmAlphabet.countGsmSeptetsUsingTables(alphaTag, false, 0, 0);
+ if (septets != -1) {
+ byte[] ret = new byte[septets];
+ GsmAlphabet.stringToGsm8BitUnpackedField(alphaTag, ret, 0, ret.length);
+ return ret;
+ }
+
+ // Strictly speaking UCS-2 disallows surrogate characters but it's much more complicated to
+ // validate that the string contains only valid UCS-2 characters. Since the read path
+ // in most modern software will decode "UCS-2" by treating it as UTF-16 this should be fine
+ // (e.g. the adnStringFieldToString has done this for a long time on Android). Also there's
+ // already a precedent in SMS applications to ignore the UCS-2/UTF-16 distinction.
+ byte[] alphaTagBytes = alphaTag.getBytes(StandardCharsets.UTF_16BE);
+ byte[] ret = new byte[alphaTagBytes.length + 1];
+ // 0x80 tags the remaining bytes as UCS-2
+ ret[0] = (byte) 0x80;
+ System.arraycopy(alphaTagBytes, 0, ret, 1, alphaTagBytes.length);
+
+ return ret;
+ }
+
+ /**
* Decodes a string field that's formatted like the EF[ADN] alpha
* identifier
*
@@ -311,7 +348,7 @@ public class IccUtils {
ret = new String(data, offset + 1, ucslen * 2, "utf-16be");
} catch (UnsupportedEncodingException ex) {
Rlog.e(LOG_TAG, "implausible UnsupportedEncodingException",
- ex);
+ ex);
}
if (ret != null) {
@@ -344,7 +381,7 @@ public class IccUtils {
len = length - 4;
base = (char) (((data[offset + 2] & 0xFF) << 8) |
- (data[offset + 3] & 0xFF));
+ (data[offset + 3] & 0xFF));
offset += 4;
isucs2 = true;
}
@@ -368,7 +405,7 @@ public class IccUtils {
count++;
ret.append(GsmAlphabet.gsm8BitUnpackedToString(data,
- offset, count));
+ offset, count));
offset += count;
len -= count;
diff --git a/test-base/Android.bp b/test-base/Android.bp
index c7c9fc739189..0b7a3981a403 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -19,6 +19,16 @@
// This contains the junit.framework and android.test classes that were in
// Android API level 25 excluding those from android.test.runner.
// Also contains the com.android.internal.util.Predicate[s] classes.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-CPL-1.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_sdk_library {
name: "android.test.base",
diff --git a/test-base/hiddenapi/Android.bp b/test-base/hiddenapi/Android.bp
index c202467517aa..d4f52d0fc6cd 100644
--- a/test-base/hiddenapi/Android.bp
+++ b/test-base/hiddenapi/Android.bp
@@ -19,6 +19,15 @@
// UnsupportedAppUsage annotation to tag those methods that are accessible via the hiddenapi.
// Relies on the convention that modules with name <x>-hiddenapi provide hiddenapi information for
// module <x> that is on the bootclasspath.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "android.test.base-hiddenapi",
compile_dex: true,
diff --git a/test-legacy/Android.mk b/test-legacy/Android.mk
index af26c5b80717..ce5e4cf46695 100644
--- a/test-legacy/Android.mk
+++ b/test-legacy/Android.mk
@@ -27,6 +27,9 @@ ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
include $(CLEAR_VARS)
LOCAL_MODULE := android.test.legacy
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-MIT SPDX-license-identifier-Unicode-DFS
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
LOCAL_SDK_VERSION := current
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 7d0f92fac4c7..a2447d71c3bd 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -16,6 +16,15 @@
// Build the android.test.mock library
// ===================================
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_sdk_library {
name: "android.test.mock",
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index 1f6db8403eee..fe007e39f717 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -16,6 +16,16 @@
// Build the android.test.runner library
// =====================================
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-CPL-1.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_sdk_library {
name: "android.test.runner",
diff --git a/test-runner/tests/Android.bp b/test-runner/tests/Android.bp
index d74cee4937c9..ac21bcb9d124 100644
--- a/test-runner/tests/Android.bp
+++ b/test-runner/tests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworkTestRunnerTests",
diff --git a/tests/AccessibilityEventsLogger/Android.bp b/tests/AccessibilityEventsLogger/Android.bp
index ead165602254..c403f9bf961e 100644
--- a/tests/AccessibilityEventsLogger/Android.bp
+++ b/tests/AccessibilityEventsLogger/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AccessibilityEventsLogger",
srcs: ["**/*.java"],
diff --git a/tests/AccessoryDisplay/common/Android.bp b/tests/AccessoryDisplay/common/Android.bp
index 3ce4c5718d13..fd3af1ecf1c9 100644
--- a/tests/AccessoryDisplay/common/Android.bp
+++ b/tests/AccessoryDisplay/common/Android.bp
@@ -13,6 +13,15 @@
// limitations under the License.
// Build the application.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library_static {
name: "AccessoryDisplayCommon",
sdk_version: "current",
diff --git a/tests/AccessoryDisplay/sink/Android.bp b/tests/AccessoryDisplay/sink/Android.bp
index 4e50a81d8c24..d825c60b437f 100644
--- a/tests/AccessoryDisplay/sink/Android.bp
+++ b/tests/AccessoryDisplay/sink/Android.bp
@@ -13,6 +13,15 @@
// limitations under the License.
// Build the application.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AccessoryDisplaySink",
sdk_version: "current",
diff --git a/tests/AccessoryDisplay/source/Android.bp b/tests/AccessoryDisplay/source/Android.bp
index 6d8087f5e7dd..6ed752ee2c8f 100644
--- a/tests/AccessoryDisplay/source/Android.bp
+++ b/tests/AccessoryDisplay/source/Android.bp
@@ -13,6 +13,15 @@
// limitations under the License.
// Build the application.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AccessoryDisplaySource",
sdk_version: "current",
diff --git a/tests/ActivityManagerPerfTests/stub-app/Android.bp b/tests/ActivityManagerPerfTests/stub-app/Android.bp
index a3c1f5b2f17d..19225e4c879e 100644
--- a/tests/ActivityManagerPerfTests/stub-app/Android.bp
+++ b/tests/ActivityManagerPerfTests/stub-app/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "ActivityManagerPerfTestsStubApp1",
static_libs: ["ActivityManagerPerfTestsUtils"],
@@ -65,4 +74,3 @@ android_test_helper_app {
"--auto-add-overlay",
],
}
-
diff --git a/tests/ActivityManagerPerfTests/test-app/Android.bp b/tests/ActivityManagerPerfTests/test-app/Android.bp
index ef9d587581c4..5fd1d5a0ae69 100644
--- a/tests/ActivityManagerPerfTests/test-app/Android.bp
+++ b/tests/ActivityManagerPerfTests/test-app/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ActivityManagerPerfTestsTestApp",
srcs: ["src/**/*.java"],
diff --git a/tests/ActivityManagerPerfTests/tests/Android.bp b/tests/ActivityManagerPerfTests/tests/Android.bp
index 2ae2cc49bb7a..c8dbf811c69b 100644
--- a/tests/ActivityManagerPerfTests/tests/Android.bp
+++ b/tests/ActivityManagerPerfTests/tests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ActivityManagerPerfTests",
srcs: ["src/**/*.java"],
diff --git a/tests/ActivityManagerPerfTests/utils/Android.bp b/tests/ActivityManagerPerfTests/utils/Android.bp
index 766c3acf3c09..99c43c8d8fca 100644
--- a/tests/ActivityManagerPerfTests/utils/Android.bp
+++ b/tests/ActivityManagerPerfTests/utils/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test {
name: "ActivityManagerPerfTestsUtils",
sdk_version: "current",
diff --git a/tests/ActivityTests/Android.bp b/tests/ActivityTests/Android.bp
index 01828624fa4a..be6096fb31cf 100644
--- a/tests/ActivityTests/Android.bp
+++ b/tests/ActivityTests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ActivityTest",
srcs: ["**/*.java"],
diff --git a/tests/ActivityViewTest/Android.bp b/tests/ActivityViewTest/Android.bp
index e7b8c8e1d058..95178a0fb9a5 100644
--- a/tests/ActivityViewTest/Android.bp
+++ b/tests/ActivityViewTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ActivityViewTest",
srcs: ["src/**/*.java"],
diff --git a/tests/AmSlam/Android.bp b/tests/AmSlam/Android.bp
index a8e575a39da4..cc33d88ba363 100644
--- a/tests/AmSlam/Android.bp
+++ b/tests/AmSlam/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AmSlam",
srcs: ["**/*.java"],
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
index 39dc9c286d5f..4e98f4264e11 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -12,11 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test_host {
name: "ApkVerityTest",
srcs: ["src/**/*.java"],
libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
- static_libs: ["frameworks-base-hostutils"],
+ static_libs: [
+ "block_device_writer_jar",
+ "frameworks-base-hostutils",
+ ],
test_suites: ["general-tests", "vts"],
target_required: [
"block_device_writer_module",
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/Android.bp b/tests/ApkVerityTest/ApkVerityTestApp/Android.bp
index 69632b215822..adf8f9f9d321 100644
--- a/tests/ApkVerityTest/ApkVerityTestApp/Android.bp
+++ b/tests/ApkVerityTest/ApkVerityTestApp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "ApkVerityTestApp",
manifest: "AndroidManifest.xml",
diff --git a/tests/ApkVerityTest/OWNERS b/tests/ApkVerityTest/OWNERS
new file mode 100644
index 000000000000..d67285ede44a
--- /dev/null
+++ b/tests/ApkVerityTest/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 36824
+
+victorhsieh@google.com
diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp
index 37fbc29470f6..0b5f0f611916 100644
--- a/tests/ApkVerityTest/block_device_writer/Android.bp
+++ b/tests/ApkVerityTest/block_device_writer/Android.bp
@@ -14,6 +14,15 @@
// This is a cc_test just because it supports test_suites. This should be converted to something
// like cc_binary_test_helper once supported, thus auto_gen_config:false below.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_test {
// Depending on how the test runs, the executable may be uploaded to different location.
// Before the bug in the file pusher is fixed, workaround by making the name unique.
@@ -51,3 +60,9 @@ cc_test {
test_suites: ["general-tests", "pts", "vts"],
gtest: false,
}
+
+java_library_host {
+ name: "block_device_writer_jar",
+ srcs: ["src/**/*.java"],
+ libs: ["tradefed", "junit"],
+}
diff --git a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
new file mode 100644
index 000000000000..5c2c15b22bb0
--- /dev/null
+++ b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
@@ -0,0 +1,100 @@
+/*
+ * 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.blockdevicewriter;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import java.util.ArrayList;
+
+/**
+ * Wrapper for block_device_writer command.
+ *
+ * <p>To use this class, please push block_device_writer binary to /data/local/tmp.
+ * 1. In Android.bp, add:
+ * <pre>
+ * target_required: ["block_device_writer_module"],
+ * </pre>
+ * 2. In AndroidText.xml, add:
+ * <pre>
+ * <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ * <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
+ * </target_preparer>
+ * </pre>
+ */
+public final class BlockDeviceWriter {
+ private static final String EXECUTABLE = "/data/local/tmp/block_device_writer";
+
+ /**
+ * Modifies a byte of the file directly against the backing block storage.
+ *
+ * The effect can only be observed when the page cache is read from disk again. See
+ * {@link #dropCaches} for details.
+ */
+ public static void damageFileAgainstBlockDevice(ITestDevice device, String path,
+ long offsetOfTargetingByte)
+ throws DeviceNotAvailableException {
+ assertThat(path).startsWith("/data/");
+ ITestDevice.MountPointInfo mountPoint = device.getMountPointInfo("/data");
+ ArrayList<String> args = new ArrayList<>();
+ args.add(EXECUTABLE);
+ if ("f2fs".equals(mountPoint.type)) {
+ args.add("--use-f2fs-pinning");
+ }
+ args.add(mountPoint.filesystem);
+ args.add(path);
+ args.add(Long.toString(offsetOfTargetingByte));
+ CommandResult result = device.executeShellV2Command(String.join(" ", args));
+ assertWithMessage(
+ String.format("stdout=%s\nstderr=%s", result.getStdout(), result.getStderr()))
+ .that(result.getStatus()).isEqualTo(CommandStatus.SUCCESS);
+ }
+
+ /**
+ * Drops file caches so that the result of {@link #damageFileAgainstBlockDevice} can be
+ * observed. If a process has an open FD or memory map of the damaged file, cache eviction won't
+ * happen and the damage cannot be observed.
+ */
+ public static void dropCaches(ITestDevice device) throws DeviceNotAvailableException {
+ CommandResult result = device.executeShellV2Command(
+ "sync && echo 1 > /proc/sys/vm/drop_caches");
+ assertThat(result.getStatus()).isEqualTo(CommandStatus.SUCCESS);
+ }
+
+ public static void assertFileNotOpen(ITestDevice device, String path)
+ throws DeviceNotAvailableException {
+ CommandResult result = device.executeShellV2Command("lsof " + path);
+ assertThat(result.getStatus()).isEqualTo(CommandStatus.SUCCESS);
+ assertThat(result.getStdout()).isEmpty();
+ }
+
+ /**
+ * Checks if the give offset of a file can be read.
+ * This method will return false if the file has fs-verity enabled and is damaged at the offset.
+ */
+ public static boolean canReadByte(ITestDevice device, String filePath, long offset)
+ throws DeviceNotAvailableException {
+ CommandResult result = device.executeShellV2Command(
+ "dd if=" + filePath + " bs=1 count=1 skip=" + Long.toString(offset));
+ return result.getStatus() == CommandStatus.SUCCESS;
+ }
+}
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
index d0eb9befbdee..ab3572ba2173 100644
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -24,6 +24,7 @@ import static org.junit.Assert.fail;
import android.platform.test.annotations.RootPermissionTest;
+import com.android.blockdevicewriter.BlockDeviceWriter;
import com.android.fsverity.AddFsVerityCertRule;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
@@ -334,22 +335,23 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
long offsetFirstByte = 0;
// The first two pages should be both readable at first.
- assertTrue(canReadByte(apkPath, offsetFirstByte));
+ assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetFirstByte));
if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
- assertTrue(canReadByte(apkPath, offsetFirstByte + FSVERITY_PAGE_SIZE));
+ assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath,
+ offsetFirstByte + FSVERITY_PAGE_SIZE));
}
// Damage the file directly against the block device.
damageFileAgainstBlockDevice(apkPath, offsetFirstByte);
// Expect actual read from disk to fail but only at damaged page.
- dropCaches();
- assertFalse(canReadByte(apkPath, offsetFirstByte));
+ BlockDeviceWriter.dropCaches(mDevice);
+ assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetFirstByte));
if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
long lastByteOfTheSamePage =
offsetFirstByte % FSVERITY_PAGE_SIZE + FSVERITY_PAGE_SIZE - 1;
- assertFalse(canReadByte(apkPath, lastByteOfTheSamePage));
- assertTrue(canReadByte(apkPath, lastByteOfTheSamePage + 1));
+ assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage));
+ assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage + 1));
}
}
@@ -362,21 +364,22 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
long offsetOfLastByte = apkSize - 1;
// The first two pages should be both readable at first.
- assertTrue(canReadByte(apkPath, offsetOfLastByte));
+ assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetOfLastByte));
if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
- assertTrue(canReadByte(apkPath, offsetOfLastByte - FSVERITY_PAGE_SIZE));
+ assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath,
+ offsetOfLastByte - FSVERITY_PAGE_SIZE));
}
// Damage the file directly against the block device.
damageFileAgainstBlockDevice(apkPath, offsetOfLastByte);
// Expect actual read from disk to fail but only at damaged page.
- dropCaches();
- assertFalse(canReadByte(apkPath, offsetOfLastByte));
+ BlockDeviceWriter.dropCaches(mDevice);
+ assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetOfLastByte));
if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
long firstByteOfTheSamePage = offsetOfLastByte - offsetOfLastByte % FSVERITY_PAGE_SIZE;
- assertFalse(canReadByte(apkPath, firstByteOfTheSamePage));
- assertTrue(canReadByte(apkPath, firstByteOfTheSamePage - 1));
+ assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage));
+ assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage - 1));
}
}
@@ -395,8 +398,8 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
// from filesystem cache. Forcing GC workarounds the problem.
int retry = 5;
for (; retry > 0; retry--) {
- dropCaches();
- if (!canReadByte(path, kTargetOffset)) {
+ BlockDeviceWriter.dropCaches(mDevice);
+ if (!BlockDeviceWriter.canReadByte(mDevice, path, kTargetOffset)) {
break;
}
try {
@@ -451,16 +454,6 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
return Long.parseLong(expectRemoteCommandToSucceed("stat -c '%s' " + packageName).trim());
}
- private void dropCaches() throws DeviceNotAvailableException {
- expectRemoteCommandToSucceed("sync && echo 1 > /proc/sys/vm/drop_caches");
- }
-
- private boolean canReadByte(String filePath, long offset) throws DeviceNotAvailableException {
- CommandResult result = mDevice.executeShellV2Command(
- "dd if=" + filePath + " bs=1 count=1 skip=" + Long.toString(offset));
- return result.getStatus() == CommandStatus.SUCCESS;
- }
-
private String expectRemoteCommandToSucceed(String cmd) throws DeviceNotAvailableException {
CommandResult result = mDevice.executeShellV2Command(cmd);
assertEquals("`" + cmd + "` failed: " + result.getStderr(), CommandStatus.SUCCESS,
diff --git a/tests/ApkVerityTest/testdata/Android.bp b/tests/ApkVerityTest/testdata/Android.bp
index c10b0cef21d7..ccfc4c99a347 100644
--- a/tests/ApkVerityTest/testdata/Android.bp
+++ b/tests/ApkVerityTest/testdata/Android.bp
@@ -12,6 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-MIT
+ // SPDX-license-identifier-Unicode-DFS
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "ApkVerityTestKeyPem",
srcs: ["ApkVerityTestKey.pem"],
@@ -74,4 +85,3 @@ genrule {
srcs: [":ApkVerityTestAppSplitDm"],
out: ["ApkVerityTestAppSplit.dm.fsv_sig"],
}
-
diff --git a/tests/AppLaunch/Android.bp b/tests/AppLaunch/Android.bp
index 75db55122553..f838c5a80c28 100644
--- a/tests/AppLaunch/Android.bp
+++ b/tests/AppLaunch/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AppLaunch",
// Only compile source java files in this apk.
diff --git a/tests/AppLaunchWear/Android.bp b/tests/AppLaunchWear/Android.bp
index 8d34b6eb9c0f..e2fc4735a7c2 100644
--- a/tests/AppLaunchWear/Android.bp
+++ b/tests/AppLaunchWear/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AppLaunchWear",
// Only compile source java files in this apk.
diff --git a/tests/AppResourcesLoaders/Android.bp b/tests/AppResourcesLoaders/Android.bp
index e5739dbf181c..d882db8b18a2 100644
--- a/tests/AppResourcesLoaders/Android.bp
+++ b/tests/AppResourcesLoaders/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AppResourcesLoaders",
srcs: ["**/*.java"],
diff --git a/tests/AppResourcesLoaders/Overlay/Android.bp b/tests/AppResourcesLoaders/Overlay/Android.bp
index 80443f6c3c00..b063023f7d19 100644
--- a/tests/AppResourcesLoaders/Overlay/Android.bp
+++ b/tests/AppResourcesLoaders/Overlay/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "AppResourcesLoaders_Overlay",
}
diff --git a/tests/Assist/Android.bp b/tests/Assist/Android.bp
index 216e75109dde..033c14017442 100644
--- a/tests/Assist/Android.bp
+++ b/tests/Assist/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "Assist",
srcs: ["**/*.java"],
diff --git a/tests/BackgroundDexOptServiceIntegrationTests/Android.bp b/tests/BackgroundDexOptServiceIntegrationTests/Android.bp
index a85d129b013a..0a45d5381f84 100644
--- a/tests/BackgroundDexOptServiceIntegrationTests/Android.bp
+++ b/tests/BackgroundDexOptServiceIntegrationTests/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "BackgroundDexOptServiceIntegrationTests",
srcs: ["src/**/*.java"],
diff --git a/tests/BandwidthTests/Android.bp b/tests/BandwidthTests/Android.bp
index 523f5226cd2c..a7fc89db32e1 100644
--- a/tests/BandwidthTests/Android.bp
+++ b/tests/BandwidthTests/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "BandwidthEnforcementTest",
platform_apis: true,
diff --git a/tests/BatteryWaster/Android.bp b/tests/BatteryWaster/Android.bp
index 4698910adb40..1fa4f82cd9f4 100644
--- a/tests/BatteryWaster/Android.bp
+++ b/tests/BatteryWaster/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "BatteryWaster",
srcs: ["**/*.java"],
diff --git a/tests/BiDiTests/Android.bp b/tests/BiDiTests/Android.bp
index c659e8c1257e..79ae41fb0c82 100644
--- a/tests/BiDiTests/Android.bp
+++ b/tests/BiDiTests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "BiDiTests",
// Only compile source java files in this apk.
diff --git a/tests/BlobStoreTestUtils/Android.bp b/tests/BlobStoreTestUtils/Android.bp
index 53d36389a52a..c4faf7f4fb11 100644
--- a/tests/BlobStoreTestUtils/Android.bp
+++ b/tests/BlobStoreTestUtils/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "BlobStoreTestUtils",
srcs: ["src/**/*.java"],
@@ -21,4 +30,4 @@ java_library {
"androidx.test.ext.junit",
],
sdk_version: "test_current",
-} \ No newline at end of file
+}
diff --git a/tests/BootImageProfileTest/Android.bp b/tests/BootImageProfileTest/Android.bp
index 1b097a8af0f9..9fb5aa21f985 100644
--- a/tests/BootImageProfileTest/Android.bp
+++ b/tests/BootImageProfileTest/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test_host {
name: "BootImageProfileTest",
srcs: ["src/**/*.java"],
diff --git a/tests/BrowserPowerTest/Android.bp b/tests/BrowserPowerTest/Android.bp
index 1d358cbe6e75..a8a9897c0e86 100644
--- a/tests/BrowserPowerTest/Android.bp
+++ b/tests/BrowserPowerTest/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "BrowserPowerTests",
libs: [
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp
index 125deb521ddc..b889b0d29a8b 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp
@@ -13,6 +13,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_test_library {
name: "libsmartcamera_jni",
sdk_version: "14",
diff --git a/tests/CameraPrewarmTest/Android.bp b/tests/CameraPrewarmTest/Android.bp
index eaf453b6f3ae..07c49232efc5 100644
--- a/tests/CameraPrewarmTest/Android.bp
+++ b/tests/CameraPrewarmTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "CameraPrewarmTest",
srcs: ["**/*.java"],
diff --git a/tests/Codegen/Android.bp b/tests/Codegen/Android.bp
index 966c5602959c..ddbf16817b94 100644
--- a/tests/Codegen/Android.bp
+++ b/tests/Codegen/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "CodegenTests",
srcs: [
diff --git a/tests/Compatibility/Android.bp b/tests/Compatibility/Android.bp
index c14e705b09ab..c58c99e1c4f0 100644
--- a/tests/Compatibility/Android.bp
+++ b/tests/Compatibility/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "AppCompatibilityTest",
static_libs: ["androidx.test.rules"],
diff --git a/tests/CoreTests/android/Android.bp b/tests/CoreTests/android/Android.bp
index 24134e8fb7f0..e2f194b04437 100644
--- a/tests/CoreTests/android/Android.bp
+++ b/tests/CoreTests/android/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "LegacyCoreTests",
srcs: ["**/*.java"],
diff --git a/tests/DataIdleTest/Android.bp b/tests/DataIdleTest/Android.bp
index 19656ce32b29..f9509cc9a4bf 100644
--- a/tests/DataIdleTest/Android.bp
+++ b/tests/DataIdleTest/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "DataIdleTest",
platform_apis: true,
diff --git a/tests/DozeTest/Android.bp b/tests/DozeTest/Android.bp
index f1be029f58d5..36ea91a84a0d 100644
--- a/tests/DozeTest/Android.bp
+++ b/tests/DozeTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "DozeTest",
// Only compile source java files in this apk.
diff --git a/tests/DpiTest/Android.bp b/tests/DpiTest/Android.bp
index 7d6a78ba1581..52bb08bbc8ad 100644
--- a/tests/DpiTest/Android.bp
+++ b/tests/DpiTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "DensityTest",
srcs: ["**/*.java"],
diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.mk b/tests/DynamicCodeLoggerIntegrationTests/Android.mk
index 2d58ce8baddc..bfb5b076237a 100644
--- a/tests/DynamicCodeLoggerIntegrationTests/Android.mk
+++ b/tests/DynamicCodeLoggerIntegrationTests/Android.mk
@@ -22,6 +22,9 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE := DynamicCodeLoggerTestLibrary
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/dcl)
include $(BUILD_JAVA_LIBRARY)
@@ -35,6 +38,9 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE := DynamicCodeLoggerNativeTestLibrary
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
LOCAL_SRC_FILES := src/cpp/com_android_dcl_Jni.cpp
LOCAL_HEADER_LIBRARIES := jni_headers
LOCAL_SDK_VERSION := 28
@@ -48,6 +54,9 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE := DynamicCodeLoggerNativeExecutable
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
LOCAL_SRC_FILES := src/cpp/test_executable.cpp
include $(BUILD_EXECUTABLE)
diff --git a/tests/FeatureSplit/base/Android.bp b/tests/FeatureSplit/base/Android.bp
index ab25464a82fc..89c26d2c2256 100644
--- a/tests/FeatureSplit/base/Android.bp
+++ b/tests/FeatureSplit/base/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "FeatureSplitBase",
srcs: ["**/*.java"],
diff --git a/tests/FeatureSplit/feature1/Android.bp b/tests/FeatureSplit/feature1/Android.bp
index 706a4f544393..2f8aa8b6eb28 100644
--- a/tests/FeatureSplit/feature1/Android.bp
+++ b/tests/FeatureSplit/feature1/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FeatureSplit1",
srcs: ["**/*.java"],
diff --git a/tests/FeatureSplit/feature2/Android.bp b/tests/FeatureSplit/feature2/Android.bp
index a3634821f6fd..2a5c432d1499 100644
--- a/tests/FeatureSplit/feature2/Android.bp
+++ b/tests/FeatureSplit/feature2/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FeatureSplit2",
srcs: ["**/*.java"],
diff --git a/tests/FixVibrateSetting/Android.bp b/tests/FixVibrateSetting/Android.bp
index 5608a2b5e15d..bd7c701026ba 100644
--- a/tests/FixVibrateSetting/Android.bp
+++ b/tests/FixVibrateSetting/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "FixVibrateSetting",
srcs: ["**/*.java"],
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 0792e8b272a8..217a72b90fd4 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FlickerTests",
srcs: ["src/**/*.java", "src/**/*.kt"],
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index e68fbd8724de..b2719fbcac82 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -25,7 +25,6 @@
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.server.wm.flicker"/>
- <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
<option name="shell-timeout" value="6600s" />
<option name="test-timeout" value="6600s" />
<option name="hidden-api-checks" value="false" />
diff --git a/tests/FlickerTests/OWNERS b/tests/FlickerTests/OWNERS
index f35a318acbf7..b5561010e7f9 100644
--- a/tests/FlickerTests/OWNERS
+++ b/tests/FlickerTests/OWNERS
@@ -1,2 +1,3 @@
+# Bug component: 909476
include /services/core/java/com/android/server/wm/OWNERS
natanieljr@google.com \ No newline at end of file
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 89c6663df343..c5447c1ccf71 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -17,8 +17,11 @@
package com.android.server.wm.flicker
import android.platform.helpers.IAppHelper
+import com.android.server.wm.flicker.dsl.EventLogAssertionBuilder
import com.android.server.wm.flicker.dsl.EventLogAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.WmAssertionBuilder
import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_LAYER_NAME
@@ -31,6 +34,247 @@ const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
const val WALLPAPER_TITLE = "Wallpaper"
@JvmOverloads
+fun WmAssertionBuilder.statusBarWindowIsAlwaysVisible(bugId: Int = 0) {
+ all("statusBarWindowIsAlwaysVisible", bugId) {
+ this.showsAboveAppWindow(NAV_BAR_LAYER_NAME)
+ }
+}
+
+@JvmOverloads
+fun WmAssertionBuilder.navBarWindowIsAlwaysVisible(bugId: Int = 0) {
+ all("navBarWindowIsAlwaysVisible", bugId) {
+ this.showsAboveAppWindow(NAV_BAR_LAYER_NAME)
+ }
+}
+
+fun WmAssertionBuilder.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ ignoreWindows: List<String> = emptyList(),
+ bugId: Int = 0
+) {
+ all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId) {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows)
+ }
+}
+
+fun WmAssertionBuilder.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper, bugId: Int = 0) {
+ all("launcherReplacesAppWindowAsTopWindow", bugId) {
+ this.showsAppWindowOnTop(testApp.getPackage())
+ .then()
+ .showsAppWindowOnTop("Launcher")
+ }
+}
+
+fun WmAssertionBuilder.wallpaperWindowBecomesVisible(bugId: Int = 0) {
+ all("wallpaperWindowBecomesVisible", bugId) {
+ this.hidesBelowAppWindow(WALLPAPER_TITLE)
+ .then()
+ .showsBelowAppWindow(WALLPAPER_TITLE)
+ }
+}
+
+fun WmAssertionBuilder.wallpaperWindowBecomesInvisible(bugId: Int = 0) {
+ all("wallpaperWindowBecomesInvisible", bugId) {
+ this.showsBelowAppWindow("Wallpaper")
+ .then()
+ .hidesBelowAppWindow("Wallpaper")
+ }
+}
+
+fun WmAssertionBuilder.appWindowAlwaysVisibleOnTop(
+ packageName: String,
+ bugId: Int = 0
+) {
+ all("appWindowAlwaysVisibleOnTop", bugId) {
+ this.showsAppWindowOnTop(packageName)
+ }
+}
+
+fun WmAssertionBuilder.appWindowBecomesVisible(appName: String, bugId: Int = 0) {
+ all("appWindowBecomesVisible", bugId) {
+ this.hidesAppWindow(appName)
+ .then()
+ .showsAppWindow(appName)
+ }
+}
+
+fun WmAssertionBuilder.appWindowBecomesInVisible(appName: String, bugId: Int = 0) {
+ all("appWindowBecomesInVisible", bugId) {
+ this.showsAppWindow(appName)
+ .then()
+ .hidesAppWindow(appName)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.noUncoveredRegions(
+ beginRotation: Int,
+ endRotation: Int = beginRotation,
+ allStates: Boolean = true,
+ bugId: Int = 0
+) {
+ val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
+ val endingBounds = WindowUtils.getDisplayBounds(endRotation)
+ if (allStates) {
+ all("noUncoveredRegions", bugId) {
+ if (startingBounds == endingBounds) {
+ this.coversAtLeastRegion(startingBounds)
+ } else {
+ this.coversAtLeastRegion(startingBounds)
+ .then()
+ .coversAtLeastRegion(endingBounds)
+ }
+ }
+ } else {
+ start("noUncoveredRegions_StartingPos") {
+ this.coversAtLeastRegion(startingBounds)
+ }
+ end("noUncoveredRegions_EndingPos") {
+ this.coversAtLeastRegion(endingBounds)
+ }
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.navBarLayerIsAlwaysVisible(
+ rotatesScreen: Boolean = false,
+ bugId: Int = 0
+) {
+ if (rotatesScreen) {
+ all("navBarLayerIsAlwaysVisible", bugId) {
+ this.showsLayer(NAV_BAR_LAYER_NAME)
+ .then()
+ .hidesLayer(NAV_BAR_LAYER_NAME)
+ .then()
+ .showsLayer(NAV_BAR_LAYER_NAME)
+ }
+ } else {
+ all("navBarLayerIsAlwaysVisible", bugId) {
+ this.showsLayer(NAV_BAR_LAYER_NAME)
+ }
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.statusBarLayerIsAlwaysVisible(
+ rotatesScreen: Boolean = false,
+ bugId: Int = 0
+) {
+ if (rotatesScreen) {
+ all("statusBarLayerIsAlwaysVisible", bugId) {
+ this.showsLayer(STATUS_BAR_WINDOW_NAME)
+ .then()
+ hidesLayer(STATUS_BAR_WINDOW_NAME)
+ .then()
+ .showsLayer(STATUS_BAR_WINDOW_NAME)
+ }
+ } else {
+ all("statusBarLayerIsAlwaysVisible", bugId) {
+ this.showsLayer(STATUS_BAR_WINDOW_NAME)
+ }
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.navBarLayerRotatesAndScales(
+ beginRotation: Int,
+ endRotation: Int = beginRotation,
+ bugId: Int = 0
+) {
+ val startingPos = WindowUtils.getNavigationBarPosition(beginRotation)
+ val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
+
+ start("navBarLayerRotatesAndScales_StartingPos", bugId) {
+ this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos)
+ }
+ end("navBarLayerRotatesAndScales_EndingPost", bugId) {
+ this.hasVisibleRegion(NAV_BAR_LAYER_NAME, endingPos)
+ }
+
+ /*if (startingPos == endingPos) {
+ all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) {
+ this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+ }
+ }*/
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.statusBarLayerRotatesScales(
+ beginRotation: Int,
+ endRotation: Int = beginRotation,
+ bugId: Int = 0
+) {
+ val startingPos = WindowUtils.getStatusBarPosition(beginRotation)
+ val endingPos = WindowUtils.getStatusBarPosition(endRotation)
+
+ start("statusBarLayerRotatesScales_StartingPos", bugId) {
+ this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, startingPos)
+ }
+ end("statusBarLayerRotatesScales_EndingPos", bugId) {
+ this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, endingPos)
+ }
+}
+
+fun LayersAssertionBuilder.visibleLayersShownMoreThanOneConsecutiveEntry(
+ ignoreLayers: List<String> = emptyList(),
+ bugId: Int = 0
+) {
+ all("visibleLayersShownMoreThanOneConsecutiveEntry", bugId) {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoreLayers)
+ }
+}
+
+fun LayersAssertionBuilder.appLayerReplacesWallpaperLayer(appName: String, bugId: Int = 0) {
+ all("appLayerReplacesWallpaperLayer", bugId) {
+ this.showsLayer("Wallpaper")
+ .then()
+ .replaceVisibleLayer("Wallpaper", appName)
+ }
+}
+
+fun LayersAssertionBuilder.wallpaperLayerReplacesAppLayer(testApp: IAppHelper, bugId: Int = 0) {
+ all("appLayerReplacesWallpaperLayer", bugId) {
+ this.showsLayer(testApp.getPackage())
+ .then()
+ .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE)
+ }
+}
+
+fun LayersAssertionBuilder.layerAlwaysVisible(packageName: String, bugId: Int = 0) {
+ all("layerAlwaysVisible", bugId) {
+ this.showsLayer(packageName)
+ }
+}
+
+fun LayersAssertionBuilder.layerBecomesVisible(packageName: String, bugId: Int = 0) {
+ all("layerBecomesVisible", bugId) {
+ this.hidesLayer(packageName)
+ .then()
+ .showsLayer(packageName)
+ }
+}
+
+fun LayersAssertionBuilder.layerBecomesInvisible(packageName: String, bugId: Int = 0) {
+ all("layerBecomesInvisible", bugId) {
+ this.showsLayer(packageName)
+ .then()
+ .hidesLayer(packageName)
+ }
+}
+
+fun EventLogAssertionBuilder.focusChanges(vararg windows: String, bugId: Int = 0) {
+ all("focusChanges", bugId) {
+ this.focusChanges(windows)
+ }
+}
+
+fun EventLogAssertionBuilder.focusDoesNotChange(bugId: Int = 0) {
+ all("focusDoesNotChange", bugId) {
+ this.focusDoesNotChange()
+ }
+}
+
+@JvmOverloads
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun WmAssertionBuilderLegacy.statusBarWindowIsAlwaysVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -41,6 +285,7 @@ fun WmAssertionBuilderLegacy.statusBarWindowIsAlwaysVisible(
}
@JvmOverloads
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun WmAssertionBuilderLegacy.navBarWindowIsAlwaysVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -50,6 +295,7 @@ fun WmAssertionBuilderLegacy.navBarWindowIsAlwaysVisible(
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun WmAssertionBuilderLegacy.visibleWindowsShownMoreThanOneConsecutiveEntry(
ignoreWindows: List<String> = emptyList(),
bugId: Int = 0,
@@ -60,6 +306,7 @@ fun WmAssertionBuilderLegacy.visibleWindowsShownMoreThanOneConsecutiveEntry(
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun WmAssertionBuilderLegacy.launcherReplacesAppWindowAsTopWindow(
testApp: IAppHelper,
bugId: Int = 0,
@@ -67,33 +314,36 @@ fun WmAssertionBuilderLegacy.launcherReplacesAppWindowAsTopWindow(
) {
all("launcherReplacesAppWindowAsTopWindow", bugId, enabled) {
this.showsAppWindowOnTop(testApp.getPackage())
- .then()
- .showsAppWindowOnTop("Launcher")
+ .then()
+ .showsAppWindowOnTop("Launcher")
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun WmAssertionBuilderLegacy.wallpaperWindowBecomesVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
all("wallpaperWindowBecomesVisible", bugId, enabled) {
this.hidesBelowAppWindow(WALLPAPER_TITLE)
- .then()
- .showsBelowAppWindow(WALLPAPER_TITLE)
+ .then()
+ .showsBelowAppWindow(WALLPAPER_TITLE)
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun WmAssertionBuilderLegacy.wallpaperWindowBecomesInvisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
all("wallpaperWindowBecomesInvisible", bugId, enabled) {
this.showsBelowAppWindow("Wallpaper")
- .then()
- .hidesBelowAppWindow("Wallpaper")
+ .then()
+ .hidesBelowAppWindow("Wallpaper")
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun WmAssertionBuilderLegacy.appWindowAlwaysVisibleOnTop(
packageName: String,
bugId: Int = 0,
@@ -104,6 +354,7 @@ fun WmAssertionBuilderLegacy.appWindowAlwaysVisibleOnTop(
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun WmAssertionBuilderLegacy.appWindowBecomesVisible(
appName: String,
bugId: Int = 0,
@@ -111,11 +362,12 @@ fun WmAssertionBuilderLegacy.appWindowBecomesVisible(
) {
all("appWindowBecomesVisible", bugId, enabled) {
this.hidesAppWindow(appName)
- .then()
- .showsAppWindow(appName)
+ .then()
+ .showsAppWindow(appName)
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun WmAssertionBuilderLegacy.appWindowBecomesInVisible(
appName: String,
bugId: Int = 0,
@@ -123,11 +375,12 @@ fun WmAssertionBuilderLegacy.appWindowBecomesInVisible(
) {
all("appWindowBecomesInVisible", bugId, enabled) {
this.showsAppWindow(appName)
- .then()
- .hidesAppWindow(appName)
+ .then()
+ .hidesAppWindow(appName)
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
@JvmOverloads
fun LayersAssertionBuilderLegacy.noUncoveredRegions(
beginRotation: Int,
@@ -144,8 +397,8 @@ fun LayersAssertionBuilderLegacy.noUncoveredRegions(
this.coversAtLeastRegion(startingBounds)
} else {
this.coversAtLeastRegion(startingBounds)
- .then()
- .coversAtLeastRegion(endingBounds)
+ .then()
+ .coversAtLeastRegion(endingBounds)
}
}
} else {
@@ -158,6 +411,7 @@ fun LayersAssertionBuilderLegacy.noUncoveredRegions(
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
@JvmOverloads
fun LayersAssertionBuilderLegacy.navBarLayerIsAlwaysVisible(
rotatesScreen: Boolean = false,
@@ -167,10 +421,10 @@ fun LayersAssertionBuilderLegacy.navBarLayerIsAlwaysVisible(
if (rotatesScreen) {
all("navBarLayerIsAlwaysVisible", bugId, enabled) {
this.showsLayer(NAV_BAR_LAYER_NAME)
- .then()
- .hidesLayer(NAV_BAR_LAYER_NAME)
- .then()
- .showsLayer(NAV_BAR_LAYER_NAME)
+ .then()
+ .hidesLayer(NAV_BAR_LAYER_NAME)
+ .then()
+ .showsLayer(NAV_BAR_LAYER_NAME)
}
} else {
all("navBarLayerIsAlwaysVisible", bugId, enabled) {
@@ -179,6 +433,7 @@ fun LayersAssertionBuilderLegacy.navBarLayerIsAlwaysVisible(
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
@JvmOverloads
fun LayersAssertionBuilderLegacy.statusBarLayerIsAlwaysVisible(
rotatesScreen: Boolean = false,
@@ -188,10 +443,10 @@ fun LayersAssertionBuilderLegacy.statusBarLayerIsAlwaysVisible(
if (rotatesScreen) {
all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
this.showsLayer(STATUS_BAR_LAYER_NAME)
- .then()
- hidesLayer(STATUS_BAR_LAYER_NAME)
- .then()
- .showsLayer(STATUS_BAR_LAYER_NAME)
+ .then()
+ .hidesLayer(STATUS_BAR_LAYER_NAME)
+ .then()
+ .showsLayer(STATUS_BAR_LAYER_NAME)
}
} else {
all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
@@ -200,6 +455,7 @@ fun LayersAssertionBuilderLegacy.statusBarLayerIsAlwaysVisible(
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
@JvmOverloads
fun LayersAssertionBuilderLegacy.navBarLayerRotatesAndScales(
beginRotation: Int,
@@ -224,6 +480,7 @@ fun LayersAssertionBuilderLegacy.navBarLayerRotatesAndScales(
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
@JvmOverloads
fun LayersAssertionBuilderLegacy.statusBarLayerRotatesScales(
beginRotation: Int,
@@ -242,8 +499,9 @@ fun LayersAssertionBuilderLegacy.statusBarLayerRotatesScales(
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun LayersAssertionBuilderLegacy.visibleLayersShownMoreThanOneConsecutiveEntry(
- ignoreLayers: List<String> = emptyList(),
+ ignoreLayers: List<String> = kotlin.collections.emptyList(),
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -252,6 +510,7 @@ fun LayersAssertionBuilderLegacy.visibleLayersShownMoreThanOneConsecutiveEntry(
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun LayersAssertionBuilderLegacy.appLayerReplacesWallpaperLayer(
appName: String,
bugId: Int = 0,
@@ -259,11 +518,12 @@ fun LayersAssertionBuilderLegacy.appLayerReplacesWallpaperLayer(
) {
all("appLayerReplacesWallpaperLayer", bugId, enabled) {
this.showsLayer("Wallpaper")
- .then()
- .replaceVisibleLayer("Wallpaper", appName)
+ .then()
+ .replaceVisibleLayer("Wallpaper", appName)
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun LayersAssertionBuilderLegacy.wallpaperLayerReplacesAppLayer(
testApp: IAppHelper,
bugId: Int = 0,
@@ -271,11 +531,12 @@ fun LayersAssertionBuilderLegacy.wallpaperLayerReplacesAppLayer(
) {
all("appLayerReplacesWallpaperLayer", bugId, enabled) {
this.showsLayer(testApp.getPackage())
- .then()
- .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE)
+ .then()
+ .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE)
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun LayersAssertionBuilderLegacy.layerAlwaysVisible(
packageName: String,
bugId: Int = 0,
@@ -286,6 +547,7 @@ fun LayersAssertionBuilderLegacy.layerAlwaysVisible(
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun LayersAssertionBuilderLegacy.layerBecomesVisible(
packageName: String,
bugId: Int = 0,
@@ -293,11 +555,12 @@ fun LayersAssertionBuilderLegacy.layerBecomesVisible(
) {
all("layerBecomesVisible", bugId, enabled) {
this.hidesLayer(packageName)
- .then()
- .showsLayer(packageName)
+ .then()
+ .showsLayer(packageName)
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun LayersAssertionBuilderLegacy.layerBecomesInvisible(
packageName: String,
bugId: Int = 0,
@@ -305,11 +568,12 @@ fun LayersAssertionBuilderLegacy.layerBecomesInvisible(
) {
all("layerBecomesInvisible", bugId, enabled) {
this.showsLayer(packageName)
- .then()
- .hidesLayer(packageName)
+ .then()
+ .hidesLayer(packageName)
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun EventLogAssertionBuilderLegacy.focusChanges(
vararg windows: String,
bugId: Int = 0,
@@ -320,6 +584,7 @@ fun EventLogAssertionBuilderLegacy.focusChanges(
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun EventLogAssertionBuilderLegacy.focusDoesNotChange(
bugId: Int = 0,
enabled: Boolean = bugId == 0
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 b5fd4a50ae66..c507841ffb71 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,7 +16,6 @@
package com.android.server.wm.flicker.close
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -50,7 +49,6 @@ import org.junit.runners.Parameterized
* Test app closes by pressing back button
* To run this test: `atest FlickerTests:CloseAppBackButtonTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -65,7 +63,7 @@ class CloseAppBackButtonTest(
val testApp = SimpleAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag("closeAppBackButton", configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -89,29 +87,47 @@ class CloseAppBackButtonTest(
}
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173684672)
+ val isRotated = configuration.startRotation.isRotated()
- launcherReplacesAppWindowAsTopWindow(testApp)
- wallpaperWindowBecomesVisible()
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ launcherReplacesAppWindowAsTopWindow(testApp)
+ wallpaperWindowBecomesVisible()
+ }
+
+ layersTrace {
+ noUncoveredRegions(configuration.startRotation,
+ Surface.ROTATION_0)
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ wallpaperLayerReplacesAppLayer(testApp)
+
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ }
+ }
}
- layersTrace {
- noUncoveredRegions(configuration.startRotation,
- Surface.ROTATION_0)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0,
- enabled = !configuration.startRotation.isRotated())
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173684672)
+ flaky {
+ windowManagerTrace {
+ visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173684672)
+ }
+
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173684672)
- wallpaperLayerReplacesAppLayer(testApp)
+ if (isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_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 584e4b16fab7..d1c3efe35c54 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
@@ -37,6 +37,7 @@ import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEn
import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
@@ -64,7 +65,7 @@ class CloseAppHomeButtonTest(
val testApp = SimpleAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag("closeAppHomeButton", configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -88,28 +89,46 @@ class CloseAppHomeButtonTest(
}
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173689015)
+ val isRotated = configuration.startRotation.isRotated()
- launcherReplacesAppWindowAsTopWindow(testApp)
- wallpaperWindowBecomesVisible()
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ launcherReplacesAppWindowAsTopWindow(testApp)
+ wallpaperWindowBecomesVisible()
+ }
+
+ layersTrace {
+ noUncoveredRegions(configuration.startRotation,
+ Surface.ROTATION_0)
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ wallpaperLayerReplacesAppLayer(testApp)
+
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ }
+ }
}
- layersTrace {
- val isRotation0 = configuration.startRotation == Surface.ROTATION_0
- noUncoveredRegions(configuration.startRotation,
- Surface.ROTATION_0)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0, enabled = isRotation0)
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0, enabled = isRotation0)
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173689015)
+ flaky {
+ windowManagerTrace {
+ visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173689015)
+ }
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173689015)
- wallpaperLayerReplacesAppLayer(testApp)
+ if (isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ }
+ }
}
}
}
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 1a4744980b1e..323236ed9962 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
@@ -43,7 +43,7 @@ fun Flicker.setRotation(rotation: Int) {
wmHelper.waitForAppTransitionIdle()
// Ensure WindowManagerService wait until all animations have completed
- instrumentation.getUiAutomation().syncInputTransactions()
+ instrumentation.uiAutomation.syncInputTransactions()
} catch (e: RemoteException) {
throw RuntimeException(e)
}
@@ -71,8 +71,8 @@ fun buildTestTag(
/**
* Build a test tag for the test
* @param testName Name of the transition(s) being tested
- * @param app App being launcher
* @param configuration Configuration for the test
+ * @param extraInfo Additional information to append to the tag
*
* @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
@@ -92,9 +92,30 @@ fun buildTestTag(
/**
* Build a test tag for the test
+ * @param configuration Configuration for the test
+ * @param extraInfo Additional information to append to the tag
+ *
+ * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
+</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
+@JvmOverloads
+fun buildTestTag(
+ configuration: Bundle,
+ extraInfo: String = ""
+): String {
+ return buildTestTag(testName = null,
+ app = null,
+ beginRotation = configuration.startRotation,
+ endRotation = configuration.endRotation,
+ app2 = null,
+ extraInfo = extraInfo)
+}
+
+/**
+ * Build a test tag for the test
* @param testName Name of the transition(s) being tested
* @param app App being launcher
* @param configuration Configuration for the test
+ * @param extraInfo Additional information to append to the tag
*
* @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
@@ -121,14 +142,17 @@ fun buildTestTag(
* @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
</EXTRA></NAME> */
fun buildTestTag(
- testName: String,
+ testName: String?,
app: String?,
beginRotation: Int,
endRotation: Int,
app2: String?,
extraInfo: String
): String {
- var testTag = testName
+ var testTag = ""
+ if (testName != null) {
+ testTag += testName
+ }
if (app != null) {
testTag += "__$app"
}
@@ -142,5 +166,9 @@ fun buildTestTag(
if (extraInfo.isNotEmpty()) {
testTag += "__$extraInfo"
}
+
+ if (testTag.startsWith("__")) {
+ testTag = testTag.drop(2)
+ }
return testTag
}
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 fde97ba8b4df..c7736f825e27 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
@@ -16,8 +16,6 @@
package com.android.server.wm.flicker.ime
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -48,11 +46,9 @@ import org.junit.runners.Parameterized
* Test IME window closing back to app window transitions.
* To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
class CloseImeAutoOpenWindowToAppTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
@@ -66,7 +62,7 @@ class CloseImeAutoOpenWindowToAppTest(
.buildTest(instrumentation, repetitions = 5) { configuration ->
val testApp = ImeAppAutoFocusHelper(instrumentation,
configuration.startRotation)
- withTestName { buildTestTag("imeToAppAutoOpen", configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -89,26 +85,39 @@ class CloseImeAutoOpenWindowToAppTest(
testApp.closeIME(device, wmHelper)
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(listOf("InputMethod"))
+ val isRotated = configuration.startRotation.isRotated()
- imeAppWindowIsAlwaysVisible(testApp)
+ postsubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE))
+ imeAppWindowIsAlwaysVisible(testApp)
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.startRotation)
+ imeLayerBecomesInvisible()
+ imeAppLayerIsAlwaysVisible(testApp)
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation)
+ statusBarLayerRotatesScales(configuration.startRotation)
+ }
+ }
}
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation)
- navBarLayerRotatesAndScales(configuration.startRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(configuration.startRotation,
- enabled = !configuration.startRotation.isRotated())
- visibleLayersShownMoreThanOneConsecutiveEntry()
+ flaky {
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry()
- imeLayerBecomesInvisible()
- imeAppLayerIsAlwaysVisible(testApp)
+ if (isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation)
+ statusBarLayerRotatesScales(configuration.startRotation)
+ }
+ }
}
}
}
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 ab7c08f7902b..aa24456c652f 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
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.ime
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -47,7 +46,6 @@ import org.junit.runners.Parameterized
* Test IME window closing back to app window transitions.
* To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -65,7 +63,7 @@ class CloseImeAutoOpenWindowToHomeTest(
val testApp = ImeAppAutoFocusHelper(instrumentation,
configuration.startRotation)
withTestName {
- buildTestTag("imeToHomeAutoOpen", configuration)
+ buildTestTag(configuration)
}
repeat { configuration.repetitions }
setup {
@@ -90,32 +88,50 @@ class CloseImeAutoOpenWindowToHomeTest(
wmHelper.waitImeWindowGone()
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ val isRotated = configuration.startRotation.isRotated()
- imeWindowBecomesInvisible()
- imeAppWindowBecomesInvisible(testApp)
- }
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE))
+
+ imeWindowBecomesInvisible()
+ imeAppWindowBecomesInvisible(testApp)
+ }
- layersTrace {
- noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0,
- enabled = !configuration.startRotation.isRotated())
- navBarLayerIsAlwaysVisible(
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerIsAlwaysVisible(
- enabled = !configuration.startRotation.isRotated())
- visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE),
- enabled = !configuration.startRotation.isRotated())
+ layersTrace {
+ noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
+ imeLayerBecomesInvisible()
+ imeAppLayerBecomesInvisible(testApp)
+
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE))
+ }
+ }
+ }
- imeLayerBecomesInvisible()
- imeAppLayerBecomesInvisible(testApp)
+ flaky {
+ layersTrace {
+ if (isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE))
+ }
+ }
}
}
}
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 0503cce0fbb1..2bd5abb640e5 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
@@ -16,8 +16,6 @@
package com.android.server.wm.flicker.ime
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -47,11 +45,9 @@ import org.junit.runners.Parameterized
* Test IME window closing back to app window transitions.
* To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
class CloseImeWindowToAppTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
@@ -64,7 +60,7 @@ class CloseImeWindowToAppTest(
val testApp = ImeAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag("imeToApp", configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -86,24 +82,25 @@ class CloseImeWindowToAppTest(
testApp.closeIME(device, wmHelper)
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(listOf("InputMethod"))
+ postsubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE))
+ imeAppWindowIsAlwaysVisible(testApp)
+ }
- imeAppWindowIsAlwaysVisible(testApp)
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation)
- navBarLayerRotatesAndScales(configuration.startRotation)
- statusBarLayerRotatesScales(configuration.startRotation)
- visibleLayersShownMoreThanOneConsecutiveEntry()
-
- imeLayerBecomesInvisible()
- imeAppLayerIsAlwaysVisible(testApp)
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.startRotation)
+ navBarLayerRotatesAndScales(configuration.startRotation)
+ statusBarLayerRotatesScales(configuration.startRotation)
+ visibleLayersShownMoreThanOneConsecutiveEntry()
+ imeLayerBecomesInvisible()
+ imeAppLayerIsAlwaysVisible(testApp)
+ }
}
}
}
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 9cb075b8a0bd..7b61bb58446c 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
@@ -16,8 +16,6 @@
package com.android.server.wm.flicker.ime
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -47,11 +45,9 @@ import org.junit.runners.Parameterized
* Test IME window closing to home transitions.
* To run this test: `atest FlickerTests:CloseImeWindowToHomeTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
class CloseImeWindowToHomeTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
@@ -63,7 +59,7 @@ class CloseImeWindowToHomeTest(
val testApp = ImeAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag("imeToHome", configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -91,31 +87,47 @@ class CloseImeWindowToHomeTest(
}
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ val isRotated = configuration.startRotation.isRotated()
- imeWindowBecomesInvisible()
- imeAppWindowBecomesInvisible(testApp)
+ postsubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE))
+ imeWindowBecomesInvisible()
+ imeAppWindowBecomesInvisible(testApp)
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ imeLayerBecomesInvisible()
+ imeAppLayerBecomesInvisible(testApp)
+ noUncoveredRegions(configuration.startRotation,
+ Surface.ROTATION_0)
+
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ }
+ }
}
- layersTrace {
- noUncoveredRegions(configuration.startRotation,
- Surface.ROTATION_0)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0,
- enabled = !configuration.startRotation.isRotated())
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE),
- enabled = false)
+ flaky {
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE))
- imeLayerBecomesInvisible()
- imeAppLayerBecomesInvisible(testApp)
+ if (isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ }
+ }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index c775cb855ff4..cfdd8564128f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -17,106 +17,82 @@
package com.android.server.wm.flicker.ime
import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
-import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
+import com.android.server.wm.flicker.dsl.WmAssertionBuilder
const val IME_WINDOW_TITLE = "InputMethod"
@JvmOverloads
-fun LayersAssertionBuilderLegacy.imeLayerBecomesVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeLayerBecomesVisible", bugId, enabled) {
+fun LayersAssertionBuilder.imeLayerBecomesVisible(bugId: Int = 0) {
+ all("imeLayerBecomesVisible", bugId) {
this.hidesLayer(IME_WINDOW_TITLE)
.then()
.showsLayer(IME_WINDOW_TITLE)
}
}
-fun LayersAssertionBuilderLegacy.imeLayerBecomesInvisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeLayerBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun LayersAssertionBuilder.imeLayerBecomesInvisible(bugId: Int = 0) {
+ all("imeLayerBecomesInvisible", bugId) {
this.showsLayer(IME_WINDOW_TITLE)
.then()
.hidesLayer(IME_WINDOW_TITLE)
}
}
-fun LayersAssertionBuilderLegacy.imeAppLayerIsAlwaysVisible(
- testApp: IAppHelper,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeAppLayerIsAlwaysVisible", bugId, enabled) {
+@JvmOverloads
+fun LayersAssertionBuilder.imeAppLayerIsAlwaysVisible(testApp: IAppHelper, bugId: Int = 0) {
+ all("imeAppLayerIsAlwaysVisible", bugId) {
this.showsLayer(testApp.getPackage())
}
}
-fun WmAssertionBuilderLegacy.imeAppWindowIsAlwaysVisible(
- testApp: IAppHelper,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeAppWindowIsAlwaysVisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeAppWindowIsAlwaysVisible(testApp: IAppHelper, bugId: Int = 0) {
+ all("imeAppWindowIsAlwaysVisible", bugId) {
this.showsAppWindowOnTop(testApp.getPackage())
}
}
-fun WmAssertionBuilderLegacy.imeWindowBecomesVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeWindowBecomesVisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeWindowBecomesVisible(bugId: Int = 0) {
+ all("imeWindowBecomesVisible", bugId) {
this.hidesNonAppWindow(IME_WINDOW_TITLE)
.then()
.showsNonAppWindow(IME_WINDOW_TITLE)
}
}
-fun WmAssertionBuilderLegacy.imeWindowBecomesInvisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeWindowBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeWindowBecomesInvisible(bugId: Int = 0) {
+ all("imeWindowBecomesInvisible", bugId) {
this.showsNonAppWindow(IME_WINDOW_TITLE)
.then()
.hidesNonAppWindow(IME_WINDOW_TITLE)
}
}
-fun WmAssertionBuilderLegacy.imeAppWindowBecomesVisible(
- windowName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeAppWindowBecomesVisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeAppWindowBecomesVisible(windowName: String, bugId: Int = 0) {
+ all("imeAppWindowBecomesVisible", bugId) {
this.hidesAppWindow(windowName)
.then()
.showsAppWindow(windowName)
}
}
-fun WmAssertionBuilderLegacy.imeAppWindowBecomesInvisible(
- testApp: IAppHelper,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeAppWindowBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeAppWindowBecomesInvisible(testApp: IAppHelper, bugId: Int = 0) {
+ all("imeAppWindowBecomesInvisible", bugId) {
this.showsAppWindowOnTop(testApp.getPackage())
.then()
.appWindowNotOnTop(testApp.getPackage())
}
}
-fun LayersAssertionBuilderLegacy.imeAppLayerBecomesInvisible(
- testApp: IAppHelper,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeAppLayerBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun LayersAssertionBuilder.imeAppLayerBecomesInvisible(testApp: IAppHelper, bugId: Int = 0) {
+ all("imeAppLayerBecomesInvisible", bugId) {
this.skipUntilFirstAssertion()
.showsLayer(testApp.getPackage())
.then()
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 13c6cd7e6e18..9e94d6e49527 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
@@ -16,8 +16,6 @@
package com.android.server.wm.flicker.ime
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -50,11 +48,9 @@ import org.junit.runners.Parameterized
* Test IME window opening transitions.
* To run this test: `atest FlickerTests:OpenImeWindowTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
class OpenImeWindowTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
@@ -66,7 +62,7 @@ class OpenImeWindowTest(
val testApp = ImeAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag("openIme", configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -88,27 +84,43 @@ class OpenImeWindowTest(
}
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
+ val isRotated = configuration.startRotation.isRotated()
- imeWindowBecomesVisible()
- appWindowAlwaysVisibleOnTop(testApp.`package`)
+ postsubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+
+ imeWindowBecomesVisible()
+ appWindowAlwaysVisibleOnTop(testApp.`package`)
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.startRotation)
+ imeLayerBecomesVisible()
+ layerAlwaysVisible(testApp.`package`)
+
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation)
+ statusBarLayerRotatesScales(configuration.startRotation)
+ }
+ }
}
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation)
- navBarLayerRotatesAndScales(configuration.startRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(configuration.startRotation,
- enabled = !configuration.startRotation.isRotated())
- visibleLayersShownMoreThanOneConsecutiveEntry()
+ flaky {
+ windowManagerTrace {
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry()
- imeLayerBecomesVisible()
- layerAlwaysVisible(testApp.`package`)
+ if (isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation)
+ statusBarLayerRotatesScales(configuration.startRotation)
+ }
+ }
}
}
}
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 6cc64dfd8836..2fe49af26cc1 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
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.ime
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -52,7 +51,6 @@ import org.junit.runners.Parameterized
* Test IME window opening transitions.
* To run this test: `atest FlickerTests:ReOpenImeWindowTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -67,34 +65,37 @@ class ReOpenImeWindowTest(
val testAppComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation, repetitions = 1) { configuration ->
- val testApp = ImeAppAutoFocusHelper(instrumentation,
- configuration.startRotation)
- withTestName { buildTestTag("reOpenImeAutoFocus", configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- testApp.launchViaIntent(wmHelper)
- testApp.openIME(device, wmHelper)
- }
- eachRun {
- device.pressRecentApps()
- wmHelper.waitImeWindowGone()
- wmHelper.waitForAppTransitionIdle()
- this.setRotation(configuration.startRotation)
- }
+ val testApp = ImeAppAutoFocusHelper(instrumentation,
+ configuration.startRotation)
+ withTestName { buildTestTag(configuration) }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ testApp.launchViaIntent(wmHelper)
+ testApp.openIME(device, wmHelper)
}
- transitions {
- device.reopenAppFromOverview()
- wmHelper.waitImeWindowShown()
+ eachRun {
+ device.pressRecentApps()
+ wmHelper.waitImeWindowGone()
+ wmHelper.waitForAppTransitionIdle()
+ this.setRotation(configuration.startRotation)
}
- teardown {
- test {
- this.setRotation(Surface.ROTATION_0)
- testApp.exit()
- }
+ }
+ transitions {
+ device.reopenAppFromOverview()
+ wmHelper.waitImeWindowShown()
+ }
+ teardown {
+ test {
+ this.setRotation(Surface.ROTATION_0)
+ testApp.exit()
}
- assertions {
+ }
+ assertions {
+ val isRotated = configuration.startRotation.isRotated()
+
+ presubmit {
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
@@ -107,21 +108,34 @@ class ReOpenImeWindowTest(
layersTrace {
noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
statusBarLayerIsAlwaysVisible()
navBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(enabled = false)
-
imeLayerBecomesVisible()
appLayerReplacesWallpaperLayer(testAppComponentName.className)
+
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
+ }
+ }
+
+ flaky {
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ if (isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
}
}
}
+ }
}
}
} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
index 1bd1190313d8..be3fa5fa3cdf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
@@ -17,14 +17,10 @@
package com.android.server.wm.flicker.launch
import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.WmAssertionBuilder
-fun WmAssertionBuilderLegacy.appWindowReplacesLauncherAsTopWindow(
- testApp: IAppHelper,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("appWindowReplacesLauncherAsTopWindow", bugId, enabled) {
+fun WmAssertionBuilder.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper, bugId: Int = 0) {
+ all("appWindowReplacesLauncherAsTopWindow", bugId) {
this.showsAppWindowOnTop("Launcher")
.then()
.showsAppWindowOnTop("Snapshot", testApp.getPackage())
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 010ebf2c2788..0ec0b04339cd 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,7 +16,6 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -51,7 +50,6 @@ import org.junit.runners.Parameterized
* Test cold launch app from launcher.
* To run this test: `atest FlickerTests:OpenAppColdTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -66,7 +64,7 @@ class OpenAppColdTest(
val testApp = SimpleAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation) { configuration ->
- withTestName { buildTestTag("openAppCold", testApp, configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -88,33 +86,48 @@ class OpenAppColdTest(
}
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
+ val isRotated = configuration.startRotation.isRotated()
- appWindowReplacesLauncherAsTopWindow(testApp)
- wallpaperWindowBecomesInvisible()
- }
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
+ appWindowReplacesLauncherAsTopWindow(testApp)
+ wallpaperWindowBecomesInvisible()
+ }
+
+ layersTrace {
+ // During testing the launcher is always in portrait mode
+ noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ appLayerReplacesWallpaperLayer(testApp.`package`)
- layersTrace {
- // During testing the launcher is always in portrait mode
- noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(enabled = false)
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
+ }
- appLayerReplacesWallpaperLayer(testApp.`package`)
+ eventLog {
+ focusChanges("NexusLauncherActivity", testApp.`package`)
+ }
}
- eventLog {
- focusChanges("NexusLauncherActivity", testApp.`package`)
+ flaky {
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ if (isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
+ }
}
}
}
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 5e08921c2253..84cc8e3ab058 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,7 +16,6 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -52,7 +51,6 @@ import org.junit.runners.Parameterized
* Launch an app from the recents app view (the overview)
* To run this test: `atest FlickerTests:OpenAppFromOverviewTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -67,7 +65,7 @@ class OpenAppFromOverviewTest(
val testApp = SimpleAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag("openAppFromOverview", configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -92,36 +90,57 @@ class OpenAppFromOverviewTest(
}
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
+ val isRotated = configuration.startRotation.isRotated()
- appWindowReplacesLauncherAsTopWindow(testApp)
- wallpaperWindowBecomesInvisible()
- }
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ appWindowReplacesLauncherAsTopWindow(testApp)
+ wallpaperWindowBecomesInvisible()
+ }
+
+ layersTrace {
+ appLayerReplacesWallpaperLayer(testApp.`package`)
- layersTrace {
- noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation,
- bugId = 141361128)
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerIsAlwaysVisible(
- enabled = Surface.ROTATION_0 == configuration.endRotation)
- navBarLayerIsAlwaysVisible(
- enabled = Surface.ROTATION_0 == configuration.endRotation)
- visibleLayersShownMoreThanOneConsecutiveEntry(
- enabled = false)
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ } else {
+ statusBarLayerIsAlwaysVisible()
+ navBarLayerIsAlwaysVisible()
+ }
+ }
- appLayerReplacesWallpaperLayer(testApp.`package`)
+ eventLog {
+ focusChanges("NexusLauncherActivity", testApp.`package`)
+ }
}
- eventLog {
- focusChanges("NexusLauncherActivity", testApp.`package`)
+ postsubmit {
+ windowManagerTrace {
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+ }
+
+ flaky {
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry()
+ noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation,
+ bugId = 141361128)
+
+ if (isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ } else {
+ statusBarLayerIsAlwaysVisible()
+ navBarLayerIsAlwaysVisible()
+ }
+ }
}
}
}
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 092cd4d2dad6..1f375a5cdea8 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,7 +16,6 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -51,7 +50,6 @@ import org.junit.runners.Parameterized
* Test warm launch app.
* To run this test: `atest FlickerTests:OpenAppWarmTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -64,7 +62,7 @@ class OpenAppWarmTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTest
val testApp = SimpleAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation) { configuration ->
- withTestName { buildTestTag("openAppWarm", testApp, configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -91,33 +89,49 @@ class OpenAppWarmTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTest
}
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
+ val isRotated = configuration.startRotation.isRotated()
- appWindowReplacesLauncherAsTopWindow(testApp)
- wallpaperWindowBecomesInvisible()
- }
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ appWindowReplacesLauncherAsTopWindow(testApp)
+ wallpaperWindowBecomesInvisible()
+ }
- layersTrace {
- // During testing the launcher is always in portrait mode
- noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(enabled = false)
+ layersTrace {
+ // During testing the launcher is always in portrait mode
+ noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ appLayerReplacesWallpaperLayer(testApp.`package`)
- appLayerReplacesWallpaperLayer(testApp.`package`)
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
+ }
+
+ eventLog {
+ focusChanges("NexusLauncherActivity", testApp.`package`)
+ }
}
- eventLog {
- focusChanges("NexusLauncherActivity", testApp.`package`)
+ flaky {
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ if (isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
+ }
}
}
}
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 1c44b21c0f9a..7bfdd96b9af8 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,7 +17,6 @@
package com.android.server.wm.flicker.rotation
import android.os.Bundle
-import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
@@ -48,7 +47,6 @@ import org.junit.runners.Parameterized
* Cycle through supported app rotations.
* To run this test: `atest FlickerTests:ChangeAppRotationTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -67,51 +65,56 @@ class ChangeAppRotationTest(
@JvmStatic
fun getParams(): Collection<Array<Any>> {
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName { buildTestTag("changeAppRotation", configuration) }
+ withTestName { buildTestTag(configuration) }
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- noUncoveredRegions(configuration.startRotation,
- configuration.endRotation, allStates = false)
- navBarLayerRotatesAndScales(configuration.startRotation,
- configuration.endRotation, bugId = 140855415)
- statusBarLayerRotatesScales(configuration.startRotation,
- configuration.endRotation, bugId = 140855415)
- visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 140855415)
+ layersTrace {
+ noUncoveredRegions(configuration.startRotation,
+ configuration.endRotation, allStates = false)
+
+ all("screenshotLayerBecomesInvisible") {
+ this.showsLayer(testApp.getPackage())
+ .then()
+ .showsLayer(SCREENSHOT_LAYER)
+ .then()
+ .showsLayer(testApp.getPackage())
+ }
+ }
}
- layersTrace {
- val startingPos = WindowUtils.getDisplayBounds(
- configuration.startRotation)
- val endingPos = WindowUtils.getDisplayBounds(
- configuration.endRotation)
+ flaky {
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible(bugId = 140855415)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ configuration.endRotation, bugId = 140855415)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ configuration.endRotation, bugId = 140855415)
+ visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 140855415)
- start("appLayerRotates_StartingPos", bugId = 140855415) {
- this.hasVisibleRegion(testApp.getPackage(), startingPos)
- }
+ val startingPos = WindowUtils.getDisplayBounds(
+ configuration.startRotation)
+ val endingPos = WindowUtils.getDisplayBounds(
+ configuration.endRotation)
- end("appLayerRotates_EndingPos", bugId = 140855415) {
- this.hasVisibleRegion(testApp.getPackage(), endingPos)
- }
+ start("appLayerRotates_StartingPos", bugId = 140855415) {
+ this.hasVisibleRegion(testApp.getPackage(), startingPos)
+ }
- all("screenshotLayerBecomesInvisible") {
- this.showsLayer(testApp.getPackage())
- .then()
- .showsLayer(SCREENSHOT_LAYER)
- .then()
- .showsLayer(testApp.getPackage())
+ end("appLayerRotates_EndingPos", bugId = 140855415) {
+ this.hasVisibleRegion(testApp.getPackage(), endingPos)
+ }
}
- }
- eventLog {
- focusDoesNotChange(bugId = 151179149)
+ eventLog {
+ focusDoesNotChange(bugId = 151179149)
+ }
}
}
}
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 f04131b64cd4..78614640a372 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
@@ -17,7 +17,6 @@
package com.android.server.wm.flicker.rotation
import android.os.Bundle
-import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
@@ -51,7 +50,6 @@ import org.junit.runners.Parameterized
* Cycle through supported app rotations using seamless rotations.
* To run this test: `atest FlickerTests:SeamlessAppRotationTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -97,63 +95,67 @@ class SeamlessAppRotationTest(
} else {
""
}
- buildTestTag("seamlessRotation", configuration, extraInfo = extra)
+ buildTestTag(configuration, extraInfo = extra)
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible(bugId = 140855415)
- statusBarWindowIsAlwaysVisible(bugId = 140855415)
- visibleWindowsShownMoreThanOneConsecutiveEntry()
- appWindowAlwaysVisibleOnTop(testApp.`package`)
- }
+ val startingBounds = WindowUtils.getDisplayBounds(configuration.startRotation)
+ val endingBounds = WindowUtils.getDisplayBounds(configuration.endRotation)
+
+ presubmit {
+ windowManagerTrace {
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
+ appWindowAlwaysVisibleOnTop(testApp.`package`)
+ }
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- noUncoveredRegions(configuration.startRotation,
- configuration.endRotation, allStates = false, bugId = 147659548)
- navBarLayerRotatesAndScales(configuration.startRotation,
- configuration.endRotation,
- enabled = false)
- statusBarLayerRotatesScales(configuration.startRotation,
- configuration.endRotation, enabled = false)
- visibleLayersShownMoreThanOneConsecutiveEntry(
- enabled = configuration.startRotation == configuration.endRotation)
- layerAlwaysVisible(testApp.`package`)
+ layersTrace {
+ layerAlwaysVisible(testApp.`package`)
+ }
}
- layersTrace {
- val startingBounds = WindowUtils
- .getDisplayBounds(configuration.startRotation)
- val endingBounds = WindowUtils
- .getDisplayBounds(configuration.endRotation)
-
- all("appLayerRotates", bugId = 147659548) {
- if (startingBounds == endingBounds) {
- this.hasVisibleRegion(
- testApp.`package`, startingBounds)
- } else {
- this.hasVisibleRegion(testApp.`package`,
- startingBounds)
- .then()
- .hasVisibleRegion(testApp.`package`,
- endingBounds)
- }
+ flaky {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible(bugId = 140855415)
+ statusBarWindowIsAlwaysVisible(bugId = 140855415)
}
- all("noUncoveredRegions", bugId = 147659548) {
- if (startingBounds == endingBounds) {
- this.coversAtLeastRegion(startingBounds)
- } else {
- this.coversAtLeastRegion(startingBounds)
- .then()
- .coversAtLeastRegion(endingBounds)
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible(bugId = 140855415)
+ noUncoveredRegions(configuration.startRotation,
+ configuration.endRotation, allStates = false, bugId = 147659548)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ configuration.endRotation)
+ visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ all("appLayerRotates", bugId = 147659548) {
+ if (startingBounds == endingBounds) {
+ this.hasVisibleRegion(
+ testApp.`package`, startingBounds)
+ } else {
+ this.hasVisibleRegion(testApp.`package`,
+ startingBounds)
+ .then()
+ .hasVisibleRegion(testApp.`package`,
+ endingBounds)
+ }
+ }
+
+ all("noUncoveredRegions", bugId = 147659548) {
+ if (startingBounds == endingBounds) {
+ this.coversAtLeastRegion(startingBounds)
+ } else {
+ this.coversAtLeastRegion(startingBounds)
+ .then()
+ .coversAtLeastRegion(endingBounds)
+ }
}
}
- }
- eventLog {
- focusDoesNotChange(bugId = 151179149)
+ eventLog {
+ focusDoesNotChange(bugId = 151179149)
+ }
}
}
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/Android.bp b/tests/FlickerTests/test-apps/flickerapp/Android.bp
index 502779723c36..78660c04d8d4 100644
--- a/tests/FlickerTests/test-apps/flickerapp/Android.bp
+++ b/tests/FlickerTests/test-apps/flickerapp/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FlickerTestApp",
srcs: ["**/*.java"],
diff --git a/tests/FrameworkPerf/Android.bp b/tests/FrameworkPerf/Android.bp
index a259ebd05fa8..9be3ab795b86 100644
--- a/tests/FrameworkPerf/Android.bp
+++ b/tests/FrameworkPerf/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworkPerf",
srcs: ["**/*.java"],
diff --git a/tests/GamePerformance/Android.bp b/tests/GamePerformance/Android.bp
index 02908d3a4cd4..f250a1bbdaca 100644
--- a/tests/GamePerformance/Android.bp
+++ b/tests/GamePerformance/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "GamePerformance",
// Don't include this package in any target
diff --git a/tests/GridLayoutTest/Android.bp b/tests/GridLayoutTest/Android.bp
index b4b5ba561c3f..71d884c4111f 100644
--- a/tests/GridLayoutTest/Android.bp
+++ b/tests/GridLayoutTest/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "GridLayoutTest",
srcs: ["**/*.java"],
diff --git a/tests/HierarchyViewerTest/Android.bp b/tests/HierarchyViewerTest/Android.bp
index 814c88328118..9c5d1c09cedd 100644
--- a/tests/HierarchyViewerTest/Android.bp
+++ b/tests/HierarchyViewerTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "HierarchyViewerTest",
srcs: ["**/*.java"],
diff --git a/tests/HugeBackup/Android.bp b/tests/HugeBackup/Android.bp
index b44c4578a853..7d4e52acb9b2 100644
--- a/tests/HugeBackup/Android.bp
+++ b/tests/HugeBackup/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "HugeBackup",
// Only compile source java files in this apk.
diff --git a/tests/HwAccelerationTest/Android.bp b/tests/HwAccelerationTest/Android.bp
index 37d3f5d4d97f..76063227eac1 100644
--- a/tests/HwAccelerationTest/Android.bp
+++ b/tests/HwAccelerationTest/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "HwAccelerationTest",
srcs: ["**/*.java"],
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 1a940c75cfa4..c6c67feeed72 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -753,6 +753,15 @@
</intent-filter>
</activity>
+ <activity android:name="RenderEffectShaderActivity"
+ android:label="RenderEffect/Shader"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="TextActivity"
android:label="Text/Simple Text"
android:theme="@android:style/Theme.NoTitleBar"
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java
new file mode 100644
index 000000000000..661d48a84768
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java
@@ -0,0 +1,107 @@
+/*
+ * 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.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.RenderEffect;
+import android.graphics.RenderNode;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class RenderEffectShaderActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ LinearLayout layout = new LinearLayout(this);
+ layout.setClipChildren(false);
+ layout.setGravity(Gravity.CENTER);
+ layout.setOrientation(LinearLayout.VERTICAL);
+
+
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(500, 500);
+ params.bottomMargin = 100;
+
+ layout.addView(new ShaderRenderEffectView(this), params);
+
+ setContentView(layout);
+ }
+
+ public static class ShaderRenderEffectView extends View {
+
+ private final Paint mPaint;
+ private final RenderNode mRenderNode;
+
+ public ShaderRenderEffectView(Context c) {
+ super(c);
+
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mRenderNode = new RenderNode("blurNode");
+
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (changed) {
+ LinearGradient gradient = new LinearGradient(
+ 0f, 0f,
+ 0f, bottom - top,
+ new int[]{Color.CYAN, Color.MAGENTA},
+ null,
+ Shader.TileMode.CLAMP
+ );
+ mRenderNode.setRenderEffect(
+ RenderEffect.createShaderEffect(gradient)
+ );
+
+ int width = right - left;
+ int height = bottom - top;
+ mRenderNode.setPosition(0, 0, width, height);
+ Canvas canvas = mRenderNode.beginRecording(width, height);
+ mPaint.setColor(Color.BLUE);
+
+ canvas.drawRect(
+ 0,
+ 0,
+ width,
+ height,
+ mPaint
+ );
+
+ mPaint.setColor(Color.RED);
+ canvas.drawCircle((right - left) / 2f, (bottom - top) / 2f, 50f, mPaint);
+
+ mRenderNode.endRecording();
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRenderNode(mRenderNode);
+ }
+ }
+}
diff --git a/tests/Internal/Android.bp b/tests/Internal/Android.bp
index 9da17db6a573..ef45864dd93b 100644
--- a/tests/Internal/Android.bp
+++ b/tests/Internal/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "InternalTests",
proto: {
diff --git a/tests/JankBench/Android.bp b/tests/JankBench/Android.bp
index 166639d2e7db..39dd197bf681 100644
--- a/tests/JankBench/Android.bp
+++ b/tests/JankBench/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "JankBench",
manifest: "app/src/main/AndroidManifest.xml",
diff --git a/tests/JobSchedulerPerfTests/Android.bp b/tests/JobSchedulerPerfTests/Android.bp
index 2ae8c33b60a7..eb9b2f636191 100644
--- a/tests/JobSchedulerPerfTests/Android.bp
+++ b/tests/JobSchedulerPerfTests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "JobSchedulerPerfTests",
srcs: ["src/**/*.java"],
diff --git a/tests/JobSchedulerTestApp/Android.bp b/tests/JobSchedulerTestApp/Android.bp
index bac0220e5591..893a983ca9d9 100644
--- a/tests/JobSchedulerTestApp/Android.bp
+++ b/tests/JobSchedulerTestApp/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "JobSchedulerTestApp",
srcs: ["src/**/*.java"],
diff --git a/tests/LargeAssetTest/Android.bp b/tests/LargeAssetTest/Android.bp
index 499e6a0721a1..2a6de77fb170 100644
--- a/tests/LargeAssetTest/Android.bp
+++ b/tests/LargeAssetTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "LargeAssetTest",
srcs: ["**/*.java"],
diff --git a/tests/LegacyAssistant/Android.bp b/tests/LegacyAssistant/Android.bp
index fef924d1cd89..ab8ef8885c45 100644
--- a/tests/LegacyAssistant/Android.bp
+++ b/tests/LegacyAssistant/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "LegacyAssistant",
srcs: ["**/*.java"],
diff --git a/tests/LocalizationTest/Android.bp b/tests/LocalizationTest/Android.bp
index c4bfcb1d2261..4e0b0a89d972 100644
--- a/tests/LocalizationTest/Android.bp
+++ b/tests/LocalizationTest/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "LocalizationTest",
srcs: ["java/**/*.kt"],
diff --git a/tests/LocationTracker/Android.bp b/tests/LocationTracker/Android.bp
index f0075a9c37bd..538687c7db45 100644
--- a/tests/LocationTracker/Android.bp
+++ b/tests/LocationTracker/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "LocationTracker",
srcs: ["**/*.java"],
diff --git a/tests/LotsOfApps/Android.bp b/tests/LotsOfApps/Android.bp
index 68b9f88ecfd7..5f6c089188c7 100644
--- a/tests/LotsOfApps/Android.bp
+++ b/tests/LotsOfApps/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "LotsOfApps",
srcs: ["**/*.java"],
diff --git a/tests/LowStorageTest/Android.bp b/tests/LowStorageTest/Android.bp
index e72e4a5e2559..6dcf39d606dd 100644
--- a/tests/LowStorageTest/Android.bp
+++ b/tests/LowStorageTest/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "lowstoragetest",
certificate: "platform",
diff --git a/tests/ManagedProfileLifecycleStressTest/Android.bp b/tests/ManagedProfileLifecycleStressTest/Android.bp
index 639ce3cfe935..3ef6322a2984 100644
--- a/tests/ManagedProfileLifecycleStressTest/Android.bp
+++ b/tests/ManagedProfileLifecycleStressTest/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test_host {
name: "ManagedProfileLifecycleStressTest",
srcs: ["src/**/*.java"],
diff --git a/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp
index 1f47b03d0074..7a9b6cfb134f 100644
--- a/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp
+++ b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "DummyDPC",
defaults: ["cts_defaults"],
diff --git a/tests/MemoryUsage/Android.bp b/tests/MemoryUsage/Android.bp
index aeb533882d4f..e30a0a7cd8b5 100644
--- a/tests/MemoryUsage/Android.bp
+++ b/tests/MemoryUsage/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "MemoryUsage",
// Only compile source java files in this apk.
diff --git a/tests/MirrorSurfaceTest/Android.bp b/tests/MirrorSurfaceTest/Android.bp
index e359c64cc982..1368f260725f 100644
--- a/tests/MirrorSurfaceTest/Android.bp
+++ b/tests/MirrorSurfaceTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "MirrorSurfaceTest",
srcs: ["src/**/*.java"],
diff --git a/tests/NativeProcessesMemoryTest/Android.bp b/tests/NativeProcessesMemoryTest/Android.bp
index f2625bf2db11..b7160e95dbb6 100644
--- a/tests/NativeProcessesMemoryTest/Android.bp
+++ b/tests/NativeProcessesMemoryTest/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test_host {
name: "native-processes-memory-test",
srcs: ["src/**/*.java"],
diff --git a/tests/NetworkSecurityConfigTest/Android.bp b/tests/NetworkSecurityConfigTest/Android.bp
index cf8ca57862b4..473eadbcad73 100644
--- a/tests/NetworkSecurityConfigTest/Android.bp
+++ b/tests/NetworkSecurityConfigTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "NetworkSecurityConfigTests",
certificate: "platform",
diff --git a/tests/NullHomeTest/Android.bp b/tests/NullHomeTest/Android.bp
index fc71d0deaac6..d8799a87e8c9 100644
--- a/tests/NullHomeTest/Android.bp
+++ b/tests/NullHomeTest/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "NullHomeTest",
srcs: ["src/**/*.java"],
diff --git a/tests/OdmApps/Android.bp b/tests/OdmApps/Android.bp
index d86f9cc81a5f..de86498afd27 100644
--- a/tests/OdmApps/Android.bp
+++ b/tests/OdmApps/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test_host {
name: "OdmAppsTest",
srcs: ["src/**/*.java"],
diff --git a/tests/OdmApps/app/Android.bp b/tests/OdmApps/app/Android.bp
index 5eb8590b6e06..a33a1cf4690e 100644
--- a/tests/OdmApps/app/Android.bp
+++ b/tests/OdmApps/app/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "TestOdmApp",
test_suites: ["device-tests"],
diff --git a/tests/OdmApps/priv-app/Android.bp b/tests/OdmApps/priv-app/Android.bp
index 9dd477cf6ad3..7527729c8267 100644
--- a/tests/OdmApps/priv-app/Android.bp
+++ b/tests/OdmApps/priv-app/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "TestOdmPrivApp",
test_suites: ["device-tests"],
diff --git a/tests/OneMedia/Android.bp b/tests/OneMedia/Android.bp
index 11e12f35741c..5c7317735bc7 100644
--- a/tests/OneMedia/Android.bp
+++ b/tests/OneMedia/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "OneMedia",
srcs: [
diff --git a/tests/PackageWatchdog/Android.bp b/tests/PackageWatchdog/Android.bp
index 0b75039cf69f..1e1dc8458560 100644
--- a/tests/PackageWatchdog/Android.bp
+++ b/tests/PackageWatchdog/Android.bp
@@ -13,6 +13,15 @@
// limitations under the License.
// PackageWatchdogTest
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "PackageWatchdogTest",
srcs: ["src/**/*.java"],
diff --git a/tests/PackageWatchdog/TEST_MAPPING b/tests/PackageWatchdog/TEST_MAPPING
new file mode 100644
index 000000000000..6494a273eced
--- /dev/null
+++ b/tests/PackageWatchdog/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "PackageWatchdogTest"
+ }
+ ]
+}
diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp
index 7d918cc4c18b..f0f9c4bdd721 100644
--- a/tests/PlatformCompatGating/Android.bp
+++ b/tests/PlatformCompatGating/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "PlatformCompatGating",
// Only compile source java files in this apk.
diff --git a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt
index eb04f6907748..ac9e6817a230 100644
--- a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt
+++ b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt
@@ -76,11 +76,11 @@ class PlatformCompatCommandNotInstalledTest {
Params(enableDisable = null, targetSdk = 29, result = false),
Params(enableDisable = null, targetSdk = 30, result = true),
- Params(enableDisable = true, targetSdk = 29, result = true),
+ Params(enableDisable = true, targetSdk = 29, result = false),
Params(enableDisable = true, targetSdk = 30, result = true),
Params(enableDisable = false, targetSdk = 29, result = false),
- Params(enableDisable = false, targetSdk = 30, result = false)
+ Params(enableDisable = false, targetSdk = 30, result = true)
)
}
diff --git a/tests/PlatformCompatGating/test-rules/Android.bp b/tests/PlatformCompatGating/test-rules/Android.bp
index 10fa2dc0d7c6..5f91f9d0e505 100644
--- a/tests/PlatformCompatGating/test-rules/Android.bp
+++ b/tests/PlatformCompatGating/test-rules/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "platform-compat-test-rules",
srcs: ["src/**/*.java"],
@@ -23,4 +32,4 @@ java_library {
"truth-prebuilt",
"core-compat-test-rules"
],
-} \ No newline at end of file
+}
diff --git a/tests/ProtoInputStreamTests/Android.bp b/tests/ProtoInputStreamTests/Android.bp
index ecc405664128..0029080b5a89 100644
--- a/tests/ProtoInputStreamTests/Android.bp
+++ b/tests/ProtoInputStreamTests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ProtoInputStreamTests",
proto: {
diff --git a/tests/RemoteDisplayProvider/Android.bp b/tests/RemoteDisplayProvider/Android.bp
index 6c7798fb3faf..55732d14af46 100644
--- a/tests/RemoteDisplayProvider/Android.bp
+++ b/tests/RemoteDisplayProvider/Android.bp
@@ -13,6 +13,15 @@
// limitations under the License.
// Build the application.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "RemoteDisplayProviderTest",
sdk_version: "system_current",
diff --git a/tests/RenderThreadTest/Android.bp b/tests/RenderThreadTest/Android.bp
index 165977607219..b18b04edb4c4 100644
--- a/tests/RenderThreadTest/Android.bp
+++ b/tests/RenderThreadTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "RenderThreadTest",
// Only compile source java files in this apk.
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 7dd003eb9755..95044dc5e7bb 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "RollbackTest",
manifest: "RollbackTest/AndroidManifest.xml",
@@ -116,4 +125,4 @@ apex {
key: "com.android.apex.apkrollback.test.key",
apps: ["TestAppACrashingV2"],
installable: false,
-} \ No newline at end of file
+}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 401d87af5a33..0508125edfc8 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -125,21 +125,11 @@ public class StagedRollbackTest {
}
/**
- * Test rollbacks of staged installs involving only apks with bad update.
- * Trigger rollback phase.
- */
- @Test
- public void testBadApkOnly_Phase3_Crash() throws Exception {
- // One more crash to trigger rollback
- RollbackUtils.sendCrashBroadcast(TestApp.A, 1);
- }
-
- /**
* Test rollbacks of staged installs involving only apks.
* Confirm rollback phase.
*/
@Test
- public void testBadApkOnly_Phase4_VerifyRollback() throws Exception {
+ public void testBadApkOnly_Phase3_VerifyRollback() throws Exception {
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
InstallUtils.processUserData(TestApp.A);
@@ -447,8 +437,10 @@ public class StagedRollbackTest {
Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1),
Rollback.from(TestApp.A, 0).to(TestApp.A1));
- // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback
- RollbackUtils.sendCrashBroadcast(TestApp.A, 5);
+ // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT-1 times
+ RollbackUtils.sendCrashBroadcast(TestApp.A, 4);
+ // Sleep for a while to make sure we don't trigger rollback
+ Thread.sleep(TimeUnit.SECONDS.toMillis(30));
}
@Test
@@ -504,6 +496,45 @@ public class StagedRollbackTest {
}
@Test
+ public void testWatchdogMonitorsAcrossReboots_Phase1_Install() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ Install.single(TestApp.A1).commit();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ InstallUtils.processUserData(TestApp.A);
+
+ Install.single(TestApp.ACrashing2).setEnableRollback().setStaged().commit();
+ }
+
+ @Test
+ public void testWatchdogMonitorsAcrossReboots_Phase2_VerifyInstall() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+
+ // Trigger rollback of test app.
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
+ Integer.toString(5), false);
+
+ // The final crash that causes rollback will come from the host side.
+ RollbackUtils.sendCrashBroadcast(TestApp.A, 4);
+ }
+
+ @Test
+ public void testWatchdogMonitorsAcrossReboots_Phase3_VerifyRollback() {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ InstallUtils.processUserData(TestApp.A);
+
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+ rm.getRecentlyCommittedRollbacks(), TestApp.A);
+ assertThat(rollback).isNotNull();
+ assertThat(rollback).packagesContainsExactly(
+ Rollback.from(TestApp.A2).to(TestApp.A1));
+ assertThat(rollback).causePackagesContainsExactly(TestApp.ACrashing2);
+ assertThat(rollback).isStaged();
+ assertThat(rollback.getCommittedSessionId()).isNotEqualTo(-1);
+ }
+
+ @Test
public void hasMainlineModule() throws Exception {
String pkgName = getModuleMetadataPackageName();
boolean existed = InstrumentationRegistry.getInstrumentation().getContext()
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 1d5730fb4427..65fb7b6c8cc6 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -153,13 +153,14 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
getDevice().reboot();
runPhase("testBadApkOnly_Phase2_VerifyInstall");
- // Trigger rollback and wait for reboot to happen
- runPhase("testBadApkOnly_Phase3_Crash");
+ // Launch the app to crash to trigger rollback
+ startActivity(TESTAPP_A);
+ // Wait for reboot to happen
waitForDeviceNotAvailable(2, TimeUnit.MINUTES);
getDevice().waitForDeviceAvailable();
- runPhase("testBadApkOnly_Phase4_VerifyRollback");
+ runPhase("testBadApkOnly_Phase3_VerifyRollback");
assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_APP_CRASH, TESTAPP_A);
assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null);
@@ -304,8 +305,10 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
getDevice().reboot();
// Verify apex was installed and then crash the apk
runPhase("testRollbackApexWithApkCrashing_Phase2_Crash");
- // Wait for crash to trigger rollback
- waitForDeviceNotAvailable(5, TimeUnit.MINUTES);
+ // Launch the app to crash to trigger rollback
+ startActivity(TESTAPP_A);
+ // Wait for reboot to happen
+ waitForDeviceNotAvailable(2, TimeUnit.MINUTES);
getDevice().waitForDeviceAvailable();
// Verify rollback occurred due to crash of apk-in-apex
runPhase("testRollbackApexWithApkCrashing_Phase3_VerifyRollback");
@@ -551,6 +554,30 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
});
}
+ /**
+ * Tests that packages are monitored across multiple reboots.
+ */
+ @Test
+ public void testWatchdogMonitorsAcrossReboots() throws Exception {
+ runPhase("testWatchdogMonitorsAcrossReboots_Phase1_Install");
+
+ // The first reboot will make the rollback available.
+ // Information about which packages are monitored will be persisted to a file before the
+ // second reboot, and read from disk after the second reboot.
+ getDevice().reboot();
+ getDevice().reboot();
+
+ runPhase("testWatchdogMonitorsAcrossReboots_Phase2_VerifyInstall");
+
+ // Launch the app to crash to trigger rollback
+ startActivity(TESTAPP_A);
+ // Wait for reboot to happen
+ waitForDeviceNotAvailable(2, TimeUnit.MINUTES);
+ getDevice().waitForDeviceAvailable();
+
+ runPhase("testWatchdogMonitorsAcrossReboots_Phase3_VerifyRollback");
+ }
+
private void pushTestApex() throws Exception {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";
@@ -631,6 +658,12 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
}
}
+ private void startActivity(String packageName) throws Exception {
+ String cmd = "am start -S -a android.intent.action.MAIN "
+ + "-c android.intent.category.LAUNCHER " + packageName;
+ getDevice().executeShellCommand(cmd);
+ }
+
private void crashProcess(String processName, int numberOfCrashes) throws Exception {
String pid = "";
String lastPid = "invalid";
diff --git a/tests/SerialChat/Android.bp b/tests/SerialChat/Android.bp
index 3c18035a4aab..8719e01031d5 100644
--- a/tests/SerialChat/Android.bp
+++ b/tests/SerialChat/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "SerialChat",
srcs: ["**/*.java"],
diff --git a/tests/ServiceCrashTest/Android.bp b/tests/ServiceCrashTest/Android.bp
index 40a377de852f..fb98b7631b7e 100644
--- a/tests/ServiceCrashTest/Android.bp
+++ b/tests/ServiceCrashTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ServiceCrashTest",
// Only compile source java files in this apk.
diff --git a/tests/SharedLibrary/client/Android.bp b/tests/SharedLibrary/client/Android.bp
index dbf6dc94eb8d..6eab7c2e2dce 100644
--- a/tests/SharedLibrary/client/Android.bp
+++ b/tests/SharedLibrary/client/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "SharedLibraryClient",
srcs: ["**/*.java"],
diff --git a/tests/SharedLibrary/lib/Android.bp b/tests/SharedLibrary/lib/Android.bp
index f69d388ef0f1..0595cb1e116a 100644
--- a/tests/SharedLibrary/lib/Android.bp
+++ b/tests/SharedLibrary/lib/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "SharedLibrary",
srcs: ["**/*.java"],
diff --git a/tests/ShowWhenLockedApp/Android.bp b/tests/ShowWhenLockedApp/Android.bp
index dba564c91059..f24834a53a36 100644
--- a/tests/ShowWhenLockedApp/Android.bp
+++ b/tests/ShowWhenLockedApp/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "ShowWhenLocked",
srcs: ["**/*.java"],
diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
index 9b15b0445642..5c2644844e14 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
@@ -34,12 +34,14 @@ class ColorModeControls : LinearLayout, WindowObserver {
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
displayManager = context.getSystemService(DisplayManager::class.java)!!
+ displayId = context.getDisplayId()
}
private var window: Window? = null
private var currentModeDisplay: TextView? = null
private val displayManager: DisplayManager
private var targetSdrWhitePointIndex = 0
+ private var displayId: Int
private val whitePoint get() = SDR_WHITE_POINTS[targetSdrWhitePointIndex]
@@ -107,7 +109,7 @@ class ColorModeControls : LinearLayout, WindowObserver {
// Imperfect, but close enough, synchronization by waiting for frame commit to set the value
viewTreeObserver.registerFrameCommitCallback {
try {
- displayManager.setTemporaryBrightness(level)
+ displayManager.setTemporaryBrightness(displayId, level)
} catch (ex: Exception) {
// Ignore a permission denied rejection - it doesn't meaningfully change much
}
diff --git a/tests/SmokeTest/Android.bp b/tests/SmokeTest/Android.bp
index bc45ee6799b4..4c853e3c5267 100644
--- a/tests/SmokeTest/Android.bp
+++ b/tests/SmokeTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "SmokeTestApp",
// This builds "SmokeTestApp"
diff --git a/tests/SmokeTest/tests/Android.bp b/tests/SmokeTest/tests/Android.bp
index ceb2d193de79..5542dd0119ea 100644
--- a/tests/SmokeTest/tests/Android.bp
+++ b/tests/SmokeTest/tests/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "SmokeTest",
// Include all test java files.
diff --git a/tests/SmokeTestApps/Android.bp b/tests/SmokeTestApps/Android.bp
index 0feb00040eac..3505fe1c4afb 100644
--- a/tests/SmokeTestApps/Android.bp
+++ b/tests/SmokeTestApps/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "SmokeTestTriggerApps",
srcs: ["src/**/*.java"],
diff --git a/tests/SoundTriggerTestApp/Android.bp b/tests/SoundTriggerTestApp/Android.bp
index d3a1300b8e12..09f1e10561e3 100644
--- a/tests/SoundTriggerTestApp/Android.bp
+++ b/tests/SoundTriggerTestApp/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "SoundTriggerTestApp",
srcs: ["**/*.java"],
diff --git a/tests/Split/Android.bp b/tests/Split/Android.bp
index d8c89bab2857..727b2026461c 100644
--- a/tests/Split/Android.bp
+++ b/tests/Split/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "Split",
srcs: ["**/*.java"],
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index 3a40696d1add..2e57467e55fe 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "StagedInstallInternalTestApp",
manifest: "app/AndroidManifest.xml",
@@ -41,4 +50,3 @@ java_test_host {
test_suites: ["general-tests"],
test_config: "StagedInstallInternalTest.xml",
}
-
diff --git a/tests/StagedInstallTest/StagedInstallInternalTest.xml b/tests/StagedInstallTest/StagedInstallInternalTest.xml
index 1b8fa672fe38..1f22cae8f3cf 100644
--- a/tests/StagedInstallTest/StagedInstallInternalTest.xml
+++ b/tests/StagedInstallTest/StagedInstallInternalTest.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
<configuration description="Runs the internal staged install tests">
- <option name="test-suite-tag" value="StagedInstallTest" />
+ <option name="test-suite-tag" value="StagedInstallInternalTest" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="StagedInstallInternalTestApp.apk" />
diff --git a/tests/StagedInstallTest/TEST_MAPPING b/tests/StagedInstallTest/TEST_MAPPING
index fa2a60b21b50..cc31f2c98425 100644
--- a/tests/StagedInstallTest/TEST_MAPPING
+++ b/tests/StagedInstallTest/TEST_MAPPING
@@ -1,6 +1,16 @@
{
"presubmit-large": [
{
+ "name": "StagedInstallInternalTest",
+ "options": [
+ {
+ "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
"name": "StagedInstallInternalTest"
}
]
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index 2201efd3a7ac..8dc53ac26715 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -25,6 +25,7 @@ import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import android.cts.install.lib.host.InstallUtilsHost;
+import android.platform.test.annotations.LargeTest;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.ddmlib.Log;
@@ -201,6 +202,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
// Test rollback-app command waits for staged sessions to be ready
@Test
+ @LargeTest
public void testAdbRollbackAppWaitsForStagedReady() throws Exception {
assumeTrue("Device does not support updating APEX",
mHostUtils.isApexUpdateSupported());
diff --git a/tests/StatusBar/Android.bp b/tests/StatusBar/Android.bp
index 0b650ed3afc8..2fad051300c7 100644
--- a/tests/StatusBar/Android.bp
+++ b/tests/StatusBar/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "StatusBarTest",
srcs: ["**/*.java"],
diff --git a/tests/SurfaceComposition/Android.bp b/tests/SurfaceComposition/Android.bp
index 53e4d52b2efd..f5aba8f5a2f2 100644
--- a/tests/SurfaceComposition/Android.bp
+++ b/tests/SurfaceComposition/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "SurfaceComposition",
// Don't include this package in any target
diff --git a/tests/SurfaceControlViewHostTest/Android.bp b/tests/SurfaceControlViewHostTest/Android.bp
index e4e060010eea..0127ba559500 100644
--- a/tests/SurfaceControlViewHostTest/Android.bp
+++ b/tests/SurfaceControlViewHostTest/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "SurfaceControlViewHostTest",
srcs: ["**/*.java"],
diff --git a/tests/SystemMemoryTest/device/Android.bp b/tests/SystemMemoryTest/device/Android.bp
index 2bf0fec0fd1f..d7cec1aedaed 100644
--- a/tests/SystemMemoryTest/device/Android.bp
+++ b/tests/SystemMemoryTest/device/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test_helper_app {
name: "SystemMemoryTestDevice",
sdk_version: "current",
diff --git a/tests/SystemMemoryTest/host/Android.bp b/tests/SystemMemoryTest/host/Android.bp
index 3bb5489dab6c..79744625b752 100644
--- a/tests/SystemMemoryTest/host/Android.bp
+++ b/tests/SystemMemoryTest/host/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test_host {
name: "system-memory-test",
srcs: ["src/**/*.java"],
diff --git a/tests/SystemUIDemoModeController/Android.bp b/tests/SystemUIDemoModeController/Android.bp
index 1e4c43792d70..d952cf6a4b3e 100644
--- a/tests/SystemUIDemoModeController/Android.bp
+++ b/tests/SystemUIDemoModeController/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "DemoModeController",
srcs: ["**/*.java"],
diff --git a/tests/TaskOrganizerTest/Android.bp b/tests/TaskOrganizerTest/Android.bp
index 91900626a9cc..9b72d359aae6 100644
--- a/tests/TaskOrganizerTest/Android.bp
+++ b/tests/TaskOrganizerTest/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "TaskOrganizerTest",
srcs: ["**/*.java","**/*.kt"],
diff --git a/tests/TelephonyCommonTests/Android.bp b/tests/TelephonyCommonTests/Android.bp
index 4f7569d4f451..a9fbfd97225d 100644
--- a/tests/TelephonyCommonTests/Android.bp
+++ b/tests/TelephonyCommonTests/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "TelephonyCommonTests",
srcs: [
diff --git a/tests/TouchLatency/Android.bp b/tests/TouchLatency/Android.bp
index 1174bcb0d4d6..3a9e240d9746 100644
--- a/tests/TouchLatency/Android.bp
+++ b/tests/TouchLatency/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "TouchLatency",
manifest: "app/src/main/AndroidManifest.xml",
diff --git a/tests/TransformTest/Android.bp b/tests/TransformTest/Android.bp
index fd7aaeb35feb..f58fe8f13bda 100644
--- a/tests/TransformTest/Android.bp
+++ b/tests/TransformTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "TransformTest",
srcs: ["**/*.java"],
diff --git a/tests/TransitionTests/Android.bp b/tests/TransitionTests/Android.bp
index 57f19e38330d..4daa5b84f71e 100644
--- a/tests/TransitionTests/Android.bp
+++ b/tests/TransitionTests/Android.bp
@@ -1,3 +1,14 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-MIT
+ // SPDX-license-identifier-Unicode-DFS
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "TransitionTests",
// Only compile source java files in this apk.
diff --git a/tests/TtsTests/Android.bp b/tests/TtsTests/Android.bp
index b137523803a6..b7aa5d4a38aa 100644
--- a/tests/TtsTests/Android.bp
+++ b/tests/TtsTests/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "TtsTests",
srcs: ["**/*.java"],
diff --git a/tests/UiBench/Android.bp b/tests/UiBench/Android.bp
index e0608e288459..0d2f2ef46cab 100644
--- a/tests/UiBench/Android.bp
+++ b/tests/UiBench/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "UiBench",
sdk_version: "current",
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index d809fe8dad56..43a5078c3c24 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -16,8 +16,14 @@ java_test_host {
name: "UpdatableSystemFontTest",
srcs: ["src/**/*.java"],
libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
- static_libs: ["frameworks-base-hostutils"],
+ static_libs: [
+ "block_device_writer_jar",
+ "frameworks-base-hostutils",
+ ],
test_suites: ["general-tests", "vts"],
+ target_required: [
+ "block_device_writer_module",
+ ],
data: [
":NotoColorEmojiTtf",
":UpdatableSystemFontTestCertDer",
diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml
index efe5d703880c..7b919bd4b114 100644
--- a/tests/UpdatableSystemFontTest/AndroidTest.xml
+++ b/tests/UpdatableSystemFontTest/AndroidTest.xml
@@ -21,6 +21,7 @@
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
+ <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
<option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
<option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" />
<option name="push" value="UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig" />
diff --git a/tests/UpdatableSystemFontTest/TEST_MAPPING b/tests/UpdatableSystemFontTest/TEST_MAPPING
index a5c447934f43..7fbf426c71c4 100644
--- a/tests/UpdatableSystemFontTest/TEST_MAPPING
+++ b/tests/UpdatableSystemFontTest/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "UpdatableSystemFontTest"
}
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 6d161a5d7b3a..e249f8a99b0c 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -21,7 +21,9 @@ import static com.google.common.truth.Truth.assertWithMessage;
import android.platform.test.annotations.RootPermissionTest;
+import com.android.blockdevicewriter.BlockDeviceWriter;
import com.android.fsverity.AddFsVerityCertRule;
+import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -34,6 +36,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -126,6 +130,44 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test {
TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
}
+ @Test
+ public void reboot() throws Exception {
+ expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
+ String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ assertThat(fontPath).startsWith("/data/fonts/files/");
+
+ expectRemoteCommandToSucceed("stop");
+ expectRemoteCommandToSucceed("start");
+ waitUntilFontCommandIsReady();
+ String fontPathAfterReboot = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ assertThat(fontPathAfterReboot).isEqualTo(fontPath);
+ }
+
+ @Test
+ public void reboot_clearDamagedFiles() throws Exception {
+ expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
+ TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
+ String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ assertThat(fontPath).startsWith("/data/fonts/files/");
+ assertThat(BlockDeviceWriter.canReadByte(getDevice(), fontPath, 0)).isTrue();
+
+ BlockDeviceWriter.damageFileAgainstBlockDevice(getDevice(), fontPath, 0);
+ expectRemoteCommandToSucceed("stop");
+ // We have to make sure system_server is gone before dropping caches, because system_server
+ // process holds font memory maps and prevents cache eviction.
+ waitUntilSystemServerIsGone();
+ BlockDeviceWriter.assertFileNotOpen(getDevice(), fontPath);
+ BlockDeviceWriter.dropCaches(getDevice());
+ assertThat(BlockDeviceWriter.canReadByte(getDevice(), fontPath, 0)).isFalse();
+
+ expectRemoteCommandToSucceed("start");
+ waitUntilFontCommandIsReady();
+ String fontPathAfterReboot = getFontPath(NOTO_COLOR_EMOJI_TTF);
+ assertWithMessage("Damaged file should be deleted")
+ .that(fontPathAfterReboot).startsWith("/system");
+ }
+
private String getFontPath(String fontFileName) throws Exception {
// TODO: add a dedicated command for testing.
String lines = expectRemoteCommandToSucceed("cmd font dump");
@@ -153,4 +195,39 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test {
.that(result.getStatus())
.isNotEqualTo(CommandStatus.SUCCESS);
}
+
+ private void waitUntilFontCommandIsReady() {
+ waitUntil(TimeUnit.SECONDS.toMillis(30), () -> {
+ try {
+ return getDevice().executeShellV2Command("cmd font status").getStatus()
+ == CommandStatus.SUCCESS;
+ } catch (DeviceNotAvailableException e) {
+ return false;
+ }
+ });
+ }
+
+ private void waitUntilSystemServerIsGone() {
+ waitUntil(TimeUnit.SECONDS.toMillis(30), () -> {
+ try {
+ return getDevice().executeShellV2Command("pid system_server").getStatus()
+ == CommandStatus.FAILED;
+ } catch (DeviceNotAvailableException e) {
+ return false;
+ }
+ });
+ }
+
+ private void waitUntil(long timeoutMillis, Supplier<Boolean> func) {
+ long untilMillis = System.currentTimeMillis() + timeoutMillis;
+ do {
+ if (func.get()) return;
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ throw new AssertionError("Interrupted", e);
+ }
+ } while (System.currentTimeMillis() < untilMillis);
+ throw new AssertionError("Timed out");
+ }
}
diff --git a/tests/UsageReportingTest/Android.bp b/tests/UsageReportingTest/Android.bp
index 0bac5a224b26..dfce0705df59 100644
--- a/tests/UsageReportingTest/Android.bp
+++ b/tests/UsageReportingTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "UsageReportingTest",
// Only compile source java files in this apk.
diff --git a/tests/UsageStatsPerfTests/Android.bp b/tests/UsageStatsPerfTests/Android.bp
index 3991fb8366ac..0e372a3ef95a 100644
--- a/tests/UsageStatsPerfTests/Android.bp
+++ b/tests/UsageStatsPerfTests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "UsageStatsPerfTests",
srcs: ["src/**/*.java"],
diff --git a/tests/UsageStatsTest/Android.bp b/tests/UsageStatsTest/Android.bp
index 0808b05ec053..afb266bf326d 100644
--- a/tests/UsageStatsTest/Android.bp
+++ b/tests/UsageStatsTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "UsageStatsTest",
// Only compile source java files in this apk.
diff --git a/tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.bp b/tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.bp
index c7e9df0fe9cf..9133baed7179 100644
--- a/tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.bp
+++ b/tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.bp
@@ -16,6 +16,15 @@
//#################################################
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AoapTestDeviceApp",
srcs: ["src/**/*.java"],
diff --git a/tests/UsbHostExternalManagmentTest/AoapTestHost/Android.bp b/tests/UsbHostExternalManagmentTest/AoapTestHost/Android.bp
index 6fa58cb5c682..68930023de96 100644
--- a/tests/UsbHostExternalManagmentTest/AoapTestHost/Android.bp
+++ b/tests/UsbHostExternalManagmentTest/AoapTestHost/Android.bp
@@ -16,6 +16,15 @@
//#################################################
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AoapTestHostApp",
srcs: ["src/**/*.java"],
diff --git a/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.bp b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.bp
index edd4205968b3..2fca4d35fe01 100644
--- a/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.bp
+++ b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.bp
@@ -17,6 +17,15 @@
//#################################################
// TODO: should this be android_helper_test_app?
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "UsbHostExternalManagementTestApp",
srcs: ["src/**/*.java"],
diff --git a/tests/UsbManagerTests/Android.bp b/tests/UsbManagerTests/Android.bp
index a03c6e223b74..97fbf5b32035 100644
--- a/tests/UsbManagerTests/Android.bp
+++ b/tests/UsbManagerTests/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "UsbManagerTests",
srcs: ["src/**/*.java"],
diff --git a/tests/UsbManagerTests/lib/Android.bp b/tests/UsbManagerTests/lib/Android.bp
index 3c5d91b326d0..994484cd63bf 100644
--- a/tests/UsbManagerTests/lib/Android.bp
+++ b/tests/UsbManagerTests/lib/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "UsbManagerTestLib",
srcs: ["src/**/*.java"],
diff --git a/tests/UsbTests/Android.bp b/tests/UsbTests/Android.bp
index 7c2be9b63ac3..9328b67795cb 100644
--- a/tests/UsbTests/Android.bp
+++ b/tests/UsbTests/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "UsbTests",
srcs: ["**/*.java"],
diff --git a/tests/UsesFeature2Test/Android.bp b/tests/UsesFeature2Test/Android.bp
index a1b77d07ccdc..624e4ec7ebd8 100644
--- a/tests/UsesFeature2Test/Android.bp
+++ b/tests/UsesFeature2Test/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "UsesFeature2Test",
srcs: ["**/*.java"],
diff --git a/tests/VectorDrawableTest/Android.bp b/tests/VectorDrawableTest/Android.bp
index 13f318ef7c42..9da7c5fdbb17 100644
--- a/tests/VectorDrawableTest/Android.bp
+++ b/tests/VectorDrawableTest/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "VectorDrawableTest",
srcs: ["**/*.java"],
diff --git a/tests/VoiceEnrollment/Android.bp b/tests/VoiceEnrollment/Android.bp
index e43b38c5a034..b5d62bbd68a8 100644
--- a/tests/VoiceEnrollment/Android.bp
+++ b/tests/VoiceEnrollment/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "VoiceEnrollment",
srcs: ["**/*.java"],
diff --git a/tests/VoiceInteraction/Android.bp b/tests/VoiceInteraction/Android.bp
index 7059473fb63f..1aa7fafcd37c 100644
--- a/tests/VoiceInteraction/Android.bp
+++ b/tests/VoiceInteraction/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "VoiceInteraction",
srcs: ["**/*.java"],
diff --git a/tests/WallpaperTest/Android.bp b/tests/WallpaperTest/Android.bp
index f68b6ec2452d..b009af25f9da 100644
--- a/tests/WallpaperTest/Android.bp
+++ b/tests/WallpaperTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "WallpaperTest",
srcs: ["src/**/*.java"],
diff --git a/tests/WindowAnimationJank/Android.bp b/tests/WindowAnimationJank/Android.bp
index 50b2297386cc..ed86aa5f90ea 100644
--- a/tests/WindowAnimationJank/Android.bp
+++ b/tests/WindowAnimationJank/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "WindowAnimationJank",
srcs: ["src/**/*.java"],
diff --git a/tests/WindowInsetsTests/Android.bp b/tests/WindowInsetsTests/Android.bp
index 7272152dc257..b1f4819fc0bb 100644
--- a/tests/WindowInsetsTests/Android.bp
+++ b/tests/WindowInsetsTests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "WindowInsetsTests",
srcs: ["src/**/*.java"],
@@ -24,4 +33,3 @@ android_test {
"com.google.android.material_material",
],
}
-
diff --git a/tests/appwidgets/AppWidgetHostTest/Android.bp b/tests/appwidgets/AppWidgetHostTest/Android.bp
index 24b76136c045..a3838e5405d1 100644
--- a/tests/appwidgets/AppWidgetHostTest/Android.bp
+++ b/tests/appwidgets/AppWidgetHostTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "AppWidgetHostTest",
srcs: ["**/*.java"],
diff --git a/tests/appwidgets/AppWidgetProviderTest/Android.bp b/tests/appwidgets/AppWidgetProviderTest/Android.bp
index a1a599177a01..a9ee7ad93663 100644
--- a/tests/appwidgets/AppWidgetProviderTest/Android.bp
+++ b/tests/appwidgets/AppWidgetProviderTest/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "AppWidgetProvider",
srcs: ["**/*.java"],
diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk
index e9618300fc48..9b155c930871 100644
--- a/tests/backup/Android.mk
+++ b/tests/backup/Android.mk
@@ -24,6 +24,9 @@ LOCAL_SRC_FILES := \
LOCAL_CFLAGS := -Wall -Werror
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := backup_helper_test
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_SHARED_LIBRARIES := libandroidfw libutils
diff --git a/tests/benchmarks/Android.bp b/tests/benchmarks/Android.bp
index f16ddb9bc68e..f87ca2ef928b 100644
--- a/tests/benchmarks/Android.bp
+++ b/tests/benchmarks/Android.bp
@@ -15,6 +15,15 @@
// build framework base core benchmarks
// ============================================================
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "networkStatsFactory-benchmarks",
installable: true,
diff --git a/tests/libs-permissions/Android.bp b/tests/libs-permissions/Android.bp
index 66a1f83dc308..a8ce8a4cd956 100644
--- a/tests/libs-permissions/Android.bp
+++ b/tests/libs-permissions/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "com.android.test.libs.product",
installable: true,
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index ffde68eab578..81224957b2c7 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -1,6 +1,15 @@
//########################################################################
// Build FrameworksNetTests package
//########################################################################
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_defaults {
name: "FrameworksNetTests-jni-defaults",
jni_libs: [
diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml
index 6bed5a80d129..4c60ccf60615 100644
--- a/tests/net/AndroidManifest.xml
+++ b/tests/net/AndroidManifest.xml
@@ -48,6 +48,7 @@
<uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
<uses-permission android:name="android.permission.NETWORK_FACTORY" />
<uses-permission android:name="android.permission.NETWORK_STATS_PROVIDER" />
+ <uses-permission android:name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp
index c271f49ee537..babb81c5fa34 100644
--- a/tests/net/common/Android.bp
+++ b/tests/net/common/Android.bp
@@ -16,6 +16,15 @@
// Tests in this folder are included both in unit tests and CTS.
// They must be fast and stable, and exercise public or test APIs.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "FrameworksNetCommonTests",
srcs: ["java/**/*.java", "java/**/*.kt"],
diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
index b2bcfeb9019d..ad5bbf220d57 100644
--- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt
+++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
@@ -54,12 +54,26 @@ class CaptivePortalDataTest {
}
.build()
+ private val dataFromPasspoint = CaptivePortalData.Builder()
+ .setUserPortalUrl(Uri.parse("https://tc.example.com/passpoint"),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
+ .setVenueInfoUrl(Uri.parse("https://venue.example.com/passpoint"),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
+ .setCaptive(true)
+ .apply {
+ if (SdkLevel.isAtLeastS()) {
+ setVenueFriendlyName("venue friendly name")
+ }
+ }
+ .build()
+
private fun makeBuilder() = CaptivePortalData.Builder(data)
@Test
fun testParcelUnparcel() {
- val fieldCount = if (SdkLevel.isAtLeastS()) 8 else 7
+ val fieldCount = if (SdkLevel.isAtLeastS()) 10 else 7
assertParcelSane(data, fieldCount)
+ assertParcelSane(dataFromPasspoint, fieldCount)
assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build())
assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build())
@@ -83,6 +97,27 @@ class CaptivePortalDataTest {
assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") }
assertNotEqualsAfterChange { it.setVenueFriendlyName(null) }
}
+
+ assertEquals(dataFromPasspoint, CaptivePortalData.Builder(dataFromPasspoint).build())
+ assertNotEqualsAfterChange { it.setUserPortalUrl(
+ Uri.parse("https://tc.example.com/passpoint")) }
+ assertNotEqualsAfterChange { it.setUserPortalUrl(
+ Uri.parse("https://tc.example.com/passpoint"),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) }
+ assertNotEqualsAfterChange { it.setUserPortalUrl(
+ Uri.parse("https://tc.example.com/other"),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) }
+ assertNotEqualsAfterChange { it.setUserPortalUrl(
+ Uri.parse("https://tc.example.com/passpoint"),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) }
+ assertNotEqualsAfterChange { it.setVenueInfoUrl(
+ Uri.parse("https://venue.example.com/passpoint")) }
+ assertNotEqualsAfterChange { it.setVenueInfoUrl(
+ Uri.parse("https://venue.example.com/other"),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) }
+ assertNotEqualsAfterChange { it.setVenueInfoUrl(
+ Uri.parse("https://venue.example.com/passpoint"),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) }
}
@Test
@@ -130,6 +165,22 @@ class CaptivePortalDataTest {
assertEquals("venue friendly name", data.venueFriendlyName)
}
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ fun testGetVenueInfoUrlSource() {
+ assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER,
+ data.venueInfoUrlSource)
+ assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT,
+ dataFromPasspoint.venueInfoUrlSource)
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ fun testGetUserPortalUrlSource() {
+ assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER,
+ data.userPortalUrlSource)
+ assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT,
+ dataFromPasspoint.userPortalUrlSource)
+ }
+
private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) =
CaptivePortalData.Builder(this).apply { mutator(this) }.build()
diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
index d232a507454d..fd29a9539de8 100644
--- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
+++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
@@ -40,7 +40,7 @@ import java.util.Map;
@SmallTest
public class OemNetworkPreferencesTest {
- private static final int TEST_PREF = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_DEFAULT;
+ private static final int TEST_PREF = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED;
private static final String TEST_PACKAGE = "com.google.apps.contacts";
private final OemNetworkPreferences.Builder mBuilder = new OemNetworkPreferences.Builder();
@@ -54,7 +54,7 @@ public class OemNetworkPreferencesTest {
@Test
public void testBuilderRemoveNetworkPreferenceRequiresNonNullPackageName() {
assertThrows(NullPointerException.class,
- () -> mBuilder.removeNetworkPreference(null));
+ () -> mBuilder.clearNetworkPreference(null));
}
@Test
@@ -129,7 +129,7 @@ public class OemNetworkPreferencesTest {
assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
- mBuilder.removeNetworkPreference(TEST_PACKAGE);
+ mBuilder.clearNetworkPreference(TEST_PACKAGE);
networkPreferences = mBuilder.build().getNetworkPreferences();
assertFalse(networkPreferences.containsKey(TEST_PACKAGE));
diff --git a/tests/net/deflake/Android.bp b/tests/net/deflake/Android.bp
index b1b017131c64..58ece37ef647 100644
--- a/tests/net/deflake/Android.bp
+++ b/tests/net/deflake/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test_host {
name: "FrameworksNetDeflakeTest",
srcs: ["src/**/*.kt"],
diff --git a/tests/net/integration/Android.bp b/tests/net/integration/Android.bp
index 69742b9bf7b2..4d1e3371e0a7 100644
--- a/tests/net/integration/Android.bp
+++ b/tests/net/integration/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksNetIntegrationTests",
platform_apis: true,
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index c2fddf3d9e82..6a09b0237a38 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -35,6 +35,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
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 org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -329,6 +330,9 @@ public class ConnectivityManagerTest {
mustFail(() -> { manager.registerDefaultNetworkCallback(null, handler); });
mustFail(() -> { manager.registerDefaultNetworkCallback(callback, null); });
+ mustFail(() -> { manager.registerSystemDefaultNetworkCallback(null, handler); });
+ mustFail(() -> { manager.registerSystemDefaultNetworkCallback(callback, null); });
+
mustFail(() -> { manager.unregisterNetworkCallback(nullCallback); });
mustFail(() -> { manager.unregisterNetworkCallback(nullIntent); });
mustFail(() -> { manager.releaseNetworkRequest(nullIntent); });
@@ -345,15 +349,17 @@ public class ConnectivityManagerTest {
@Test
public void testRequestType() throws Exception {
final String testPkgName = "MyPackage";
+ final String testAttributionTag = "MyTag";
final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
when(mCtx.getOpPackageName()).thenReturn(testPkgName);
+ when(mCtx.getAttributionTag()).thenReturn(testAttributionTag);
final NetworkRequest request = makeRequest(1);
final NetworkCallback callback = new ConnectivityManager.NetworkCallback();
manager.requestNetwork(request, callback);
verify(mService).requestNetwork(eq(request.networkCapabilities),
eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
- eq(testPkgName), eq(null));
+ eq(testPkgName), eq(testAttributionTag));
reset(mService);
// Verify that register network callback does not calls requestNetwork at all.
@@ -361,19 +367,26 @@ public class ConnectivityManagerTest {
verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(),
anyInt(), any(), any());
verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(),
- eq(testPkgName));
+ 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(testPkgName), eq(null));
+ eq(testPkgName), eq(testAttributionTag));
reset(mService);
manager.requestBackgroundNetwork(request, null, callback);
verify(mService).requestNetwork(eq(request.networkCapabilities),
eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
- eq(testPkgName), eq(null));
+ 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(testPkgName), eq(testAttributionTag));
reset(mService);
}
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
index 91fcbc0fd5d7..b39555d15dcb 100644
--- a/tests/net/java/android/net/NetworkTemplateTest.kt
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -35,7 +35,6 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import kotlin.test.assertFalse
@@ -60,16 +59,13 @@ class NetworkTemplateTest {
subscriberId: String? = null,
ssid: String? = null
): NetworkState {
- val info = mock(NetworkInfo::class.java)
- doReturn(type).`when`(info).type
- doReturn(NetworkInfo.State.CONNECTED).`when`(info).state
val lp = LinkProperties()
val caps = NetworkCapabilities().apply {
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false)
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true)
setSSID(ssid)
}
- return NetworkState(info, lp, caps, mock(Network::class.java), subscriberId, ssid)
+ return NetworkState(type, lp, caps, mock(Network::class.java), subscriberId)
}
private fun NetworkTemplate.assertMatches(ident: NetworkIdentity) =
diff --git a/tests/net/java/android/net/VpnManagerTest.java b/tests/net/java/android/net/VpnManagerTest.java
index 95a794235a2e..c548e30761c9 100644
--- a/tests/net/java/android/net/VpnManagerTest.java
+++ b/tests/net/java/android/net/VpnManagerTest.java
@@ -49,7 +49,7 @@ public class VpnManagerTest {
private static final String IDENTITY_STRING = "Identity";
private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
- private IConnectivityManager mMockCs;
+ private IVpnManager mMockService;
private VpnManager mVpnManager;
private final MockContext mMockContext =
new MockContext() {
@@ -61,24 +61,26 @@ public class VpnManagerTest {
@Before
public void setUp() throws Exception {
- mMockCs = mock(IConnectivityManager.class);
- mVpnManager = new VpnManager(mMockContext, mMockCs);
+ mMockService = mock(IVpnManager.class);
+ mVpnManager = new VpnManager(mMockContext, mMockService);
}
@Test
public void testProvisionVpnProfilePreconsented() throws Exception {
final PlatformVpnProfile profile = getPlatformVpnProfile();
- when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(true);
+ when(mMockService.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME)))
+ .thenReturn(true);
// Expect there to be no intent returned, as consent has already been granted.
assertNull(mVpnManager.provisionVpnProfile(profile));
- verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
+ verify(mMockService).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
}
@Test
public void testProvisionVpnProfileNeedsConsent() throws Exception {
final PlatformVpnProfile profile = getPlatformVpnProfile();
- when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(false);
+ when(mMockService.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME)))
+ .thenReturn(false);
// Expect intent to be returned, as consent has not already been granted.
final Intent intent = mVpnManager.provisionVpnProfile(profile);
@@ -88,25 +90,25 @@ public class VpnManagerTest {
ComponentName.unflattenFromString(
"com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog");
assertEquals(expectedComponentName, intent.getComponent());
- verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
+ verify(mMockService).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
}
@Test
public void testDeleteProvisionedVpnProfile() throws Exception {
mVpnManager.deleteProvisionedVpnProfile();
- verify(mMockCs).deleteVpnProfile(eq(PKG_NAME));
+ verify(mMockService).deleteVpnProfile(eq(PKG_NAME));
}
@Test
public void testStartProvisionedVpnProfile() throws Exception {
mVpnManager.startProvisionedVpnProfile();
- verify(mMockCs).startVpnProfile(eq(PKG_NAME));
+ verify(mMockService).startVpnProfile(eq(PKG_NAME));
}
@Test
public void testStopProvisionedVpnProfile() throws Exception {
mVpnManager.stopProvisionedVpnProfile();
- verify(mMockCs).stopVpnProfile(eq(PKG_NAME));
+ verify(mMockService).stopVpnProfile(eq(PKG_NAME));
}
private Ikev2VpnProfile getPlatformVpnProfile() throws Exception {
diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/tests/net/java/android/net/VpnTransportInfoTest.java
new file mode 100644
index 000000000000..866f38c84333
--- /dev/null
+++ b/tests/net/java/android/net/VpnTransportInfoTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static com.android.testutils.ParcelUtils.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+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 VpnTransportInfoTest {
+
+ @Test
+ public void testParceling() {
+ VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
+ assertParcelSane(v, 1 /* fieldCount */);
+ }
+
+ @Test
+ public void testEqualsAndHashCode() {
+ VpnTransportInfo v1 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
+ VpnTransportInfo v2 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE);
+ VpnTransportInfo v3 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
+ assertNotEquals(v1, v2);
+ assertEquals(v1, v3);
+ assertEquals(v1.hashCode(), v3.hashCode());
+ }
+} \ No newline at end of file
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 0674138044ff..24e559225027 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -66,6 +66,8 @@ 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.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL;
@@ -82,6 +84,11 @@ 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.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;
+import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
+import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED;
import static android.net.RouteInfo.RTN_UNREACHABLE;
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.IPPROTO_TCP;
@@ -169,6 +176,7 @@ import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
import android.net.INetworkStatsService;
+import android.net.IOnSetOemNetworkPreferenceListener;
import android.net.IQosCallback;
import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel;
@@ -192,6 +200,7 @@ import android.net.NetworkStack;
import android.net.NetworkStackClient;
import android.net.NetworkState;
import android.net.NetworkTestResultParcelable;
+import android.net.OemNetworkPreferences;
import android.net.ProxyInfo;
import android.net.QosCallbackException;
import android.net.QosFilter;
@@ -200,11 +209,13 @@ import android.net.ResolverParamsParcel;
import android.net.RouteInfo;
import android.net.RouteInfoParcel;
import android.net.SocketKeepalive;
+import android.net.TransportInfo;
import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
import android.net.VpnManager;
+import android.net.VpnTransportInfo;
import android.net.metrics.IpConnectivityLog;
import android.net.shared.NetworkMonitorUtils;
import android.net.shared.PrivateDnsConfig;
@@ -298,6 +309,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -331,11 +343,12 @@ public class ConnectivityServiceTest {
private static final String TAG = "ConnectivityServiceTest";
private static final int TIMEOUT_MS = 500;
- private static final int TEST_LINGER_DELAY_MS = 300;
- // Chosen to be less than the linger timeout. This ensures that we can distinguish between a
- // LOST callback that arrives immediately and a LOST callback that arrives after the linger
- // timeout. For this, our assertions should run fast enough to leave less than
- // (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
+ private static final int TEST_LINGER_DELAY_MS = 400;
+ private static final int TEST_NASCENT_DELAY_MS = 300;
+ // Chosen to be less than the linger and nascent timeout. This ensures that we can distinguish
+ // between a LOST callback that arrives immediately and a LOST callback that arrives after
+ // the linger/nascent timeout. For this, our assertions should run fast enough to leave
+ // less than (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
// supposedly fired, and the time we call expectCallback.
private static final int TEST_CALLBACK_TIMEOUT_MS = 250;
// Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to
@@ -347,6 +360,7 @@ public class ConnectivityServiceTest {
private static final long TIMESTAMP = 1234L;
private static final int NET_ID = 110;
+ private static final int OEM_PREF_ANY_NET_ID = -1;
// Set a non-zero value to verify the flow to set tcp init rwnd value.
private static final int TEST_TCP_INIT_RWND = 60;
@@ -356,17 +370,26 @@ public class ConnectivityServiceTest {
private static final String WIFI_WOL_IFNAME = "test_wlan_wol";
private static final String VPN_IFNAME = "tun10042";
private static final String TEST_PACKAGE_NAME = "com.android.test.package";
+ private static final int TEST_PACKAGE_UID = 123;
private static final String ALWAYS_ON_PACKAGE = "com.android.test.alwaysonvpn";
private static final String INTERFACE_NAME = "interface";
- private static final String TEST_VENUE_URL_NA = "https://android.com/";
+ private static final String TEST_VENUE_URL_NA_PASSPOINT = "https://android.com/";
+ private static final String TEST_VENUE_URL_NA_OTHER = "https://example.com/";
+ private static final String TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT =
+ "https://android.com/terms/";
+ private static final String TEST_TERMS_AND_CONDITIONS_URL_NA_OTHER =
+ "https://example.com/terms/";
private static final String TEST_VENUE_URL_CAPPORT = "https://android.com/capport/";
+ private static final String TEST_USER_PORTAL_API_URL_CAPPORT =
+ "https://android.com/user/api/capport/";
private static final String TEST_FRIENDLY_NAME = "Network friendly name";
private static final String TEST_REDIRECT_URL = "http://example.com/firstPath";
private MockContext mServiceContext;
private HandlerThread mCsHandlerThread;
+ private HandlerThread mVMSHandlerThread;
private ConnectivityService.Dependencies mDeps;
private ConnectivityService mService;
private WrappedConnectivityManager mCm;
@@ -381,6 +404,7 @@ public class ConnectivityServiceTest {
private TestNetIdManager mNetIdManager;
private QosCallbackMockHelper mQosCallbackMockHelper;
private QosCallbackTracker mQosCallbackTracker;
+ private VpnManagerService mVpnManagerService;
// State variables required to emulate NetworkPolicyManagerService behaviour.
private int mUidRules = RULE_NONE;
@@ -975,10 +999,12 @@ public class ConnectivityServiceTest {
// Used to collect the networks requests managed by this factory. This is a duplicate of
// the internal information stored in the NetworkFactory (which is private).
private SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
+ private final HandlerThread mHandlerSendingRequests;
public MockNetworkFactory(Looper looper, Context context, String logTag,
- NetworkCapabilities filter) {
+ NetworkCapabilities filter, HandlerThread threadSendingRequests) {
super(looper, context, logTag, filter);
+ mHandlerSendingRequests = threadSendingRequests;
}
public int getMyRequestCount() {
@@ -1032,7 +1058,8 @@ public class ConnectivityServiceTest {
public void terminate() {
super.terminate();
// Make sure there are no remaining requests unaccounted for.
- assertNull(mRequestHistory.poll(TIMEOUT_MS, r -> true));
+ HandlerUtils.waitForIdle(mHandlerSendingRequests, TIMEOUT_MS);
+ assertNull(mRequestHistory.poll(0, r -> true));
}
// Trigger releasing the request as unfulfillable
@@ -1109,7 +1136,7 @@ public class ConnectivityServiceTest {
}
@Override
- public int getActiveAppVpnType() {
+ public int getActiveVpnType() {
return mVpnType;
}
@@ -1122,10 +1149,12 @@ public class ConnectivityServiceTest {
private void registerAgent(boolean isAlwaysMetered, Set<UidRange> uids, LinkProperties lp)
throws Exception {
if (mAgentRegistered) throw new IllegalStateException("already registered");
+ updateState(NetworkInfo.DetailedState.CONNECTING, "registerAgent");
mConfig = new VpnConfig();
setUids(uids);
if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
mInterface = VPN_IFNAME;
+ mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType()));
mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
mNetworkCapabilities);
mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
@@ -1251,14 +1280,52 @@ public class ConnectivityServiceTest {
r -> new UidRangeParcel(r.start, r.stop)).toArray(UidRangeParcel[]::new);
}
+ private VpnManagerService makeVpnManagerService() {
+ final VpnManagerService.Dependencies deps = new VpnManagerService.Dependencies() {
+ public int getCallingUid() {
+ return mDeps.getCallingUid();
+ }
+
+ public HandlerThread makeHandlerThread() {
+ return mVMSHandlerThread;
+ }
+
+ public KeyStore getKeyStore() {
+ return mKeyStore;
+ }
+
+ public INetd getNetd() {
+ return mMockNetd;
+ }
+
+ public INetworkManagementService getINetworkManagementService() {
+ return mNetworkManagementService;
+ }
+ };
+ return new VpnManagerService(mServiceContext, deps);
+ }
+
+ private void assertVpnTransportInfo(NetworkCapabilities nc, int type) {
+ assertNotNull(nc);
+ final TransportInfo ti = nc.getTransportInfo();
+ assertTrue("VPN TransportInfo is not a VpnTransportInfo: " + ti,
+ ti instanceof VpnTransportInfo);
+ assertEquals(type, ((VpnTransportInfo) ti).type);
+
+ }
+
+ private void processBroadcastForVpn(Intent intent) {
+ mServiceContext.sendBroadcast(intent);
+ HandlerUtils.waitForIdle(mVMSHandlerThread, TIMEOUT_MS);
+ waitForIdle();
+ }
+
private void mockVpn(int uid) {
- synchronized (mService.mVpns) {
+ synchronized (mVpnManagerService.mVpns) {
int userId = UserHandle.getUserId(uid);
mMockVpn = new MockVpn(userId);
- // This has no effect unless the VPN is actually connected, because things like
- // getActiveNetworkForUidInternal call getNetworkAgentInfoForNetId on the VPN
- // netId, and check if that network is actually connected.
- mService.mVpns.put(userId, mMockVpn);
+ // Every running user always has a Vpn in the mVpns array, even if no VPN is running.
+ mVpnManagerService.mVpns.put(userId, mMockVpn);
}
}
@@ -1376,6 +1443,7 @@ public class ConnectivityServiceTest {
FakeSettingsProvider.clearSettingsProvider();
mServiceContext = new MockContext(InstrumentationRegistry.getContext(),
new FakeSettingsProvider());
+ mServiceContext.setUseRegisteredHandlers(true);
LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
LocalServices.addService(
NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
@@ -1385,6 +1453,7 @@ public class ConnectivityServiceTest {
initAlarmManager(mAlarmManager, mAlarmManagerThread.getThreadHandler());
mCsHandlerThread = new HandlerThread("TestConnectivityService");
+ mVMSHandlerThread = new HandlerThread("TestVpnManagerService");
mDeps = makeDependencies();
returnRealCallingUid();
mService = new ConnectivityService(mServiceContext,
@@ -1395,6 +1464,7 @@ public class ConnectivityServiceTest {
mMockNetd,
mDeps);
mService.mLingerDelayMs = TEST_LINGER_DELAY_MS;
+ mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS;
verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any());
final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
@@ -1406,6 +1476,8 @@ public class ConnectivityServiceTest {
// getSystemService() correctly.
mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
mService.systemReadyInternal();
+ mVpnManagerService = makeVpnManagerService();
+ mVpnManagerService.systemReady();
mockVpn(Process.myUid());
mCm.bindProcessToNetwork(null);
mQosCallbackTracker = mock(QosCallbackTracker.class);
@@ -1433,7 +1505,6 @@ public class ConnectivityServiceTest {
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
- doReturn(mKeyStore).when(deps).getKeyStore();
doAnswer(inv -> {
mPolicyTracker = new WrappedMultinetworkPolicyTracker(
inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
@@ -1546,10 +1617,13 @@ public class ConnectivityServiceTest {
}
switch (transport) {
case TRANSPORT_WIFI:
- assertEquals(mCm.getActiveNetwork(), mWiFiNetworkAgent.getNetwork());
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
break;
case TRANSPORT_CELLULAR:
- assertEquals(mCm.getActiveNetwork(), mCellNetworkAgent.getNetwork());
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ break;
+ case TRANSPORT_ETHERNET:
+ assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
break;
default:
break;
@@ -1558,6 +1632,7 @@ public class ConnectivityServiceTest {
assertNotNull(mCm.getNetworkInfo(mCm.getActiveNetwork()));
assertEquals(transportToLegacyType(transport),
mCm.getNetworkInfo(mCm.getActiveNetwork()).getType());
+ assertNotNull(mCm.getActiveNetworkInfoForUid(Process.myUid()));
// Test getNetworkCapabilities(Network)
assertNotNull(mCm.getNetworkCapabilities(mCm.getActiveNetwork()));
assertTrue(mCm.getNetworkCapabilities(mCm.getActiveNetwork()).hasTransport(transport));
@@ -1737,6 +1812,108 @@ public class ConnectivityServiceTest {
verifyNoNetwork();
}
+ /**
+ * Verify a newly created network will be inactive instead of torn down even if no one is
+ * requesting.
+ */
+ @Test
+ public void testNewNetworkInactive() throws Exception {
+ // Create a callback that monitoring the testing network.
+ final TestNetworkCallback listenCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), listenCallback);
+
+ // 1. Create a network that is not requested by anyone, and does not satisfy any of the
+ // default requests. Verify that the network will be inactive instead of torn down.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connectWithoutInternet();
+ listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ listenCallback.assertNoCallback();
+
+ // Verify that the network will be torn down after nascent expiry. A small period of time
+ // is added in case of flakiness.
+ final int nascentTimeoutMs =
+ mService.mNascentDelayMs + mService.mNascentDelayMs / 4;
+ listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, nascentTimeoutMs);
+
+ // 2. Create a network that is satisfied by a request comes later.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connectWithoutInternet();
+ listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ final TestNetworkCallback wifiCallback = new TestNetworkCallback();
+ mCm.requestNetwork(wifiRequest, wifiCallback);
+ wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+ // Verify that the network will be kept since the request is still satisfied. And is able
+ // to get disconnected as usual if the request is released after the nascent timer expires.
+ listenCallback.assertNoCallback(nascentTimeoutMs);
+ mCm.unregisterNetworkCallback(wifiCallback);
+ listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+ // 3. Create a network that is satisfied by a request comes later.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connectWithoutInternet();
+ listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ mCm.requestNetwork(wifiRequest, wifiCallback);
+ wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+ // Verify that the network will still be torn down after the request gets removed.
+ mCm.unregisterNetworkCallback(wifiCallback);
+ listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+ // There is no need to ensure that LOSING is never sent in the common case that the
+ // network immediately satisfies a request that was already present, because it is already
+ // verified anywhere whenever {@code TestNetworkCallback#expectAvailable*} is called.
+
+ mCm.unregisterNetworkCallback(listenCallback);
+ }
+
+ /**
+ * Verify a newly created network will be inactive and switch to background if only background
+ * request is satisfied.
+ */
+ @Test
+ public void testNewNetworkInactive_bgNetwork() throws Exception {
+ // Create a callback that monitoring the wifi network.
+ final TestNetworkCallback wifiListenCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build(), wifiListenCallback);
+
+ // Create callbacks that can monitor background and foreground mobile networks.
+ // This is done by granting using background networks permission before registration. Thus,
+ // the service will not add {@code NET_CAPABILITY_FOREGROUND} by default.
+ grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid());
+ final TestNetworkCallback bgMobileListenCallback = new TestNetworkCallback();
+ final TestNetworkCallback fgMobileListenCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build(), bgMobileListenCallback);
+ mCm.registerNetworkCallback(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_FOREGROUND).build(), fgMobileListenCallback);
+
+ // Connect wifi, which satisfies default request.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ wifiListenCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+
+ // Connect a cellular network, verify that satisfies only the background callback.
+ setAlwaysOnNetworks(true);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ bgMobileListenCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ fgMobileListenCallback.assertNoCallback();
+ assertFalse(isForegroundNetwork(mCellNetworkAgent));
+
+ mCellNetworkAgent.disconnect();
+ bgMobileListenCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ fgMobileListenCallback.assertNoCallback();
+
+ mCm.unregisterNetworkCallback(wifiListenCallback);
+ mCm.unregisterNetworkCallback(bgMobileListenCallback);
+ mCm.unregisterNetworkCallback(fgMobileListenCallback);
+ }
+
@Test
public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
// Test bringing up unvalidated WiFi
@@ -1993,6 +2170,24 @@ public class ConnectivityServiceTest {
}
}
+ static void expectOnLost(TestNetworkAgentWrapper network, TestNetworkCallback ... callbacks) {
+ for (TestNetworkCallback c : callbacks) {
+ c.expectCallback(CallbackEntry.LOST, network);
+ }
+ }
+
+ static void expectAvailableCallbacksUnvalidatedWithSpecifier(TestNetworkAgentWrapper network,
+ NetworkSpecifier specifier, TestNetworkCallback ... callbacks) {
+ for (TestNetworkCallback c : callbacks) {
+ c.expectCallback(CallbackEntry.AVAILABLE, network);
+ c.expectCapabilitiesThat(network, (nc) ->
+ !nc.hasCapability(NET_CAPABILITY_VALIDATED)
+ && Objects.equals(specifier, nc.getNetworkSpecifier()));
+ c.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, network);
+ c.expectCallback(CallbackEntry.BLOCKED_STATUS, network);
+ }
+ }
+
@Test
public void testStateChangeNetworkCallbacks() throws Exception {
final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
@@ -2599,7 +2794,7 @@ public class ConnectivityServiceTest {
final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests");
handlerThread.start();
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
- mServiceContext, "testFactory", filter);
+ mServiceContext, "testFactory", filter, mCsHandlerThread);
testFactory.setScoreFilter(40);
ConditionVariable cv = testFactory.getNetworkStartedCV();
testFactory.register();
@@ -2707,7 +2902,7 @@ public class ConnectivityServiceTest {
// does not crash.
for (int i = 0; i < 100; i++) {
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
- mServiceContext, "testFactory", filter);
+ mServiceContext, "testFactory", filter, mCsHandlerThread);
// Register the factory and don't be surprised when the default request arrives.
testFactory.register();
testFactory.expectRequestAdd();
@@ -3171,39 +3366,68 @@ public class ConnectivityServiceTest {
}
private class CaptivePortalTestData {
- CaptivePortalTestData(CaptivePortalData naData, CaptivePortalData capportData,
- CaptivePortalData expectedMergedData) {
- mNaData = naData;
+ CaptivePortalTestData(CaptivePortalData naPasspointData, CaptivePortalData capportData,
+ CaptivePortalData naOtherData, CaptivePortalData expectedMergedPasspointData,
+ CaptivePortalData expectedMergedOtherData) {
+ mNaPasspointData = naPasspointData;
mCapportData = capportData;
- mExpectedMergedData = expectedMergedData;
+ mNaOtherData = naOtherData;
+ mExpectedMergedPasspointData = expectedMergedPasspointData;
+ mExpectedMergedOtherData = expectedMergedOtherData;
}
- public final CaptivePortalData mNaData;
+ public final CaptivePortalData mNaPasspointData;
public final CaptivePortalData mCapportData;
- public final CaptivePortalData mExpectedMergedData;
+ public final CaptivePortalData mNaOtherData;
+ public final CaptivePortalData mExpectedMergedPasspointData;
+ public final CaptivePortalData mExpectedMergedOtherData;
+
}
private CaptivePortalTestData setupCaptivePortalData() {
final CaptivePortalData capportData = new CaptivePortalData.Builder()
.setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL))
.setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT))
+ .setUserPortalUrl(Uri.parse(TEST_USER_PORTAL_API_URL_CAPPORT))
.setExpiryTime(1000000L)
.setBytesRemaining(12345L)
.build();
- final CaptivePortalData naData = new CaptivePortalData.Builder()
+ final CaptivePortalData naPasspointData = new CaptivePortalData.Builder()
+ .setBytesRemaining(80802L)
+ .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_PASSPOINT),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
+ .setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
+ .setVenueFriendlyName(TEST_FRIENDLY_NAME).build();
+
+ final CaptivePortalData naOtherData = new CaptivePortalData.Builder()
.setBytesRemaining(80802L)
- .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA))
+ .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_OTHER),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER)
+ .setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_OTHER),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER)
.setVenueFriendlyName(TEST_FRIENDLY_NAME).build();
- final CaptivePortalData expectedMergedData = new CaptivePortalData.Builder()
+ final CaptivePortalData expectedMergedPasspointData = new CaptivePortalData.Builder()
.setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL))
.setBytesRemaining(12345L)
.setExpiryTime(1000000L)
- .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA))
+ .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_PASSPOINT),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
+ .setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
.setVenueFriendlyName(TEST_FRIENDLY_NAME).build();
- return new CaptivePortalTestData(naData, capportData, expectedMergedData);
+ final CaptivePortalData expectedMergedOtherData = new CaptivePortalData.Builder()
+ .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL))
+ .setBytesRemaining(12345L)
+ .setExpiryTime(1000000L)
+ .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT))
+ .setUserPortalUrl(Uri.parse(TEST_USER_PORTAL_API_URL_CAPPORT))
+ .setVenueFriendlyName(TEST_FRIENDLY_NAME).build();
+ return new CaptivePortalTestData(naPasspointData, capportData, naOtherData,
+ expectedMergedPasspointData, expectedMergedOtherData);
}
@Test
@@ -3217,15 +3441,26 @@ public class ConnectivityServiceTest {
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData()));
- // Venue URL and friendly name from Network agent, confirm that API data gets precedence
- // on the bytes remaining.
+ // Venue URL, T&C URL and friendly name from Network agent with Passpoint source, confirm
+ // that API data gets precedence on the bytes remaining.
final LinkProperties linkProperties = new LinkProperties();
- linkProperties.setCaptivePortalData(captivePortalTestData.mNaData);
+ linkProperties.setCaptivePortalData(captivePortalTestData.mNaPasspointData);
mWiFiNetworkAgent.sendLinkProperties(linkProperties);
// Make sure that the capport data is merged
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
- lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData()));
+ lp -> captivePortalTestData.mExpectedMergedPasspointData
+ .equals(lp.getCaptivePortalData()));
+
+ // Now send this information from non-Passpoint source, confirm that Capport data takes
+ // precedence
+ linkProperties.setCaptivePortalData(captivePortalTestData.mNaOtherData);
+ mWiFiNetworkAgent.sendLinkProperties(linkProperties);
+
+ // Make sure that the capport data is merged
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mExpectedMergedOtherData
+ .equals(lp.getCaptivePortalData()));
// Create a new LP with no Network agent capport data
final LinkProperties newLps = new LinkProperties();
@@ -3242,12 +3477,12 @@ public class ConnectivityServiceTest {
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> lp.getCaptivePortalData() == null);
- newLps.setCaptivePortalData(captivePortalTestData.mNaData);
+ newLps.setCaptivePortalData(captivePortalTestData.mNaPasspointData);
mWiFiNetworkAgent.sendLinkProperties(newLps);
// Make sure that only the network agent capport data is available
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
- lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData()));
+ lp -> captivePortalTestData.mNaPasspointData.equals(lp.getCaptivePortalData()));
}
@Test
@@ -3258,12 +3493,12 @@ public class ConnectivityServiceTest {
// Venue URL and friendly name from Network agent, confirm that API data gets precedence
// on the bytes remaining.
final LinkProperties linkProperties = new LinkProperties();
- linkProperties.setCaptivePortalData(captivePortalTestData.mNaData);
+ linkProperties.setCaptivePortalData(captivePortalTestData.mNaPasspointData);
mWiFiNetworkAgent.sendLinkProperties(linkProperties);
// Make sure that the data is saved correctly
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
- lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData()));
+ lp -> captivePortalTestData.mNaPasspointData.equals(lp.getCaptivePortalData()));
// Expected merged data: Network agent data is preferred, and values that are not used by
// it are merged from capport data
@@ -3271,7 +3506,8 @@ public class ConnectivityServiceTest {
// Make sure that the Capport data is merged correctly
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
- lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData()));
+ lp -> captivePortalTestData.mExpectedMergedPasspointData.equals(
+ lp.getCaptivePortalData()));
// Now set the naData to null
linkProperties.setCaptivePortalData(null);
@@ -3282,6 +3518,32 @@ public class ConnectivityServiceTest {
lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData()));
}
+ @Test
+ public void testMergeCaptivePortalDataFromNetworkAgentOtherSourceFirstThenCapport()
+ throws Exception {
+ final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi();
+ final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData();
+
+ // Venue URL and friendly name from Network agent, confirm that API data gets precedence
+ // on the bytes remaining.
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setCaptivePortalData(captivePortalTestData.mNaOtherData);
+ mWiFiNetworkAgent.sendLinkProperties(linkProperties);
+
+ // Make sure that the data is saved correctly
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mNaOtherData.equals(lp.getCaptivePortalData()));
+
+ // Expected merged data: Network agent data is preferred, and values that are not used by
+ // it are merged from capport data
+ mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData);
+
+ // Make sure that the Capport data is merged correctly
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mExpectedMergedOtherData.equals(
+ lp.getCaptivePortalData()));
+ }
+
private NetworkRequest.Builder newWifiRequestBuilder() {
return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI);
}
@@ -3289,11 +3551,9 @@ public class ConnectivityServiceTest {
/**
* Verify request matching behavior with network specifiers.
*
- * Note: this test is somewhat problematic since it involves removing capabilities from
- * agents - i.e. agents rejecting requests which they previously accepted. This is flagged
- * as a WTF bug in
- * {@link ConnectivityService#mixInCapabilities(NetworkAgentInfo, NetworkCapabilities)} but
- * does work.
+ * This test does not check updating the specifier on a live network because the specifier is
+ * immutable and this triggers a WTF in
+ * {@link ConnectivityService#mixInCapabilities(NetworkAgentInfo, NetworkCapabilities)}.
*/
@Test
public void testNetworkSpecifier() throws Exception {
@@ -3378,60 +3638,49 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- cEmpty1.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- cEmpty2.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- cEmpty3.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- cEmpty4.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, null /* specifier */,
+ cEmpty1, cEmpty2, cEmpty3, cEmpty4);
assertNoCallbacks(cFoo, cBar);
+ mWiFiNetworkAgent.disconnect();
+ expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4);
+
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.setNetworkSpecifier(nsFoo);
- cFoo.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- for (TestNetworkCallback c: emptyCallbacks) {
- c.expectCapabilitiesThat(mWiFiNetworkAgent,
- (caps) -> caps.getNetworkSpecifier().equals(nsFoo));
- }
- cFoo.expectCapabilitiesThat(mWiFiNetworkAgent,
- (caps) -> caps.getNetworkSpecifier().equals(nsFoo));
+ mWiFiNetworkAgent.connect(false);
+ expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, nsFoo,
+ cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo);
+ cBar.assertNoCallback();
assertEquals(nsFoo,
mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier());
- cFoo.assertNoCallback();
+ assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo);
+
+ mWiFiNetworkAgent.disconnect();
+ expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.setNetworkSpecifier(nsBar);
- cFoo.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
- cBar.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- for (TestNetworkCallback c: emptyCallbacks) {
- c.expectCapabilitiesThat(mWiFiNetworkAgent,
- (caps) -> caps.getNetworkSpecifier().equals(nsBar));
- }
- cBar.expectCapabilitiesThat(mWiFiNetworkAgent,
- (caps) -> caps.getNetworkSpecifier().equals(nsBar));
+ mWiFiNetworkAgent.connect(false);
+ expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, nsBar,
+ cEmpty1, cEmpty2, cEmpty3, cEmpty4, cBar);
+ cFoo.assertNoCallback();
assertEquals(nsBar,
mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier());
- cBar.assertNoCallback();
+ mWiFiNetworkAgent.disconnect();
+ expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4, cBar);
+ cFoo.assertNoCallback();
+
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.setNetworkSpecifier(new ConfidentialMatchAllNetworkSpecifier());
- cFoo.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- for (TestNetworkCallback c : emptyCallbacks) {
- c.expectCapabilitiesThat(mWiFiNetworkAgent,
- (caps) -> caps.getNetworkSpecifier() == null);
- }
- cFoo.expectCapabilitiesThat(mWiFiNetworkAgent,
- (caps) -> caps.getNetworkSpecifier() == null);
- cBar.expectCapabilitiesThat(mWiFiNetworkAgent,
- (caps) -> caps.getNetworkSpecifier() == null);
+ mWiFiNetworkAgent.connect(false);
+ expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, null /* specifier */,
+ cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo, cBar);
assertNull(
mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier());
- cFoo.assertNoCallback();
- cBar.assertNoCallback();
-
- mWiFiNetworkAgent.setNetworkSpecifier(null);
- cFoo.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
- cBar.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
- for (TestNetworkCallback c: emptyCallbacks) {
- c.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mWiFiNetworkAgent);
- }
- assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo, cBar);
+ mWiFiNetworkAgent.disconnect();
+ expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo, cBar);
}
/**
@@ -3534,10 +3783,19 @@ public class ConnectivityServiceTest {
@Test
public void testRegisterDefaultNetworkCallback() throws Exception {
+ // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback.
+ mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
+ PERMISSION_GRANTED);
+
final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultNetworkCallback);
defaultNetworkCallback.assertNoCallback();
+ final Handler handler = new Handler(ConnectivityThread.getInstanceLooper());
+ final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback();
+ mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback, handler);
+ systemDefaultCallback.assertNoCallback();
+
// Create a TRANSPORT_CELLULAR request to keep the mobile interface up
// whenever Wi-Fi is up. Without this, the mobile network agent is
// reaped before any other activity can take place.
@@ -3552,27 +3810,35 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ systemDefaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+ assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up wifi and expect CALLBACK_AVAILABLE.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
cellNetworkCallback.assertNoCallback();
defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ systemDefaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+ assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring down cell. Expect no default network callback, since it wasn't the default.
mCellNetworkAgent.disconnect();
cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
defaultNetworkCallback.assertNoCallback();
+ systemDefaultCallback.assertNoCallback();
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+ assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up cell. Expect no default network callback, since it won't be the default.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultNetworkCallback.assertNoCallback();
+ systemDefaultCallback.assertNoCallback();
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+ assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring down wifi. Expect the default network callback to notified of LOST wifi
// followed by AVAILABLE cell.
@@ -3580,19 +3846,25 @@ public class ConnectivityServiceTest {
cellNetworkCallback.assertNoCallback();
defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ systemDefaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ systemDefaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
mMockVpn.establishForMyUid();
assertUidRangesUpdatedForMyUid(true);
defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ systemDefaultCallback.assertNoCallback();
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+ assertEquals(null, systemDefaultCallback.getLastAvailableNetwork());
mMockVpn.disconnect();
defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+ systemDefaultCallback.assertNoCallback();
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
}
@@ -3658,6 +3930,24 @@ public class ConnectivityServiceTest {
mCm.unregisterNetworkCallback(cellNetworkCallback);
}
+ @Test
+ public void testRegisterSystemDefaultCallbackRequiresNetworkSettings() throws Exception {
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(false /* validated */);
+
+ final Handler handler = new Handler(ConnectivityThread.getInstanceLooper());
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ assertThrows(SecurityException.class,
+ () -> mCm.registerSystemDefaultNetworkCallback(callback, handler));
+ callback.assertNoCallback();
+
+ mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
+ PERMISSION_GRANTED);
+ mCm.registerSystemDefaultNetworkCallback(callback, handler);
+ callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ mCm.unregisterNetworkCallback(callback);
+ }
+
private void setCaptivePortalMode(int mode) {
ContentResolver cr = mServiceContext.getContentResolver();
Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, mode);
@@ -3849,7 +4139,7 @@ public class ConnectivityServiceTest {
.addTransportType(TRANSPORT_CELLULAR)
.addCapability(NET_CAPABILITY_INTERNET);
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
- mServiceContext, "testFactory", filter);
+ mServiceContext, "testFactory", filter, mCsHandlerThread);
testFactory.setScoreFilter(40);
// Register the factory and expect it to start looking for a network.
@@ -3894,8 +4184,10 @@ public class ConnectivityServiceTest {
setAlwaysOnNetworks(false);
testFactory.expectRequestRemove();
- // ... and cell data to be torn down.
- cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ // ... and cell data to be torn down after nascent network timeout.
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
+ mService.mNascentDelayMs + TEST_CALLBACK_TIMEOUT_MS);
+ waitForIdle();
assertLength(1, mCm.getAllNetworks());
} finally {
testFactory.terminate();
@@ -4195,7 +4487,7 @@ public class ConnectivityServiceTest {
.addTransportType(TRANSPORT_WIFI)
.addCapability(NET_CAPABILITY_INTERNET);
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
- mServiceContext, "testFactory", filter);
+ mServiceContext, "testFactory", filter, mCsHandlerThread);
testFactory.setScoreFilter(40);
// Register the factory and expect it to receive the default request.
@@ -5300,20 +5592,20 @@ public class ConnectivityServiceTest {
// MOBILE_IFNAME even though the default network is wifi.
// TODO: fix this to pass in the actual default network interface. Whether or not the VPN
// applies to the system server UID should not have any bearing on network stats.
- mService.setUnderlyingNetworksForVpn(onlyCell);
+ mMockVpn.setUnderlyingNetworks(onlyCell);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME});
reset(mStatsService);
- mService.setUnderlyingNetworksForVpn(cellAndWifi);
+ mMockVpn.setUnderlyingNetworks(cellAndWifi);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME, WIFI_IFNAME});
reset(mStatsService);
// Null underlying networks are ignored.
- mService.setUnderlyingNetworksForVpn(cellNullAndWifi);
+ mMockVpn.setUnderlyingNetworks(cellNullAndWifi);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME, WIFI_IFNAME});
@@ -5362,25 +5654,25 @@ public class ConnectivityServiceTest {
// is probably a performance improvement (though it's very unlikely that a VPN would declare
// no underlying networks).
// Also, for the same reason as above, the active interface passed in is null.
- mService.setUnderlyingNetworksForVpn(new Network[0]);
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
// Specifying only a null underlying network is the same as no networks.
- mService.setUnderlyingNetworksForVpn(onlyNull);
+ mMockVpn.setUnderlyingNetworks(onlyNull);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
// Specifying networks that are all disconnected is the same as specifying no networks.
- mService.setUnderlyingNetworksForVpn(onlyCell);
+ mMockVpn.setUnderlyingNetworks(onlyCell);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
// Passing in null again means follow the default network again.
- mService.setUnderlyingNetworksForVpn(null);
+ mMockVpn.setUnderlyingNetworks(null);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{WIFI_IFNAME});
@@ -5855,7 +6147,7 @@ public class ConnectivityServiceTest {
mMockVpn.establishForMyUid(false, true, false);
assertUidRangesUpdatedForMyUid(true);
final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
- mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork});
+ mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
callback.expectAvailableCallbacksUnvalidated(mMockVpn);
assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_VPN));
@@ -6015,6 +6307,10 @@ public class ConnectivityServiceTest {
@Test
public void testVpnNetworkActive() throws Exception {
+ // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback.
+ mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
+ PERMISSION_GRANTED);
+
final int uid = Process.myUid();
final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
@@ -6022,6 +6318,7 @@ public class ConnectivityServiceTest {
final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback();
final NetworkRequest genericNotVpnRequest = new NetworkRequest.Builder().build();
final NetworkRequest genericRequest = new NetworkRequest.Builder()
.removeCapability(NET_CAPABILITY_NOT_VPN).build();
@@ -6035,6 +6332,8 @@ public class ConnectivityServiceTest {
mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
mCm.registerDefaultNetworkCallback(defaultCallback);
+ mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback,
+ new Handler(ConnectivityThread.getInstanceLooper()));
defaultCallback.assertNoCallback();
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -6044,12 +6343,13 @@ public class ConnectivityServiceTest {
genericNotVpnNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ systemDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
vpnNetworkCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
final Set<UidRange> ranges = uidRangesForUid(uid);
mMockVpn.registerAgent(ranges);
- mService.setUnderlyingNetworksForVpn(new Network[0]);
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
// VPN networks do not satisfy the default request and are automatically validated
// by NetworkMonitor
@@ -6064,7 +6364,10 @@ public class ConnectivityServiceTest {
wifiNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ systemDefaultCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+ assertEquals(mWiFiNetworkAgent.getNetwork(),
+ systemDefaultCallback.getLastAvailableNetwork());
ranges.clear();
mMockVpn.setUids(ranges);
@@ -6081,6 +6384,7 @@ public class ConnectivityServiceTest {
// much, but that is the reason the test here has to check for an update to the
// capabilities instead of the expected LOST then AVAILABLE.
defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
+ systemDefaultCallback.assertNoCallback();
ranges.add(new UidRange(uid, uid));
mMockVpn.setUids(ranges);
@@ -6092,6 +6396,7 @@ public class ConnectivityServiceTest {
// TODO : Here like above, AVAILABLE would be correct, but because this can't actually
// happen outside of the test, ConnectivityService does not rematch callbacks.
defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
+ systemDefaultCallback.assertNoCallback();
mWiFiNetworkAgent.disconnect();
@@ -6100,6 +6405,7 @@ public class ConnectivityServiceTest {
wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
vpnNetworkCallback.assertNoCallback();
defaultCallback.assertNoCallback();
+ systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
mMockVpn.disconnect();
@@ -6108,12 +6414,14 @@ public class ConnectivityServiceTest {
wifiNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+ systemDefaultCallback.assertNoCallback();
assertEquals(null, mCm.getActiveNetwork());
mCm.unregisterNetworkCallback(genericNetworkCallback);
mCm.unregisterNetworkCallback(wifiNetworkCallback);
mCm.unregisterNetworkCallback(vpnNetworkCallback);
mCm.unregisterNetworkCallback(defaultCallback);
+ mCm.unregisterNetworkCallback(systemDefaultCallback);
}
@Test
@@ -6249,11 +6557,13 @@ public class ConnectivityServiceTest {
assertTrue(nc.hasCapability(NET_CAPABILITY_VALIDATED));
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+
+ assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE);
}
private void assertDefaultNetworkCapabilities(int userId, NetworkAgentWrapper... networks) {
final NetworkCapabilities[] defaultCaps = mService.getDefaultNetworkCapabilitiesForUser(
- userId, "com.android.calling.package");
+ userId, "com.android.calling.package", "com.test");
final String defaultCapsString = Arrays.toString(defaultCaps);
assertEquals(defaultCapsString, defaultCaps.length, networks.length);
final Set<NetworkCapabilities> defaultCapsSet = new ArraySet<>(defaultCaps);
@@ -6288,6 +6598,7 @@ public class ConnectivityServiceTest {
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
// A VPN without underlying networks is not suspended.
assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE);
final int userId = UserHandle.getUserId(Process.myUid());
assertDefaultNetworkCapabilities(userId /* no networks */);
@@ -6297,7 +6608,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mCellNetworkAgent.connect(true);
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6312,7 +6623,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mWiFiNetworkAgent.connect(true);
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6323,7 +6634,7 @@ public class ConnectivityServiceTest {
assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Don't disconnect, but note the VPN is not using wifi any more.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6354,7 +6665,7 @@ public class ConnectivityServiceTest {
vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
// Use Wifi but not cell. Note the VPN is now unmetered and not suspended.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6365,7 +6676,7 @@ public class ConnectivityServiceTest {
assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent);
// Use both again.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6380,7 +6691,7 @@ public class ConnectivityServiceTest {
vpnNetworkCallback.assertNoCallback();
// Stop using WiFi. The VPN is suspended again.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
@@ -6391,7 +6702,7 @@ public class ConnectivityServiceTest {
assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Use both again.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6451,6 +6762,7 @@ public class ConnectivityServiceTest {
// By default, VPN is set to track default network (i.e. its underlying networks is null).
// In case of no default network, VPN is considered metered.
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE);
// Connect to Cell; Cell is the default network.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
@@ -6508,6 +6820,7 @@ public class ConnectivityServiceTest {
NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertNotNull("nc=" + nc, nc.getUids());
assertEquals(nc.getUids(), uidRangesForUid(uid));
+ assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE);
// Set an underlying network and expect to see the VPN transports change.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -6523,12 +6836,11 @@ public class ConnectivityServiceTest {
.thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID));
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
+ addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER));
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
// Send a USER_ADDED broadcast for it.
- // The BroadcastReceiver for this broadcast checks that is being run on the handler thread.
- final Handler handler = new Handler(mCsHandlerThread.getLooper());
- handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
+ processBroadcastForVpn(addedIntent);
// Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added
// restricted user.
@@ -6551,8 +6863,9 @@ public class ConnectivityServiceTest {
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
+ removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER));
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
- handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
+ processBroadcastForVpn(removedIntent);
// Expect that the VPN gains the UID range for the restricted user, and that the capability
// change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved.
@@ -6592,8 +6905,8 @@ public class ConnectivityServiceTest {
// Enable always-on VPN lockdown. The main user loses network access because no VPN is up.
final ArrayList<String> allowList = new ArrayList<>();
- mService.setAlwaysOnVpnPackage(PRIMARY_USER, ALWAYS_ON_PACKAGE, true /* lockdown */,
- allowList);
+ mVpnManagerService.setAlwaysOnVpnPackage(PRIMARY_USER, ALWAYS_ON_PACKAGE,
+ true /* lockdown */, allowList);
waitForIdle();
assertNull(mCm.getActiveNetworkForUid(uid));
// This is arguably overspecified: a UID that is not running doesn't have an active network.
@@ -6608,10 +6921,9 @@ public class ConnectivityServiceTest {
RESTRICTED_USER_INFO));
// TODO: check that VPN app within restricted profile still has access, etc.
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
+ addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER));
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
- final Handler handler = new Handler(mCsHandlerThread.getLooper());
- handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
- waitForIdle();
+ processBroadcastForVpn(addedIntent);
assertNull(mCm.getActiveNetworkForUid(uid));
assertNull(mCm.getActiveNetworkForUid(restrictedUid));
@@ -6620,13 +6932,14 @@ public class ConnectivityServiceTest {
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
+ removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER));
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
- handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
- waitForIdle();
+ processBroadcastForVpn(removedIntent);
assertNull(mCm.getActiveNetworkForUid(uid));
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
- mService.setAlwaysOnVpnPackage(PRIMARY_USER, null, false /* lockdown */, allowList);
+ mVpnManagerService.setAlwaysOnVpnPackage(PRIMARY_USER, null, false /* lockdown */,
+ allowList);
waitForIdle();
}
@@ -6724,7 +7037,7 @@ public class ConnectivityServiceTest {
// Ensure VPN is now the active network.
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// VPN is using Cell
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
waitForIdle();
@@ -6732,7 +7045,7 @@ public class ConnectivityServiceTest {
assertTrue(mCm.isActiveNetworkMetered());
// VPN is now using WiFi
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork() });
waitForIdle();
@@ -6740,7 +7053,7 @@ public class ConnectivityServiceTest {
assertFalse(mCm.isActiveNetworkMetered());
// VPN is using Cell | WiFi.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
waitForIdle();
@@ -6748,7 +7061,7 @@ public class ConnectivityServiceTest {
assertTrue(mCm.isActiveNetworkMetered());
// VPN is using WiFi | Cell.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork(), mCellNetworkAgent.getNetwork() });
waitForIdle();
@@ -6756,7 +7069,7 @@ public class ConnectivityServiceTest {
assertTrue(mCm.isActiveNetworkMetered());
// VPN is not using any underlying networks.
- mService.setUnderlyingNetworksForVpn(new Network[0]);
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
waitForIdle();
// VPN without underlying networks is treated as metered.
@@ -6783,7 +7096,7 @@ public class ConnectivityServiceTest {
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// VPN is tracking current platform default (WiFi).
- mService.setUnderlyingNetworksForVpn(null);
+ mMockVpn.setUnderlyingNetworks(null);
waitForIdle();
// Despite VPN using WiFi (which is unmetered), VPN itself is marked as always metered.
@@ -6791,7 +7104,7 @@ public class ConnectivityServiceTest {
// VPN explicitly declares WiFi as its underlying network.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork() });
waitForIdle();
@@ -7002,7 +7315,8 @@ public class ConnectivityServiceTest {
final int uid = Process.myUid();
final int userId = UserHandle.getUserId(uid);
final ArrayList<String> allowList = new ArrayList<>();
- mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+ mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */,
+ allowList);
waitForIdle();
UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1);
@@ -7024,7 +7338,7 @@ public class ConnectivityServiceTest {
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
// Disable lockdown, expect to see the network unblocked.
- mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+ mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
vpnUidCallback.assertNoCallback();
@@ -7037,7 +7351,8 @@ public class ConnectivityServiceTest {
// Add our UID to the allowlist and re-enable lockdown, expect network is not blocked.
allowList.add(TEST_PACKAGE_NAME);
- mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+ mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */,
+ allowList);
callback.assertNoCallback();
defaultCallback.assertNoCallback();
vpnUidCallback.assertNoCallback();
@@ -7070,11 +7385,12 @@ public class ConnectivityServiceTest {
// Disable lockdown, remove our UID from the allowlist, and re-enable lockdown.
// Everything should now be blocked.
- mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+ mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
waitForIdle();
expectNetworkRejectNonSecureVpn(inOrder, false, piece1, piece2, piece3);
allowList.clear();
- mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+ mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */,
+ allowList);
waitForIdle();
expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
@@ -7087,7 +7403,7 @@ public class ConnectivityServiceTest {
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
// Disable lockdown. Everything is unblocked.
- mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+ mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
assertBlockedCallbackInAnyOrder(callback, false, mWiFiNetworkAgent, mCellNetworkAgent);
vpnUidCallback.assertNoCallback();
@@ -7099,7 +7415,8 @@ public class ConnectivityServiceTest {
// Enable and disable an always-on VPN package without lockdown. Expect no changes.
reset(mMockNetd);
- mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, false /* lockdown */, allowList);
+ mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, false /* lockdown */,
+ allowList);
inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any());
callback.assertNoCallback();
defaultCallback.assertNoCallback();
@@ -7110,7 +7427,7 @@ public class ConnectivityServiceTest {
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
- mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+ mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any());
callback.assertNoCallback();
defaultCallback.assertNoCallback();
@@ -7122,7 +7439,8 @@ public class ConnectivityServiceTest {
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
// Enable lockdown and connect a VPN. The VPN is not blocked.
- mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+ mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */,
+ allowList);
defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
vpnUidCallback.assertNoCallback();
@@ -7168,10 +7486,14 @@ public class ConnectivityServiceTest {
when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
}
- private void establishLegacyLockdownVpn() throws Exception {
+ private void establishLegacyLockdownVpn(Network underlying) throws Exception {
+ // The legacy lockdown VPN only supports userId 0, and must have an underlying network.
+ assertNotNull(underlying);
+ mMockVpn.setVpnType(VpnManager.TYPE_VPN_LEGACY);
// The legacy lockdown VPN only supports userId 0.
final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
mMockVpn.registerAgent(ranges);
+ mMockVpn.setUnderlyingNetworks(new Network[]{underlying});
mMockVpn.connect(true);
}
@@ -7179,6 +7501,9 @@ public class ConnectivityServiceTest {
public void testLegacyLockdownVpn() throws Exception {
mServiceContext.setPermission(
Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
+ // For LockdownVpnTracker to call registerSystemDefaultNetworkCallback.
+ mServiceContext.setPermission(
+ Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
final TestNetworkCallback callback = new TestNetworkCallback();
@@ -7187,6 +7512,10 @@ public class ConnectivityServiceTest {
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
+ final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback();
+ mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback,
+ new Handler(ConnectivityThread.getInstanceLooper()));
+
// Pretend lockdown VPN was configured.
setupLegacyLockdownVpn();
@@ -7198,10 +7527,9 @@ public class ConnectivityServiceTest {
// Send a USER_UNLOCKED broadcast so CS starts LockdownVpnTracker.
final int userId = UserHandle.getUserId(Process.myUid());
final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED);
+ addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- final Handler handler = new Handler(mCsHandlerThread.getLooper());
- handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
- waitForIdle();
+ processBroadcastForVpn(addedIntent);
// Lockdown VPN disables teardown and enables lockdown.
assertFalse(mMockVpn.getEnableTeardown());
@@ -7218,6 +7546,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(false /* validated */);
callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+ systemDefaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
waitForIdle();
assertNull(mMockVpn.getAgent());
@@ -7229,6 +7558,8 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.sendLinkProperties(cellLp);
callback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
defaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ systemDefaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
+ mCellNetworkAgent);
waitForIdle();
assertNull(mMockVpn.getAgent());
@@ -7238,6 +7569,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.disconnect();
callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ systemDefaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
b1.expectBroadcast();
// When lockdown VPN is active, the NetworkInfo state in CONNECTIVITY_ACTION is overwritten
@@ -7247,6 +7579,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(false /* validated */);
callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+ systemDefaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
b1.expectBroadcast();
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
@@ -7269,9 +7602,10 @@ public class ConnectivityServiceTest {
mMockVpn.expectStartLegacyVpnRunner();
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
- establishLegacyLockdownVpn();
+ establishLegacyLockdownVpn(mCellNetworkAgent.getNetwork());
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ systemDefaultCallback.assertNoCallback();
NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
b1.expectBroadcast();
b2.expectBroadcast();
@@ -7283,6 +7617,7 @@ public class ConnectivityServiceTest {
assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI));
assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertVpnTransportInfo(vpnNc, VpnManager.TYPE_VPN_LEGACY);
// Switch default network from cell to wifi. Expect VPN to disconnect and reconnect.
final LinkProperties wifiLp = new LinkProperties();
@@ -7310,11 +7645,10 @@ public class ConnectivityServiceTest {
// fact that a VPN is connected should only result in the VPN itself being unblocked, not
// any other network. Bug in isUidBlockedByVpn?
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- callback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasTransport(TRANSPORT_WIFI));
callback.expectCallback(CallbackEntry.LOST, mMockVpn);
- defaultCallback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasTransport(TRANSPORT_WIFI));
defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
+ systemDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// While the VPN is reconnecting on the new network, everything is blocked.
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
@@ -7325,9 +7659,10 @@ public class ConnectivityServiceTest {
// The VPN comes up again on wifi.
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
- establishLegacyLockdownVpn();
+ establishLegacyLockdownVpn(mWiFiNetworkAgent.getNetwork());
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ systemDefaultCallback.assertNoCallback();
b1.expectBroadcast();
b2.expectBroadcast();
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -7341,14 +7676,10 @@ public class ConnectivityServiceTest {
assertTrue(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
// Disconnect cell. Nothing much happens since it's not the default network.
- // Whenever LockdownVpnTracker is connected, it will send a connected broadcast any time any
- // NetworkInfo is updated. This is probably a bug.
- // TODO: consider fixing this.
- b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
mCellNetworkAgent.disconnect();
- b1.expectBroadcast();
callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
defaultCallback.assertNoCallback();
+ systemDefaultCallback.assertNoCallback();
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
@@ -7358,6 +7689,7 @@ public class ConnectivityServiceTest {
b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
b1.expectBroadcast();
callback.expectCapabilitiesThat(mMockVpn, nc -> !nc.hasTransport(TRANSPORT_WIFI));
b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
@@ -7896,8 +8228,8 @@ public class ConnectivityServiceTest {
reset(mNetworkManagementService);
mCellNetworkAgent.connect(true);
networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
- eq(NetworkCapabilities.TRANSPORT_CELLULAR));
+ verify(mMockNetd, times(1)).idletimerAddInterface(eq(MOBILE_IFNAME), anyInt(),
+ eq(Integer.toString(TRANSPORT_CELLULAR)));
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
final LinkProperties wifiLp = new LinkProperties();
@@ -7905,25 +8237,27 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
// Network switch
- reset(mNetworkManagementService);
mWiFiNetworkAgent.connect(true);
networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- verify(mNetworkManagementService, times(1)).addIdleTimer(eq(WIFI_IFNAME), anyInt(),
- eq(NetworkCapabilities.TRANSPORT_WIFI));
- verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(MOBILE_IFNAME));
+ verify(mMockNetd, times(1)).idletimerAddInterface(eq(WIFI_IFNAME), anyInt(),
+ eq(Integer.toString(TRANSPORT_WIFI)));
+ verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
+ eq(Integer.toString(TRANSPORT_CELLULAR)));
// Disconnect wifi and switch back to cell
- reset(mNetworkManagementService);
+ reset(mMockNetd);
mWiFiNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
assertNoCallbacks(networkCallback);
- verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
- verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
- eq(NetworkCapabilities.TRANSPORT_CELLULAR));
+ verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(WIFI_IFNAME), anyInt(),
+ eq(Integer.toString(TRANSPORT_WIFI)));
+ verify(mMockNetd, times(1)).idletimerAddInterface(eq(MOBILE_IFNAME), anyInt(),
+ eq(Integer.toString(TRANSPORT_CELLULAR)));
// reconnect wifi
+ reset(mMockNetd);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
wifiLp.setInterfaceName(WIFI_IFNAME);
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
@@ -7931,9 +8265,12 @@ public class ConnectivityServiceTest {
networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ verify(mMockNetd, times(1)).idletimerAddInterface(eq(WIFI_IFNAME), anyInt(),
+ eq(Integer.toString(TRANSPORT_WIFI)));
+ verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
+ eq(Integer.toString(TRANSPORT_CELLULAR)));
// Disconnect cell
- reset(mNetworkManagementService);
reset(mMockNetd);
mCellNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
@@ -7941,17 +8278,18 @@ public class ConnectivityServiceTest {
// sent as network being switched. Ensure rule removal for cell will not be triggered
// unexpectedly before network being removed.
waitForIdle();
- verify(mNetworkManagementService, times(0)).removeIdleTimer(eq(MOBILE_IFNAME));
+ verify(mMockNetd, times(0)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
+ eq(Integer.toString(TRANSPORT_CELLULAR)));
verify(mMockNetd, times(1)).networkDestroy(eq(mCellNetworkAgent.getNetwork().netId));
verify(mMockDnsResolver, times(1))
.destroyNetworkCache(eq(mCellNetworkAgent.getNetwork().netId));
// Disconnect wifi
ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
- reset(mNetworkManagementService);
mWiFiNetworkAgent.disconnect();
b.expectBroadcast();
- verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
+ verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(WIFI_IFNAME), anyInt(),
+ eq(Integer.toString(TRANSPORT_WIFI)));
// Clean up
mCm.unregisterNetworkCallback(networkCallback);
@@ -8272,7 +8610,8 @@ public class ConnectivityServiceTest {
when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
if (op != null) {
- when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
+ when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()),
+ eq(mContext.getPackageName()), eq(getAttributionTag()), anyString()))
.thenReturn(AppOpsManager.MODE_ALLOWED);
}
@@ -8285,7 +8624,7 @@ public class ConnectivityServiceTest {
final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, callerUid, mContext.getPackageName()).getOwnerUid();
+ netCap, callerUid, mContext.getPackageName(), getAttributionTag()).getOwnerUid();
}
private void verifyWifiInfoCopyNetCapsForCallerPermission(
@@ -8295,7 +8634,7 @@ public class ConnectivityServiceTest {
final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo);
mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, callerUid, mContext.getPackageName());
+ netCap, callerUid, mContext.getPackageName(), getAttributionTag());
verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable));
}
@@ -8414,11 +8753,7 @@ public class ConnectivityServiceTest {
final int myUid = Process.myUid();
setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_PLATFORM);
- try {
- mService.getConnectionOwnerUid(getTestConnectionInfo());
- fail("Expected SecurityException for non-VpnService app");
- } catch (SecurityException expected) {
- }
+ assertEquals(INVALID_UID, mService.getConnectionOwnerUid(getTestConnectionInfo()));
}
@Test
@@ -8426,11 +8761,7 @@ public class ConnectivityServiceTest {
final int myUid = Process.myUid();
setupConnectionOwnerUidAsVpnApp(myUid + 1, VpnManager.TYPE_VPN_SERVICE);
- try {
- mService.getConnectionOwnerUid(getTestConnectionInfo());
- fail("Expected SecurityException for non-VpnService app");
- } catch (SecurityException expected) {
- }
+ assertEquals(INVALID_UID, mService.getConnectionOwnerUid(getTestConnectionInfo()));
}
@Test
@@ -8643,7 +8974,7 @@ public class ConnectivityServiceTest {
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
- assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {naiWithoutUid.network}));
+ assertTrue(mMockVpn.setUnderlyingNetworks(new Network[] {naiWithoutUid.network}));
waitForIdle();
assertTrue(
"Active VPN permission not applied",
@@ -8651,7 +8982,7 @@ public class ConnectivityServiceTest {
Process.myPid(), Process.myUid(), naiWithoutUid,
mContext.getOpPackageName()));
- assertTrue(mService.setUnderlyingNetworksForVpn(null));
+ assertTrue(mMockVpn.setUnderlyingNetworks(null));
waitForIdle();
assertFalse(
"VPN shouldn't receive callback on non-underlying network",
@@ -9127,4 +9458,884 @@ public class ConnectivityServiceTest {
}
fail("TOO_MANY_REQUESTS never thrown");
}
+
+ private void mockGetApplicationInfo(@NonNull final String packageName, @NonNull final int uid)
+ throws Exception {
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = uid;
+ when(mPackageManager.getApplicationInfo(eq(packageName), anyInt()))
+ .thenReturn(applicationInfo);
+ }
+
+ private void mockHasSystemFeature(@NonNull final String featureName,
+ @NonNull final boolean hasFeature) {
+ when(mPackageManager.hasSystemFeature(eq(featureName)))
+ .thenReturn(hasFeature);
+ }
+
+ private UidRange getNriFirstUidRange(
+ @NonNull final ConnectivityService.NetworkRequestInfo nri) {
+ return nri.mRequests.get(0).networkCapabilities.getUids().iterator().next();
+ }
+
+ private OemNetworkPreferences createDefaultOemNetworkPreferences(
+ @OemNetworkPreferences.OemNetworkPreference final int preference)
+ throws Exception {
+ // Arrange PackageManager mocks
+ mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID);
+
+ // Build OemNetworkPreferences object
+ return new OemNetworkPreferences.Builder()
+ .addNetworkPreference(TEST_PACKAGE_NAME, preference)
+ .build();
+ }
+
+ @Test
+ public void testOemNetworkRequestFactoryPreferenceUninitializedThrowsError()
+ throws PackageManager.NameNotFoundException {
+ @OemNetworkPreferences.OemNetworkPreference final int prefToTest =
+ OEM_NETWORK_PREFERENCE_UNINITIALIZED;
+
+ // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
+ assertThrows(IllegalArgumentException.class,
+ () -> mService.new OemNetworkRequestFactory()
+ .createNrisFromOemNetworkPreferences(
+ createDefaultOemNetworkPreferences(prefToTest)));
+ }
+
+ @Test
+ public void testOemNetworkRequestFactoryPreferenceOemPaid()
+ throws Exception {
+ // Expectations
+ final int expectedNumOfNris = 1;
+ final int expectedNumOfRequests = 3;
+
+ @OemNetworkPreferences.OemNetworkPreference final int prefToTest =
+ OEM_NETWORK_PREFERENCE_OEM_PAID;
+
+ // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
+ final ArraySet<ConnectivityService.NetworkRequestInfo> nris =
+ mService.new OemNetworkRequestFactory()
+ .createNrisFromOemNetworkPreferences(
+ createDefaultOemNetworkPreferences(prefToTest));
+
+ final List<NetworkRequest> mRequests = nris.iterator().next().mRequests;
+ assertEquals(expectedNumOfNris, nris.size());
+ assertEquals(expectedNumOfRequests, mRequests.size());
+ assertTrue(mRequests.get(0).isListen());
+ assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_VALIDATED));
+ assertTrue(mRequests.get(1).isRequest());
+ assertTrue(mRequests.get(1).hasCapability(NET_CAPABILITY_OEM_PAID));
+ assertTrue(mRequests.get(2).isRequest());
+ assertTrue(mService.getDefaultRequest().networkCapabilities.equalsNetCapabilities(
+ mRequests.get(2).networkCapabilities));
+ }
+
+ @Test
+ public void testOemNetworkRequestFactoryPreferenceOemPaidNoFallback()
+ throws Exception {
+ // Expectations
+ final int expectedNumOfNris = 1;
+ final int expectedNumOfRequests = 2;
+
+ @OemNetworkPreferences.OemNetworkPreference final int prefToTest =
+ OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK;
+
+ // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
+ final ArraySet<ConnectivityService.NetworkRequestInfo> nris =
+ mService.new OemNetworkRequestFactory()
+ .createNrisFromOemNetworkPreferences(
+ createDefaultOemNetworkPreferences(prefToTest));
+
+ final List<NetworkRequest> mRequests = nris.iterator().next().mRequests;
+ assertEquals(expectedNumOfNris, nris.size());
+ assertEquals(expectedNumOfRequests, mRequests.size());
+ assertTrue(mRequests.get(0).isListen());
+ assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_VALIDATED));
+ assertTrue(mRequests.get(1).isRequest());
+ assertTrue(mRequests.get(1).hasCapability(NET_CAPABILITY_OEM_PAID));
+ }
+
+ @Test
+ public void testOemNetworkRequestFactoryPreferenceOemPaidOnly()
+ throws Exception {
+ // Expectations
+ final int expectedNumOfNris = 1;
+ final int expectedNumOfRequests = 1;
+
+ @OemNetworkPreferences.OemNetworkPreference final int prefToTest =
+ OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
+
+ // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
+ final ArraySet<ConnectivityService.NetworkRequestInfo> nris =
+ mService.new OemNetworkRequestFactory()
+ .createNrisFromOemNetworkPreferences(
+ createDefaultOemNetworkPreferences(prefToTest));
+
+ final List<NetworkRequest> mRequests = nris.iterator().next().mRequests;
+ assertEquals(expectedNumOfNris, nris.size());
+ assertEquals(expectedNumOfRequests, mRequests.size());
+ assertTrue(mRequests.get(0).isRequest());
+ assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_OEM_PAID));
+ }
+
+ @Test
+ public void testOemNetworkRequestFactoryPreferenceOemPrivateOnly()
+ throws Exception {
+ // Expectations
+ final int expectedNumOfNris = 1;
+ final int expectedNumOfRequests = 1;
+
+ @OemNetworkPreferences.OemNetworkPreference final int prefToTest =
+ OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
+
+ // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
+ final ArraySet<ConnectivityService.NetworkRequestInfo> nris =
+ mService.new OemNetworkRequestFactory()
+ .createNrisFromOemNetworkPreferences(
+ createDefaultOemNetworkPreferences(prefToTest));
+
+ final List<NetworkRequest> mRequests = nris.iterator().next().mRequests;
+ assertEquals(expectedNumOfNris, nris.size());
+ assertEquals(expectedNumOfRequests, mRequests.size());
+ assertTrue(mRequests.get(0).isRequest());
+ assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_OEM_PRIVATE));
+ assertFalse(mRequests.get(0).hasCapability(NET_CAPABILITY_OEM_PAID));
+ }
+
+ @Test
+ public void testOemNetworkRequestFactoryCreatesCorrectNumOfNris()
+ throws Exception {
+ // Expectations
+ final int expectedNumOfNris = 2;
+
+ // Arrange PackageManager mocks
+ final String testPackageName2 = "com.google.apps.dialer";
+ mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID);
+ mockGetApplicationInfo(testPackageName2, TEST_PACKAGE_UID);
+
+ // Build OemNetworkPreferences object
+ final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID;
+ final int testOemPref2 = OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK;
+ final OemNetworkPreferences pref = new OemNetworkPreferences.Builder()
+ .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref)
+ .addNetworkPreference(testPackageName2, testOemPref2)
+ .build();
+
+ // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
+ final ArraySet<ConnectivityService.NetworkRequestInfo> nris =
+ mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(pref);
+
+ assertNotNull(nris);
+ assertEquals(expectedNumOfNris, nris.size());
+ }
+
+ @Test
+ public void testOemNetworkRequestFactoryCorrectlySetsUids()
+ throws Exception {
+ // Arrange PackageManager mocks
+ final String testPackageName2 = "com.google.apps.dialer";
+ final int testPackageNameUid2 = 456;
+ mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID);
+ mockGetApplicationInfo(testPackageName2, testPackageNameUid2);
+
+ // Build OemNetworkPreferences object
+ final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID;
+ final int testOemPref2 = OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK;
+ final OemNetworkPreferences pref = new OemNetworkPreferences.Builder()
+ .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref)
+ .addNetworkPreference(testPackageName2, testOemPref2)
+ .build();
+
+ // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
+ final List<ConnectivityService.NetworkRequestInfo> nris =
+ new ArrayList<>(
+ mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(
+ pref));
+
+ // Sort by uid to access nris by index
+ nris.sort(Comparator.comparingInt(nri -> getNriFirstUidRange(nri).start));
+ assertEquals(TEST_PACKAGE_UID, getNriFirstUidRange(nris.get(0)).start);
+ assertEquals(TEST_PACKAGE_UID, getNriFirstUidRange(nris.get(0)).stop);
+ assertEquals(testPackageNameUid2, getNriFirstUidRange(nris.get(1)).start);
+ assertEquals(testPackageNameUid2, getNriFirstUidRange(nris.get(1)).stop);
+ }
+
+ @Test
+ public void testOemNetworkRequestFactoryAddsPackagesToCorrectPreference()
+ throws Exception {
+ // Expectations
+ final int expectedNumOfNris = 1;
+ final int expectedNumOfAppUids = 2;
+
+ // Arrange PackageManager mocks
+ final String testPackageName2 = "com.google.apps.dialer";
+ final int testPackageNameUid2 = 456;
+ mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID);
+ mockGetApplicationInfo(testPackageName2, testPackageNameUid2);
+
+ // Build OemNetworkPreferences object
+ final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID;
+ final OemNetworkPreferences pref = new OemNetworkPreferences.Builder()
+ .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref)
+ .addNetworkPreference(testPackageName2, testOemPref)
+ .build();
+
+ // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
+ final ArraySet<ConnectivityService.NetworkRequestInfo> nris =
+ mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(pref);
+
+ assertEquals(expectedNumOfNris, nris.size());
+ assertEquals(expectedNumOfAppUids,
+ nris.iterator().next().mRequests.get(0).networkCapabilities.getUids().size());
+ }
+
+ @Test
+ public void testSetOemNetworkPreferenceNullListenerAndPrefParamThrowsNpe() {
+ mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true);
+
+ // Act on ConnectivityService.setOemNetworkPreference()
+ assertThrows(NullPointerException.class,
+ () -> mService.setOemNetworkPreference(
+ null,
+ null));
+ }
+
+ @Test
+ public void testSetOemNetworkPreferenceFailsForNonAutomotive()
+ throws Exception {
+ mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, false);
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
+
+ // Act on ConnectivityService.setOemNetworkPreference()
+ assertThrows(UnsupportedOperationException.class,
+ () -> mService.setOemNetworkPreference(
+ createDefaultOemNetworkPreferences(networkPref),
+ new TestOemListenerCallback()));
+ }
+
+ private void setOemNetworkPreferenceAgentConnected(final int transportType,
+ final boolean connectAgent) throws Exception {
+ switch(transportType) {
+ // Corresponds to a metered cellular network. Will be used for the default network.
+ case TRANSPORT_CELLULAR:
+ if (!connectAgent) {
+ mCellNetworkAgent.disconnect();
+ break;
+ }
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+ mCellNetworkAgent.connect(true);
+ break;
+ // Corresponds to a restricted ethernet network with OEM_PAID/OEM_PRIVATE.
+ case TRANSPORT_ETHERNET:
+ if (!connectAgent) {
+ stopOemManagedNetwork();
+ break;
+ }
+ startOemManagedNetwork(true);
+ break;
+ // Corresponds to unmetered Wi-Fi.
+ case TRANSPORT_WIFI:
+ if (!connectAgent) {
+ mWiFiNetworkAgent.disconnect();
+ break;
+ }
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent.connect(true);
+ break;
+ default:
+ throw new AssertionError("Unsupported transport type passed in.");
+
+ }
+ waitForIdle();
+ }
+
+ private void startOemManagedNetwork(final boolean isOemPaid) throws Exception {
+ mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
+ mEthernetNetworkAgent.addCapability(
+ isOemPaid ? NET_CAPABILITY_OEM_PAID : NET_CAPABILITY_OEM_PRIVATE);
+ mEthernetNetworkAgent.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ mEthernetNetworkAgent.connect(true);
+ }
+
+ private void stopOemManagedNetwork() {
+ mEthernetNetworkAgent.disconnect();
+ waitForIdle();
+ }
+
+ private void verifyMultipleDefaultNetworksTracksCorrectly(
+ final int expectedOemRequestsSize,
+ @NonNull final Network expectedDefaultNetwork,
+ @NonNull final Network expectedPerAppNetwork) {
+ // The current test setup assumes two tracked default network requests; one for the default
+ // network and the other for the OEM network preference being tested. This will be validated
+ // each time to confirm it doesn't change under test.
+ final int expectedDefaultNetworkRequestsSize = 2;
+ assertEquals(expectedDefaultNetworkRequestsSize, mService.mDefaultNetworkRequests.size());
+ for (final ConnectivityService.NetworkRequestInfo defaultRequest
+ : mService.mDefaultNetworkRequests) {
+ final Network defaultNetwork = defaultRequest.getSatisfier() == null
+ ? null : defaultRequest.getSatisfier().network();
+ // If this is the default request.
+ if (defaultRequest == mService.mDefaultRequest) {
+ assertEquals(
+ expectedDefaultNetwork,
+ defaultNetwork);
+ // Make sure this value doesn't change.
+ assertEquals(1, defaultRequest.mRequests.size());
+ continue;
+ }
+ assertEquals(expectedPerAppNetwork, defaultNetwork);
+ assertEquals(expectedOemRequestsSize, defaultRequest.mRequests.size());
+ }
+ }
+
+ private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
+ @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup)
+ throws Exception {
+ final int testPackageNameUid = 123;
+ final String testPackageName = "per.app.defaults.package";
+ setupMultipleDefaultNetworksForOemNetworkPreferenceTest(
+ networkPrefToSetup, testPackageNameUid, testPackageName);
+ }
+
+ private void setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(
+ @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup)
+ throws Exception {
+ final int testPackageNameUid = Process.myUid();
+ final String testPackageName = "per.app.defaults.package";
+ setupMultipleDefaultNetworksForOemNetworkPreferenceTest(
+ networkPrefToSetup, testPackageNameUid, testPackageName);
+ }
+
+ private void setupMultipleDefaultNetworksForOemNetworkPreferenceTest(
+ @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup,
+ final int testPackageUid, @NonNull final String testPackageName) throws Exception {
+ // Only the default request should be included at start.
+ assertEquals(1, mService.mDefaultNetworkRequests.size());
+
+ final UidRangeParcel[] uidRanges =
+ toUidRangeStableParcels(uidRangesForUid(testPackageUid));
+ setupSetOemNetworkPreferenceForPreferenceTest(
+ networkPrefToSetup, uidRanges, testPackageName);
+ }
+
+ private void setupSetOemNetworkPreferenceForPreferenceTest(
+ @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup,
+ @NonNull final UidRangeParcel[] uidRanges,
+ @NonNull final String testPackageName)
+ throws Exception {
+ mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true);
+
+ // These tests work off a single UID therefore using 'start' is valid.
+ mockGetApplicationInfo(testPackageName, uidRanges[0].start);
+
+ // Build OemNetworkPreferences object
+ final OemNetworkPreferences pref = new OemNetworkPreferences.Builder()
+ .addNetworkPreference(testPackageName, networkPrefToSetup)
+ .build();
+
+ // Act on ConnectivityService.setOemNetworkPreference()
+ final TestOemListenerCallback mOnSetOemNetworkPreferenceTestListener =
+ new TestOemListenerCallback();
+ mService.setOemNetworkPreference(pref, mOnSetOemNetworkPreferenceTestListener);
+
+ // Verify call returned successfully
+ mOnSetOemNetworkPreferenceTestListener.expectOnComplete();
+ }
+
+ private static class TestOemListenerCallback implements IOnSetOemNetworkPreferenceListener {
+ final CompletableFuture<Object> mDone = new CompletableFuture<>();
+
+ @Override
+ public void onComplete() {
+ mDone.complete(new Object());
+ }
+
+ void expectOnComplete() throws Exception {
+ try {
+ mDone.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ fail("Expected onComplete() not received after " + TIMEOUT_MS + " ms");
+ }
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+ }
+
+ @Test
+ public void testMultiDefaultGetActiveNetworkIsCorrect() throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
+ final int expectedOemPrefRequestSize = 1;
+
+ // Setup the test process to use networkPref for their default network.
+ setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
+
+ // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID.
+ // The active network for the default should be null at this point as this is a retricted
+ // network.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ null,
+ mEthernetNetworkAgent.getNetwork());
+
+ // Verify that the active network is correct
+ verifyActiveNetwork(TRANSPORT_ETHERNET);
+ }
+
+ @Test
+ public void testMultiDefaultIsActiveNetworkMeteredIsCorrect() throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
+ final int expectedOemPrefRequestSize = 1;
+
+ // Setup the test process to use networkPref for their default network.
+ setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
+
+ // Returns true by default when no network is available.
+ assertTrue(mCm.isActiveNetworkMetered());
+
+ // Connect to an unmetered restricted network that will only be available to the OEM pref.
+ mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
+ mEthernetNetworkAgent.addCapability(NET_CAPABILITY_OEM_PAID);
+ mEthernetNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ mEthernetNetworkAgent.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ mEthernetNetworkAgent.connect(true);
+ waitForIdle();
+
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ null,
+ mEthernetNetworkAgent.getNetwork());
+
+ assertFalse(mCm.isActiveNetworkMetered());
+ }
+
+ @Test
+ public void testPerAppDefaultRegisterDefaultNetworkCallback() throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
+ final int expectedOemPrefRequestSize = 1;
+ final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback();
+
+ // Register the default network callback before the pref is already set. This means that
+ // the policy will be applied to the callback on setOemNetworkPreference().
+ mCm.registerDefaultNetworkCallback(defaultNetworkCallback);
+ defaultNetworkCallback.assertNoCallback();
+
+ // Setup the test process to use networkPref for their default network.
+ setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
+
+ // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID.
+ // The active nai for the default is null at this point as this is a restricted network.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ null,
+ mEthernetNetworkAgent.getNetwork());
+
+ // At this point with a restricted network used, the available callback should trigger
+ defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
+ assertEquals(defaultNetworkCallback.getLastAvailableNetwork(),
+ mEthernetNetworkAgent.getNetwork());
+
+ // Now bring down the default network which should trigger a LOST callback.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
+
+ // At this point, with no network is available, the lost callback should trigger
+ defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+
+ // Confirm we can unregister without issues.
+ mCm.unregisterNetworkCallback(defaultNetworkCallback);
+ }
+
+ @Test
+ public void testPerAppDefaultRegisterDefaultNetworkCallbackAfterPrefSet() throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
+ final int expectedOemPrefRequestSize = 1;
+ final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback();
+
+ // Setup the test process to use networkPref for their default network.
+ setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
+
+ // Register the default network callback after the pref is already set. This means that
+ // the policy will be applied to the callback on requestNetwork().
+ mCm.registerDefaultNetworkCallback(defaultNetworkCallback);
+ defaultNetworkCallback.assertNoCallback();
+
+ // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID.
+ // The active nai for the default is null at this point as this is a restricted network.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ null,
+ mEthernetNetworkAgent.getNetwork());
+
+ // At this point with a restricted network used, the available callback should trigger
+ defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
+ assertEquals(defaultNetworkCallback.getLastAvailableNetwork(),
+ mEthernetNetworkAgent.getNetwork());
+
+ // Now bring down the default network which should trigger a LOST callback.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
+
+ // At this point, with no network is available, the lost callback should trigger
+ defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+
+ // Confirm we can unregister without issues.
+ mCm.unregisterNetworkCallback(defaultNetworkCallback);
+ }
+
+ @Test
+ public void testPerAppDefaultRegisterDefaultNetworkCallbackDoesNotFire() throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
+ final int expectedOemPrefRequestSize = 1;
+ final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback();
+ final int userId = UserHandle.getUserId(Process.myUid());
+
+ mCm.registerDefaultNetworkCallback(defaultNetworkCallback);
+ defaultNetworkCallback.assertNoCallback();
+
+ // Setup a process different than the test process to use the default network. This means
+ // that the defaultNetworkCallback won't be tracked by the per-app policy.
+ setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(networkPref);
+
+ // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID.
+ // The active nai for the default is null at this point as this is a restricted network.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ null,
+ mEthernetNetworkAgent.getNetwork());
+
+ // As this callback does not have access to the OEM_PAID network, it will not fire.
+ defaultNetworkCallback.assertNoCallback();
+ assertDefaultNetworkCapabilities(userId /* no networks */);
+
+ // Bring up unrestricted cellular. This should now satisfy the default network.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mCellNetworkAgent.getNetwork(),
+ mEthernetNetworkAgent.getNetwork());
+
+ // At this point with an unrestricted network used, the available callback should trigger
+ defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ assertEquals(defaultNetworkCallback.getLastAvailableNetwork(),
+ mCellNetworkAgent.getNetwork());
+ assertDefaultNetworkCapabilities(userId, mCellNetworkAgent);
+
+ // Now bring down the per-app network.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
+
+ // Since the callback didn't use the per-app network, no callback should fire.
+ defaultNetworkCallback.assertNoCallback();
+
+ // Now bring down the default network.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false);
+
+ // As this callback was tracking the default, this should now trigger.
+ defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+
+ // Confirm we can unregister without issues.
+ mCm.unregisterNetworkCallback(defaultNetworkCallback);
+ }
+
+ private void verifySetOemNetworkPreferenceForPreference(
+ @NonNull final UidRangeParcel[] uidRanges,
+ final int addUidRangesNetId,
+ final int addUidRangesTimes,
+ final int removeUidRangesNetId,
+ final int removeUidRangesTimes,
+ final boolean shouldDestroyNetwork) throws RemoteException {
+ final boolean useAnyIdForAdd = OEM_PREF_ANY_NET_ID == addUidRangesNetId;
+ final boolean useAnyIdForRemove = OEM_PREF_ANY_NET_ID == removeUidRangesNetId;
+
+ // Validate netd.
+ verify(mMockNetd, times(addUidRangesTimes))
+ .networkAddUidRanges(
+ (useAnyIdForAdd ? anyInt() : eq(addUidRangesNetId)), eq(uidRanges));
+ verify(mMockNetd, times(removeUidRangesTimes))
+ .networkRemoveUidRanges(
+ (useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId)), eq(uidRanges));
+ if (shouldDestroyNetwork) {
+ verify(mMockNetd, times(1))
+ .networkDestroy((useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId)));
+ }
+ reset(mMockNetd);
+ }
+
+ /**
+ * Test the tracked default requests clear previous OEM requests on setOemNetworkPreference().
+ * @throws Exception
+ */
+ @Test
+ public void testSetOemNetworkPreferenceClearPreviousOemValues() throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference int networkPref =
+ OEM_NETWORK_PREFERENCE_OEM_PAID;
+ final int testPackageUid = 123;
+ final String testPackageName = "com.google.apps.contacts";
+ final UidRangeParcel[] uidRanges =
+ toUidRangeStableParcels(uidRangesForUid(testPackageUid));
+
+ // Validate the starting requests only includes the fallback request.
+ assertEquals(1, mService.mDefaultNetworkRequests.size());
+
+ // Add an OEM default network request to track.
+ setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, testPackageName);
+
+ // Two requests should exist, one for the fallback and one for the pref.
+ assertEquals(2, mService.mDefaultNetworkRequests.size());
+
+ networkPref = OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
+ setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, testPackageName);
+
+ // Two requests should still exist validating the previous per-app request was replaced.
+ assertEquals(2, mService.mDefaultNetworkRequests.size());
+ }
+
+ /**
+ * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID following in order:
+ * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID -> fallback
+ * @throws Exception
+ */
+ @Test
+ public void testMultilayerForPreferenceOemPaidEvaluatesCorrectly()
+ throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OEM_NETWORK_PREFERENCE_OEM_PAID;
+
+ // Arrange PackageManager mocks
+ final int testPackageNameUid = 123;
+ final UidRangeParcel[] uidRanges =
+ toUidRangeStableParcels(uidRangesForUid(testPackageNameUid));
+ setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
+
+ // Verify the starting state. No networks should be connected.
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Test lowest to highest priority requests.
+ // Bring up metered cellular. This will satisfy the fallback network.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mEthernetNetworkAgent.getNetwork().netId, 1 /* times */,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mWiFiNetworkAgent.getNetwork().netId, 1 /* times */,
+ mEthernetNetworkAgent.getNetwork().netId, 1 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Disconnecting OEM_PAID should have no effect as it is lower in priority then unmetered.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
+ // netd should not be called as default networks haven't changed.
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Disconnecting unmetered should put PANS on lowest priority fallback request.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ mWiFiNetworkAgent.getNetwork().netId, 0 /* times */,
+ true /* shouldDestroyNetwork */);
+
+ // Disconnecting the fallback network should result in no connectivity.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ mCellNetworkAgent.getNetwork().netId, 0 /* times */,
+ true /* shouldDestroyNetwork */);
+ }
+
+ /**
+ * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK following in order:
+ * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID
+ * @throws Exception
+ */
+ @Test
+ public void testMultilayerForPreferenceOemPaidNoFallbackEvaluatesCorrectly()
+ throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK;
+
+ // Arrange PackageManager mocks
+ final int testPackageNameUid = 123;
+ final UidRangeParcel[] uidRanges =
+ toUidRangeStableParcels(uidRangesForUid(testPackageNameUid));
+ setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
+
+ // Verify the starting state. This preference doesn't support using the fallback network
+ // therefore should be on the disconnected network as it has no networks to connect to.
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mService.mNoServiceNetwork.network.getNetId(), 1 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Test lowest to highest priority requests.
+ // Bring up metered cellular. This will satisfy the fallback network.
+ // This preference should not use this network as it doesn't support fallback usage.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mEthernetNetworkAgent.getNetwork().netId, 1 /* times */,
+ mService.mNoServiceNetwork.network.getNetId(), 1 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mWiFiNetworkAgent.getNetwork().netId, 1 /* times */,
+ mEthernetNetworkAgent.getNetwork().netId, 1 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Disconnecting unmetered should put PANS on OEM_PAID.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mEthernetNetworkAgent.getNetwork().netId, 1 /* times */,
+ mWiFiNetworkAgent.getNetwork().netId, 0 /* times */,
+ true /* shouldDestroyNetwork */);
+
+ // Disconnecting OEM_PAID should result in no connectivity.
+ // OEM_PAID_NO_FALLBACK not supporting a fallback now uses the disconnected network.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mService.mNoServiceNetwork.network.getNetId(), 1 /* times */,
+ mEthernetNetworkAgent.getNetwork().netId, 0 /* times */,
+ true /* shouldDestroyNetwork */);
+ }
+
+ /**
+ * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY following in order:
+ * NET_CAPABILITY_OEM_PAID
+ * This preference should only apply to OEM_PAID networks.
+ * @throws Exception
+ */
+ @Test
+ public void testMultilayerForPreferenceOemPaidOnlyEvaluatesCorrectly()
+ throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
+
+ // Arrange PackageManager mocks
+ final int testPackageNameUid = 123;
+ final UidRangeParcel[] uidRanges =
+ toUidRangeStableParcels(uidRangesForUid(testPackageNameUid));
+ setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
+
+ // Verify the starting state. This preference doesn't support using the fallback network
+ // therefore should be on the disconnected network as it has no networks to connect to.
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mService.mNoServiceNetwork.network.getNetId(), 1 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Bring up metered cellular. This should not apply to this preference.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Bring up unmetered Wi-Fi. This should not apply to this preference.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mEthernetNetworkAgent.getNetwork().netId, 1 /* times */,
+ mService.mNoServiceNetwork.network.getNetId(), 1 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Disconnecting OEM_PAID should result in no connectivity.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mService.mNoServiceNetwork.network.getNetId(), 1 /* times */,
+ mEthernetNetworkAgent.getNetwork().netId, 0 /* times */,
+ true /* shouldDestroyNetwork */);
+ }
+
+ /**
+ * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY following in order:
+ * NET_CAPABILITY_OEM_PRIVATE
+ * This preference should only apply to OEM_PRIVATE networks.
+ * @throws Exception
+ */
+ @Test
+ public void testMultilayerForPreferenceOemPrivateOnlyEvaluatesCorrectly()
+ throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
+
+ // Arrange PackageManager mocks
+ final int testPackageNameUid = 123;
+ final UidRangeParcel[] uidRanges =
+ toUidRangeStableParcels(uidRangesForUid(testPackageNameUid));
+ setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
+
+ // Verify the starting state. This preference doesn't support using the fallback network
+ // therefore should be on the disconnected network as it has no networks to connect to.
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mService.mNoServiceNetwork.network.getNetId(), 1 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Bring up metered cellular. This should not apply to this preference.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Bring up unmetered Wi-Fi. This should not apply to this preference.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Bring up ethernet with OEM_PRIVATE. This will satisfy NET_CAPABILITY_OEM_PRIVATE.
+ startOemManagedNetwork(false);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mEthernetNetworkAgent.getNetwork().netId, 1 /* times */,
+ mService.mNoServiceNetwork.network.getNetId(), 1 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Disconnecting OEM_PRIVATE should result in no connectivity.
+ stopOemManagedNetwork();
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mService.mNoServiceNetwork.network.getNetId(), 1 /* times */,
+ mEthernetNetworkAgent.getNetwork().netId, 0 /* times */,
+ true /* shouldDestroyNetwork */);
+ }
}
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 799bcc82d2a9..c86224a71978 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -33,6 +33,7 @@ import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.InetAddresses;
import android.net.IpSecAlgorithm;
@@ -44,6 +45,7 @@ import android.net.IpSecTransformResponse;
import android.net.IpSecTunnelInterfaceResponse;
import android.net.IpSecUdpEncapResponse;
import android.net.LinkAddress;
+import android.net.LinkProperties;
import android.net.Network;
import android.os.Binder;
import android.os.INetworkManagementService;
@@ -53,6 +55,8 @@ import android.test.mock.MockContext;
import androidx.test.filters.SmallTest;
+import com.android.server.IpSecService.TunnelInterfaceRecord;
+
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@@ -109,6 +113,7 @@ public class IpSecServiceParameterizedTest {
};
AppOpsManager mMockAppOps = mock(AppOpsManager.class);
+ ConnectivityManager mMockConnectivityMgr = mock(ConnectivityManager.class);
MockContext mMockContext = new MockContext() {
@Override
@@ -116,12 +121,22 @@ public class IpSecServiceParameterizedTest {
switch(name) {
case Context.APP_OPS_SERVICE:
return mMockAppOps;
+ case Context.CONNECTIVITY_SERVICE:
+ return mMockConnectivityMgr;
default:
return null;
}
}
@Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ if (ConnectivityManager.class == serviceClass) {
+ return Context.CONNECTIVITY_SERVICE;
+ }
+ return null;
+ }
+
+ @Override
public PackageManager getPackageManager() {
return mMockPkgMgr;
}
@@ -151,6 +166,10 @@ public class IpSecServiceParameterizedTest {
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
private static final int REMOTE_ENCAP_PORT = 4500;
+ private static final String BLESSED_PACKAGE = "blessedPackage";
+ private static final String SYSTEM_PACKAGE = "systemPackage";
+ private static final String BAD_PACKAGE = "badPackage";
+
public IpSecServiceParameterizedTest(
String sourceAddr, String destAddr, String localInnerAddr, int family) {
mSourceAddr = sourceAddr;
@@ -174,15 +193,15 @@ public class IpSecServiceParameterizedTest {
when(mMockPkgMgr.hasSystemFeature(anyString())).thenReturn(true);
// A package granted the AppOp for MANAGE_IPSEC_TUNNELS will be MODE_ALLOWED.
- when(mMockAppOps.noteOp(anyInt(), anyInt(), eq("blessedPackage")))
- .thenReturn(AppOpsManager.MODE_ALLOWED);
+ when(mMockAppOps.noteOp(anyInt(), anyInt(), eq(BLESSED_PACKAGE)))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
// A system package will not be granted the app op, so this should fall back to
// a permissions check, which should pass.
- when(mMockAppOps.noteOp(anyInt(), anyInt(), eq("systemPackage")))
- .thenReturn(AppOpsManager.MODE_DEFAULT);
+ when(mMockAppOps.noteOp(anyInt(), anyInt(), eq(SYSTEM_PACKAGE)))
+ .thenReturn(AppOpsManager.MODE_DEFAULT);
// A mismatch between the package name and the UID will return MODE_IGNORED.
- when(mMockAppOps.noteOp(anyInt(), anyInt(), eq("badPackage")))
- .thenReturn(AppOpsManager.MODE_IGNORED);
+ when(mMockAppOps.noteOp(anyInt(), anyInt(), eq(BAD_PACKAGE)))
+ .thenReturn(AppOpsManager.MODE_IGNORED);
}
//TODO: Add a test to verify SPI.
@@ -338,7 +357,7 @@ public class IpSecServiceParameterizedTest {
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
assertEquals(IpSecManager.Status.OK, createTransformResp.status);
verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
@@ -352,7 +371,7 @@ public class IpSecServiceParameterizedTest {
ipSecConfig.setAuthenticatedEncryption(AEAD_ALGO);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
assertEquals(IpSecManager.Status.OK, createTransformResp.status);
verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
@@ -370,14 +389,14 @@ public class IpSecServiceParameterizedTest {
if (mFamily == AF_INET) {
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
assertEquals(IpSecManager.Status.OK, createTransformResp.status);
verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port);
} else {
try {
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6");
} catch (IllegalArgumentException expected) {
}
@@ -396,14 +415,14 @@ public class IpSecServiceParameterizedTest {
if (mFamily == AF_INET) {
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
assertEquals(IpSecManager.Status.OK, createTransformResp.status);
verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port);
} else {
try {
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6");
} catch (IllegalArgumentException expected) {
}
@@ -417,12 +436,12 @@ public class IpSecServiceParameterizedTest {
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
assertEquals(IpSecManager.Status.OK, createTransformResp.status);
// Attempting to create transform a second time with the same SPIs should throw an error...
try {
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
fail("IpSecService should have thrown an error for reuse of SPI");
} catch (IllegalStateException expected) {
}
@@ -430,7 +449,7 @@ public class IpSecServiceParameterizedTest {
// ... even if the transform is deleted
mIpSecService.deleteTransform(createTransformResp.resourceId);
try {
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
fail("IpSecService should have thrown an error for reuse of SPI");
} catch (IllegalStateException expected) {
}
@@ -443,7 +462,7 @@ public class IpSecServiceParameterizedTest {
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
@@ -467,7 +486,7 @@ public class IpSecServiceParameterizedTest {
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
mIpSecService.deleteTransform(createTransformResp.resourceId);
verify(mMockNetd, times(1))
@@ -515,7 +534,7 @@ public class IpSecServiceParameterizedTest {
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
IpSecService.RefcountedResource refcountedRecord =
@@ -562,7 +581,7 @@ public class IpSecServiceParameterizedTest {
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
if (closeSpiBeforeApply) {
mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
@@ -592,7 +611,7 @@ public class IpSecServiceParameterizedTest {
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
// Close SPI record
mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
@@ -638,7 +657,7 @@ public class IpSecServiceParameterizedTest {
@Test
public void testCreateTunnelInterface() throws Exception {
IpSecTunnelInterfaceResponse createTunnelResp =
- createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
+ createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
// Check that we have stored the tracking object, and retrieve it
IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
@@ -661,11 +680,11 @@ public class IpSecServiceParameterizedTest {
@Test
public void testDeleteTunnelInterface() throws Exception {
IpSecTunnelInterfaceResponse createTunnelResp =
- createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
+ createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
- mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId, "blessedPackage");
+ mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId, BLESSED_PACKAGE);
// Verify quota and RefcountedResource objects cleaned up
assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
@@ -678,10 +697,73 @@ public class IpSecServiceParameterizedTest {
}
}
+ private Network createFakeUnderlyingNetwork(String interfaceName) {
+ final Network fakeNetwork = new Network(1000);
+ final LinkProperties fakeLp = new LinkProperties();
+ fakeLp.setInterfaceName(interfaceName);
+ when(mMockConnectivityMgr.getLinkProperties(eq(fakeNetwork))).thenReturn(fakeLp);
+ return fakeNetwork;
+ }
+
+ @Test
+ public void testSetNetworkForTunnelInterface() throws Exception {
+ final IpSecTunnelInterfaceResponse createTunnelResp =
+ createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
+ final Network newFakeNetwork = createFakeUnderlyingNetwork("newFakeNetworkInterface");
+ final int tunnelIfaceResourceId = createTunnelResp.resourceId;
+ mIpSecService.setNetworkForTunnelInterface(
+ tunnelIfaceResourceId, newFakeNetwork, BLESSED_PACKAGE);
+
+ final IpSecService.UserRecord userRecord =
+ mIpSecService.mUserResourceTracker.getUserRecord(mUid);
+ assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent);
+
+ final TunnelInterfaceRecord tunnelInterfaceInfo =
+ userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelIfaceResourceId);
+ assertEquals(newFakeNetwork, tunnelInterfaceInfo.getUnderlyingNetwork());
+ }
+
+ @Test
+ public void testSetNetworkForTunnelInterfaceFailsForInvalidResourceId() throws Exception {
+ final IpSecTunnelInterfaceResponse createTunnelResp =
+ createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
+ final Network newFakeNetwork = new Network(1000);
+
+ try {
+ mIpSecService.setNetworkForTunnelInterface(
+ IpSecManager.INVALID_RESOURCE_ID, newFakeNetwork, BLESSED_PACKAGE);
+ fail("Expected an IllegalArgumentException for invalid resource ID.");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testSetNetworkForTunnelInterfaceFailsWhenSettingTunnelNetwork() throws Exception {
+ final IpSecTunnelInterfaceResponse createTunnelResp =
+ createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
+ final int tunnelIfaceResourceId = createTunnelResp.resourceId;
+ final IpSecService.UserRecord userRecord =
+ mIpSecService.mUserResourceTracker.getUserRecord(mUid);
+ final TunnelInterfaceRecord tunnelInterfaceInfo =
+ userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelIfaceResourceId);
+
+ final Network newFakeNetwork =
+ createFakeUnderlyingNetwork(tunnelInterfaceInfo.getInterfaceName());
+
+ try {
+ mIpSecService.setNetworkForTunnelInterface(
+ tunnelIfaceResourceId, newFakeNetwork, BLESSED_PACKAGE);
+ fail(
+ "Expected an IllegalArgumentException because the underlying network is the"
+ + " network being exposed by this tunnel.");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
@Test
public void testTunnelInterfaceBinderDeath() throws Exception {
IpSecTunnelInterfaceResponse createTunnelResp =
- createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
+ createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
IpSecService.RefcountedResource refcountedRecord =
@@ -718,9 +800,9 @@ public class IpSecServiceParameterizedTest {
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
IpSecTunnelInterfaceResponse createTunnelResp =
- createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
+ createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
if (closeSpiBeforeApply) {
mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
@@ -728,8 +810,8 @@ public class IpSecServiceParameterizedTest {
int transformResourceId = createTransformResp.resourceId;
int tunnelResourceId = createTunnelResp.resourceId;
- mIpSecService.applyTunnelModeTransform(tunnelResourceId, IpSecManager.DIRECTION_OUT,
- transformResourceId, "blessedPackage");
+ mIpSecService.applyTunnelModeTransform(
+ tunnelResourceId, IpSecManager.DIRECTION_OUT, transformResourceId, BLESSED_PACKAGE);
for (int selAddrFamily : ADDRESS_FAMILIES) {
verify(mMockNetd)
@@ -758,17 +840,17 @@ public class IpSecServiceParameterizedTest {
addAuthAndCryptToIpSecConfig(ipSecConfig);
IpSecTransformResponse createTransformResp =
- mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE);
IpSecTunnelInterfaceResponse createTunnelResp =
- createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
+ createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE);
// Close SPI record
mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
int transformResourceId = createTransformResp.resourceId;
int tunnelResourceId = createTunnelResp.resourceId;
- mIpSecService.applyTunnelModeTransform(tunnelResourceId, IpSecManager.DIRECTION_OUT,
- transformResourceId, "blessedPackage");
+ mIpSecService.applyTunnelModeTransform(
+ tunnelResourceId, IpSecManager.DIRECTION_OUT, transformResourceId, BLESSED_PACKAGE);
for (int selAddrFamily : ADDRESS_FAMILIES) {
verify(mMockNetd)
@@ -790,7 +872,7 @@ public class IpSecServiceParameterizedTest {
@Test
public void testAddRemoveAddressFromTunnelInterface() throws Exception {
- for (String pkgName : new String[]{"blessedPackage", "systemPackage"}) {
+ for (String pkgName : new String[] {BLESSED_PACKAGE, SYSTEM_PACKAGE}) {
IpSecTunnelInterfaceResponse createTunnelResp =
createAndValidateTunnel(mSourceAddr, mDestinationAddr, pkgName);
mIpSecService.addAddressToTunnelInterface(
@@ -816,7 +898,7 @@ public class IpSecServiceParameterizedTest {
public void testAddTunnelFailsForBadPackageName() throws Exception {
try {
IpSecTunnelInterfaceResponse createTunnelResp =
- createAndValidateTunnel(mSourceAddr, mDestinationAddr, "badPackage");
+ createAndValidateTunnel(mSourceAddr, mDestinationAddr, BAD_PACKAGE);
fail("Expected a SecurityException for badPackage.");
} catch (SecurityException expected) {
}
@@ -830,7 +912,7 @@ public class IpSecServiceParameterizedTest {
try {
String addr = Inet4Address.getLoopbackAddress().getHostAddress();
mIpSecService.createTunnelInterface(
- addr, addr, new Network(0), new Binder(), "blessedPackage");
+ addr, addr, new Network(0), new Binder(), BLESSED_PACKAGE);
fail("Expected UnsupportedOperationException for disabled feature");
} catch (UnsupportedOperationException expected) {
}
diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index cd4cfcf18804..d01dc03f5fe1 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -51,6 +51,7 @@ import com.android.server.connectivity.NetworkNotificationManager.NotificationTy
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.AdditionalAnswers;
@@ -67,6 +68,8 @@ import java.util.List;
@SmallTest
public class NetworkNotificationManagerTest {
+ private static final String TEST_SSID = "Test SSID";
+ private static final String TEST_EXTRA_INFO = "extra";
static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities();
static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities();
static final NetworkCapabilities VPN_CAPABILITIES = new NetworkCapabilities();
@@ -76,6 +79,7 @@ public class NetworkNotificationManagerTest {
WIFI_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
WIFI_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ WIFI_CAPABILITIES.setSSID(TEST_SSID);
// Set the underyling network to wifi.
VPN_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
@@ -129,7 +133,7 @@ public class NetworkNotificationManagerTest {
when(mCtx.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx);
when(mCtx.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
.thenReturn(mNotificationManager);
- when(mNetworkInfo.getExtraInfo()).thenReturn("extra");
+ when(mNetworkInfo.getExtraInfo()).thenReturn(TEST_EXTRA_INFO);
when(mResources.getColor(anyInt(), any())).thenReturn(0xFF607D8B);
when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
@@ -143,11 +147,11 @@ public class NetworkNotificationManagerTest {
.notify(eq(tag), eq(PRIVATE_DNS_BROKEN.eventId), any());
final int transportType = NetworkNotificationManager.approximateTransportType(nai);
if (transportType == NetworkCapabilities.TRANSPORT_WIFI) {
- verify(mResources, times(1)).getString(title, eq(any()));
+ verify(mResources, times(1)).getString(eq(title), eq(TEST_EXTRA_INFO));
} else {
verify(mResources, times(1)).getString(title);
}
- verify(mResources, times(1)).getString(R.string.private_dns_broken_detailed);
+ verify(mResources, times(1)).getString(eq(R.string.private_dns_broken_detailed));
}
@Test
@@ -196,6 +200,9 @@ public class NetworkNotificationManagerTest {
}
@Test
+ @Ignore
+ // Ignored because the code under test calls Log.wtf, which crashes the tests on eng builds.
+ // TODO: re-enable after fixing this (e.g., turn Log.wtf into exceptions that this test catches)
public void testNoInternetNotificationsNotShownForCellular() {
mManager.showNotification(100, NO_INTERNET, mCellNai, mWifiNai, null, false);
mManager.showNotification(101, LOST_INTERNET, mCellNai, mWifiNai, null, false);
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 3556c72776dc..8f5ae97bc4c5 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -89,8 +89,8 @@ import java.util.Set;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PermissionMonitorTest {
- private static final int MOCK_USER1 = 0;
- private static final int MOCK_USER2 = 1;
+ private static final UserHandle MOCK_USER1 = UserHandle.of(0);
+ private static final UserHandle MOCK_USER2 = UserHandle.of(1);
private static final int MOCK_UID1 = 10001;
private static final int MOCK_UID2 = 10086;
private static final int SYSTEM_UID1 = 1000;
@@ -123,10 +123,7 @@ public class PermissionMonitorTest {
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
when(mUserManager.getUserHandles(eq(true))).thenReturn(
- Arrays.asList(new UserHandle[] {
- new UserHandle(MOCK_USER1),
- new UserHandle(MOCK_USER2),
- }));
+ Arrays.asList(new UserHandle[] { MOCK_USER1, MOCK_USER2 }));
mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
@@ -184,7 +181,8 @@ public class PermissionMonitorTest {
return packageInfo;
}
- private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid, int userId) {
+ private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid,
+ UserHandle user) {
final PackageInfo pkgInfo;
if (hasSystemPermission) {
pkgInfo = systemPackageInfoWithPermissions(
@@ -192,7 +190,7 @@ public class PermissionMonitorTest {
} else {
pkgInfo = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, new String[] {}, "");
}
- pkgInfo.applicationInfo.uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
+ pkgInfo.applicationInfo.uid = UserHandle.getUid(user, UserHandle.getAppId(uid));
return pkgInfo;
}
@@ -382,8 +380,8 @@ public class PermissionMonitorTest {
}).when(mockNetd).networkClearPermissionForUser(any(int[].class));
}
- public void expectPermission(Boolean permission, int[] users, int[] apps) {
- for (final int user : users) {
+ public void expectPermission(Boolean permission, UserHandle[] users, int[] apps) {
+ for (final UserHandle user : users) {
for (final int app : apps) {
final int uid = UserHandle.getUid(user, app);
if (!mApps.containsKey(uid)) {
@@ -396,8 +394,8 @@ public class PermissionMonitorTest {
}
}
- public void expectNoPermission(int[] users, int[] apps) {
- for (final int user : users) {
+ public void expectNoPermission(UserHandle[] users, int[] apps) {
+ for (final UserHandle user : users) {
for (final int app : apps) {
final int uid = UserHandle.getUid(user, app);
if (mApps.containsKey(uid)) {
@@ -425,46 +423,48 @@ public class PermissionMonitorTest {
// Add SYSTEM_PACKAGE2, expect only have network permission.
mPermissionMonitor.onUserAdded(MOCK_USER1);
- addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_UID);
- mNetdMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+ addPackageForUsers(new UserHandle[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_UID);
+ mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{SYSTEM_UID});
// Add SYSTEM_PACKAGE1, expect permission escalate.
- addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_UID);
- mNetdMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+ addPackageForUsers(new UserHandle[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_UID);
+ mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{SYSTEM_UID});
mPermissionMonitor.onUserAdded(MOCK_USER2);
- mNetdMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+ mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
new int[]{SYSTEM_UID});
- addPackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
- mNetdMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+ addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
+ mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
new int[]{SYSTEM_UID});
- mNetdMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+ mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1, MOCK_USER2},
new int[]{MOCK_UID1});
// Remove MOCK_UID1, expect no permission left for all user.
mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
- removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
- mNetdMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, new int[]{MOCK_UID1});
+ removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
+ mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1, MOCK_USER2},
+ new int[]{MOCK_UID1});
// Remove SYSTEM_PACKAGE1, expect permission downgrade.
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2});
- removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, SYSTEM_PACKAGE1, SYSTEM_UID);
- mNetdMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+ removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2},
+ SYSTEM_PACKAGE1, SYSTEM_UID);
+ mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1, MOCK_USER2},
new int[]{SYSTEM_UID});
mPermissionMonitor.onUserRemoved(MOCK_USER1);
- mNetdMonitor.expectPermission(NETWORK, new int[]{MOCK_USER2}, new int[]{SYSTEM_UID});
+ mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER2}, new int[]{SYSTEM_UID});
// Remove all packages, expect no permission left.
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{});
- removePackageForUsers(new int[]{MOCK_USER2}, SYSTEM_PACKAGE2, SYSTEM_UID);
- mNetdMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+ removePackageForUsers(new UserHandle[]{MOCK_USER2}, SYSTEM_PACKAGE2, SYSTEM_UID);
+ mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1, MOCK_USER2},
new int[]{SYSTEM_UID, MOCK_UID1});
// Remove last user, expect no redundant clearPermission is invoked.
mPermissionMonitor.onUserRemoved(MOCK_USER2);
- mNetdMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+ mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1, MOCK_USER2},
new int[]{SYSTEM_UID, MOCK_UID1});
}
@@ -548,14 +548,14 @@ public class PermissionMonitorTest {
// Normal package add/remove operations will trigger multiple intent for uids corresponding to
// each user. To simulate generic package operations, the onPackageAdded/Removed will need to be
// called multiple times with the uid corresponding to each user.
- private void addPackageForUsers(int[] users, String packageName, int uid) {
- for (final int user : users) {
+ private void addPackageForUsers(UserHandle[] users, String packageName, int uid) {
+ for (final UserHandle user : users) {
mPermissionMonitor.onPackageAdded(packageName, UserHandle.getUid(user, uid));
}
}
- private void removePackageForUsers(int[] users, String packageName, int uid) {
- for (final int user : users) {
+ private void removePackageForUsers(UserHandle[] users, String packageName, int uid) {
+ for (final UserHandle user : users) {
mPermissionMonitor.onPackageRemoved(packageName, UserHandle.getUid(user, uid));
}
}
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 32c6a75bd904..cffd2d1d428f 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -25,6 +25,7 @@ import static android.net.ConnectivityManager.NetworkCallback;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -74,6 +75,7 @@ import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.VpnManager;
import android.net.VpnService;
+import android.net.VpnTransportInfo;
import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.os.Build.VERSION_CODES;
@@ -953,14 +955,7 @@ public class VpnTest {
}
private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception {
- // TODO(b/175883995): once these tests have been updated for the changes to the UserManager
- // API, remove this ad-hoc setup code and use setMockedUsers(primaryUser) again.
- // setMockedUsers(primaryUser);
- final ArrayList<UserInfo> users = new ArrayList<>();
- users.add(primaryUser);
- when(mUserManager.getAliveUsers()).thenReturn(users);
- when(mUserManager.getUserInfo(primaryUser.id)).thenReturn(primaryUser);
- when(mUserManager.canHaveRestrictedProfile()).thenReturn(false);
+ setMockedUsers(primaryUser);
// Dummy egress interface
final LinkProperties lp = new LinkProperties();
@@ -991,6 +986,13 @@ public class VpnTest {
startRacoon("hostname", "5.6.7.8"); // address returned by deps.resolve
}
+ private void assertTransportInfoMatches(NetworkCapabilities nc, int type) {
+ assertNotNull(nc);
+ VpnTransportInfo ti = (VpnTransportInfo) nc.getTransportInfo();
+ assertNotNull(ti);
+ assertEquals(type, ti.type);
+ }
+
public void startRacoon(final String serverAddr, final String expectedAddr)
throws Exception {
final ConditionVariable legacyRunnerReady = new ConditionVariable();
@@ -1027,8 +1029,10 @@ public class VpnTest {
// Now wait for the runner to be ready before testing for the route.
ArgumentCaptor<LinkProperties> lpCaptor = ArgumentCaptor.forClass(LinkProperties.class);
+ ArgumentCaptor<NetworkCapabilities> ncCaptor =
+ ArgumentCaptor.forClass(NetworkCapabilities.class);
verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(),
- lpCaptor.capture(), any(), anyInt(), any(), anyInt());
+ lpCaptor.capture(), ncCaptor.capture(), anyInt(), 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
@@ -1038,6 +1042,8 @@ public class VpnTest {
final List<RouteInfo> actualRoutes = lpCaptor.getValue().getRoutes();
assertTrue("Expected throw route (" + expectedRoute + ") not found in " + actualRoutes,
actualRoutes.contains(expectedRoute));
+
+ assertTransportInfoMatches(ncCaptor.getValue(), VpnManager.TYPE_VPN_LEGACY);
} finally {
// Now interrupt the thread, unblock the runner and clean up.
vpn.mVpnRunner.exitVpnRunner();
@@ -1159,10 +1165,6 @@ public class VpnTest {
doReturn(UserHandle.of(userId)).when(asUserContext).getUser();
when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt()))
.thenReturn(asUserContext);
- when(asUserContext.getSystemServiceName(UserManager.class))
- .thenReturn(Context.USER_SERVICE);
- when(asUserContext.getSystemService(UserManager.class))
- .thenReturn(mUserManager);
final TestLooper testLooper = new TestLooper();
final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService,
mNetd, userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index dde78aa54199..d644739ea25e 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -80,8 +80,6 @@ import android.net.INetworkStatsSession;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
import android.net.NetworkState;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
@@ -1456,8 +1454,6 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
}
private static NetworkState buildWifiState(boolean isMetered, @NonNull String iface) {
- final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
- info.setDetailedState(DetailedState.CONNECTED, null, null);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(iface);
final NetworkCapabilities capabilities = new NetworkCapabilities();
@@ -1465,7 +1461,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
capabilities.setSSID(TEST_SSID);
- return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
+ return new NetworkState(TYPE_WIFI, prop, capabilities, WIFI_NETWORK, null);
}
private static NetworkState buildMobile3gState(String subscriberId) {
@@ -1473,17 +1469,13 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
}
private static NetworkState buildMobile3gState(String subscriberId, boolean isRoaming) {
- final NetworkInfo info = new NetworkInfo(
- TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UMTS, null, null);
- info.setDetailedState(DetailedState.CONNECTED, null, null);
- info.setRoaming(isRoaming);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities capabilities = new NetworkCapabilities();
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
+ return new NetworkState(TYPE_MOBILE, prop, capabilities, MOBILE_NETWORK, subscriberId);
}
private NetworkStats buildEmptyStats() {
@@ -1491,11 +1483,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
}
private static NetworkState buildVpnState() {
- final NetworkInfo info = new NetworkInfo(TYPE_VPN, 0, null, null);
- info.setDetailedState(DetailedState.CONNECTED, null, null);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TUN_IFACE);
- return new NetworkState(info, prop, new NetworkCapabilities(), VPN_NETWORK, null, null);
+ return new NetworkState(TYPE_VPN, prop, new NetworkCapabilities(), VPN_NETWORK, null);
}
private long getElapsedRealtime() {
diff --git a/tests/net/jni/Android.bp b/tests/net/jni/Android.bp
index 9225ffb24bd8..22a04f5c0945 100644
--- a/tests/net/jni/Android.bp
+++ b/tests/net/jni/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library_shared {
name: "libnetworkstatsfactorytestjni",
diff --git a/tests/net/smoketest/Android.bp b/tests/net/smoketest/Android.bp
index 84ae2b5d845e..1535f3ddcb38 100644
--- a/tests/net/smoketest/Android.bp
+++ b/tests/net/smoketest/Android.bp
@@ -9,6 +9,15 @@
//
// TODO: remove this hack when there is a better solution for jni_libs that includes
// dependent libraries.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksNetSmokeTests",
defaults: ["FrameworksNetTests-jni-defaults"],
@@ -19,4 +28,4 @@ android_test {
"mockito-target-minus-junit4",
"services.core",
],
-} \ No newline at end of file
+}
diff --git a/tests/notification/Android.bp b/tests/notification/Android.bp
index f05edafbf8b7..1c1b5a231e86 100644
--- a/tests/notification/Android.bp
+++ b/tests/notification/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "NotificationTests",
// Include all test java files.
diff --git a/tests/permission/Android.bp b/tests/permission/Android.bp
index bd07009de7b3..bbc2358d206e 100644
--- a/tests/permission/Android.bp
+++ b/tests/permission/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworkPermissionTests",
// Include all test java files.
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
index d1d6a26790fd..0f920b36cffd 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
@@ -16,8 +16,11 @@
package com.android.framework.permission.tests;
+import android.content.Context;
import android.os.Binder;
-import android.os.IVibratorService;
+import android.os.CombinedVibrationEffect;
+import android.os.IBinder;
+import android.os.IVibratorManagerService;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -32,27 +35,28 @@ import junit.framework.TestCase;
* Verify that Hardware apis cannot be called without required permissions.
*/
@SmallTest
-public class VibratorServicePermissionTest extends TestCase {
+public class VibratorManagerServicePermissionTest extends TestCase {
- private IVibratorService mVibratorService;
+ private IVibratorManagerService mVibratorService;
@Override
protected void setUp() throws Exception {
- mVibratorService = IVibratorService.Stub.asInterface(
- ServiceManager.getService("vibrator"));
+ mVibratorService = IVibratorManagerService.Stub.asInterface(
+ ServiceManager.getService(Context.VIBRATOR_MANAGER_SERVICE));
}
/**
- * Test that calling {@link android.os.IVibratorService#vibrate(long)} requires permissions.
+ * Test that calling {@link android.os.IVibratorManagerService#vibrate(int, String,
+ * CombinedVibrationEffect, VibrationAttributes, String, IBinder)} requires permissions.
* <p>Tests permission:
- * {@link android.Manifest.permission#VIBRATE}
- * @throws RemoteException
+ * {@link android.Manifest.permission#VIBRATE}
*/
public void testVibrate() throws RemoteException {
try {
- final VibrationEffect effect =
- VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE);
- final VibrationAttributes attrs = new VibrationAttributes.Builder()
+ CombinedVibrationEffect effect =
+ CombinedVibrationEffect.createSynced(
+ VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
+ VibrationAttributes attrs = new VibrationAttributes.Builder()
.setUsage(VibrationAttributes.USAGE_ALARM)
.build();
mVibratorService.vibrate(Process.myUid(), null, effect, attrs,
@@ -64,10 +68,10 @@ public class VibratorServicePermissionTest extends TestCase {
}
/**
- * Test that calling {@link android.os.IVibratorService#cancelVibrate()} requires permissions.
+ * Test that calling {@link android.os.IVibratorManagerService#cancelVibrate(IBinder)} requires
+ * permissions.
* <p>Tests permission:
- * {@link android.Manifest.permission#VIBRATE}
- * @throws RemoteException
+ * {@link android.Manifest.permission#VIBRATE}
*/
public void testCancelVibrate() throws RemoteException {
try {
diff --git a/tests/privapp-permissions/Android.bp b/tests/privapp-permissions/Android.bp
index b6618508d95d..082b08dea91f 100644
--- a/tests/privapp-permissions/Android.bp
+++ b/tests/privapp-permissions/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_app {
name: "PrivAppPermissionTest",
sdk_version: "current",
diff --git a/tests/testables/Android.bp b/tests/testables/Android.bp
index eb6811cf490e..c0e3d630d1ab 100644
--- a/tests/testables/Android.bp
+++ b/tests/testables/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "testables",
srcs: ["src/**/*.java"],
diff --git a/tests/testables/tests/Android.bp b/tests/testables/tests/Android.bp
index e1a58be7bd68..ba323d3fe47a 100644
--- a/tests/testables/tests/Android.bp
+++ b/tests/testables/tests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "TestablesTests",
platform_apis: true,
diff --git a/tests/utils/StubIME/Android.bp b/tests/utils/StubIME/Android.bp
index 668c92c86c51..d86068cef8b4 100644
--- a/tests/utils/StubIME/Android.bp
+++ b/tests/utils/StubIME/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "StubIME",
srcs: ["src/**/*.java"],
diff --git a/tests/utils/hostutils/Android.bp b/tests/utils/hostutils/Android.bp
index c9ad70280aa6..05f3c74218c6 100644
--- a/tests/utils/hostutils/Android.bp
+++ b/tests/utils/hostutils/Android.bp
@@ -13,6 +13,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library_host {
name: "frameworks-base-hostutils",
diff --git a/tests/utils/testutils/Android.bp b/tests/utils/testutils/Android.bp
index a6625ab9c17f..af9786b92f40 100644
--- a/tests/utils/testutils/Android.bp
+++ b/tests/utils/testutils/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library {
name: "frameworks-base-testutils",
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
index 1102624da031..4a1f96d145bd 100644
--- a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
+++ b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
@@ -42,6 +42,8 @@ public class BroadcastInterceptingContext extends ContextWrapper {
private final List<BroadcastInterceptor> mInterceptors = new ArrayList<>();
+ private boolean mUseRegisteredHandlers;
+
public abstract class FutureIntent extends FutureTask<Intent> {
public FutureIntent() {
super(
@@ -61,17 +63,24 @@ public class BroadcastInterceptingContext extends ContextWrapper {
public class BroadcastInterceptor extends FutureIntent {
private final BroadcastReceiver mReceiver;
private final IntentFilter mFilter;
+ private final Handler mHandler;
- public BroadcastInterceptor(BroadcastReceiver receiver, IntentFilter filter) {
+ public BroadcastInterceptor(BroadcastReceiver receiver, IntentFilter filter,
+ Handler handler) {
mReceiver = receiver;
mFilter = filter;
+ mHandler = mUseRegisteredHandlers ? handler : null;
}
public boolean dispatchBroadcast(Intent intent) {
if (mFilter.match(getContentResolver(), intent, false, TAG) > 0) {
if (mReceiver != null) {
final Context context = BroadcastInterceptingContext.this;
- mReceiver.onReceive(context, intent);
+ if (mHandler == null) {
+ mReceiver.onReceive(context, intent);
+ } else {
+ mHandler.post(() -> mReceiver.onReceive(context, intent));
+ }
return false;
} else {
set(intent);
@@ -116,25 +125,38 @@ public class BroadcastInterceptingContext extends ContextWrapper {
}
public FutureIntent nextBroadcastIntent(IntentFilter filter) {
- final BroadcastInterceptor interceptor = new BroadcastInterceptor(null, filter);
+ final BroadcastInterceptor interceptor = new BroadcastInterceptor(null, filter, null);
synchronized (mInterceptors) {
mInterceptors.add(interceptor);
}
return interceptor;
}
- @Override
- public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ /**
+ * Whether to send broadcasts to registered handlers. By default, receivers are called
+ * synchronously by sendBroadcast. If this method is called with {@code true}, the receiver is
+ * instead called by a runnable posted to the Handler specified when the receiver was
+ * registered. This method applies only to future registrations, already-registered receivers
+ * are unaffected.
+ */
+ public void setUseRegisteredHandlers(boolean use) {
synchronized (mInterceptors) {
- mInterceptors.add(new BroadcastInterceptor(receiver, filter));
+ mUseRegisteredHandlers = use;
}
- return null;
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ return registerReceiver(receiver, filter, null, null);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
- return registerReceiver(receiver, filter);
+ synchronized (mInterceptors) {
+ mInterceptors.add(new BroadcastInterceptor(receiver, filter, scheduler));
+ }
+ return null;
}
@Override
diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp
index c04ddd78e69b..41f73cd9c706 100644
--- a/tests/vcn/Android.bp
+++ b/tests/vcn/Android.bp
@@ -2,6 +2,15 @@
// Build FrameworksVcnTests package
//########################################################################
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksVcnTests",
srcs: [
@@ -21,7 +30,6 @@ android_test {
"services.core",
],
libs: [
- "android.net.ipsec.ike.stubs.module_lib",
"android.test.runner",
"android.test.base",
"android.test.mock",
diff --git a/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java b/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java
new file mode 100644
index 000000000000..36f5e41462e8
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeSaProposal;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.SaProposal;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+
+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 VcnControlPlaneIkeConfigTest {
+ private static final IkeSessionParams IKE_PARAMS;
+ private static final TunnelModeChildSessionParams CHILD_PARAMS;
+
+ static {
+ IkeSaProposal ikeProposal =
+ new IkeSaProposal.Builder()
+ .addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128)
+ .addDhGroup(DH_GROUP_2048_BIT_MODP)
+ .addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC)
+ .build();
+
+ Context mockContext = mock(Context.class);
+ ConnectivityManager mockConnectManager = mock(ConnectivityManager.class);
+ doReturn(mockConnectManager)
+ .when(mockContext)
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ doReturn(mock(Network.class)).when(mockConnectManager).getActiveNetwork();
+
+ final String serverHostname = "192.0.2.100";
+ final String testLocalId = "test.client.com";
+ final String testRemoteId = "test.server.com";
+ final byte[] psk = "psk".getBytes();
+
+ IKE_PARAMS =
+ new IkeSessionParams.Builder(mockContext)
+ .setServerHostname(serverHostname)
+ .addSaProposal(ikeProposal)
+ .setLocalIdentification(new IkeFqdnIdentification(testLocalId))
+ .setRemoteIdentification(new IkeFqdnIdentification(testRemoteId))
+ .setAuthPsk(psk)
+ .build();
+
+ ChildSaProposal childProposal =
+ new ChildSaProposal.Builder()
+ .addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128)
+ .build();
+ CHILD_PARAMS =
+ new TunnelModeChildSessionParams.Builder().addSaProposal(childProposal).build();
+ }
+
+ // Package private for use in VcnGatewayConnectionConfigTest
+ static VcnControlPlaneIkeConfig buildTestConfig() {
+ return new VcnControlPlaneIkeConfig(IKE_PARAMS, CHILD_PARAMS);
+ }
+
+ @Test
+ public void testGetters() {
+ final VcnControlPlaneIkeConfig config = buildTestConfig();
+ assertEquals(IKE_PARAMS, config.getIkeSessionParams());
+ assertEquals(CHILD_PARAMS, config.getChildSessionParams());
+ }
+
+ @Test
+ public void testConstructConfigWithoutIkeParams() {
+ try {
+ new VcnControlPlaneIkeConfig(null, CHILD_PARAMS);
+ fail("Expect to fail because ikeParams was null");
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ @Test
+ public void testBuilderConfigWithoutChildParams() {
+ try {
+ new VcnControlPlaneIkeConfig(IKE_PARAMS, null);
+ fail("Expect to fail because childParams was null");
+ } catch (NullPointerException expected) {
+ }
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 3e659d0bc128..5b17aadc50a6 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -18,6 +18,7 @@ package android.net.vcn;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
import android.net.NetworkCapabilities;
@@ -57,17 +58,22 @@ public class VcnGatewayConnectionConfigTest {
};
public static final int MAX_MTU = 1360;
+ public static final VcnControlPlaneConfig CONTROL_PLANE_CONFIG =
+ VcnControlPlaneIkeConfigTest.buildTestConfig();
+
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfig() {
return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
}
+ private static VcnGatewayConnectionConfig.Builder newBuilder() {
+ return new VcnGatewayConnectionConfig.Builder(CONTROL_PLANE_CONFIG);
+ }
+
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
final VcnGatewayConnectionConfig.Builder builder =
- new VcnGatewayConnectionConfig.Builder()
- .setRetryInterval(RETRY_INTERVALS_MS)
- .setMaxMtu(MAX_MTU);
+ newBuilder().setRetryInterval(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
for (int caps : exposedCaps) {
builder.addExposedCapability(caps);
@@ -81,9 +87,19 @@ public class VcnGatewayConnectionConfigTest {
}
@Test
+ public void testBuilderRequiresNonNullControlPlaneConfig() {
+ try {
+ new VcnGatewayConnectionConfig.Builder(null).build();
+
+ fail("Expected exception due to invalid control plane config");
+ } catch (NullPointerException e) {
+ }
+ }
+
+ @Test
public void testBuilderRequiresNonEmptyExposedCaps() {
try {
- new VcnGatewayConnectionConfig.Builder()
+ newBuilder()
.addRequiredUnderlyingCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build();
@@ -95,9 +111,7 @@ public class VcnGatewayConnectionConfigTest {
@Test
public void testBuilderRequiresNonEmptyUnderlyingCaps() {
try {
- new VcnGatewayConnectionConfig.Builder()
- .addExposedCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build();
+ newBuilder().addExposedCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).build();
fail("Expected exception due to invalid required underlying capabilities");
} catch (IllegalArgumentException e) {
@@ -107,7 +121,7 @@ public class VcnGatewayConnectionConfigTest {
@Test
public void testBuilderRequiresNonNullRetryInterval() {
try {
- new VcnGatewayConnectionConfig.Builder().setRetryInterval(null);
+ newBuilder().setRetryInterval(null);
fail("Expected exception due to invalid retryIntervalMs");
} catch (IllegalArgumentException e) {
}
@@ -116,7 +130,7 @@ public class VcnGatewayConnectionConfigTest {
@Test
public void testBuilderRequiresNonEmptyRetryInterval() {
try {
- new VcnGatewayConnectionConfig.Builder().setRetryInterval(new long[0]);
+ newBuilder().setRetryInterval(new long[0]);
fail("Expected exception due to invalid retryIntervalMs");
} catch (IllegalArgumentException e) {
}
@@ -125,8 +139,7 @@ public class VcnGatewayConnectionConfigTest {
@Test
public void testBuilderRequiresValidMtu() {
try {
- new VcnGatewayConnectionConfig.Builder()
- .setMaxMtu(VcnGatewayConnectionConfig.MIN_MTU_V6 - 1);
+ newBuilder().setMaxMtu(VcnGatewayConnectionConfig.MIN_MTU_V6 - 1);
fail("Expected exception due to invalid mtu");
} catch (IllegalArgumentException e) {
}
@@ -144,6 +157,9 @@ public class VcnGatewayConnectionConfigTest {
Arrays.sort(underlyingCaps);
assertArrayEquals(UNDERLYING_CAPS, underlyingCaps);
+ assertEquals(CONTROL_PLANE_CONFIG, config.getControlPlaneConfig());
+ assertFalse(CONTROL_PLANE_CONFIG == config.getControlPlaneConfig());
+
assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMs());
assertEquals(MAX_MTU, config.getMaxMtu());
}
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index f9db408462b7..708767605508 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -22,28 +22,40 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
+import android.net.vcn.VcnManager.VcnStatusCallback;
+import android.net.vcn.VcnManager.VcnStatusCallbackBinder;
import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener;
+import android.os.ParcelUuid;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import java.net.UnknownHostException;
+import java.util.UUID;
import java.util.concurrent.Executor;
public class VcnManagerTest {
+ private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
+ private static final int[] UNDERLYING_NETWORK_CAPABILITIES = {
+ NetworkCapabilities.NET_CAPABILITY_IMS, NetworkCapabilities.NET_CAPABILITY_INTERNET
+ };
private static final Executor INLINE_EXECUTOR = Runnable::run;
private IVcnManagementService mMockVcnManagementService;
private VcnUnderlyingNetworkPolicyListener mMockPolicyListener;
+ private VcnStatusCallback mMockStatusCallback;
private Context mContext;
private VcnManager mVcnManager;
@@ -52,6 +64,7 @@ public class VcnManagerTest {
public void setUp() {
mMockVcnManagementService = mock(IVcnManagementService.class);
mMockPolicyListener = mock(VcnUnderlyingNetworkPolicyListener.class);
+ mMockStatusCallback = mock(VcnStatusCallback.class);
mContext = getContext();
mVcnManager = new VcnManager(mContext, mMockVcnManagementService);
@@ -65,7 +78,7 @@ public class VcnManagerTest {
ArgumentCaptor.forClass(IVcnUnderlyingNetworkPolicyListener.class);
verify(mMockVcnManagementService).addVcnUnderlyingNetworkPolicyListener(captor.capture());
- assertTrue(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+ assertTrue(VcnManager.getAllPolicyListeners().containsKey(mMockPolicyListener));
IVcnUnderlyingNetworkPolicyListener listenerWrapper = captor.getValue();
listenerWrapper.onPolicyChanged();
@@ -78,7 +91,7 @@ public class VcnManagerTest {
mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
- assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+ assertFalse(VcnManager.getAllPolicyListeners().containsKey(mMockPolicyListener));
verify(mMockVcnManagementService)
.addVcnUnderlyingNetworkPolicyListener(
any(IVcnUnderlyingNetworkPolicyListener.class));
@@ -88,7 +101,7 @@ public class VcnManagerTest {
public void testRemoveVcnUnderlyingNetworkPolicyListenerUnknownListener() throws Exception {
mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
- assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+ assertFalse(VcnManager.getAllPolicyListeners().containsKey(mMockPolicyListener));
verify(mMockVcnManagementService, never())
.addVcnUnderlyingNetworkPolicyListener(
any(IVcnUnderlyingNetworkPolicyListener.class));
@@ -132,4 +145,74 @@ public class VcnManagerTest {
public void testGetUnderlyingNetworkPolicyNullLinkProperties() throws Exception {
mVcnManager.getUnderlyingNetworkPolicy(new NetworkCapabilities(), null);
}
+
+ @Test
+ public void testRegisterVcnStatusCallback() throws Exception {
+ mVcnManager.registerVcnStatusCallback(SUB_GROUP, INLINE_EXECUTOR, mMockStatusCallback);
+
+ verify(mMockVcnManagementService)
+ .registerVcnStatusCallback(eq(SUB_GROUP), notNull(), any());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testRegisterVcnStatusCallbackAlreadyRegistered() throws Exception {
+ mVcnManager.registerVcnStatusCallback(SUB_GROUP, INLINE_EXECUTOR, mMockStatusCallback);
+ mVcnManager.registerVcnStatusCallback(SUB_GROUP, INLINE_EXECUTOR, mMockStatusCallback);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testRegisterVcnStatusCallbackNullSubscriptionGroup() throws Exception {
+ mVcnManager.registerVcnStatusCallback(null, INLINE_EXECUTOR, mMockStatusCallback);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testRegisterVcnStatusCallbackNullExecutor() throws Exception {
+ mVcnManager.registerVcnStatusCallback(SUB_GROUP, null, mMockStatusCallback);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testRegisterVcnStatusCallbackNullCallback() throws Exception {
+ mVcnManager.registerVcnStatusCallback(SUB_GROUP, INLINE_EXECUTOR, null);
+ }
+
+ @Test
+ public void testUnregisterVcnStatusCallback() throws Exception {
+ mVcnManager.registerVcnStatusCallback(SUB_GROUP, INLINE_EXECUTOR, mMockStatusCallback);
+
+ mVcnManager.unregisterVcnStatusCallback(mMockStatusCallback);
+
+ verify(mMockVcnManagementService).unregisterVcnStatusCallback(any());
+ }
+
+ @Test
+ public void testUnregisterUnknownVcnStatusCallback() throws Exception {
+ mVcnManager.unregisterVcnStatusCallback(mMockStatusCallback);
+
+ verifyNoMoreInteractions(mMockVcnManagementService);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testUnregisterNullVcnStatusCallback() throws Exception {
+ mVcnManager.unregisterVcnStatusCallback(null);
+ }
+
+ @Test
+ public void testVcnStatusCallbackBinder() throws Exception {
+ IVcnStatusCallback cbBinder =
+ new VcnStatusCallbackBinder(INLINE_EXECUTOR, mMockStatusCallback);
+
+ cbBinder.onEnteredSafeMode();
+ verify(mMockStatusCallback).onEnteredSafeMode();
+
+ cbBinder.onGatewayConnectionError(
+ UNDERLYING_NETWORK_CAPABILITIES,
+ VcnManager.VCN_ERROR_CODE_NETWORK_ERROR,
+ "java.net.UnknownHostException",
+ "exception_message");
+ verify(mMockStatusCallback)
+ .onGatewayConnectionError(
+ eq(UNDERLYING_NETWORK_CAPABILITIES),
+ eq(VcnManager.VCN_ERROR_CODE_NETWORK_ERROR),
+ any(UnknownHostException.class));
+ }
}
diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java
new file mode 100644
index 000000000000..2110d6ee7c86
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.vcn;
+
+import static com.android.testutils.ParcelUtils.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.net.TelephonyNetworkSpecifier;
+
+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 VcnUnderlyingNetworkSpecifierTest {
+ private static final int[] TEST_SUB_IDS = new int[] {1, 2, 3, 5};
+
+ @Test
+ public void testGetSubIds() {
+ final VcnUnderlyingNetworkSpecifier specifier =
+ new VcnUnderlyingNetworkSpecifier(TEST_SUB_IDS);
+
+ assertEquals(TEST_SUB_IDS, specifier.getSubIds());
+ }
+
+ @Test
+ public void testParceling() {
+ final VcnUnderlyingNetworkSpecifier specifier =
+ new VcnUnderlyingNetworkSpecifier(TEST_SUB_IDS);
+ assertParcelSane(specifier, 1);
+ }
+
+ @Test
+ public void testCanBeSatisfiedByTelephonyNetworkSpecifier() {
+ final TelephonyNetworkSpecifier telSpecifier =
+ new TelephonyNetworkSpecifier(TEST_SUB_IDS[0]);
+
+ final VcnUnderlyingNetworkSpecifier specifier =
+ new VcnUnderlyingNetworkSpecifier(TEST_SUB_IDS);
+ assertTrue(specifier.canBeSatisfiedBy(telSpecifier));
+ }
+}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 485964487fda..45b2381ce06d 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -16,12 +16,17 @@
package com.android.server;
+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 com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static org.junit.Assert.assertEquals;
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;
@@ -38,9 +43,11 @@ import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.content.Context;
import android.net.ConnectivityManager;
@@ -48,6 +55,7 @@ import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.net.NetworkCapabilities.Transport;
import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnConfigTest;
@@ -66,7 +74,9 @@ import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.VcnManagementService.VcnSafemodeCallback;
+import com.android.internal.util.LocationPermissionChecker;
+import com.android.server.VcnManagementService.VcnCallback;
+import com.android.server.VcnManagementService.VcnStatusCallbackInfo;
import com.android.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
@@ -106,6 +116,7 @@ public class VcnManagementServiceTest {
Collections.unmodifiableMap(Collections.singletonMap(TEST_UUID_1, TEST_VCN_CONFIG));
private static final int TEST_SUBSCRIPTION_ID = 1;
+ private static final int TEST_SUBSCRIPTION_ID_2 = 2;
private static final SubscriptionInfo TEST_SUBSCRIPTION_INFO =
new SubscriptionInfo(
TEST_SUBSCRIPTION_ID /* id */,
@@ -142,14 +153,17 @@ public class VcnManagementServiceTest {
mock(PersistableBundleUtils.LockingReadWriteHelper.class);
private final TelephonySubscriptionTracker mSubscriptionTracker =
mock(TelephonySubscriptionTracker.class);
+ private final LocationPermissionChecker mLocationPermissionChecker =
+ mock(LocationPermissionChecker.class);
- private final ArgumentCaptor<VcnSafemodeCallback> mSafemodeCallbackCaptor =
- ArgumentCaptor.forClass(VcnSafemodeCallback.class);
+ private final ArgumentCaptor<VcnCallback> mVcnCallbackCaptor =
+ ArgumentCaptor.forClass(VcnCallback.class);
private final VcnManagementService mVcnMgmtSvc;
private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener =
mock(IVcnUnderlyingNetworkPolicyListener.class);
+ private final IVcnStatusCallback mMockStatusCallback = mock(IVcnStatusCallback.class);
private final IBinder mMockIBinder = mock(IBinder.class);
public VcnManagementServiceTest() throws Exception {
@@ -166,6 +180,7 @@ public class VcnManagementServiceTest {
doReturn(TEST_PACKAGE_NAME).when(mMockContext).getOpPackageName();
+ doReturn(mMockContext).when(mVcnContext).getContext();
doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper();
doReturn(TEST_UID).when(mMockDeps).getBinderCallingUid();
doReturn(mVcnContext)
@@ -183,6 +198,9 @@ public class VcnManagementServiceTest {
doReturn(mConfigReadWriteHelper)
.when(mMockDeps)
.newPersistableBundleLockingReadWriteHelper(any());
+ doReturn(mLocationPermissionChecker)
+ .when(mMockDeps)
+ .newLocationPermissionChecker(eq(mMockContext));
// Setup VCN instance generation
doAnswer((invocation) -> {
@@ -201,6 +219,7 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
doReturn(mMockIBinder).when(mMockPolicyListener).asBinder();
+ doReturn(mMockIBinder).when(mMockStatusCallback).asBinder();
// Make sure the profiles are loaded.
mTestLooper.dispatchAll();
@@ -537,59 +556,121 @@ public class VcnManagementServiceTest {
Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup));
}
- private void verifyMergedNetworkCapabilitiesIsVcnManaged(
- NetworkCapabilities mergedCapabilities, @Transport int transportType) {
+ private void verifyMergedNetworkCapabilities(
+ NetworkCapabilities mergedCapabilities,
+ @Transport int transportType,
+ boolean isVcnManaged,
+ boolean isRestricted) {
assertTrue(mergedCapabilities.hasTransport(transportType));
- assertFalse(
+ assertEquals(
+ !isVcnManaged,
mergedCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED));
+ assertEquals(
+ !isRestricted,
+ mergedCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED));
+ }
+
+ private void setupSubscriptionAndStartVcn(int subId, ParcelUuid subGrp, boolean isVcnActive) {
+ setUpVcnSubscription(subId, subGrp);
+ final Vcn vcn = startAndGetVcnInstance(subGrp);
+ doReturn(isVcnActive).when(vcn).isActive();
+ }
+
+ private VcnUnderlyingNetworkPolicy startVcnAndGetPolicyForTransport(
+ int subId, ParcelUuid subGrp, boolean isVcnActive, int transport) {
+ setupSubscriptionAndStartVcn(subId, subGrp, isVcnActive);
+
+ final NetworkCapabilities.Builder ncBuilder = new NetworkCapabilities.Builder();
+ ncBuilder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+ if (transport == TRANSPORT_CELLULAR) {
+ ncBuilder
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID));
+ } else if (transport == TRANSPORT_WIFI) {
+ WifiInfo wifiInfo = mock(WifiInfo.class);
+ when(wifiInfo.makeCopy(anyBoolean())).thenReturn(wifiInfo);
+ when(mMockDeps.getSubIdForWifiInfo(eq(wifiInfo))).thenReturn(TEST_SUBSCRIPTION_ID);
+
+ ncBuilder
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .setTransportInfo(wifiInfo);
+ } else {
+ throw new IllegalArgumentException("Unknown transport");
+ }
+
+ return mVcnMgmtSvc.getUnderlyingNetworkPolicy(ncBuilder.build(), new LinkProperties());
}
@Test
public void testGetUnderlyingNetworkPolicyCellular() throws Exception {
- setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
+ final VcnUnderlyingNetworkPolicy policy =
+ startVcnAndGetPolicyForTransport(
+ TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_CELLULAR);
- NetworkCapabilities nc =
- new NetworkCapabilities.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID))
- .build();
+ assertFalse(policy.isTeardownRequested());
+ verifyMergedNetworkCapabilities(
+ policy.getMergedNetworkCapabilities(),
+ TRANSPORT_CELLULAR,
+ true /* isVcnManaged */,
+ false /* isRestricted */);
+ }
- VcnUnderlyingNetworkPolicy policy =
- mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
+ @Test
+ public void testGetUnderlyingNetworkPolicyCellular_safeMode() throws Exception {
+ final VcnUnderlyingNetworkPolicy policy =
+ startVcnAndGetPolicyForTransport(
+ TEST_SUBSCRIPTION_ID,
+ TEST_UUID_2,
+ false /* isActive */,
+ TRANSPORT_CELLULAR);
assertFalse(policy.isTeardownRequested());
- verifyMergedNetworkCapabilitiesIsVcnManaged(
- policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR);
+ verifyMergedNetworkCapabilities(
+ policy.getMergedNetworkCapabilities(),
+ NetworkCapabilities.TRANSPORT_CELLULAR,
+ false /* isVcnManaged */,
+ false /* isRestricted */);
}
@Test
public void testGetUnderlyingNetworkPolicyWifi() throws Exception {
- setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
+ final VcnUnderlyingNetworkPolicy policy =
+ startVcnAndGetPolicyForTransport(
+ TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_WIFI);
- WifiInfo wifiInfo = mock(WifiInfo.class);
- when(wifiInfo.makeCopy(anyBoolean())).thenReturn(wifiInfo);
- when(mMockDeps.getSubIdForWifiInfo(eq(wifiInfo))).thenReturn(TEST_SUBSCRIPTION_ID);
- NetworkCapabilities nc =
- new NetworkCapabilities.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .setTransportInfo(wifiInfo)
- .build();
+ assertFalse(policy.isTeardownRequested());
+ verifyMergedNetworkCapabilities(
+ policy.getMergedNetworkCapabilities(),
+ NetworkCapabilities.TRANSPORT_WIFI,
+ true /* isVcnManaged */,
+ true /* isRestricted */);
+ }
- VcnUnderlyingNetworkPolicy policy =
- mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
+ @Test
+ public void testGetUnderlyingNetworkPolicyVcnWifi_safeMode() throws Exception {
+ final VcnUnderlyingNetworkPolicy policy =
+ startVcnAndGetPolicyForTransport(
+ TEST_SUBSCRIPTION_ID, TEST_UUID_2, false /* isActive */, TRANSPORT_WIFI);
assertFalse(policy.isTeardownRequested());
- verifyMergedNetworkCapabilitiesIsVcnManaged(
- policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI);
+ verifyMergedNetworkCapabilities(
+ policy.getMergedNetworkCapabilities(),
+ NetworkCapabilities.TRANSPORT_WIFI,
+ false /* isVcnManaged */,
+ true /* isRestricted */);
}
@Test
public void testGetUnderlyingNetworkPolicyNonVcnNetwork() throws Exception {
+ setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_1, true /* isActive */);
+
NetworkCapabilities nc =
new NetworkCapabilities.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID))
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_2))
.build();
VcnUnderlyingNetworkPolicy policy =
@@ -640,24 +721,138 @@ public class VcnManagementServiceTest {
verify(mMockPolicyListener).onPolicyChanged();
}
- @Test
- public void testVcnSafemodeCallbackOnEnteredSafemode() throws Exception {
- TelephonySubscriptionSnapshot snapshot =
- triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
+ private void verifyVcnCallback(
+ @NonNull ParcelUuid subGroup, @NonNull TelephonySubscriptionSnapshot snapshot)
+ throws Exception {
verify(mMockDeps)
.newVcn(
eq(mVcnContext),
- eq(TEST_UUID_1),
+ eq(subGroup),
eq(TEST_VCN_CONFIG),
eq(snapshot),
- mSafemodeCallbackCaptor.capture());
+ mVcnCallbackCaptor.capture());
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
- VcnSafemodeCallback safemodeCallback = mSafemodeCallbackCaptor.getValue();
- safemodeCallback.onEnteredSafemode();
+ VcnCallback vcnCallback = mVcnCallbackCaptor.getValue();
+ vcnCallback.onEnteredSafeMode();
- assertFalse(mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1).isActive());
verify(mMockPolicyListener).onPolicyChanged();
}
+
+ @Test
+ public void testVcnCallbackOnEnteredSafeMode() throws Exception {
+ TelephonySubscriptionSnapshot snapshot =
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
+
+ verifyVcnCallback(TEST_UUID_1, snapshot);
+ }
+
+ private void triggerVcnStatusCallbackOnEnteredSafeMode(
+ @NonNull ParcelUuid subGroup,
+ @NonNull String pkgName,
+ int uid,
+ boolean hasPermissionsforSubGroup,
+ boolean hasLocationPermission)
+ throws Exception {
+ TelephonySubscriptionSnapshot snapshot =
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(subGroup));
+
+ doReturn(hasPermissionsforSubGroup)
+ .when(snapshot)
+ .packageHasPermissionsForSubscriptionGroup(eq(subGroup), eq(pkgName));
+
+ doReturn(hasLocationPermission)
+ .when(mLocationPermissionChecker)
+ .checkLocationPermission(eq(pkgName), any(), eq(uid), any());
+
+ mVcnMgmtSvc.registerVcnStatusCallback(subGroup, mMockStatusCallback, pkgName);
+
+ // Trigger systemReady() to set up LocationPermissionChecker
+ mVcnMgmtSvc.systemReady();
+
+ verifyVcnCallback(subGroup, snapshot);
+ }
+
+ @Test
+ public void testVcnStatusCallbackOnEnteredSafeModeWithCarrierPrivileges() throws Exception {
+ triggerVcnStatusCallbackOnEnteredSafeMode(
+ TEST_UUID_1,
+ TEST_PACKAGE_NAME,
+ TEST_UID,
+ true /* hasPermissionsforSubGroup */,
+ true /* hasLocationPermission */);
+
+ verify(mMockStatusCallback, times(1)).onEnteredSafeMode();
+ }
+
+ @Test
+ public void testVcnStatusCallbackOnEnteredSafeModeWithoutCarrierPrivileges() throws Exception {
+ triggerVcnStatusCallbackOnEnteredSafeMode(
+ TEST_UUID_1,
+ TEST_PACKAGE_NAME,
+ TEST_UID,
+ false /* hasPermissionsforSubGroup */,
+ true /* hasLocationPermission */);
+
+ verify(mMockStatusCallback, never()).onEnteredSafeMode();
+ }
+
+ @Test
+ public void testVcnStatusCallbackOnEnteredSafeModeWithoutLocationPermission() throws Exception {
+ triggerVcnStatusCallbackOnEnteredSafeMode(
+ TEST_UUID_1,
+ TEST_PACKAGE_NAME,
+ TEST_UID,
+ true /* hasPermissionsforSubGroup */,
+ false /* hasLocationPermission */);
+
+ verify(mMockStatusCallback, never()).onEnteredSafeMode();
+ }
+
+ @Test
+ public void testRegisterVcnStatusCallback() throws Exception {
+ mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
+
+ Map<IBinder, VcnStatusCallbackInfo> callbacks = mVcnMgmtSvc.getAllStatusCallbacks();
+ VcnStatusCallbackInfo cbInfo = callbacks.get(mMockIBinder);
+
+ assertNotNull(cbInfo);
+ assertEquals(TEST_UUID_1, cbInfo.mSubGroup);
+ assertEquals(mMockStatusCallback, cbInfo.mCallback);
+ assertEquals(TEST_PACKAGE_NAME, cbInfo.mPkgName);
+ assertEquals(TEST_UID, cbInfo.mUid);
+ verify(mMockIBinder).linkToDeath(eq(cbInfo), anyInt());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testRegisterVcnStatusCallbackDuplicate() {
+ mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
+ mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
+ }
+
+ @Test
+ public void testUnregisterVcnStatusCallback() {
+ mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
+ Map<IBinder, VcnStatusCallbackInfo> callbacks = mVcnMgmtSvc.getAllStatusCallbacks();
+ VcnStatusCallbackInfo cbInfo = callbacks.get(mMockIBinder);
+
+ mVcnMgmtSvc.unregisterVcnStatusCallback(mMockStatusCallback);
+ assertTrue(mVcnMgmtSvc.getAllStatusCallbacks().isEmpty());
+ verify(mMockIBinder).unlinkToDeath(eq(cbInfo), anyInt());
+ }
+
+ @Test(expected = SecurityException.class)
+ public void testRegisterVcnStatusCallbackInvalidPackage() {
+ doThrow(new SecurityException()).when(mAppOpsMgr).checkPackage(TEST_UID, TEST_PACKAGE_NAME);
+
+ mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
+ }
+
+ @Test
+ public void testUnregisterVcnStatusCallbackNeverRegistered() {
+ mVcnMgmtSvc.unregisterVcnStatusCallback(mMockStatusCallback);
+
+ assertTrue(mVcnMgmtSvc.getAllStatusCallbacks().isEmpty());
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index e20070ee4f07..69c21b967917 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -18,15 +18,35 @@ package com.android.server.vcn;
import static android.net.IpSecManager.DIRECTION_IN;
import static android.net.IpSecManager.DIRECTION_OUT;
+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;
+import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR;
+import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR;
+import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.net.LinkProperties;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
+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.TemporaryFailureException;
+import android.net.vcn.VcnManager.VcnErrorCode;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -34,6 +54,11 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.util.Collections;
/** Tests for VcnGatewayConnection.ConnectedState */
@RunWith(AndroidJUnit4.class)
@@ -60,6 +85,11 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
}
@Test
+ public void testEnterStateDoesNotCancelSafeModeAlarm() {
+ verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+ }
+
+ @Test
public void testNullNetworkDoesNotTriggerDisconnect() throws Exception {
mGatewayConnection
.getUnderlyingNetworkTrackerCallback()
@@ -68,6 +98,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
verify(mIkeSession, never()).close();
+ verifyDisconnectRequestAlarmAndGetCallback(false /* expectCanceled */);
}
@Test
@@ -107,21 +138,156 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
}
@Test
+ public void testMigratedTransformsAreApplied() throws Exception {
+ getChildSessionCallback()
+ .onIpSecTransformsMigrated(makeDummyIpSecTransform(), makeDummyIpSecTransform());
+ mTestLooper.dispatchAll();
+
+ for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) {
+ verify(mIpSecSvc)
+ .applyTunnelModeTransform(
+ eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any());
+ }
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testChildOpenedRegistersNetwork() throws Exception {
+ // Verify scheduled but not canceled when entering ConnectedState
+ verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+
+ final VcnChildSessionConfiguration mMockChildSessionConfig =
+ mock(VcnChildSessionConfiguration.class);
+ doReturn(Collections.singletonList(TEST_INTERNAL_ADDR))
+ .when(mMockChildSessionConfig)
+ .getInternalAddresses();
+ doReturn(Collections.singletonList(TEST_DNS_ADDR))
+ .when(mMockChildSessionConfig)
+ .getInternalDnsServers();
+
+ getChildSessionCallback().onOpened(mMockChildSessionConfig);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+
+ final ArgumentCaptor<LinkProperties> lpCaptor =
+ ArgumentCaptor.forClass(LinkProperties.class);
+ final ArgumentCaptor<NetworkCapabilities> ncCaptor =
+ ArgumentCaptor.forClass(NetworkCapabilities.class);
+ verify(mConnMgr)
+ .registerNetworkAgent(
+ any(),
+ any(),
+ lpCaptor.capture(),
+ ncCaptor.capture(),
+ anyInt(),
+ any(),
+ anyInt());
+ verify(mIpSecSvc)
+ .addAddressToTunnelInterface(
+ eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any());
+
+ final LinkProperties lp = lpCaptor.getValue();
+ assertEquals(Collections.singletonList(TEST_INTERNAL_ADDR), lp.getLinkAddresses());
+ assertEquals(Collections.singletonList(TEST_DNS_ADDR), lp.getDnsServers());
+
+ final NetworkCapabilities nc = ncCaptor.getValue();
+ assertTrue(nc.hasTransport(TRANSPORT_CELLULAR));
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ for (int cap : mConfig.getAllExposedCapabilities()) {
+ assertTrue(nc.hasCapability(cap));
+ }
+
+ // Now that Vcn Network is up, notify it as validated and verify the SafeMode alarm is
+ // canceled
+ mGatewayConnection.mNetworkAgent.onValidationStatus(
+ NetworkAgent.VALIDATION_STATUS_VALID, null /* redirectUri */);
+ verify(mSafeModeTimeoutAlarm).cancel();
+ }
+
+ @Test
public void testChildSessionClosedTriggersDisconnect() throws Exception {
+ // Verify scheduled but not canceled when entering ConnectedState
+ verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+
getChildSessionCallback().onClosed();
mTestLooper.dispatchAll();
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+
+ // Since network never validated, verify mSafeModeTimeoutAlarm not canceled
+ verifyNoMoreInteractions(mSafeModeTimeoutAlarm);
+
+ // The child session was closed without exception, so verify that the GatewayStatusCallback
+ // was not notified
+ verifyNoMoreInteractions(mGatewayStatusCallback);
+ }
+
+ @Test
+ public void testChildSessionClosedExceptionallyNotifiesGatewayStatusCallback()
+ throws Exception {
+ final IkeInternalException exception = new IkeInternalException(mock(IOException.class));
+ getChildSessionCallback().onClosedExceptionally(exception);
+ mTestLooper.dispatchAll();
+
+ verify(mGatewayStatusCallback)
+ .onGatewayConnectionError(
+ eq(mConfig.getRequiredUnderlyingCapabilities()),
+ eq(VCN_ERROR_CODE_INTERNAL_ERROR),
+ any(),
+ any());
}
@Test
public void testIkeSessionClosedTriggersDisconnect() throws Exception {
+ // Verify scheduled but not canceled when entering ConnectedState
+ verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+
getIkeSessionCallback().onClosed();
mTestLooper.dispatchAll();
assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
verify(mIkeSession).close();
+
+ // Since network never validated, verify mSafeModeTimeoutAlarm not canceled
+ verifyNoMoreInteractions(mSafeModeTimeoutAlarm);
+
+ // IkeSession closed with no error, so verify that the GatewayStatusCallback was not
+ // notified
+ verifyNoMoreInteractions(mGatewayStatusCallback);
}
- // TODO: Add tests for childOpened() when ChildSessionConfiguration can be mocked or created
+ private void verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback(
+ IkeException cause, @VcnErrorCode int expectedErrorType) {
+ getIkeSessionCallback().onClosedExceptionally(cause);
+ mTestLooper.dispatchAll();
+
+ verify(mIkeSession).close();
+
+ verify(mGatewayStatusCallback)
+ .onGatewayConnectionError(
+ eq(mConfig.getRequiredUnderlyingCapabilities()),
+ eq(expectedErrorType),
+ any(),
+ any());
+ }
+
+ @Test
+ public void testIkeSessionClosedExceptionallyAuthenticationFailure() throws Exception {
+ verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback(
+ new AuthenticationFailedException("vcn test"), VCN_ERROR_CODE_CONFIG_ERROR);
+ }
+
+ @Test
+ public void testIkeSessionClosedExceptionallyDnsFailure() throws Exception {
+ verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback(
+ new IkeInternalException(new UnknownHostException()), VCN_ERROR_CODE_NETWORK_ERROR);
+ }
+
+ @Test
+ public void testIkeSessionClosedExceptionallyInternalFailure() throws Exception {
+ verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback(
+ new TemporaryFailureException("vcn test"), VCN_ERROR_CODE_INTERNAL_ERROR);
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
index d936183e5a0b..17ae19e086cf 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -61,6 +61,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
verify(mIkeSession).kill();
+ verifyDisconnectRequestAlarmAndGetCallback(false /* expectCanceled */);
}
@Test
@@ -73,6 +74,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
verify(mIkeSession).close();
verify(mIkeSession, never()).kill();
+ verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */);
}
@Test
@@ -92,6 +94,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
verify(mIkeSession).close();
+ verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */);
}
@Test
@@ -101,5 +104,11 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio
assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
verify(mIkeSession).close();
+ verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */);
+ }
+
+ @Test
+ public void testSafeModeTimeoutNotifiesCallback() {
+ verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mConnectingState);
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index 8643d8a2ea8a..9ea641f52e48 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -16,12 +16,18 @@
package com.android.server.vcn;
+import static android.net.IpSecManager.IpSecTunnelInterface;
+
+import static com.android.server.vcn.VcnGatewayConnection.DUMMY_ADDR;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
+import android.net.IpSecManager;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -37,7 +43,13 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
public void setUp() throws Exception {
super.setUp();
- mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectedState);
+ final IpSecTunnelInterface tunnelIface =
+ mContext.getSystemService(IpSecManager.class)
+ .createIpSecTunnelInterface(
+ DUMMY_ADDR, DUMMY_ADDR, TEST_UNDERLYING_NETWORK_RECORD_1.network);
+ mGatewayConnection.setTunnelInterface(tunnelIface);
+
+ // Don't need to transition to DisconnectedState because it is the starting state
mTestLooper.dispatchAll();
}
@@ -67,6 +79,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
mTestLooper.dispatchAll();
assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+ verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
}
@Test
@@ -77,6 +90,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
mTestLooper.dispatchAll();
assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ verifyDisconnectRequestAlarmAndGetCallback(false /* expectCanceled */);
}
@Test
@@ -86,5 +100,6 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
assertNull(mGatewayConnection.getCurrentState());
verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any());
+ verifySafeModeTimeoutAlarmAndGetCallback(true /* expectCanceled */);
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
index d0fec55a6827..7385204993c0 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
@@ -16,9 +16,9 @@
package com.android.server.vcn;
-import static com.android.server.vcn.VcnGatewayConnection.TEARDOWN_TIMEOUT_SECONDS;
import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import androidx.test.filters.SmallTest;
@@ -28,8 +28,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.concurrent.TimeUnit;
-
/** Tests for VcnGatewayConnection.DisconnectedState */
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -40,6 +38,9 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec
mGatewayConnection.setIkeSession(mGatewayConnection.buildIkeSession());
+ // ensure that mGatewayConnection has an underlying Network before entering
+ // DisconnectingState
+ mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_2);
mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectingState);
mTestLooper.dispatchAll();
}
@@ -49,12 +50,22 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec
getIkeSessionCallback().onClosed();
mTestLooper.dispatchAll();
- assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
+ verify(mMockIkeSession).close();
+ verify(mMockIkeSession, never()).kill();
+ verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */);
}
@Test
public void testTimeoutExpired() throws Exception {
- mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+ Runnable delayedEvent =
+ verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+
+ // Can't use mTestLooper to advance the time since VcnGatewayConnection uses WakeupMessages
+ // (which are mocked here). Directly invoke the runnable instead. This is still sufficient,
+ // since verifyTeardownTimeoutAlarmAndGetCallback() verifies the WakeupMessage was scheduled
+ // with the correct delay.
+ delayedEvent.run();
mTestLooper.dispatchAll();
verify(mMockIkeSession).kill();
@@ -67,5 +78,11 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec
// Should do nothing; already tearing down.
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+ }
+
+ @Test
+ public void testSafeModeTimeoutNotifiesCallback() {
+ verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mDisconnectingState);
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
new file mode 100644
index 000000000000..5b0850b03f1a
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for VcnGatewayConnection.RetryTimeoutState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnectionTestBase {
+ private long mFirstRetryInterval;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mFirstRetryInterval = mConfig.getRetryInterval()[0];
+
+ mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
+ mGatewayConnection.transitionTo(mGatewayConnection.mRetryTimeoutState);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testNewNetworkTriggerRetry() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+ verifyRetryTimeoutAlarmAndGetCallback(mFirstRetryInterval, true /* expectCanceled */);
+ }
+
+ @Test
+ public void testSameNetworkDoesNotTriggerRetry() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
+ verifyRetryTimeoutAlarmAndGetCallback(mFirstRetryInterval, false /* expectCanceled */);
+ }
+
+ @Test
+ public void testNullNetworkTriggersDisconnect() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(null);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ verifyRetryTimeoutAlarmAndGetCallback(mFirstRetryInterval, true /* expectCanceled */);
+ }
+
+ @Test
+ public void testTimeoutElapsingTriggersRetry() throws Exception {
+ final Runnable delayedEvent =
+ verifyRetryTimeoutAlarmAndGetCallback(
+ mFirstRetryInterval, false /* expectCanceled */);
+
+ // Can't use mTestLooper to advance the time since VcnGatewayConnection uses WakeupMessages
+ // (which are mocked here). Directly invoke the runnable instead. This is still sufficient,
+ // since verifyRetryTimeoutAlarmAndGetCallback() verifies the WakeupMessage was scheduled
+ // with the correct delay.
+ delayedEvent.run();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+ verifyRetryTimeoutAlarmAndGetCallback(mFirstRetryInterval, true /* expectCanceled */);
+ }
+
+ @Test
+ public void testSafeModeTimeoutNotifiesCallback() {
+ verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mRetryTimeoutState);
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index bc6bee28d14f..748c7924685d 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -132,10 +132,32 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
@Test
public void testSubscriptionSnapshotUpdateNotifiesUnderlyingNetworkTracker() {
+ verifyWakeLockSetUp();
+
final TelephonySubscriptionSnapshot updatedSnapshot =
mock(TelephonySubscriptionSnapshot.class);
mGatewayConnection.updateSubscriptionSnapshot(updatedSnapshot);
verify(mUnderlyingNetworkTracker).updateSubscriptionSnapshot(eq(updatedSnapshot));
+ verifyWakeLockAcquired();
+
+ mTestLooper.dispatchAll();
+
+ verifyWakeLockReleased();
+ }
+
+ @Test
+ public void testNonNullUnderlyingNetworkRecordUpdateCancelsAlarm() {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(null);
+
+ verifyDisconnectRequestAlarmAndGetCallback(false /* expectCanceled */);
+
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+
+ verify(mDisconnectRequestAlarm).cancel();
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 333b5b990dde..a660735470a4 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -20,17 +20,26 @@ import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkR
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
+import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.annotation.NonNull;
import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.InetAddresses;
import android.net.IpSecConfig;
import android.net.IpSecManager;
import android.net.IpSecTransform;
import android.net.IpSecTunnelInterfaceResponse;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -39,25 +48,38 @@ import android.net.ipsec.ike.IkeSessionCallback;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
+import android.os.PowerManager;
import android.os.test.TestLooper;
+import com.android.internal.util.State;
+import com.android.internal.util.WakeupMessage;
import com.android.server.IpSecService;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback;
+import com.android.server.vcn.VcnGatewayConnection.VcnWakeLock;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
+import java.net.InetAddress;
import java.util.Collections;
import java.util.UUID;
+import java.util.concurrent.TimeUnit;
public class VcnGatewayConnectionTestBase {
protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
+ protected static final InetAddress TEST_DNS_ADDR =
+ InetAddresses.parseNumericAddress("2001:DB8:0:1::");
+ protected static final LinkAddress TEST_INTERNAL_ADDR =
+ new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:0:2::"), 64);
+
protected static final int TEST_IPSEC_SPI_VALUE = 0x1234;
protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1;
protected static final int TEST_IPSEC_TRANSFORM_RESOURCE_ID = 2;
protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 3;
protected static final int TEST_SUB_ID = 5;
+ protected static final long ELAPSED_REAL_TIME = 123456789L;
protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
new UnderlyingNetworkRecord(
@@ -84,8 +106,14 @@ public class VcnGatewayConnectionTestBase {
@NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull protected final VcnGatewayConnection.Dependencies mDeps;
@NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
+ @NonNull protected final VcnWakeLock mWakeLock;
+ @NonNull protected final WakeupMessage mTeardownTimeoutAlarm;
+ @NonNull protected final WakeupMessage mDisconnectRequestAlarm;
+ @NonNull protected final WakeupMessage mRetryTimeoutAlarm;
+ @NonNull protected final WakeupMessage mSafeModeTimeoutAlarm;
@NonNull protected final IpSecService mIpSecSvc;
+ @NonNull protected final ConnectivityManager mConnMgr;
protected VcnIkeSession mMockIkeSession;
protected VcnGatewayConnection mGatewayConnection;
@@ -99,10 +127,19 @@ public class VcnGatewayConnectionTestBase {
mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class);
mDeps = mock(VcnGatewayConnection.Dependencies.class);
mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
+ mWakeLock = mock(VcnWakeLock.class);
+ mTeardownTimeoutAlarm = mock(WakeupMessage.class);
+ mDisconnectRequestAlarm = mock(WakeupMessage.class);
+ mRetryTimeoutAlarm = mock(WakeupMessage.class);
+ mSafeModeTimeoutAlarm = mock(WakeupMessage.class);
mIpSecSvc = mock(IpSecService.class);
setupIpSecManager(mContext, mIpSecSvc);
+ mConnMgr = mock(ConnectivityManager.class);
+ VcnTestUtils.setupSystemService(
+ mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+
doReturn(mContext).when(mVcnContext).getContext();
doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
@@ -110,6 +147,20 @@ public class VcnGatewayConnectionTestBase {
doReturn(mUnderlyingNetworkTracker)
.when(mDeps)
.newUnderlyingNetworkTracker(any(), any(), any(), any(), any());
+ doReturn(mWakeLock)
+ .when(mDeps)
+ .newWakeLock(eq(mContext), eq(PowerManager.PARTIAL_WAKE_LOCK), any());
+
+ setUpWakeupMessage(mTeardownTimeoutAlarm, VcnGatewayConnection.TEARDOWN_TIMEOUT_ALARM);
+ setUpWakeupMessage(mDisconnectRequestAlarm, VcnGatewayConnection.DISCONNECT_REQUEST_ALARM);
+ setUpWakeupMessage(mRetryTimeoutAlarm, VcnGatewayConnection.RETRY_TIMEOUT_ALARM);
+ setUpWakeupMessage(mSafeModeTimeoutAlarm, VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM);
+
+ doReturn(ELAPSED_REAL_TIME).when(mDeps).getElapsedRealTime();
+ }
+
+ private void setUpWakeupMessage(@NonNull WakeupMessage msg, @NonNull String cmdName) {
+ doReturn(msg).when(mDeps).newWakeupMessage(eq(mVcnContext), any(), eq(cmdName), any());
}
@Before
@@ -145,10 +196,86 @@ public class VcnGatewayConnectionTestBase {
return captor.getValue();
}
- protected ChildSessionCallback getChildSessionCallback() {
+ protected VcnChildSessionCallback getChildSessionCallback() {
ArgumentCaptor<ChildSessionCallback> captor =
ArgumentCaptor.forClass(ChildSessionCallback.class);
verify(mDeps).newIkeSession(any(), any(), any(), any(), captor.capture());
- return captor.getValue();
+ return (VcnChildSessionCallback) captor.getValue();
+ }
+
+ protected void verifyWakeLockSetUp() {
+ verify(mDeps).newWakeLock(eq(mContext), eq(PowerManager.PARTIAL_WAKE_LOCK), any());
+ verifyNoMoreInteractions(mWakeLock);
+ }
+
+ protected void verifyWakeLockAcquired() {
+ verify(mWakeLock).acquire();
+ verifyNoMoreInteractions(mWakeLock);
+ }
+
+ protected void verifyWakeLockReleased() {
+ verify(mWakeLock).release();
+ verifyNoMoreInteractions(mWakeLock);
+ }
+
+ private Runnable verifyWakeupMessageSetUpAndGetCallback(
+ @NonNull String tag,
+ @NonNull WakeupMessage msg,
+ long delayInMillis,
+ boolean expectCanceled) {
+ ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mDeps).newWakeupMessage(eq(mVcnContext), any(), eq(tag), runnableCaptor.capture());
+
+ verify(mDeps, atLeastOnce()).getElapsedRealTime();
+ verify(msg).schedule(ELAPSED_REAL_TIME + delayInMillis);
+ verify(msg, expectCanceled ? times(1) : never()).cancel();
+
+ return runnableCaptor.getValue();
+ }
+
+ protected Runnable verifyTeardownTimeoutAlarmAndGetCallback(boolean expectCanceled) {
+ return verifyWakeupMessageSetUpAndGetCallback(
+ VcnGatewayConnection.TEARDOWN_TIMEOUT_ALARM,
+ mTeardownTimeoutAlarm,
+ TimeUnit.SECONDS.toMillis(VcnGatewayConnection.TEARDOWN_TIMEOUT_SECONDS),
+ expectCanceled);
+ }
+
+ protected Runnable verifyDisconnectRequestAlarmAndGetCallback(boolean expectCanceled) {
+ return verifyWakeupMessageSetUpAndGetCallback(
+ VcnGatewayConnection.DISCONNECT_REQUEST_ALARM,
+ mDisconnectRequestAlarm,
+ TimeUnit.SECONDS.toMillis(
+ VcnGatewayConnection.NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS),
+ expectCanceled);
+ }
+
+ protected Runnable verifyRetryTimeoutAlarmAndGetCallback(
+ long delayInMillis, boolean expectCanceled) {
+ return verifyWakeupMessageSetUpAndGetCallback(
+ VcnGatewayConnection.RETRY_TIMEOUT_ALARM,
+ mRetryTimeoutAlarm,
+ delayInMillis,
+ expectCanceled);
+ }
+
+ protected Runnable verifySafeModeTimeoutAlarmAndGetCallback(boolean expectCanceled) {
+ return verifyWakeupMessageSetUpAndGetCallback(
+ VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM,
+ mSafeModeTimeoutAlarm,
+ TimeUnit.SECONDS.toMillis(VcnGatewayConnection.SAFEMODE_TIMEOUT_SECONDS),
+ expectCanceled);
+ }
+
+ protected void verifySafeModeTimeoutNotifiesCallback(@NonNull State expectedState) {
+ // SafeMode timer starts when VcnGatewayConnection exits DisconnectedState (the initial
+ // state)
+ final Runnable delayedEvent =
+ verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+ delayedEvent.run();
+ mTestLooper.dispatchAll();
+
+ verify(mGatewayStatusCallback).onEnteredSafeMode();
+ assertEquals(expectedState, mGatewayConnection.getCurrentState());
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index 66cbf84619ab..9d3368271243 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -34,7 +34,7 @@ import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
-import com.android.server.VcnManagementService.VcnSafemodeCallback;
+import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
@@ -56,7 +56,7 @@ public class VcnTest {
private VcnContext mVcnContext;
private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
private VcnNetworkProvider mVcnNetworkProvider;
- private VcnSafemodeCallback mVcnSafemodeCallback;
+ private VcnCallback mVcnCallback;
private Vcn.Dependencies mDeps;
private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor;
@@ -72,7 +72,7 @@ public class VcnTest {
mVcnContext = mock(VcnContext.class);
mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
- mVcnSafemodeCallback = mock(VcnSafemodeCallback.class);
+ mVcnCallback = mock(VcnCallback.class);
mDeps = mock(Vcn.Dependencies.class);
mTestLooper = new TestLooper();
@@ -104,7 +104,7 @@ public class VcnTest {
TEST_SUB_GROUP,
mConfig,
mSubscriptionSnapshot,
- mVcnSafemodeCallback,
+ mVcnCallback,
mDeps);
}
@@ -148,7 +148,7 @@ public class VcnTest {
}
@Test
- public void testGatewayEnteringSafemodeNotifiesVcn() {
+ public void testGatewayEnteringSafeModeNotifiesVcn() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
startVcnGatewayWithCapabilities(requestListener, capability);
@@ -168,16 +168,17 @@ public class VcnTest {
any(),
mGatewayStatusCallbackCaptor.capture());
- // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down
+ // Doesn't matter which callback this gets - any Gateway entering safe mode should shut down
// all Gateways
final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
- statusCallback.onEnteredSafemode();
+ statusCallback.onEnteredSafeMode();
mTestLooper.dispatchAll();
+ assertFalse(mVcn.isActive());
for (final VcnGatewayConnection gatewayConnection : gatewayConnections) {
verify(gatewayConnection).teardownAsynchronously();
}
verify(mVcnNetworkProvider).unregisterListener(requestListener);
- verify(mVcnSafemodeCallback).onEnteredSafemode();
+ verify(mVcnCallback).onEnteredSafeMode();
}
}
diff --git a/tools/aapt/Android.bp b/tools/aapt/Android.bp
index a594e5bf0ce1..c75ba71c4432 100644
--- a/tools/aapt/Android.bp
+++ b/tools/aapt/Android.bp
@@ -19,6 +19,23 @@
// targets here.
// ==========================================================
+package {
+ default_applicable_licenses: ["frameworks_base_tools_aapt_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_base_tools_aapt_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "NOTICE",
+ ],
+}
+
cc_defaults {
name: "aapt_defaults",
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 46ae2ecd9406..df727e0f883e 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
toolSources = [
"cmd/Command.cpp",
"cmd/Compile.cpp",
diff --git a/tools/aapt2/integration-tests/AutoVersionTest/Android.bp b/tools/aapt2/integration-tests/AutoVersionTest/Android.bp
index 79fb5734cd68..bfd35083366e 100644
--- a/tools/aapt2/integration-tests/AutoVersionTest/Android.bp
+++ b/tools/aapt2/integration-tests/AutoVersionTest/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AaptAutoVersionTest",
sdk_version: "current",
diff --git a/tools/aapt2/integration-tests/BasicTest/Android.bp b/tools/aapt2/integration-tests/BasicTest/Android.bp
index a94a01f12c9e..7db9d2698cc7 100644
--- a/tools/aapt2/integration-tests/BasicTest/Android.bp
+++ b/tools/aapt2/integration-tests/BasicTest/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AaptBasicTest",
sdk_version: "current",
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk
index 7bf8cf84426c..c084849a9d28 100644
--- a/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk
@@ -20,9 +20,12 @@ include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_AAPT_NAMESPACES := true
LOCAL_MODULE := AaptTestMergeOnly_LeafLib
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_MIN_SDK_VERSION := 21
LOCAL_AAPT_FLAGS := --merge-only
-include $(BUILD_STATIC_JAVA_LIBRARY) \ No newline at end of file
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk
index ba781c56a913..699ad79ecf1a 100644
--- a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk
@@ -20,9 +20,12 @@ include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_AAPT_NAMESPACES := true
LOCAL_MODULE := AaptTestMergeOnly_LocalLib
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_MIN_SDK_VERSION := 21
LOCAL_AAPT_FLAGS := --merge-only
-include $(BUILD_STATIC_JAVA_LIBRARY) \ No newline at end of file
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk
index c723d905db5b..dd4170234258 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk
@@ -20,6 +20,9 @@ include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_AAPT_NAMESPACES := true
LOCAL_MODULE := AaptTestNamespace_LibOne
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk
index 90a7f627e591..0d11bcbda64d 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk
+++ b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk
@@ -20,6 +20,9 @@ include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_AAPT_NAMESPACES := true
LOCAL_MODULE := AaptTestNamespace_LibTwo
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
diff --git a/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp
index 9aadff3d619e..80404eeb8d8e 100644
--- a/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AaptTestStaticLib_App",
diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp
index 4c8181343a33..a84da43c70c8 100644
--- a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "AaptTestStaticLib_LibOne",
sdk_version: "current",
diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp
index 7c4f7ed90e69..d386c3a35d20 100644
--- a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_library {
name: "AaptTestStaticLib_LibTwo",
sdk_version: "current",
diff --git a/tools/aapt2/integration-tests/SymlinkTest/Android.bp b/tools/aapt2/integration-tests/SymlinkTest/Android.bp
index 68e6148e480c..1e8cf86ed811 100644
--- a/tools/aapt2/integration-tests/SymlinkTest/Android.bp
+++ b/tools/aapt2/integration-tests/SymlinkTest/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "AaptSymlinkTest",
sdk_version: "current",
diff --git a/tools/bit/Android.bp b/tools/bit/Android.bp
index a8062719d586..f6aa0fb8f3f0 100644
--- a/tools/bit/Android.bp
+++ b/tools/bit/Android.bp
@@ -17,6 +17,15 @@
// ==========================================================
// Build the host executable: bit
// ==========================================================
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_binary_host {
name: "bit",
diff --git a/tools/codegen/Android.bp b/tools/codegen/Android.bp
index 677bee2cce81..e53ba3e18a86 100644
--- a/tools/codegen/Android.bp
+++ b/tools/codegen/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_binary_host {
name: "codegen_cli",
manifest: "manifest.txt",
diff --git a/tools/dump-coverage/Android.bp b/tools/dump-coverage/Android.bp
index 94356ebac035..f38177390972 100644
--- a/tools/dump-coverage/Android.bp
+++ b/tools/dump-coverage/Android.bp
@@ -15,6 +15,15 @@
//
// Build variants {target,host} x {32,64}
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library {
name: "libdumpcoverage",
srcs: ["dump_coverage.cc"],
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
deleted file mode 100755
index 28ff606d0381..000000000000
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ /dev/null
@@ -1,383 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-"""Generate API lists for non-SDK API enforcement."""
-import argparse
-from collections import defaultdict, namedtuple
-import functools
-import os
-import re
-import sys
-
-# Names of flags recognized by the `hiddenapi` tool.
-FLAG_SDK = 'sdk'
-FLAG_UNSUPPORTED = 'unsupported'
-FLAG_BLOCKED = 'blocked'
-FLAG_MAX_TARGET_O = 'max-target-o'
-FLAG_MAX_TARGET_P = 'max-target-p'
-FLAG_MAX_TARGET_Q = 'max-target-q'
-FLAG_MAX_TARGET_R = 'max-target-r'
-FLAG_CORE_PLATFORM_API = 'core-platform-api'
-FLAG_PUBLIC_API = 'public-api'
-FLAG_SYSTEM_API = 'system-api'
-FLAG_TEST_API = 'test-api'
-
-# List of all known flags.
-FLAGS_API_LIST = [
- FLAG_SDK,
- FLAG_UNSUPPORTED,
- FLAG_BLOCKED,
- FLAG_MAX_TARGET_O,
- FLAG_MAX_TARGET_P,
- FLAG_MAX_TARGET_Q,
- FLAG_MAX_TARGET_R,
-]
-ALL_FLAGS = FLAGS_API_LIST + [
- FLAG_CORE_PLATFORM_API,
- FLAG_PUBLIC_API,
- FLAG_SYSTEM_API,
- FLAG_TEST_API,
-]
-
-FLAGS_API_LIST_SET = set(FLAGS_API_LIST)
-ALL_FLAGS_SET = set(ALL_FLAGS)
-
-# Option specified after one of FLAGS_API_LIST to indicate that
-# only known and otherwise unassigned entries should be assign the
-# given flag.
-# For example, the max-target-P list is checked in as it was in P,
-# but signatures have changes since then. The flag instructs this
-# script to skip any entries which do not exist any more.
-FLAG_IGNORE_CONFLICTS = "ignore-conflicts"
-
-# Option specified after one of FLAGS_API_LIST to express that all
-# apis within a given set of packages should be assign the given flag.
-FLAG_PACKAGES = "packages"
-
-# Option specified after one of FLAGS_API_LIST to indicate an extra
-# tag that should be added to the matching APIs.
-FLAG_TAG = "tag"
-
-# Regex patterns of fields/methods used in serialization. These are
-# considered public API despite being hidden.
-SERIALIZATION_PATTERNS = [
- r'readObject\(Ljava/io/ObjectInputStream;\)V',
- r'readObjectNoData\(\)V',
- r'readResolve\(\)Ljava/lang/Object;',
- r'serialVersionUID:J',
- r'serialPersistentFields:\[Ljava/io/ObjectStreamField;',
- r'writeObject\(Ljava/io/ObjectOutputStream;\)V',
- r'writeReplace\(\)Ljava/lang/Object;',
-]
-
-# Single regex used to match serialization API. It combines all the
-# SERIALIZATION_PATTERNS into a single regular expression.
-SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$')
-
-# Predicates to be used with filter_apis.
-HAS_NO_API_LIST_ASSIGNED = lambda api, flags: not FLAGS_API_LIST_SET.intersection(flags)
-IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
-
-
-class StoreOrderedOptions(argparse.Action):
- """An argparse action that stores a number of option arguments in the order that
- they were specified.
- """
- def __call__(self, parser, args, values, option_string = None):
- items = getattr(args, self.dest, None)
- if items is None:
- items = []
- items.append([option_string.lstrip('-'), values])
- setattr(args, self.dest, items)
-
-def get_args():
- """Parses command line arguments.
-
- Returns:
- Namespace: dictionary of parsed arguments
- """
- parser = argparse.ArgumentParser()
- parser.add_argument('--output', required=True)
- parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE',
- help='CSV files to be merged into output')
-
- for flag in ALL_FLAGS:
- parser.add_argument('--' + flag, dest='ordered_flags', metavar='TXT_FILE',
- action=StoreOrderedOptions, help='lists of entries with flag "' + flag + '"')
- parser.add_argument('--' + FLAG_IGNORE_CONFLICTS, dest='ordered_flags', nargs=0,
- action=StoreOrderedOptions, help='Indicates that only known and otherwise unassigned '
- 'entries should be assign the given flag. Must follow a list of entries and applies '
- 'to the preceding such list.')
- parser.add_argument('--' + FLAG_PACKAGES, dest='ordered_flags', nargs=0,
- action=StoreOrderedOptions, help='Indicates that the previous list of entries '
- 'is a list of packages. All members in those packages will be given the flag. '
- 'Must follow a list of entries and applies to the preceding such list.')
- parser.add_argument('--' + FLAG_TAG, dest='ordered_flags', nargs=1,
- action=StoreOrderedOptions, help='Adds an extra tag to the previous list of entries. '
- 'Must follow a list of entries and applies to the preceding such list.')
-
- return parser.parse_args()
-
-
-def read_lines(filename):
- """Reads entire file and return it as a list of lines.
-
- Lines which begin with a hash are ignored.
-
- Args:
- filename (string): Path to the file to read from.
-
- Returns:
- Lines of the file as a list of string.
- """
- with open(filename, 'r') as f:
- lines = f.readlines();
- lines = filter(lambda line: not line.startswith('#'), lines)
- lines = map(lambda line: line.strip(), lines)
- return set(lines)
-
-
-def write_lines(filename, lines):
- """Writes list of lines into a file, overwriting the file if it exists.
-
- Args:
- filename (string): Path to the file to be writting into.
- lines (list): List of strings to write into the file.
- """
- lines = map(lambda line: line + '\n', lines)
- with open(filename, 'w') as f:
- f.writelines(lines)
-
-
-def extract_package(signature):
- """Extracts the package from a signature.
-
- Args:
- signature (string): JNI signature of a method or field.
-
- Returns:
- The package name of the class containing the field/method.
- """
- full_class_name = signature.split(";->")[0]
- # Example: Landroid/hardware/radio/V1_2/IRadio$Proxy
- if (full_class_name[0] != "L"):
- raise ValueError("Expected to start with 'L': %s" % full_class_name)
- full_class_name = full_class_name[1:]
- # If full_class_name doesn't contain '/', then package_name will be ''.
- package_name = full_class_name.rpartition("/")[0]
- return package_name.replace('/', '.')
-
-
-class FlagsDict:
- def __init__(self):
- self._dict_keyset = set()
- self._dict = defaultdict(set)
-
- def _check_entries_set(self, keys_subset, source):
- assert isinstance(keys_subset, set)
- assert keys_subset.issubset(self._dict_keyset), (
- "Error: {} specifies signatures not present in code:\n"
- "{}"
- "Please visit go/hiddenapi for more information.").format(
- source, "".join(map(lambda x: " " + str(x) + "\n", keys_subset - self._dict_keyset)))
-
- def _check_flags_set(self, flags_subset, source):
- assert isinstance(flags_subset, set)
- assert flags_subset.issubset(ALL_FLAGS_SET), (
- "Error processing: {}\n"
- "The following flags were not recognized: \n"
- "{}\n"
- "Please visit go/hiddenapi for more information.").format(
- source, "\n".join(flags_subset - ALL_FLAGS_SET))
-
- def filter_apis(self, filter_fn):
- """Returns APIs which match a given predicate.
-
- This is a helper function which allows to filter on both signatures (keys) and
- flags (values). The built-in filter() invokes the lambda only with dict's keys.
-
- Args:
- filter_fn : Function which takes two arguments (signature/flags) and returns a boolean.
-
- Returns:
- A set of APIs which match the predicate.
- """
- return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset))
-
- def get_valid_subset_of_unassigned_apis(self, api_subset):
- """Sanitizes a key set input to only include keys which exist in the dictionary
- and have not been assigned any API list flags.
-
- Args:
- entries_subset (set/list): Key set to be sanitized.
-
- Returns:
- Sanitized key set.
- """
- assert isinstance(api_subset, set)
- return api_subset.intersection(self.filter_apis(HAS_NO_API_LIST_ASSIGNED))
-
- def generate_csv(self):
- """Constructs CSV entries from a dictionary.
-
- Old versions of flags are used to generate the file.
-
- Returns:
- List of lines comprising a CSV file. See "parse_and_merge_csv" for format description.
- """
- lines = []
- for api in self._dict:
- flags = sorted(self._dict[api])
- lines.append(",".join([api] + flags))
- return sorted(lines)
-
- def parse_and_merge_csv(self, csv_lines, source = "<unknown>"):
- """Parses CSV entries and merges them into a given dictionary.
-
- The expected CSV format is:
- <api signature>,<flag1>,<flag2>,...,<flagN>
-
- Args:
- csv_lines (list of strings): Lines read from a CSV file.
- source (string): Origin of `csv_lines`. Will be printed in error messages.
-
- Throws:
- AssertionError if parsed flags are invalid.
- """
- # Split CSV lines into arrays of values.
- csv_values = [ line.split(',') for line in csv_lines ]
-
- # Update the full set of API signatures.
- self._dict_keyset.update([ csv[0] for csv in csv_values ])
-
- # Check that all flags are known.
- csv_flags = set()
- for csv in csv_values:
- csv_flags.update(csv[1:])
- self._check_flags_set(csv_flags, source)
-
- # Iterate over all CSV lines, find entry in dict and append flags to it.
- for csv in csv_values:
- flags = csv[1:]
- if (FLAG_PUBLIC_API in flags) or (FLAG_SYSTEM_API in flags):
- flags.append(FLAG_SDK)
- self._dict[csv[0]].update(flags)
-
- def assign_flag(self, flag, apis, source="<unknown>", tag = None):
- """Assigns a flag to given subset of entries.
-
- Args:
- flag (string): One of ALL_FLAGS.
- apis (set): Subset of APIs to receive the flag.
- source (string): Origin of `entries_subset`. Will be printed in error messages.
-
- Throws:
- AssertionError if parsed API signatures of flags are invalid.
- """
- # Check that all APIs exist in the dict.
- self._check_entries_set(apis, source)
-
- # Check that the flag is known.
- self._check_flags_set(set([ flag ]), source)
-
- # Iterate over the API subset, find each entry in dict and assign the flag to it.
- for api in apis:
- self._dict[api].add(flag)
- if tag:
- self._dict[api].add(tag)
-
-
-FlagFile = namedtuple('FlagFile', ('flag', 'file', 'ignore_conflicts', 'packages', 'tag'))
-
-def parse_ordered_flags(ordered_flags):
- r = []
- currentflag, file, ignore_conflicts, packages, tag = None, None, False, False, None
- for flag_value in ordered_flags:
- flag, value = flag_value[0], flag_value[1]
- if flag in ALL_FLAGS_SET:
- if currentflag:
- r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
- ignore_conflicts, packages, tag = False, False, None
- currentflag = flag
- file = value
- else:
- if currentflag is None:
- raise argparse.ArgumentError('--%s is only allowed after one of %s' % (
- flag, ' '.join(['--%s' % f for f in ALL_FLAGS_SET])))
- if flag == FLAG_IGNORE_CONFLICTS:
- ignore_conflicts = True
- elif flag == FLAG_PACKAGES:
- packages = True
- elif flag == FLAG_TAG:
- tag = value[0]
-
-
- if currentflag:
- r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
- return r
-
-
-def main(argv):
- # Parse arguments.
- args = vars(get_args())
- flagfiles = parse_ordered_flags(args['ordered_flags'])
-
- # Initialize API->flags dictionary.
- flags = FlagsDict()
-
- # Merge input CSV files into the dictionary.
- # Do this first because CSV files produced by parsing API stubs will
- # contain the full set of APIs. Subsequent additions from text files
- # will be able to detect invalid entries, and/or filter all as-yet
- # unassigned entries.
- for filename in args["csv"]:
- flags.parse_and_merge_csv(read_lines(filename), filename)
-
- # Combine inputs which do not require any particular order.
- # (1) Assign serialization API to SDK.
- flags.assign_flag(FLAG_SDK, flags.filter_apis(IS_SERIALIZATION))
-
- # (2) Merge text files with a known flag into the dictionary.
- for info in flagfiles:
- if (not info.ignore_conflicts) and (not info.packages):
- flags.assign_flag(info.flag, read_lines(info.file), info.file, info.tag)
-
- # Merge text files where conflicts should be ignored.
- # This will only assign the given flag if:
- # (a) the entry exists, and
- # (b) it has not been assigned any other flag.
- # Because of (b), this must run after all strict assignments have been performed.
- for info in flagfiles:
- if info.ignore_conflicts:
- valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(info.file))
- flags.assign_flag(info.flag, valid_entries, filename, info.tag)
-
- # All members in the specified packages will be assigned the appropriate flag.
- for info in flagfiles:
- if info.packages:
- packages_needing_list = set(read_lines(info.file))
- should_add_signature_to_list = lambda sig,lists: extract_package(
- sig) in packages_needing_list and not lists
- valid_entries = flags.filter_apis(should_add_signature_to_list)
- flags.assign_flag(info.flag, valid_entries, info.file, info.tag)
-
- # Mark all remaining entries as blocked.
- flags.assign_flag(FLAG_BLOCKED, flags.filter_apis(HAS_NO_API_LIST_ASSIGNED))
-
- # Write output.
- write_lines(args["output"], flags.generate_csv())
-
-if __name__ == "__main__":
- main(sys.argv)
diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py
deleted file mode 100755
index 82d117fbbbab..000000000000
--- a/tools/hiddenapi/generate_hiddenapi_lists_test.py
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-"""Unit tests for Hidden API list generation."""
-import unittest
-from generate_hiddenapi_lists import *
-
-class TestHiddenapiListGeneration(unittest.TestCase):
-
- def test_filter_apis(self):
- # Initialize flags so that A and B are put on the whitelist and
- # C, D, E are left unassigned. Try filtering for the unassigned ones.
- flags = FlagsDict()
- flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B,' + FLAG_SDK,
- 'C', 'D', 'E'])
- filter_set = flags.filter_apis(lambda api, flags: not flags)
- self.assertTrue(isinstance(filter_set, set))
- self.assertEqual(filter_set, set([ 'C', 'D', 'E' ]))
-
- def test_get_valid_subset_of_unassigned_keys(self):
- # Create flags where only A is unassigned.
- flags = FlagsDict()
- flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B', 'C'])
- flags.assign_flag(FLAG_UNSUPPORTED, set(['C']))
- self.assertEqual(flags.generate_csv(),
- [ 'A,' + FLAG_SDK, 'B', 'C,' + FLAG_UNSUPPORTED ])
-
- # Check three things:
- # (1) B is selected as valid unassigned
- # (2) A is not selected because it is assigned 'whitelist'
- # (3) D is not selected because it is not a valid key
- self.assertEqual(
- flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ]))
-
- def test_parse_and_merge_csv(self):
- flags = FlagsDict()
-
- # Test empty CSV entry.
- self.assertEqual(flags.generate_csv(), [])
-
- # Test new additions.
- flags.parse_and_merge_csv([
- 'A,' + FLAG_UNSUPPORTED,
- 'B,' + FLAG_BLOCKED + ',' + FLAG_MAX_TARGET_O,
- 'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
- 'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
- 'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
- ])
- self.assertEqual(flags.generate_csv(), [
- 'A,' + FLAG_UNSUPPORTED,
- 'B,' + FLAG_BLOCKED + "," + FLAG_MAX_TARGET_O,
- 'C,' + FLAG_SYSTEM_API + ',' + FLAG_SDK,
- 'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
- 'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
- ])
-
- # Test unknown flag.
- with self.assertRaises(AssertionError):
- flags.parse_and_merge_csv([ 'Z,foo' ])
-
- def test_assign_flag(self):
- flags = FlagsDict()
- flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B'])
-
- # Test new additions.
- flags.assign_flag(FLAG_UNSUPPORTED, set([ 'A', 'B' ]))
- self.assertEqual(flags.generate_csv(),
- [ 'A,' + FLAG_UNSUPPORTED + "," + FLAG_SDK, 'B,' + FLAG_UNSUPPORTED ])
-
- # Test invalid API signature.
- with self.assertRaises(AssertionError):
- flags.assign_flag(FLAG_SDK, set([ 'C' ]))
-
- # Test invalid flag.
- with self.assertRaises(AssertionError):
- flags.assign_flag('foo', set([ 'A' ]))
-
- def test_extract_package(self):
- signature = 'Lcom/foo/bar/Baz;->method1()Lcom/bar/Baz;'
- expected_package = 'com.foo.bar'
- self.assertEqual(extract_package(signature), expected_package)
-
- signature = 'Lcom/foo1/bar/MyClass;->method2()V'
- expected_package = 'com.foo1.bar'
- self.assertEqual(extract_package(signature), expected_package)
-
- signature = 'Lcom/foo_bar/baz/MyClass;->method3()V'
- expected_package = 'com.foo_bar.baz'
- self.assertEqual(extract_package(signature), expected_package)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py
deleted file mode 100755
index 6a5b0e1347b2..000000000000
--- a/tools/hiddenapi/merge_csv.py
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-"""
-Merge multiple CSV files, possibly with different columns.
-"""
-
-import argparse
-import csv
-import io
-
-from zipfile import ZipFile
-
-args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.')
-args_parser.add_argument('--header', help='Comma separated field names; '
- 'if missing determines the header from input files.')
-args_parser.add_argument('--zip_input', help='ZIP archive with all CSV files to merge.')
-args_parser.add_argument('--output', help='Output file for merged CSV.',
- default='-', type=argparse.FileType('w'))
-args_parser.add_argument('files', nargs=argparse.REMAINDER)
-args = args_parser.parse_args()
-
-
-def dict_reader(input):
- return csv.DictReader(input, delimiter=',', quotechar='|')
-
-
-if args.zip_input and len(args.files) > 0:
- raise ValueError('Expecting either a single ZIP with CSV files'
- ' or a list of CSV files as input; not both.')
-
-csv_readers = []
-if len(args.files) > 0:
- for file in args.files:
- csv_readers.append(dict_reader(open(file, 'r')))
-elif args.zip_input:
- with ZipFile(args.zip_input) as zip:
- for entry in zip.namelist():
- if entry.endswith('.uau'):
- csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
-
-headers = set()
-if args.header:
- fieldnames = args.header.split(',')
-else:
- # Build union of all columns from source files:
- for reader in csv_readers:
- headers = headers.union(reader.fieldnames)
- fieldnames = sorted(headers)
-
-# Concatenate all files to output:
-writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
- dialect='unix', fieldnames=fieldnames)
-writer.writeheader()
-for reader in csv_readers:
- for row in reader:
- writer.writerow(row)
diff --git a/tools/incident_report/Android.bp b/tools/incident_report/Android.bp
index f2d0d0f3e553..fe519c7edff4 100644
--- a/tools/incident_report/Android.bp
+++ b/tools/incident_report/Android.bp
@@ -17,6 +17,15 @@
// ==========================================================
// Build the host executable: incident_report
// ==========================================================
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_binary_host {
name: "incident_report",
diff --git a/tools/incident_section_gen/Android.bp b/tools/incident_section_gen/Android.bp
index 0c7797eacf09..8227b60d6c09 100644
--- a/tools/incident_section_gen/Android.bp
+++ b/tools/incident_section_gen/Android.bp
@@ -17,6 +17,15 @@
// ==========================================================
// Build the host executable: incident-section-gen
// ==========================================================
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_binary_host {
name: "incident-section-gen",
cflags: [
diff --git a/tools/lock_agent/Android.bp b/tools/lock_agent/Android.bp
index 7b2ca9a65242..cabe1398903e 100644
--- a/tools/lock_agent/Android.bp
+++ b/tools/lock_agent/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_library {
name: "liblockagent",
host_supported: false,
diff --git a/tools/locked_region_code_injection/Android.bp b/tools/locked_region_code_injection/Android.bp
index 5f81a2eeb130..98c0e69cbf8e 100644
--- a/tools/locked_region_code_injection/Android.bp
+++ b/tools/locked_region_code_injection/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_binary_host {
name: "lockedregioncodeinjection",
manifest: "manifest.txt",
diff --git a/tools/obbtool/Android.bp b/tools/obbtool/Android.bp
index f87965860ce1..1c50d18ed32b 100644
--- a/tools/obbtool/Android.bp
+++ b/tools/obbtool/Android.bp
@@ -4,6 +4,15 @@
// Opaque Binary Blob (OBB) Tool
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_binary_host {
name: "obbtool",
diff --git a/tools/powermodel/Android.bp b/tools/powermodel/Android.bp
index f597aab0f464..35c135614027 100644
--- a/tools/powermodel/Android.bp
+++ b/tools/powermodel/Android.bp
@@ -1,4 +1,13 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library_host {
name: "powermodel",
srcs: [
@@ -23,4 +32,3 @@ java_test_host {
"mockito",
],
}
-
diff --git a/tools/preload-check/Android.bp b/tools/preload-check/Android.bp
index aaa6d76b39e3..73caac694cb4 100644
--- a/tools/preload-check/Android.bp
+++ b/tools/preload-check/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test_host {
name: "PreloadCheck",
srcs: ["src/**/*.java"],
diff --git a/tools/preload-check/device/Android.bp b/tools/preload-check/device/Android.bp
index f40d8ba5a287..2a866c426336 100644
--- a/tools/preload-check/device/Android.bp
+++ b/tools/preload-check/device/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test_helper_library {
name: "preload-check-device",
host_supported: false,
diff --git a/tools/preload/Android.bp b/tools/preload/Android.bp
index 809ee474969a..fad015ae35e0 100644
--- a/tools/preload/Android.bp
+++ b/tools/preload/Android.bp
@@ -1,3 +1,13 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-MIT
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library_host {
name: "preload",
srcs: [
diff --git a/tools/preload/loadclass/Android.bp b/tools/preload/loadclass/Android.bp
index 6f12015dae2b..ba36061e5fb6 100644
--- a/tools/preload/loadclass/Android.bp
+++ b/tools/preload/loadclass/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_test {
name: "loadclass",
srcs: ["**/*.java"],
diff --git a/tools/processors/staledataclass/Android.bp b/tools/processors/staledataclass/Android.bp
index 58a7d346ce1f..1e5097662a0a 100644
--- a/tools/processors/staledataclass/Android.bp
+++ b/tools/processors/staledataclass/Android.bp
@@ -1,4 +1,13 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_plugin {
name: "staledataclass-annotation-processor",
processor_class: "android.processor.staledataclass.StaleDataclassProcessor",
diff --git a/tools/processors/view_inspector/Android.bp b/tools/processors/view_inspector/Android.bp
index 069e61f5b921..ea9974f06a64 100644
--- a/tools/processors/view_inspector/Android.bp
+++ b/tools/processors/view_inspector/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_plugin {
name: "view-inspector-annotation-processor",
diff --git a/tools/protologtool/Android.bp b/tools/protologtool/Android.bp
index 0be80d31990a..4342d4eef8ee 100644
--- a/tools/protologtool/Android.bp
+++ b/tools/protologtool/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_library_host {
name: "protologtool-lib",
srcs: [
diff --git a/tools/sdkparcelables/Android.bp b/tools/sdkparcelables/Android.bp
index 00fb8aa20b18..9d773e44faea 100644
--- a/tools/sdkparcelables/Android.bp
+++ b/tools/sdkparcelables/Android.bp
@@ -1,3 +1,12 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
java_binary_host {
name: "sdkparcelables",
manifest: "manifest.txt",
diff --git a/tools/split-select/Android.bp b/tools/split-select/Android.bp
index ee822b7d7fa7..c12fc6a7f253 100644
--- a/tools/split-select/Android.bp
+++ b/tools/split-select/Android.bp
@@ -19,6 +19,15 @@
// targets here.
// ==========================================================
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_defaults {
name: "split-select_defaults",
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 1390f63248f9..1ec83a360048 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -17,6 +17,15 @@
// ==========================================================
// Build the host executable: protoc-gen-javastream
// ==========================================================
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_defaults {
name: "protoc-gen-stream-defaults",
srcs: [
diff --git a/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp
index 15b8b4105713..0423b7abd685 100644
--- a/tools/validatekeymaps/Android.bp
+++ b/tools/validatekeymaps/Android.bp
@@ -4,6 +4,15 @@
// Keymap validation tool.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
cc_binary_host {
name: "validatekeymaps",
diff --git a/wifi/java/Android.bp b/wifi/java/Android.bp
index b35b4be55818..225e750923fd 100644
--- a/wifi/java/Android.bp
+++ b/wifi/java/Android.bp
@@ -16,6 +16,15 @@
// updatable), and are generally only called by the Wifi module.
// necessary since we only want the `path` property to only refer to these files
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
filegroup {
name: "framework-wifi-non-updatable-sources-internal",
srcs: ["src/**/*.java"],
diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp
index 3f5cacff017f..c9105f7454e4 100644
--- a/wifi/tests/Android.bp
+++ b/wifi/tests/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
android_test {
name: "FrameworksWifiNonUpdatableApiTests",